Merge remote-tracking branch 'origin/dev' into release-candidate
54
.github/actions/submit_sdk/action.yml
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
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
|
||||
required: true
|
||||
catalog-token:
|
||||
description: The token to use to authenticate with the Catalog API
|
||||
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
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Submit SDK
|
||||
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-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-token }}' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"name\": \"${{ inputs.firmware-version }}\", \"target\": \"${{ inputs.firmware-target }}\", \"api\": \"${{ inputs.firmware-api }}\"}\"
|
||||
fi
|
||||
183
.github/workflows/build.yml
vendored
@ -9,13 +9,16 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TARGETS: f7 f18
|
||||
DEFAULT_TARGET: f7
|
||||
FBT_TOOLCHAIN_PATH: /runner/_work
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: [self-hosted,FlipperZeroShell]
|
||||
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 {} \;
|
||||
@ -33,69 +36,71 @@ jobs:
|
||||
TYPE="pull"
|
||||
elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
|
||||
TYPE="tag"
|
||||
echo 'FBT_BUILD_TYPE="DEBUG=0 COMPACT=1"' >> $GITHUB_ENV
|
||||
else
|
||||
TYPE="other"
|
||||
fi
|
||||
python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" || cat "${{ github.event_path }}"
|
||||
echo random_hash=$(openssl rand -base64 40 | shasum -a 256 | awk '{print $1}') >> $GITHUB_OUTPUT
|
||||
echo "event_type=$TYPE" >> $GITHUB_OUTPUT
|
||||
echo "TARGET=${{ matrix.target }}" >> $GITHUB_ENV
|
||||
echo "TARGET_HW=$(echo "${{ matrix.target }}" | sed 's/f//')" >> $GITHUB_ENV
|
||||
|
||||
- name: 'Check API versions'
|
||||
- name: 'Check API versions for consistency between targets'
|
||||
run: |
|
||||
set -e
|
||||
N_API_HEADER_SIGNATURES=`ls -1 firmware/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 firmware/targets/f*/api_symbols.csv
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: 'Make artifacts directory'
|
||||
- name: 'Build the firmware and apps'
|
||||
id: build-fw
|
||||
run: |
|
||||
rm -rf artifacts map_analyser_files
|
||||
mkdir artifacts map_analyser_files
|
||||
./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 scripts'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
run: |
|
||||
tar czpf "artifacts/flipper-z-any-scripts-${SUFFIX}.tgz" scripts
|
||||
|
||||
- name: 'Build the firmware'
|
||||
run: |
|
||||
set -e
|
||||
for TARGET in ${TARGETS}; do
|
||||
TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \
|
||||
./fbt TARGET_HW=$TARGET_HW copro_dist updater_package \
|
||||
${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }}
|
||||
mv dist/${TARGET}-*/* artifacts/
|
||||
tar czpf "artifacts/flipper-z-${TARGET}-resources-${SUFFIX}.tgz" \
|
||||
-C assets resources
|
||||
./fbt TARGET_HW=$TARGET_HW fap_dist
|
||||
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 .
|
||||
done
|
||||
|
||||
- name: "Check for uncommitted changes"
|
||||
- name: 'Check for uncommitted changes'
|
||||
run: |
|
||||
git diff --exit-code
|
||||
|
||||
- name: 'Bundle core2 firmware'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
- name: 'Copy build output'
|
||||
run: |
|
||||
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 assets 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: |
|
||||
tar czpf "artifacts/flipper-z-any-scripts-${SUFFIX}.tgz" scripts
|
||||
cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz"
|
||||
|
||||
- name: 'Copy map analyser files'
|
||||
- name: 'Upload artifacts to update server'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
run: |
|
||||
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
|
||||
|
||||
- name: 'Analyse map file'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
run: |
|
||||
source scripts/toolchain/fbtenv.sh
|
||||
get_size()
|
||||
{
|
||||
@ -119,102 +124,44 @@ jobs:
|
||||
${{ secrets.AMAP_MARIADB_DATABASE }} \
|
||||
map_analyser_files/firmware.elf.map.all
|
||||
|
||||
- name: 'Upload artifacts to update server'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
run: |
|
||||
FILES=$(for CUR in $(ls artifacts/); do echo "-F files=@artifacts/$CUR"; done)
|
||||
curl --fail -L -H "Token: ${{ secrets.INDEXER_TOKEN }}" \
|
||||
-F "branch=${BRANCH_NAME}" \
|
||||
${FILES[@]} \
|
||||
"${{ secrets.INDEXER_URL }}"/firmware/uploadfiles
|
||||
|
||||
- name: 'Find Previous Comment'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }}
|
||||
- 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: fc
|
||||
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}}
|
||||
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.commit_sha}}`:**
|
||||
**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]
|
||||
strategy:
|
||||
matrix:
|
||||
target: [f7, f18]
|
||||
steps:
|
||||
- name: 'Wipe workspace'
|
||||
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
|
||||
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@v3
|
||||
- name: 'SDK submission to dev catalog'
|
||||
if: ${{ steps.names.outputs.event_type == 'tag' && matrix.target == env.DEFAULT_TARGET }}
|
||||
uses: ./.github/actions/submit_sdk
|
||||
with:
|
||||
fetch-depth: 1
|
||||
submodules: false
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
catalog-url: ${{ secrets.CATALOG_STAGING_URL }}
|
||||
catalog-api-token: ${{ secrets.CATALOG_STAGING_API_TOKEN }}
|
||||
firmware-api: ${{ steps.build-fw.outputs.firmware_api }}
|
||||
firwmare-target: ${{ matrix.target }}
|
||||
firmware-version: ${{ steps.names.outputs.suffix }}
|
||||
|
||||
- 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.0
|
||||
- name: 'SDK submission to prod catalog'
|
||||
if: ${{ steps.names.outputs.event_type == 'tag' && matrix.target == env.DEFAULT_TARGET }}
|
||||
uses: ./.github/actions/submit_sdk
|
||||
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
|
||||
|
||||
catalog-url: ${{ secrets.CATALOG_URL }}
|
||||
catalog-api-token: ${{ secrets.CATALOG_API_TOKEN }}
|
||||
firmware-api: ${{ steps.build-fw.outputs.firmware_api }}
|
||||
firwmare-target: ${{ matrix.target }}
|
||||
firmware-version: ${{ steps.names.outputs.suffix }}
|
||||
|
||||
85
.github/workflows/build_compact.yml
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
name: 'Compact build'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
FBT_TOOLCHAIN_PATH: /runner/_work
|
||||
|
||||
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@v3
|
||||
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.0
|
||||
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 ✨" ;
|
||||
@ -1,11 +1,6 @@
|
||||
name: 'Lint sources & check submodule integrity'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
@ -15,7 +10,7 @@ env:
|
||||
|
||||
jobs:
|
||||
lint_sources_check_submodules:
|
||||
runs-on: [self-hosted,FlipperZeroShell]
|
||||
runs-on: [self-hosted, FlipperZeroShell]
|
||||
steps:
|
||||
- name: 'Wipe workspace'
|
||||
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
|
||||
@ -53,7 +48,7 @@ jobs:
|
||||
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 );
|
||||
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;
|
||||
|
||||
2
.github/workflows/merge_report.yml
vendored
@ -10,7 +10,7 @@ env:
|
||||
|
||||
jobs:
|
||||
merge_report:
|
||||
runs-on: [self-hosted,FlipperZeroShell]
|
||||
runs-on: [self-hosted, FlipperZeroShell]
|
||||
steps:
|
||||
- name: 'Wipe workspace'
|
||||
run: find ./ -mount -maxdepth 1 -exec rm -rf {} \;
|
||||
|
||||
2
.github/workflows/pvs_studio.yml
vendored
@ -4,8 +4,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
|
||||
2
.github/workflows/reindex.yml
vendored
@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
reindex:
|
||||
name: 'Reindex updates'
|
||||
runs-on: [self-hosted,FlipperZeroShell]
|
||||
runs-on: [self-hosted, FlipperZeroShell]
|
||||
steps:
|
||||
- name: Trigger reindex
|
||||
run: |
|
||||
|
||||
@ -36,6 +36,10 @@ Finally, open a [Pull Request](https://github.com/flipperdevices/flipperzero-fir
|
||||
|
||||
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:
|
||||
@ -91,7 +95,6 @@ Make sure your Flipper is on, and your firmware is functioning. Connect your Fli
|
||||
- [Hardware combos and Un-bricking](/documentation/KeyCombo.md) - recovering your Flipper from the most nasty situations
|
||||
- [Flipper File Formats](/documentation/file_formats) - everything about how Flipper stores your data and how you can work with it
|
||||
- [Universal Remotes](/documentation/UniversalRemotes.md) - contributing your infrared remote to the universal remote database
|
||||
- [Firmware Roadmap](/documentation/RoadMap.md)
|
||||
- And much more in the [documentation](/documentation) folder
|
||||
|
||||
# Project structure
|
||||
|
||||
@ -26,7 +26,6 @@ void test_furi_memmgr() {
|
||||
mu_assert_int_eq(66, ((uint8_t*)ptr)[i]);
|
||||
}
|
||||
|
||||
// TODO FL-3492: fix realloc to copy only old size, and write testcase that leftover of reallocated memory is zero-initialized
|
||||
free(ptr);
|
||||
|
||||
// allocate and zero-initialize array (calloc)
|
||||
|
||||
@ -67,7 +67,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);
|
||||
@ -149,11 +148,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);
|
||||
}
|
||||
|
||||
@ -161,7 +190,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);
|
||||
}
|
||||
|
||||
@ -179,36 +208,6 @@ 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(file_info_is_dir(&fileinfo)) {
|
||||
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);
|
||||
@ -282,24 +281,40 @@ 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,
|
||||
uint16_t tag,
|
||||
const char* str,
|
||||
uint32_t command_id,
|
||||
bool flag) {
|
||||
uint32_t command_id) {
|
||||
furi_check(message);
|
||||
|
||||
char* str_copy = NULL;
|
||||
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,10 +322,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;
|
||||
message->content.storage_list_request.include_md5 = flag;
|
||||
break;
|
||||
case PB_Main_storage_mkdir_request_tag:
|
||||
message->content.storage_mkdir_request.path = str_copy;
|
||||
break;
|
||||
@ -573,11 +584,29 @@ 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,
|
||||
bool append_md5) {
|
||||
bool append_md5,
|
||||
size_t filter_max_size) {
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
File* dir = storage_file_alloc(fs_api);
|
||||
|
||||
@ -615,7 +644,7 @@ static void test_rpc_storage_list_create_expected_list(
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if(path_contains_only_ascii(name)) {
|
||||
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;
|
||||
@ -698,17 +727,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, bool md5) {
|
||||
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, md5);
|
||||
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, md5);
|
||||
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);
|
||||
@ -718,25 +751,32 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id, boo
|
||||
}
|
||||
|
||||
MU_TEST(test_storage_list) {
|
||||
test_rpc_storage_list_run("/", ++command_id, false);
|
||||
test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, false);
|
||||
test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, false);
|
||||
test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, false);
|
||||
test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, false);
|
||||
test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, false);
|
||||
test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, false);
|
||||
test_rpc_storage_list_run("error_path", ++command_id, false);
|
||||
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);
|
||||
test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, true);
|
||||
test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, true);
|
||||
test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, true);
|
||||
test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, true);
|
||||
test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, true);
|
||||
test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, true);
|
||||
test_rpc_storage_list_run("error_path", ++command_id, true);
|
||||
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
|
||||
@ -804,8 +844,7 @@ static void test_storage_read_run(const char* path, uint32_t command_id) {
|
||||
MsgList_init(expected_msg_list);
|
||||
|
||||
test_rpc_add_read_to_list_by_reading_real_file(expected_msg_list, path, command_id);
|
||||
test_rpc_create_simple_message(
|
||||
&request, PB_Main_storage_read_request_tag, path, command_id, false);
|
||||
test_rpc_create_simple_message(&request, PB_Main_storage_read_request_tag, path, command_id);
|
||||
test_rpc_encode_and_feed_one(&request, 0);
|
||||
test_rpc_decode_and_compare(expected_msg_list, 0);
|
||||
|
||||
@ -859,8 +898,7 @@ static void test_rpc_storage_info_run(const char* path, uint32_t command_id) {
|
||||
MsgList_t expected_msg_list;
|
||||
MsgList_init(expected_msg_list);
|
||||
|
||||
test_rpc_create_simple_message(
|
||||
&request, PB_Main_storage_info_request_tag, path, command_id, false);
|
||||
test_rpc_create_simple_message(&request, PB_Main_storage_info_request_tag, path, command_id);
|
||||
|
||||
PB_Main* response = MsgList_push_new(expected_msg_list);
|
||||
response->command_id = command_id;
|
||||
@ -892,8 +930,7 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) {
|
||||
MsgList_t expected_msg_list;
|
||||
MsgList_init(expected_msg_list);
|
||||
|
||||
test_rpc_create_simple_message(
|
||||
&request, PB_Main_storage_stat_request_tag, path, command_id, false);
|
||||
test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id);
|
||||
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
FileInfo fileinfo;
|
||||
@ -1005,11 +1042,7 @@ static void test_storage_write_read_run(
|
||||
test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, *command_id);
|
||||
|
||||
test_rpc_create_simple_message(
|
||||
MsgList_push_raw(input_msg_list),
|
||||
PB_Main_storage_read_request_tag,
|
||||
path,
|
||||
++*command_id,
|
||||
false);
|
||||
MsgList_push_raw(input_msg_list), PB_Main_storage_read_request_tag, path, ++*command_id);
|
||||
test_rpc_add_read_or_write_to_list(
|
||||
expected_msg_list,
|
||||
READ_RESPONSE,
|
||||
@ -1082,8 +1115,7 @@ MU_TEST(test_storage_interrupt_continuous_same_system) {
|
||||
MsgList_push_new(input_msg_list),
|
||||
PB_Main_storage_mkdir_request_tag,
|
||||
TEST_DIR "dir1",
|
||||
command_id + 1,
|
||||
false);
|
||||
command_id + 1);
|
||||
test_rpc_add_read_or_write_to_list(
|
||||
input_msg_list,
|
||||
WRITE_REQUEST,
|
||||
@ -1163,8 +1195,7 @@ static void test_storage_delete_run(
|
||||
MsgList_t expected_msg_list;
|
||||
MsgList_init(expected_msg_list);
|
||||
|
||||
test_rpc_create_simple_message(
|
||||
&request, PB_Main_storage_delete_request_tag, path, command_id, false);
|
||||
test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id);
|
||||
request.content.storage_delete_request.recursive = recursive;
|
||||
test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
|
||||
|
||||
@ -1245,8 +1276,7 @@ static void test_storage_mkdir_run(const char* path, size_t command_id, PB_Comma
|
||||
MsgList_t expected_msg_list;
|
||||
MsgList_init(expected_msg_list);
|
||||
|
||||
test_rpc_create_simple_message(
|
||||
&request, PB_Main_storage_mkdir_request_tag, path, command_id, false);
|
||||
test_rpc_create_simple_message(&request, PB_Main_storage_mkdir_request_tag, path, command_id);
|
||||
test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
|
||||
|
||||
test_rpc_encode_and_feed_one(&request, 0);
|
||||
@ -1297,12 +1327,11 @@ static void test_storage_md5sum_run(
|
||||
MsgList_t expected_msg_list;
|
||||
MsgList_init(expected_msg_list);
|
||||
|
||||
test_rpc_create_simple_message(
|
||||
&request, PB_Main_storage_md5sum_request_tag, path, command_id, false);
|
||||
test_rpc_create_simple_message(&request, PB_Main_storage_md5sum_request_tag, path, command_id);
|
||||
if(status == PB_CommandStatus_OK) {
|
||||
PB_Main* response = MsgList_push_new(expected_msg_list);
|
||||
test_rpc_create_simple_message(
|
||||
response, PB_Main_storage_md5sum_response_tag, md5sum, command_id, false);
|
||||
response, PB_Main_storage_md5sum_response_tag, md5sum, command_id);
|
||||
response->command_status = status;
|
||||
} else {
|
||||
test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
|
||||
@ -1461,6 +1490,7 @@ MU_TEST_SUITE(test_rpc_storage) {
|
||||
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);
|
||||
@ -1759,8 +1789,7 @@ MU_TEST(test_rpc_multisession_storage) {
|
||||
MsgList_push_raw(input_0),
|
||||
PB_Main_storage_read_request_tag,
|
||||
TEST_DIR "file0.txt",
|
||||
++command_id,
|
||||
false);
|
||||
++command_id);
|
||||
test_rpc_add_read_or_write_to_list(
|
||||
expected_0, READ_RESPONSE, TEST_DIR "file0.txt", pattern, sizeof(pattern), 1, command_id);
|
||||
|
||||
@ -1768,8 +1797,7 @@ MU_TEST(test_rpc_multisession_storage) {
|
||||
MsgList_push_raw(input_1),
|
||||
PB_Main_storage_read_request_tag,
|
||||
TEST_DIR "file1.txt",
|
||||
++command_id,
|
||||
false);
|
||||
++command_id);
|
||||
test_rpc_add_read_or_write_to_list(
|
||||
expected_1, READ_RESPONSE, TEST_DIR "file1.txt", pattern, sizeof(pattern), 1, command_id);
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
#include <lib/subghz/devices/devices.h>
|
||||
#include <lib/subghz/devices/cc1101_configs.h>
|
||||
|
||||
#define TAG "SubGhz TEST"
|
||||
#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")
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
#include <cc1101.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define TAG "SubGhz_Device_CC1101_Ext"
|
||||
#define TAG "SubGhzDeviceCc1101Ext"
|
||||
|
||||
#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO &gpio_ext_pb2
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include "cc1101_ext.h"
|
||||
#include <lib/subghz/devices/cc1101_configs.h>
|
||||
|
||||
#define TAG "SubGhzDeviceCC1101Ext"
|
||||
#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);
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
|
||||
// Define log tag
|
||||
#define TAG "example_apps_assets"
|
||||
#define TAG "ExampleAppsAssets"
|
||||
|
||||
static void example_apps_data_print_file_content(Storage* storage, const char* path) {
|
||||
Stream* stream = file_stream_alloc(storage);
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include <storage/storage.h>
|
||||
|
||||
// Define log tag
|
||||
#define TAG "example_apps_data"
|
||||
#define TAG "ExampleAppsData"
|
||||
|
||||
// Application entry point
|
||||
int32_t example_apps_data_main(void* p) {
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
#include <loader/firmware_api/firmware_api.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define TAG "example_plugins"
|
||||
#define TAG "ExamplePlugins"
|
||||
|
||||
int32_t example_plugins_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "example_plugins"
|
||||
#define TAG "ExamplePlugins"
|
||||
|
||||
int32_t example_plugins_multi_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
#include <loader/firmware_api/firmware_api.h>
|
||||
|
||||
#define TAG "example_advanced_plugins"
|
||||
#define TAG "ExampleAdvancedPlugins"
|
||||
|
||||
int32_t example_advanced_plugins_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
#include "ducky_script_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define TAG "BadUSB"
|
||||
#define TAG "BadUsb"
|
||||
#define WORKER_TAG TAG "Worker"
|
||||
|
||||
#define BADUSB_ASCII_TO_KEY(script, x) \
|
||||
|
||||
@ -171,7 +171,7 @@ static const DuckyCmd ducky_commands[] = {
|
||||
{"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1},
|
||||
};
|
||||
|
||||
#define TAG "BadUSB"
|
||||
#define TAG "BadUsb"
|
||||
#define WORKER_TAG TAG "Worker"
|
||||
|
||||
int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) {
|
||||
|
||||
@ -18,10 +18,12 @@ GPIOItems* gpio_items_alloc() {
|
||||
}
|
||||
|
||||
items->pins = malloc(sizeof(GpioPinRecord) * items->count);
|
||||
for(size_t i = 0; i < items->count; i++) {
|
||||
size_t index = 0;
|
||||
for(size_t i = 0; i < gpio_pins_count; i++) {
|
||||
if(!gpio_pins[i].debug) {
|
||||
items->pins[i].pin = gpio_pins[i].pin;
|
||||
items->pins[i].name = gpio_pins[i].name;
|
||||
items->pins[index].pin = gpio_pins[i].pin;
|
||||
items->pins[index].name = gpio_pins[i].name;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return items;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include <toolbox/path.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define TAG "iButtonApp"
|
||||
#define TAG "IButtonApp"
|
||||
|
||||
static const NotificationSequence sequence_blink_set_yellow = {
|
||||
&message_blink_set_color_yellow,
|
||||
@ -195,16 +195,23 @@ bool ibutton_load_key(iButton* ibutton) {
|
||||
|
||||
bool ibutton_select_and_load_key(iButton* ibutton) {
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, IBUTTON_APP_EXTENSION, &I_ibutt_10px);
|
||||
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);
|
||||
}
|
||||
|
||||
return dialog_file_browser_show(
|
||||
ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options) &&
|
||||
ibutton_load_key(ibutton);
|
||||
do {
|
||||
if(!dialog_file_browser_show(
|
||||
ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options))
|
||||
break;
|
||||
success = ibutton_load_key(ibutton);
|
||||
} while(!success);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ibutton_save_key(iButton* ibutton) {
|
||||
|
||||
@ -29,7 +29,8 @@
|
||||
#include "scenes/ibutton_scene.h"
|
||||
|
||||
#define IBUTTON_APP_FOLDER ANY_PATH("ibutton")
|
||||
#define IBUTTON_APP_EXTENSION ".ibtn"
|
||||
#define IBUTTON_APP_FILENAME_PREFIX "iBtn"
|
||||
#define IBUTTON_APP_FILENAME_EXTENSION ".ibtn"
|
||||
|
||||
#define IBUTTON_KEY_NAME_SIZE 22
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "../ibutton_i.h"
|
||||
|
||||
#include <toolbox/random_name.h>
|
||||
#include <toolbox/name_generator.h>
|
||||
#include <toolbox/path.h>
|
||||
|
||||
#include <dolphin/dolphin.h>
|
||||
@ -17,7 +17,8 @@ void ibutton_scene_save_name_on_enter(void* context) {
|
||||
const bool is_new_file = furi_string_empty(ibutton->file_path);
|
||||
|
||||
if(is_new_file) {
|
||||
set_random_name(ibutton->key_name, IBUTTON_KEY_NAME_SIZE);
|
||||
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");
|
||||
@ -29,8 +30,8 @@ void ibutton_scene_save_name_on_enter(void* context) {
|
||||
IBUTTON_KEY_NAME_SIZE,
|
||||
is_new_file);
|
||||
|
||||
ValidatorIsFile* validator_is_file =
|
||||
validator_is_file_alloc_init(IBUTTON_APP_FOLDER, IBUTTON_APP_EXTENSION, ibutton->key_name);
|
||||
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);
|
||||
@ -48,7 +49,7 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||
"%s/%s%s",
|
||||
IBUTTON_APP_FOLDER,
|
||||
ibutton->key_name,
|
||||
IBUTTON_APP_EXTENSION);
|
||||
IBUTTON_APP_FILENAME_EXTENSION);
|
||||
|
||||
if(ibutton_save_key(ibutton)) {
|
||||
scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "../infrared_i.h"
|
||||
|
||||
#include "common/infrared_scene_universal_common.h"
|
||||
#include <furi_hal_rtc.h>
|
||||
|
||||
void infrared_scene_universal_ac_on_enter(void* context) {
|
||||
infrared_scene_universal_common_on_enter(context);
|
||||
|
||||
@ -215,13 +215,16 @@ bool lfrfid_save_key(LfRfid* app) {
|
||||
|
||||
lfrfid_make_app_folder(app);
|
||||
|
||||
if(furi_string_end_with(app->file_path, LFRFID_APP_EXTENSION)) {
|
||||
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_EXTENSION);
|
||||
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;
|
||||
@ -231,7 +234,8 @@ 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_EXTENSION, &I_125_10px);
|
||||
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
|
||||
|
||||
@ -40,8 +40,9 @@
|
||||
|
||||
#define LFRFID_APP_FOLDER ANY_PATH("lfrfid")
|
||||
#define LFRFID_SD_FOLDER EXT_PATH("lfrfid")
|
||||
#define LFRFID_APP_EXTENSION ".rfid"
|
||||
#define LFRFID_APP_SHADOW_EXTENSION ".shd"
|
||||
#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"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include <lib/toolbox/random_name.h>
|
||||
#include "../lfrfid_i.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <toolbox/name_generator.h>
|
||||
|
||||
void lfrfid_scene_save_name_on_enter(void* context) {
|
||||
LfRfid* app = context;
|
||||
@ -11,7 +11,10 @@ void lfrfid_scene_save_name_on_enter(void* context) {
|
||||
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);
|
||||
set_random_name(app->text_store, LFRFID_TEXT_STORE_SIZE);
|
||||
|
||||
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));
|
||||
@ -31,7 +34,7 @@ void lfrfid_scene_save_name_on_enter(void* context) {
|
||||
|
||||
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
|
||||
furi_string_get_cstr(folder_path),
|
||||
LFRFID_APP_EXTENSION,
|
||||
LFRFID_APP_FILENAME_EXTENSION,
|
||||
furi_string_get_cstr(app->file_name));
|
||||
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
|
||||
|
||||
|
||||
@ -223,7 +223,11 @@ void nfc_blink_stop(Nfc* nfc) {
|
||||
|
||||
bool nfc_save_file(Nfc* nfc) {
|
||||
furi_string_printf(
|
||||
nfc->dev->load_path, "%s/%s%s", NFC_APP_FOLDER, nfc->dev->dev_name, NFC_APP_EXTENSION);
|
||||
nfc->dev->load_path,
|
||||
"%s/%s%s",
|
||||
NFC_APP_FOLDER,
|
||||
nfc->dev->dev_name,
|
||||
NFC_APP_FILENAME_EXTENSION);
|
||||
bool file_saved = nfc_device_save(nfc->dev, furi_string_get_cstr(nfc->dev->load_path));
|
||||
return file_saved;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#include "../nfc_i.h"
|
||||
#include <lib/toolbox/random_name.h>
|
||||
#include <lib/toolbox/name_generator.h>
|
||||
#include <gui/modules/validators.h>
|
||||
#include <toolbox/path.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
@ -17,7 +17,7 @@ void nfc_scene_save_name_on_enter(void* context) {
|
||||
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));
|
||||
name_generator_make_auto(nfc->text_store, NFC_DEV_NAME_MAX_LEN, NFC_APP_FILENAME_PREFIX);
|
||||
dev_name_empty = true;
|
||||
} else {
|
||||
nfc_text_store_set(nfc, nfc->dev->dev_name);
|
||||
@ -34,14 +34,14 @@ void nfc_scene_save_name_on_enter(void* context) {
|
||||
FuriString* folder_path;
|
||||
folder_path = furi_string_alloc();
|
||||
|
||||
if(furi_string_end_with(nfc->dev->load_path, NFC_APP_EXTENSION)) {
|
||||
if(furi_string_end_with(nfc->dev->load_path, NFC_APP_FILENAME_EXTENSION)) {
|
||||
path_extract_dirname(furi_string_get_cstr(nfc->dev->load_path), folder_path);
|
||||
} else {
|
||||
furi_string_set(folder_path, NFC_APP_FOLDER);
|
||||
}
|
||||
|
||||
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
|
||||
furi_string_get_cstr(folder_path), NFC_APP_EXTENSION, nfc->dev->dev_name);
|
||||
furi_string_get_cstr(folder_path), NFC_APP_FILENAME_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);
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
#include <lib/toolbox/path.h>
|
||||
|
||||
#define RAW_FILE_NAME "Raw_signal_"
|
||||
#define TAG "SubGhzSceneReadRAW"
|
||||
#define TAG "SubGhzSceneReadRaw"
|
||||
|
||||
bool subghz_scene_read_raw_update_filename(SubGhz* subghz) {
|
||||
bool ret = false;
|
||||
@ -239,7 +239,11 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
furi_string_printf(
|
||||
temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION);
|
||||
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),
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
#include "../subghz_i.h"
|
||||
#include "subghz/types.h"
|
||||
#include <lib/toolbox/random_name.h>
|
||||
#include "../helpers/subghz_custom_event.h"
|
||||
#include <lib/subghz/protocols/raw.h>
|
||||
#include <gui/modules/validators.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <toolbox/name_generator.h>
|
||||
|
||||
#define MAX_TEXT_INPUT_LEN 22
|
||||
|
||||
@ -40,7 +40,9 @@ void subghz_scene_save_name_on_enter(void* context) {
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
@ -71,7 +73,7 @@ void subghz_scene_save_name_on_enter(void* context) {
|
||||
dev_name_empty);
|
||||
|
||||
ValidatorIsFile* validator_is_file = validator_is_file_alloc_init(
|
||||
furi_string_get_cstr(subghz->file_path), SUBGHZ_APP_EXTENSION, "");
|
||||
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);
|
||||
@ -94,7 +96,10 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
|
||||
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_EXTENSION);
|
||||
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;
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
|
||||
#define SUBGHZ_REGION_FILENAME "/int/.region_data"
|
||||
|
||||
#define TAG "SubGhz CLI"
|
||||
#define TAG "SubGhzCli"
|
||||
|
||||
static void subghz_cli_radio_device_power_on() {
|
||||
uint8_t attempts = 5;
|
||||
|
||||
@ -238,7 +238,7 @@ bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) {
|
||||
storage,
|
||||
furi_string_get_cstr(file_path),
|
||||
furi_string_get_cstr(file_name),
|
||||
SUBGHZ_APP_EXTENSION,
|
||||
SUBGHZ_APP_FILENAME_EXTENSION,
|
||||
file_name,
|
||||
max_len);
|
||||
|
||||
@ -247,7 +247,7 @@ bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) {
|
||||
"%s/%s%s",
|
||||
furi_string_get_cstr(file_path),
|
||||
furi_string_get_cstr(file_name),
|
||||
SUBGHZ_APP_EXTENSION);
|
||||
SUBGHZ_APP_FILENAME_EXTENSION);
|
||||
furi_string_set(subghz->file_path, temp_str);
|
||||
res = true;
|
||||
}
|
||||
@ -320,7 +320,8 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) {
|
||||
FuriString* file_path = furi_string_alloc();
|
||||
|
||||
DialogsFileBrowserOptions browser_options;
|
||||
dialog_file_browser_set_basic_options(&browser_options, SUBGHZ_APP_EXTENSION, &I_sub1_10px);
|
||||
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
|
||||
@ -394,7 +395,7 @@ void subghz_file_name_clear(SubGhz* subghz) {
|
||||
}
|
||||
|
||||
bool subghz_path_is_file(FuriString* path) {
|
||||
return furi_string_end_with(path, SUBGHZ_APP_EXTENSION);
|
||||
return furi_string_end_with(path, SUBGHZ_APP_FILENAME_EXTENSION);
|
||||
}
|
||||
|
||||
void subghz_lock(SubGhz* subghz) {
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
#include <assets_icons.h>
|
||||
#define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100
|
||||
#define TAG "SubGhzReadRAW"
|
||||
#define TAG "SubGhzReadRaw"
|
||||
|
||||
struct SubGhzReadRAW {
|
||||
View* view;
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
#include "hmac_sha256.h"
|
||||
#include "micro-ecc/uECC.h"
|
||||
|
||||
#define TAG "U2F"
|
||||
#define TAG "U2f"
|
||||
#define WORKER_TAG TAG "Worker"
|
||||
|
||||
#define U2F_CMD_REGISTER 0x01
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
#include <furi_hal_random.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
#define TAG "U2F"
|
||||
#define TAG "U2f"
|
||||
|
||||
#define U2F_DATA_FOLDER EXT_PATH("u2f/")
|
||||
#define U2F_CERT_FILE U2F_DATA_FOLDER "assets/cert.der"
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
#include <furi_hal_console.h>
|
||||
|
||||
#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))
|
||||
|
||||
@ -101,7 +101,6 @@ static void desktop_clock_draw_callback(Canvas* canvas, void* context) {
|
||||
char buffer[20];
|
||||
snprintf(buffer, sizeof(buffer), "%02u:%02u", hour, desktop->time_minute);
|
||||
|
||||
// TODO FL-3515: never do that, may cause visual glitches
|
||||
view_port_set_width(
|
||||
desktop->clock_viewport,
|
||||
canvas_string_width(canvas, buffer) - 1 + (desktop->time_minute % 10 == 1));
|
||||
@ -126,8 +125,6 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) {
|
||||
return true;
|
||||
case DesktopGlobalAfterAppFinished:
|
||||
animation_manager_load_and_continue_animation(desktop->animation_manager);
|
||||
// TODO FL-3497: Implement a message mechanism for loading settings and (optionally)
|
||||
// locking and unlocking
|
||||
DESKTOP_SETTINGS_LOAD(&desktop->settings);
|
||||
|
||||
desktop_clock_reconfigure(desktop);
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_rtc.h>
|
||||
#include <stdint.h>
|
||||
#include <u8g2_glue.h>
|
||||
|
||||
|
||||
@ -361,10 +361,11 @@ void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) {
|
||||
furi_assert(view_port);
|
||||
furi_check(layer < GuiLayerMAX);
|
||||
// Only fullscreen supports Vertical orientation for now
|
||||
furi_assert(
|
||||
ViewPortOrientation view_port_orientation = view_port_get_orientation(view_port);
|
||||
furi_check(
|
||||
(layer == GuiLayerFullscreen) ||
|
||||
((view_port->orientation != ViewPortOrientationVertical) &&
|
||||
(view_port->orientation != ViewPortOrientationVerticalFlip)));
|
||||
((view_port_orientation != ViewPortOrientationVertical) &&
|
||||
(view_port_orientation != ViewPortOrientationVerticalFlip)));
|
||||
|
||||
gui_lock(gui);
|
||||
// Verify that view port is not yet added
|
||||
|
||||
@ -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 FL-3514: should we allow view_dispatcher to stop without navigation_event_callback?
|
||||
view_dispatcher_stop(view_dispatcher);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2,13 +2,10 @@
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_rtc.h>
|
||||
|
||||
#include "gui.h"
|
||||
#include "gui_i.h"
|
||||
|
||||
// TODO FL-3498: add mutex to view_port ops
|
||||
|
||||
_Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count");
|
||||
_Static_assert(
|
||||
(ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 &&
|
||||
@ -95,52 +92,73 @@ 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);
|
||||
return view_port->width;
|
||||
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);
|
||||
return view_port->height;
|
||||
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);
|
||||
return view_port->is_enabled;
|
||||
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(
|
||||
@ -148,34 +166,43 @@ void view_port_input_callback_set(
|
||||
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);
|
||||
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui);
|
||||
furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||
}
|
||||
|
||||
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);
|
||||
furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk);
|
||||
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_check(furi_mutex_release(view_port->mutex) == FuriStatusOk);
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -183,13 +210,19 @@ void view_port_input(ViewPort* view_port, InputEvent* event) {
|
||||
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) {
|
||||
return view_port->orientation;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
|
||||
struct ViewPort {
|
||||
Gui* gui;
|
||||
FuriMutex* mutex;
|
||||
bool is_enabled;
|
||||
ViewPortOrientation orientation;
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) {
|
||||
if(power->state == PowerStateCharging) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
// TODO FL-3510: 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);
|
||||
|
||||
@ -15,6 +15,8 @@
|
||||
#include <stdio.h>
|
||||
#include <m-dict.h>
|
||||
|
||||
#include <bt/bt_service/bt.h>
|
||||
|
||||
#define TAG "RpcSrv"
|
||||
|
||||
typedef enum {
|
||||
@ -316,6 +318,15 @@ static int32_t rpc_session_worker(void* context) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -242,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);
|
||||
@ -253,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;
|
||||
}
|
||||
@ -271,7 +290,7 @@ 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 = request->content.storage_list_request.include_md5;
|
||||
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);
|
||||
@ -279,7 +298,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
|
||||
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;
|
||||
@ -289,7 +308,7 @@ 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;
|
||||
@ -303,11 +322,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
|
||||
list->file[i].name = name;
|
||||
|
||||
if(include_md5 && !file_info_is_dir(&fileinfo)) {
|
||||
furi_string_printf( //-V576
|
||||
md5_path,
|
||||
"%s/%s",
|
||||
request->content.storage_list_request.path,
|
||||
name);
|
||||
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;
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
#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);
|
||||
|
||||
@ -334,12 +334,20 @@ const char* storage_file_get_error_desc(File* file);
|
||||
*/
|
||||
FS_Error storage_sd_format(Storage* api);
|
||||
|
||||
/** Will unmount the SD card
|
||||
/** Will unmount the SD card.
|
||||
* Will return FSE_NOT_READY if the SD card is not mounted.
|
||||
* Will return FSE_DENIED if there are open files on the SD card.
|
||||
* @param api pointer to the api
|
||||
* @return FS_Error operation result
|
||||
*/
|
||||
FS_Error storage_sd_unmount(Storage* api);
|
||||
|
||||
/** Will mount the SD card
|
||||
* @param api pointer to the api
|
||||
* @return FS_Error operation result
|
||||
*/
|
||||
FS_Error storage_sd_mount(Storage* api);
|
||||
|
||||
/** Retrieves SD card information
|
||||
* @param api pointer to the api
|
||||
* @param info pointer to the info
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
#define MAX_EXT_LEN 16
|
||||
#define FILE_BUFFER_SIZE 512
|
||||
|
||||
#define TAG "StorageAPI"
|
||||
#define TAG "StorageApi"
|
||||
|
||||
#define S_API_PROLOGUE FuriApiLock lock = api_lock_alloc_locked();
|
||||
|
||||
@ -781,6 +781,14 @@ FS_Error storage_sd_unmount(Storage* storage) {
|
||||
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 = {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#include "storage_glue.h"
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define TAG "StorageGlue"
|
||||
|
||||
/****************** storage file ******************/
|
||||
|
||||
void storage_file_init(StorageFile* obj) {
|
||||
@ -149,3 +151,8 @@ bool storage_pop_storage_file(File* file, StorageData* storage) {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t storage_open_files_count(StorageData* storage) {
|
||||
size_t count = StorageFileList_size(storage->files);
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -68,6 +68,8 @@ void* storage_get_storage_file_data(const File* file, 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
|
||||
|
||||
@ -141,6 +141,7 @@ typedef enum {
|
||||
StorageCommandSDInfo,
|
||||
StorageCommandSDStatus,
|
||||
StorageCommandCommonResolvePath,
|
||||
StorageCommandSDMount,
|
||||
} StorageCommand;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -418,12 +418,38 @@ static FS_Error storage_process_sd_format(Storage* app) {
|
||||
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]);
|
||||
storage_data_timestamp(&app->storage[ST_EXT]);
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -630,6 +656,9 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) {
|
||||
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);
|
||||
|
||||
@ -32,8 +32,6 @@ typedef struct {
|
||||
uint32_t product_serial_number;
|
||||
uint8_t manufacturing_month;
|
||||
uint16_t manufacturing_year;
|
||||
|
||||
FS_Error error;
|
||||
} SDInfo;
|
||||
|
||||
const char* sd_api_get_fs_type_text(SDFsType fs_type);
|
||||
|
||||
@ -24,13 +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 = sd_max_mount_retry_count();
|
||||
uint8_t counter = furi_hal_sd_max_mount_retry_count();
|
||||
uint8_t bsp_result;
|
||||
SDData* sd_data = storage->data;
|
||||
|
||||
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);
|
||||
@ -39,9 +39,9 @@ static bool sd_mount_card(StorageData* storage, bool notify) {
|
||||
|
||||
if((counter % 2) == 0) {
|
||||
// power reset sd card
|
||||
bsp_result = sd_init(true);
|
||||
bsp_result = furi_hal_sd_init(true);
|
||||
} else {
|
||||
bsp_result = sd_init(false);
|
||||
bsp_result = furi_hal_sd_init(false);
|
||||
}
|
||||
|
||||
if(bsp_result) {
|
||||
@ -106,6 +106,32 @@ FS_Error sd_unmount_card(StorageData* 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);
|
||||
@ -199,18 +225,18 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
|
||||
#endif
|
||||
}
|
||||
|
||||
SD_CID cid;
|
||||
SdSpiStatus status = sd_get_cid(&cid);
|
||||
FuriHalSdInfo info;
|
||||
FuriStatus status = furi_hal_sd_info(&info);
|
||||
|
||||
if(status == SdSpiStatusOK) {
|
||||
sd_info->manufacturer_id = cid.ManufacturerID;
|
||||
memcpy(sd_info->oem_id, cid.OEM_AppliID, sizeof(cid.OEM_AppliID));
|
||||
memcpy(sd_info->product_name, cid.ProdName, sizeof(cid.ProdName));
|
||||
sd_info->product_revision_major = cid.ProdRev >> 4;
|
||||
sd_info->product_revision_minor = cid.ProdRev & 0x0F;
|
||||
sd_info->product_serial_number = cid.ProdSN;
|
||||
sd_info->manufacturing_year = 2000 + cid.ManufactYear;
|
||||
sd_info->manufacturing_month = cid.ManufactMonth;
|
||||
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);
|
||||
@ -220,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_data->sd_was_present = false;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
@ -630,7 +639,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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -9,22 +9,41 @@ static void
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@ -153,6 +153,21 @@ static void sleep_method_changed(VariableItem* item) {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -236,6 +251,12 @@ SystemSettings* system_settings_alloc() {
|
||||
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(
|
||||
|
||||
28
applications/system/hid_app/application.fam
Normal file
@ -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",
|
||||
)
|
||||
BIN
applications/system/hid_app/assets/Alt_11x7.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
applications/system/hid_app/assets/Arr_dwn_7x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Arr_up_7x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Ble_connected_15x15.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Ble_disconnected_15x15.png
Normal file
|
After Width: | Height: | Size: 657 B |
BIN
applications/system/hid_app/assets/ButtonDown_7x4.png
Normal file
|
After Width: | Height: | Size: 102 B |
BIN
applications/system/hid_app/assets/ButtonF10_5x8.png
Normal file
|
After Width: | Height: | Size: 172 B |
BIN
applications/system/hid_app/assets/ButtonF11_5x8.png
Normal file
|
After Width: | Height: | Size: 173 B |
BIN
applications/system/hid_app/assets/ButtonF12_5x8.png
Normal file
|
After Width: | Height: | Size: 180 B |
BIN
applications/system/hid_app/assets/ButtonF1_5x8.png
Normal file
|
After Width: | Height: | Size: 177 B |
BIN
applications/system/hid_app/assets/ButtonF2_5x8.png
Normal file
|
After Width: | Height: | Size: 179 B |
BIN
applications/system/hid_app/assets/ButtonF3_5x8.png
Normal file
|
After Width: | Height: | Size: 178 B |
BIN
applications/system/hid_app/assets/ButtonF4_5x8.png
Normal file
|
After Width: | Height: | Size: 177 B |
BIN
applications/system/hid_app/assets/ButtonF5_5x8.png
Normal file
|
After Width: | Height: | Size: 178 B |
BIN
applications/system/hid_app/assets/ButtonF6_5x8.png
Normal file
|
After Width: | Height: | Size: 177 B |
BIN
applications/system/hid_app/assets/ButtonF7_5x8.png
Normal file
|
After Width: | Height: | Size: 176 B |
BIN
applications/system/hid_app/assets/ButtonF8_5x8.png
Normal file
|
After Width: | Height: | Size: 176 B |
BIN
applications/system/hid_app/assets/ButtonF9_5x8.png
Normal file
|
After Width: | Height: | Size: 179 B |
BIN
applications/system/hid_app/assets/ButtonLeft_4x7.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
applications/system/hid_app/assets/ButtonRight_4x7.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
applications/system/hid_app/assets/ButtonUp_7x4.png
Normal file
|
After Width: | Height: | Size: 102 B |
BIN
applications/system/hid_app/assets/Button_18x18.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Circles_47x47.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
applications/system/hid_app/assets/Cmd_15x7.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
applications/system/hid_app/assets/Ctrl_15x7.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
applications/system/hid_app/assets/Del_12x7.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
applications/system/hid_app/assets/Esc_14x7.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
applications/system/hid_app/assets/Left_mouse_icon_9x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Like_def_11x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Like_pressed_17x17.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
applications/system/hid_app/assets/Ok_btn_9x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Ok_btn_pressed_13x13.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Pin_arrow_down_7x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Pin_arrow_left_9x7.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Pin_arrow_right_9x7.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Pin_arrow_up_7x9.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Pin_back_arrow_10x8.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
applications/system/hid_app/assets/Pressed_Button_13x13.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |