Merge remote-tracking branch 'origin/dev' into release-candidate

This commit is contained in:
Aleksandr Kutuzov 2023-09-11 22:37:30 +09:00
commit baf661d47d
205 changed files with 5267 additions and 1984 deletions

54
.github/actions/submit_sdk/action.yml vendored Normal file
View 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

View File

@ -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
View 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 ✨" ;

View File

@ -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;

View File

@ -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 {} \;

View File

@ -4,8 +4,6 @@ on:
push:
branches:
- dev
tags:
- '*'
pull_request:
env:

View File

@ -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: |

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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")

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -11,7 +11,7 @@
#include <furi.h>
#define TAG "example_plugins"
#define TAG "ExamplePlugins"
int32_t example_plugins_multi_app(void* p) {
UNUSED(p);

View File

@ -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);

View File

@ -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) \

View File

@ -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) {

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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"

View File

@ -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);

View 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;
}

View File

@ -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);

View File

@ -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),

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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"

View File

@ -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))

View File

@ -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);

View File

@ -4,7 +4,6 @@
#include <furi.h>
#include <furi_hal.h>
#include <furi_hal_rtc.h>
#include <stdint.h>
#include <u8g2_glue.h>

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -10,6 +10,7 @@
struct ViewPort {
Gui* gui;
FuriMutex* mutex;
bool is_enabled;
ViewPortOrientation orientation;

View File

@ -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);

View File

@ -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");
}
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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 = {

View File

@ -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;
}

View File

@ -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

View File

@ -141,6 +141,7 @@ typedef enum {
StorageCommandSDInfo,
StorageCommandSDStatus,
StorageCommandCommonResolvePath,
StorageCommandSDMount,
} StorageCommand;
typedef struct {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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",

View File

@ -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(

View File

@ -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);

View File

@ -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(

View 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",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Some files were not shown because too many files have changed in this diff Show More