Merge branch 'dev' into release-candidate
This commit is contained in:
commit
f6d6a626ea
10
.github/CODEOWNERS
vendored
10
.github/CODEOWNERS
vendored
@ -17,7 +17,6 @@
|
||||
/applications/gui/ @skotopes @DrZlo13 @hedger
|
||||
/applications/ibutton/ @skotopes @DrZlo13 @hedger @gsurkov
|
||||
/applications/infrared/ @skotopes @DrZlo13 @hedger @gsurkov
|
||||
/applications/infrared_monitor/ @skotopes @DrZlo13 @hedger @gsurkov
|
||||
/applications/input/ @skotopes @DrZlo13 @hedger
|
||||
/applications/lfrfid/ @skotopes @DrZlo13 @hedger
|
||||
/applications/lfrfid_debug/ @skotopes @DrZlo13 @hedger
|
||||
@ -45,12 +44,8 @@
|
||||
# Debug tools and plugins
|
||||
/debug/ @skotopes @DrZlo13 @hedger
|
||||
|
||||
# Docker
|
||||
/docker/ @skotopes @DrZlo13 @hedger @aprosvetova
|
||||
/docker-compose.yml @skotopes @DrZlo13 @hedger @aprosvetova
|
||||
|
||||
# Documentation
|
||||
/documentation/ @skotopes @DrZlo13 @hedger @aprosvetova
|
||||
/documentation/ @skotopes @DrZlo13 @hedger @drunkbatya
|
||||
|
||||
# Firmware targets
|
||||
/firmware/ @skotopes @DrZlo13 @hedger
|
||||
@ -84,8 +79,5 @@
|
||||
/lib/u8g2/ @skotopes @DrZlo13 @hedger
|
||||
/lib/update_util/ @skotopes @DrZlo13 @hedger
|
||||
|
||||
# Make tools
|
||||
/make/ @skotopes @DrZlo13 @hedger @aprosvetova
|
||||
|
||||
# Helper scripts
|
||||
/scripts/ @skotopes @DrZlo13 @hedger
|
||||
|
||||
11
.github/actions/docker/action.yml
vendored
11
.github/actions/docker/action.yml
vendored
@ -1,11 +0,0 @@
|
||||
name: 'Run in docker'
|
||||
inputs:
|
||||
run: # id of input
|
||||
description: 'A command to run'
|
||||
required: true
|
||||
default: ''
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: '../../../docker/Dockerfile'
|
||||
args:
|
||||
- ${{ inputs.run }}
|
||||
122
.github/workflows/amap_analyse.yml
vendored
Normal file
122
.github/workflows/amap_analyse.yml
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
name: 'Analyze .map file with Amap'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- "release*"
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TARGETS: f7
|
||||
|
||||
jobs:
|
||||
amap_analyse:
|
||||
runs-on: [self-hosted,FlipperZeroMacShell]
|
||||
steps:
|
||||
- name: 'Wait Build workflow'
|
||||
uses: fountainhead/action-wait-for-check@v1.0.0
|
||||
id: wait-for-build
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
checkName: 'main'
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
intervalSeconds: 20
|
||||
|
||||
- name: 'Check Build workflow status'
|
||||
if: steps.wait-for-build.outputs.conclusion == 'failure'
|
||||
run: |
|
||||
exit 1
|
||||
|
||||
- name: 'Decontaminate previous build leftovers'
|
||||
run: |
|
||||
if [ -d .git ]; then
|
||||
git submodule status \
|
||||
|| git checkout `git rev-list --max-parents=0 HEAD | tail -n 1`
|
||||
fi
|
||||
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: 'Generate prefixes by commit'
|
||||
id: names
|
||||
run: |
|
||||
REF="${{github.ref}}"
|
||||
COMMIT_HASH="$(git rev-parse HEAD)"
|
||||
SHA="$(git rev-parse --short HEAD)"
|
||||
COMMIT_MSG="${{github.event.head_commit.message}}"
|
||||
if [[ ${{ github.event_name }} == 'pull_request' ]]; then
|
||||
REF="${{github.head_ref}}"
|
||||
COMMIT_HASH="$(git log -1 --pretty=oneline | awk '{print $1}')"
|
||||
SHA="$(cut -c -8 <<< "$COMMIT_HASH")"
|
||||
COMMIT_MSG="$(git log -1 --pretty=format:"%s")"
|
||||
PULL_ID="${{github.event.pull_request.number}}"
|
||||
PULL_NAME="${{github.event.pull_request.title}}"
|
||||
fi
|
||||
BRANCH_NAME=${REF#refs/*/}
|
||||
SUFFIX=${BRANCH_NAME//\//_}-$(date +'%d%m%Y')-${SHA}
|
||||
if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
|
||||
SUFFIX=${BRANCH_NAME//\//_}
|
||||
fi
|
||||
echo "::set-output name=commit-hash::${COMMIT_HASH}"
|
||||
echo "::set-output name=commit-msg::${COMMIT_MSG}"
|
||||
echo "::set-output name=pull-id::${PULL_ID}"
|
||||
echo "::set-output name=pull-name::${PULL_NAME}"
|
||||
echo "::set-output name=branch-name::${BRANCH_NAME}"
|
||||
echo "::set-output name=suffix::${SUFFIX}"
|
||||
|
||||
- name: 'Make artifacts directory'
|
||||
run: |
|
||||
rm -rf artifacts
|
||||
mkdir artifacts
|
||||
|
||||
- name: 'Download build artifacts'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
run: |
|
||||
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
|
||||
chmod 600 ./deploy_key;
|
||||
rsync -avzP \
|
||||
-e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \
|
||||
${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:"${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${{steps.names.outputs.branch-name}}/" artifacts/;
|
||||
rm ./deploy_key;
|
||||
|
||||
- name: 'Make .map file analyse'
|
||||
run: |
|
||||
cd artifacts/
|
||||
/Applications/amap/Contents/MacOS/amap -f flipper-z-f7-firmware-${{steps.names.outputs.suffix}}.elf.map
|
||||
|
||||
- name: 'Upload report to DB'
|
||||
run: |
|
||||
FBT_TOOLCHAIN_PATH=/opt source scripts/toolchain/fbtenv.sh
|
||||
get_size()
|
||||
{
|
||||
SECTION="$1";
|
||||
arm-none-eabi-size \
|
||||
-A artifacts/flipper-z-f7-firmware-${{steps.names.outputs.suffix}}.elf \
|
||||
| grep "^$SECTION" | awk '{print $2}'
|
||||
}
|
||||
export COMMIT_HASH="${{steps.names.outputs.commit-hash}}"
|
||||
export COMMIT_MSG="${{steps.names.outputs.commit-msg}}"
|
||||
export BRANCH_NAME="${{steps.names.outputs.branch-name}}"
|
||||
export BSS_SIZE="$(get_size ".bss")"
|
||||
export TEXT_SIZE="$(get_size ".text")"
|
||||
export RODATA_SIZE="$(get_size ".rodata")"
|
||||
export DATA_SIZE="$(get_size ".data")"
|
||||
export FREE_FLASH_SIZE="$(get_size ".free_flash")"
|
||||
if [[ ${{ github.event_name }} == 'pull_request' ]]; then
|
||||
export PULL_ID="${{steps.names.outputs.pull-id}}"
|
||||
export PULL_NAME="${{steps.names.outputs.pull-name}}"
|
||||
fi
|
||||
python3 -m pip install mariadb
|
||||
python3 scripts/amap_mariadb_insert.py \
|
||||
${{ secrets.AMAP_MARIADB_USER }} \
|
||||
${{ secrets.AMAP_MARIADB_PASSWORD }} \
|
||||
${{ secrets.AMAP_MARIADB_HOST }} \
|
||||
${{ secrets.AMAP_MARIADB_PORT }} \
|
||||
${{ secrets.AMAP_MARIADB_DATABASE }} \
|
||||
artifacts/flipper-z-f7-firmware-${{steps.names.outputs.suffix}}.elf.map.all
|
||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -108,6 +108,10 @@ jobs:
|
||||
FBT_TOOLCHAIN_PATH=/opt ./fbt copro_dist
|
||||
tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz -C assets core2_firmware
|
||||
|
||||
- name: 'Copy .map file'
|
||||
run: |
|
||||
cp build/f7-firmware-D/firmware.elf.map artifacts/flipper-z-f7-firmware-${{steps.names.outputs.suffix}}.elf.map
|
||||
|
||||
- name: 'Upload artifacts to update server'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
run: |
|
||||
|
||||
1
.github/workflows/check_submodules.yml
vendored
1
.github/workflows/check_submodules.yml
vendored
@ -25,6 +25,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: 'Check protobuf branch'
|
||||
run: |
|
||||
|
||||
1
.github/workflows/lint_c.yml
vendored
1
.github/workflows/lint_c.yml
vendored
@ -28,6 +28,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: 'Check code formatting'
|
||||
id: syntax_check
|
||||
|
||||
1
.github/workflows/lint_python.yml
vendored
1
.github/workflows/lint_python.yml
vendored
@ -25,6 +25,7 @@ jobs:
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: 'Check code formatting'
|
||||
run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/opt ./fbt lint_py
|
||||
|
||||
107
.github/workflows/pvs_studio.yml
vendored
Normal file
107
.github/workflows/pvs_studio.yml
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
name: 'Static C/C++ analysis with PVS-Studio'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- "release*"
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TARGETS: f7
|
||||
DEFAULT_TARGET: f7
|
||||
|
||||
jobs:
|
||||
analyse_c_cpp:
|
||||
runs-on: [self-hosted, FlipperZeroShell]
|
||||
steps:
|
||||
- name: 'Decontaminate previous build leftovers'
|
||||
run: |
|
||||
if [ -d .git ]
|
||||
then
|
||||
git submodule status \
|
||||
|| git checkout `git rev-list --max-parents=0 HEAD | tail -n 1`
|
||||
fi
|
||||
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: 'Generate suffix and folder name'
|
||||
id: names
|
||||
run: |
|
||||
REF=${{ github.ref }}
|
||||
if [[ ${{ github.event_name }} == 'pull_request' ]]; then
|
||||
REF=${{ github.head_ref }}
|
||||
fi
|
||||
BRANCH_OR_TAG=${REF#refs/*/}
|
||||
SHA=$(git rev-parse --short HEAD)
|
||||
|
||||
if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
|
||||
SUFFIX=${BRANCH_OR_TAG//\//_}
|
||||
else
|
||||
SUFFIX=${BRANCH_OR_TAG//\//_}-$(date +'%d%m%Y')-${SHA}
|
||||
fi
|
||||
|
||||
echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV
|
||||
echo "DIST_SUFFIX=${SUFFIX}" >> $GITHUB_ENV
|
||||
echo "::set-output name=artifacts-path::${BRANCH_OR_TAG}"
|
||||
echo "::set-output name=suffix::${SUFFIX}"
|
||||
echo "::set-output name=short-hash::${SHA}"
|
||||
echo "::set-output name=default-target::${DEFAULT_TARGET}"
|
||||
|
||||
- name: 'Make reports directory'
|
||||
run: |
|
||||
rm -rf reports/
|
||||
mkdir reports
|
||||
|
||||
- name: 'Generate compile_comands.json'
|
||||
run: |
|
||||
FBT_TOOLCHAIN_PATH=/opt ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking
|
||||
|
||||
- name: 'Static code analysis'
|
||||
run: |
|
||||
FBT_TOOLCHAIN_PATH=/opt source scripts/toolchain/fbtenv.sh
|
||||
pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }}
|
||||
pvs-studio-analyzer analyze \
|
||||
@.pvsoptions \
|
||||
-j$(grep -c processor /proc/cpuinfo) \
|
||||
-f build/f7-firmware-DC/compile_commands.json \
|
||||
-o PVS-Studio.log
|
||||
|
||||
- name: 'Convert PVS-Studio output to html page'
|
||||
run: plog-converter -a GA:1,2,3 -t fullhtml PVS-Studio.log -o reports/${{steps.names.outputs.default-target}}-${{steps.names.outputs.suffix}}
|
||||
|
||||
- name: 'Upload artifacts to update server'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
run: |
|
||||
echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key;
|
||||
chmod 600 ./deploy_key;
|
||||
rsync -avrzP --mkpath \
|
||||
-e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \
|
||||
reports/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:/home/data/firmware-pvs-studio-report/"${{steps.names.outputs.artifacts-path}}/";
|
||||
rm ./deploy_key;
|
||||
|
||||
- name: 'Find Previous Comment'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }}
|
||||
uses: peter-evans/find-comment@v1
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: 'PVS-Studio report for commit'
|
||||
|
||||
- name: 'Create or update comment'
|
||||
if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request}}
|
||||
uses: peter-evans/create-or-update-comment@v1
|
||||
with:
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
**PVS-Studio report for commit `${{steps.names.outputs.short-hash}}`:**
|
||||
- [Report](https://update.flipperzero.one/builds/firmware-pvs-studio-report/${{steps.names.outputs.artifacts-path}}/${{steps.names.outputs.default-target}}-${{steps.names.outputs.suffix}}/index.html)
|
||||
edit-mode: replace
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -50,3 +50,7 @@ build/
|
||||
|
||||
# openocd output file
|
||||
openocd.log
|
||||
|
||||
# PVS Studio temporary files
|
||||
.PVS-Studio/
|
||||
PVS-Studio.log
|
||||
|
||||
22
.pvsconfig
Normal file
22
.pvsconfig
Normal file
@ -0,0 +1,22 @@
|
||||
# MLib macros we can't do much about.
|
||||
//-V:M_EACH:1048,1044
|
||||
//-V:ARRAY_DEF:760,747,568,776,729,712,654
|
||||
//-V:LIST_DEF:760,747,568,712,729,654,776
|
||||
//-V:BPTREE_DEF2:779,1086,557,773,512
|
||||
//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685
|
||||
//-V:ALGO_DEF:1048,747,1044
|
||||
|
||||
# Non-severe malloc/null pointer deref warnings
|
||||
//-V::522:2,3
|
||||
|
||||
# Warning about headers with copyleft license
|
||||
//-V::1042
|
||||
|
||||
# Potentially null argument warnings
|
||||
//-V:memset:575
|
||||
//-V:memcpy:575
|
||||
//-V:strcpy:575
|
||||
//-V:strchr:575
|
||||
|
||||
# For loop warning on M_FOREACH
|
||||
//-V:for:1044
|
||||
1
.pvsoptions
Normal file
1
.pvsoptions
Normal file
@ -0,0 +1 @@
|
||||
--rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e */arm-none-eabi/*
|
||||
24
ReadMe.md
24
ReadMe.md
@ -61,29 +61,6 @@ One liner: `./fbt firmware_flash`
|
||||
|
||||
3. Run `dfu-util -D full.dfu -a 0`
|
||||
|
||||
# Build with Docker
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Install [Docker Engine and Docker Compose](https://www.docker.com/get-started)
|
||||
2. Prepare the container:
|
||||
|
||||
```sh
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Compile everything
|
||||
|
||||
```sh
|
||||
docker-compose exec dev ./fbt
|
||||
```
|
||||
|
||||
Check `dist/` for build outputs.
|
||||
|
||||
Use **`flipper-z-{target}-full-{suffix}.dfu`** to flash your device.
|
||||
|
||||
If compilation fails, make sure all submodules are all initialized. Either clone with `--recursive` or use `git submodule update --init --recursive`.
|
||||
|
||||
# Build on Linux/macOS
|
||||
|
||||
Check out `documentation/fbt.md` for details on building and flashing firmware.
|
||||
@ -157,7 +134,6 @@ Connect your device via ST-Link and run:
|
||||
- `assets` - Assets used by applications and services
|
||||
- `furi` - Furi Core: os level primitives and helpers
|
||||
- `debug` - Debug tool: GDB-plugins, SVD-file and etc
|
||||
- `docker` - Docker image sources (used for firmware build automation)
|
||||
- `documentation` - Documentation generation system configs and input files
|
||||
- `firmware` - Firmware source code
|
||||
- `lib` - Our and 3rd party libraries, drivers and etc...
|
||||
|
||||
@ -29,23 +29,13 @@ bool archive_app_is_available(void* context, const char* path) {
|
||||
|
||||
if(app == ArchiveAppTypeU2f) {
|
||||
bool file_exists = false;
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(fs_api);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
file_exists =
|
||||
storage_file_open(file, ANY_PATH("u2f/key.u2f"), FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(file_exists) {
|
||||
storage_file_close(file);
|
||||
file_exists =
|
||||
storage_file_open(file, ANY_PATH("u2f/cnt.u2f"), FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(file_exists) {
|
||||
storage_file_close(file);
|
||||
}
|
||||
if(storage_file_exists(storage, ANY_PATH("u2f/key.u2f"))) {
|
||||
file_exists = storage_file_exists(storage, ANY_PATH("u2f/cnt.u2f"));
|
||||
}
|
||||
|
||||
storage_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return file_exists;
|
||||
} else {
|
||||
return false;
|
||||
|
||||
@ -77,14 +77,24 @@ static void archive_long_load_cb(void* context) {
|
||||
});
|
||||
}
|
||||
|
||||
void archive_file_browser_set_callbacks(ArchiveBrowserView* browser) {
|
||||
static void archive_file_browser_set_path(
|
||||
ArchiveBrowserView* browser,
|
||||
string_t path,
|
||||
const char* filter_ext,
|
||||
bool skip_assets) {
|
||||
furi_assert(browser);
|
||||
|
||||
file_browser_worker_set_callback_context(browser->worker, browser);
|
||||
file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb);
|
||||
file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb);
|
||||
file_browser_worker_set_item_callback(browser->worker, archive_list_item_cb);
|
||||
file_browser_worker_set_long_load_callback(browser->worker, archive_long_load_cb);
|
||||
if(!browser->worker_running) {
|
||||
browser->worker = file_browser_worker_alloc(path, filter_ext, skip_assets);
|
||||
file_browser_worker_set_callback_context(browser->worker, browser);
|
||||
file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb);
|
||||
file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb);
|
||||
file_browser_worker_set_item_callback(browser->worker, archive_list_item_cb);
|
||||
file_browser_worker_set_long_load_callback(browser->worker, archive_long_load_cb);
|
||||
browser->worker_running = true;
|
||||
} else {
|
||||
furi_assert(browser->worker);
|
||||
file_browser_worker_set_config(browser->worker, path, filter_ext, skip_assets);
|
||||
}
|
||||
}
|
||||
|
||||
bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx) {
|
||||
@ -438,8 +448,8 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
|
||||
tab = archive_get_tab(browser);
|
||||
if(archive_is_dir_exists(browser->path)) {
|
||||
bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true;
|
||||
file_browser_worker_set_config(
|
||||
browser->worker, browser->path, archive_get_tab_ext(tab), skip_assets);
|
||||
archive_file_browser_set_path(
|
||||
browser, browser->path, archive_get_tab_ext(tab), skip_assets);
|
||||
tab_empty = false; // Empty check will be performed later
|
||||
} else {
|
||||
tab_empty = true;
|
||||
|
||||
@ -87,4 +87,3 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key);
|
||||
void archive_enter_dir(ArchiveBrowserView* browser, string_t name);
|
||||
void archive_leave_dir(ArchiveBrowserView* browser);
|
||||
void archive_refresh_dir(ArchiveBrowserView* browser);
|
||||
void archive_file_browser_set_callbacks(ArchiveBrowserView* browser);
|
||||
|
||||
@ -82,9 +82,8 @@ uint16_t archive_favorites_count(void* context) {
|
||||
static bool archive_favourites_rescan() {
|
||||
string_t buffer;
|
||||
string_init(buffer);
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(fs_api);
|
||||
File* fav_item_file = storage_file_alloc(fs_api);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(storage);
|
||||
|
||||
bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(result) {
|
||||
@ -101,13 +100,8 @@ static bool archive_favourites_rescan() {
|
||||
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
|
||||
}
|
||||
} else {
|
||||
bool file_exists = storage_file_open(
|
||||
fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(file_exists) {
|
||||
storage_file_close(fav_item_file);
|
||||
if(storage_file_exists(storage, string_get_cstr(buffer))) {
|
||||
archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
|
||||
} else {
|
||||
storage_file_close(fav_item_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,12 +110,11 @@ static bool archive_favourites_rescan() {
|
||||
string_clear(buffer);
|
||||
|
||||
storage_file_close(file);
|
||||
storage_common_remove(fs_api, ARCHIVE_FAV_PATH);
|
||||
storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
|
||||
storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH);
|
||||
storage_common_remove(storage, ARCHIVE_FAV_PATH);
|
||||
storage_common_rename(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
|
||||
storage_common_remove(storage, ARCHIVE_FAV_TEMP_PATH);
|
||||
|
||||
storage_file_free(file);
|
||||
storage_file_free(fav_item_file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
@ -131,9 +124,8 @@ bool archive_favorites_read(void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
ArchiveBrowserView* browser = context;
|
||||
Storage* fs_api = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(fs_api);
|
||||
File* fav_item_file = storage_file_alloc(fs_api);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(storage);
|
||||
|
||||
string_t buffer;
|
||||
FileInfo file_info;
|
||||
@ -163,16 +155,12 @@ bool archive_favorites_read(void* context) {
|
||||
need_refresh = true;
|
||||
}
|
||||
} else {
|
||||
bool file_exists = storage_file_open(
|
||||
fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING);
|
||||
if(file_exists) {
|
||||
storage_common_stat(fs_api, string_get_cstr(buffer), &file_info);
|
||||
storage_file_close(fav_item_file);
|
||||
if(storage_file_exists(storage, string_get_cstr(buffer))) {
|
||||
storage_common_stat(storage, string_get_cstr(buffer), &file_info);
|
||||
archive_add_file_item(
|
||||
browser, (file_info.flags & FSF_DIRECTORY), string_get_cstr(buffer));
|
||||
file_count++;
|
||||
} else {
|
||||
storage_file_close(fav_item_file);
|
||||
need_refresh = true;
|
||||
}
|
||||
}
|
||||
@ -183,7 +171,6 @@ bool archive_favorites_read(void* context) {
|
||||
storage_file_close(file);
|
||||
string_clear(buffer);
|
||||
storage_file_free(file);
|
||||
storage_file_free(fav_item_file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
archive_set_item_count(browser, file_count);
|
||||
|
||||
@ -370,18 +370,15 @@ ArchiveBrowserView* browser_alloc() {
|
||||
return true;
|
||||
});
|
||||
|
||||
browser->worker = file_browser_worker_alloc(browser->path, "*", false);
|
||||
archive_file_browser_set_callbacks(browser);
|
||||
|
||||
file_browser_worker_set_callback_context(browser->worker, browser);
|
||||
|
||||
return browser;
|
||||
}
|
||||
|
||||
void browser_free(ArchiveBrowserView* browser) {
|
||||
furi_assert(browser);
|
||||
|
||||
file_browser_worker_free(browser->worker);
|
||||
if(browser->worker_running) {
|
||||
file_browser_worker_free(browser->worker);
|
||||
}
|
||||
|
||||
with_view_model(
|
||||
browser->view, (ArchiveBrowserViewModel * model) {
|
||||
|
||||
@ -74,6 +74,7 @@ typedef enum {
|
||||
struct ArchiveBrowserView {
|
||||
View* view;
|
||||
BrowserWorker* worker;
|
||||
bool worker_running;
|
||||
ArchiveBrowserViewCallback callback;
|
||||
void* context;
|
||||
string_t path;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include <applications/cli/cli.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
|
||||
#include "ble.h"
|
||||
#include <ble/ble.h>
|
||||
#include "bt_settings.h"
|
||||
#include "bt_service/bt.h"
|
||||
|
||||
|
||||
6
applications/bt/bt_hid_app/bt_hid.c
Executable file → Normal file
6
applications/bt/bt_hid_app/bt_hid.c
Executable file → Normal file
@ -89,8 +89,7 @@ BtHid* bt_hid_app_alloc() {
|
||||
app->submenu, "Keynote", BtHidSubmenuIndexKeynote, bt_hid_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->submenu, "Media Player", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app);
|
||||
submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app);
|
||||
submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app);
|
||||
view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit);
|
||||
view_dispatcher_add_view(
|
||||
@ -134,7 +133,8 @@ BtHid* bt_hid_app_alloc() {
|
||||
app->view_dispatcher, BtHidViewMouse, bt_hid_mouse_get_view(app->bt_hid_mouse));
|
||||
|
||||
// TODO switch to menu after Media is done
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote);
|
||||
app->view_id = BtHidViewSubmenu;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
@ -43,7 +43,10 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
|
||||
}
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote");
|
||||
|
||||
canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit");
|
||||
|
||||
// Up
|
||||
canvas_draw_icon(canvas, 21, 24, &I_Button_18x18);
|
||||
@ -97,8 +100,8 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
|
||||
elements_slightly_rounded_box(canvas, 66, 47, 60, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 110, 49, &I_Ok_btn_9x9);
|
||||
elements_multiline_text_aligned(canvas, 76, 56, AlignLeft, AlignBottom, "Back");
|
||||
canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
|
||||
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
|
||||
}
|
||||
|
||||
static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* event) {
|
||||
|
||||
@ -49,7 +49,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
// Up
|
||||
if(model->up_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 96, 12, &I_Volup_8x6);
|
||||
@ -57,7 +59,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
// Down
|
||||
if(model->down_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 96, 45, &I_Voldwn_6x6);
|
||||
@ -65,7 +69,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
// Left
|
||||
if(model->left_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
bt_hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft);
|
||||
@ -74,7 +80,9 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
// Right
|
||||
if(model->right_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
bt_hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight);
|
||||
@ -89,6 +97,12 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
|
||||
bt_hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight);
|
||||
canvas_draw_line(canvas, 100, 29, 100, 33);
|
||||
canvas_draw_line(canvas, 102, 29, 102, 33);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Exit
|
||||
canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
|
||||
}
|
||||
|
||||
static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* event) {
|
||||
|
||||
@ -36,7 +36,11 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
if(model->left_mouse_held == true) {
|
||||
elements_multiline_text_aligned(canvas, 0, 60, AlignLeft, AlignBottom, "Selecting...");
|
||||
elements_multiline_text_aligned(canvas, 0, 62, AlignLeft, AlignBottom, "Selecting...");
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
|
||||
}
|
||||
|
||||
// Keypad circles
|
||||
@ -44,7 +48,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
// Up
|
||||
if(model->up_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 81, 9, &I_Pressed_Button_13x13);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up7x9);
|
||||
@ -52,7 +58,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
// Down
|
||||
if(model->down_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 84, 43, &I_Pin_arrow_down_7x9);
|
||||
@ -60,7 +68,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
// Left
|
||||
if(model->left_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 67, 28, &I_Pin_arrow_left_9x7);
|
||||
@ -68,7 +78,9 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
// Right
|
||||
if(model->right_pressed) {
|
||||
canvas_set_bitmap_mode(canvas, 1);
|
||||
canvas_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13);
|
||||
canvas_set_bitmap_mode(canvas, 0);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 99, 28, &I_Pin_arrow_right_9x7);
|
||||
@ -76,18 +88,17 @@ static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) {
|
||||
|
||||
// Ok
|
||||
if(model->left_mouse_pressed) {
|
||||
canvas_draw_icon(canvas, 81, 25, &I_Pressed_Button_13x13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_icon(canvas, 81, 25, &I_Ok_btn_pressed_13x13);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x9);
|
||||
}
|
||||
canvas_draw_icon(canvas, 83, 27, &I_Ok_btn_9x9);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Back
|
||||
if(model->right_mouse_pressed) {
|
||||
canvas_draw_icon(canvas, 108, 48, &I_Pressed_Button_13x13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_icon(canvas, 108, 48, &I_Ok_btn_pressed_13x13);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x9);
|
||||
}
|
||||
canvas_draw_icon(canvas, 110, 50, &I_Ok_btn_9x9);
|
||||
}
|
||||
|
||||
static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) {
|
||||
|
||||
@ -220,8 +220,7 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager
|
||||
furi_assert(blocking_animation);
|
||||
animation_manager->sd_shown_sd_ok = true;
|
||||
} else if(!animation_manager->sd_shown_no_db) {
|
||||
bool db_exists = storage_common_stat(storage, EXT_PATH("Manifest"), NULL) == FSE_OK;
|
||||
if(!db_exists) {
|
||||
if(!storage_file_exists(storage, EXT_PATH("Manifest"))) {
|
||||
blocking_animation = animation_storage_find_animation(NO_DB_ANIMATION_NAME);
|
||||
furi_assert(blocking_animation);
|
||||
animation_manager->sd_shown_no_db = true;
|
||||
|
||||
@ -94,6 +94,10 @@ bool slideshow_is_loaded(Slideshow* slideshow) {
|
||||
return slideshow->loaded;
|
||||
}
|
||||
|
||||
bool slideshow_is_one_page(Slideshow* slideshow) {
|
||||
return slideshow->loaded && (slideshow->icon.frame_count == 1);
|
||||
}
|
||||
|
||||
bool slideshow_advance(Slideshow* slideshow) {
|
||||
uint8_t next_frame = slideshow->current_frame + 1;
|
||||
if(next_frame < slideshow->icon.frame_count) {
|
||||
|
||||
@ -9,6 +9,7 @@ Slideshow* slideshow_alloc();
|
||||
void slideshow_free(Slideshow* slideshow);
|
||||
bool slideshow_load(Slideshow* slideshow, const char* fspath);
|
||||
bool slideshow_is_loaded(Slideshow* slideshow);
|
||||
bool slideshow_is_one_page(Slideshow* slideshow);
|
||||
void slideshow_goback(Slideshow* slideshow);
|
||||
bool slideshow_advance(Slideshow* slideshow);
|
||||
void slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y);
|
||||
|
||||
@ -23,11 +23,12 @@ void desktop_debug_render(Canvas* canvas, void* model) {
|
||||
const Version* ver;
|
||||
char buffer[64];
|
||||
|
||||
static const char* headers[] = {"FW Version Info:", "Dolphin Info:"};
|
||||
static const char* headers[] = {"Device Info:", "Dolphin Info:"};
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 9 + STATUS_BAR_Y_SHIFT, headers[m->screen]);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 1 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignTop, headers[m->screen]);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
if(m->screen != DesktopViewStatsMeta) {
|
||||
@ -44,7 +45,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
|
||||
furi_hal_version_get_hw_region_name(),
|
||||
furi_hal_region_get_name(),
|
||||
my_name ? my_name : "Unknown");
|
||||
canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer);
|
||||
canvas_draw_str(canvas, 0, 19 + STATUS_BAR_Y_SHIFT, buffer);
|
||||
|
||||
ver = furi_hal_version_get_firmware_version();
|
||||
const BleGlueC2Info* c2_ver = NULL;
|
||||
@ -52,7 +53,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
|
||||
c2_ver = ble_glue_get_c2_info();
|
||||
#endif
|
||||
if(!ver) {
|
||||
canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, "No info");
|
||||
canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, "No info");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -62,7 +63,7 @@ void desktop_debug_render(Canvas* canvas, void* model) {
|
||||
"%s [%s]",
|
||||
version_get_version(ver),
|
||||
version_get_builddate(ver));
|
||||
canvas_draw_str(canvas, 5, 28 + STATUS_BAR_Y_SHIFT, buffer);
|
||||
canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer);
|
||||
|
||||
snprintf(
|
||||
buffer,
|
||||
@ -72,11 +73,11 @@ void desktop_debug_render(Canvas* canvas, void* model) {
|
||||
version_get_githash(ver),
|
||||
version_get_gitbranchnum(ver),
|
||||
c2_ver ? c2_ver->StackTypeString : "<none>");
|
||||
canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer);
|
||||
canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer);
|
||||
|
||||
snprintf(
|
||||
buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), version_get_gitbranch(ver));
|
||||
canvas_draw_str(canvas, 5, 50 + STATUS_BAR_Y_SHIFT, buffer);
|
||||
canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer);
|
||||
|
||||
} else {
|
||||
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
|
||||
|
||||
@ -35,8 +35,9 @@ static bool desktop_view_slideshow_input(InputEvent* event, void* context) {
|
||||
furi_assert(event);
|
||||
DesktopSlideshowView* instance = context;
|
||||
|
||||
DesktopSlideshowViewModel* model = view_get_model(instance->view);
|
||||
bool update_view = false;
|
||||
if(event->type == InputTypeShort) {
|
||||
DesktopSlideshowViewModel* model = view_get_model(instance->view);
|
||||
bool end_slideshow = false;
|
||||
switch(event->key) {
|
||||
case InputKeyLeft:
|
||||
@ -54,15 +55,18 @@ static bool desktop_view_slideshow_input(InputEvent* event, void* context) {
|
||||
if(end_slideshow) {
|
||||
instance->callback(DesktopSlideshowCompleted, instance->context);
|
||||
}
|
||||
view_commit_model(instance->view, true);
|
||||
update_view = true;
|
||||
} else if(event->key == InputKeyOk) {
|
||||
if(event->type == InputTypePress) {
|
||||
furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT);
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
furi_timer_stop(instance->timer);
|
||||
furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG);
|
||||
if(!slideshow_is_one_page(model->slideshow)) {
|
||||
furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG);
|
||||
}
|
||||
}
|
||||
}
|
||||
view_commit_model(instance->view, update_view);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -79,12 +83,12 @@ static void desktop_view_slideshow_enter(void* context) {
|
||||
instance->timer =
|
||||
furi_timer_alloc(desktop_first_start_timer_callback, FuriTimerTypeOnce, instance);
|
||||
|
||||
furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG);
|
||||
|
||||
DesktopSlideshowViewModel* model = view_get_model(instance->view);
|
||||
model->slideshow = slideshow_alloc();
|
||||
if(!slideshow_load(model->slideshow, SLIDESHOW_FS_PATH)) {
|
||||
instance->callback(DesktopSlideshowCompleted, instance->context);
|
||||
} else if(!slideshow_is_one_page(model->slideshow)) {
|
||||
furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_LONG);
|
||||
}
|
||||
view_commit_model(instance->view, false);
|
||||
}
|
||||
|
||||
@ -14,8 +14,8 @@
|
||||
#define DOLPHIN_STATE_PATH INT_PATH(DOLPHIN_STATE_FILE_NAME)
|
||||
#define DOLPHIN_STATE_HEADER_MAGIC 0xD0
|
||||
#define DOLPHIN_STATE_HEADER_VERSION 0x01
|
||||
#define LEVEL2_THRESHOLD 735
|
||||
#define LEVEL3_THRESHOLD 2940
|
||||
#define LEVEL2_THRESHOLD 300
|
||||
#define LEVEL3_THRESHOLD 1800
|
||||
#define BUTTHURT_MAX 14
|
||||
#define BUTTHURT_MIN 0
|
||||
|
||||
|
||||
@ -99,6 +99,11 @@ static bool browser_folder_check_and_switch(string_t path) {
|
||||
FileInfo file_info;
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool is_root = false;
|
||||
|
||||
if(string_search_rchar(path, '/') == 0) {
|
||||
is_root = true;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
// Check if folder is existing and navigate back if not
|
||||
if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) {
|
||||
|
||||
@ -162,6 +162,19 @@ void widget_add_text_box_element(
|
||||
widget_add_element(widget, text_box_element);
|
||||
}
|
||||
|
||||
void widget_add_text_scroll_element(
|
||||
Widget* widget,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t width,
|
||||
uint8_t height,
|
||||
const char* text) {
|
||||
furi_assert(widget);
|
||||
WidgetElement* text_scroll_element =
|
||||
widget_element_text_scroll_create(x, y, width, height, text);
|
||||
widget_add_element(widget, text_scroll_element);
|
||||
}
|
||||
|
||||
void widget_add_button_element(
|
||||
Widget* widget,
|
||||
GuiButtonType button_type,
|
||||
|
||||
@ -105,6 +105,27 @@ void widget_add_text_box_element(
|
||||
const char* text,
|
||||
bool strip_to_dots);
|
||||
|
||||
/** Add Text Scroll Element
|
||||
*
|
||||
* @param widget Widget instance
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param width width to fit text
|
||||
* @param height height to fit text
|
||||
* @param[in] text Formatted text. Default format: align left, Secondary font.
|
||||
* The following formats are available:
|
||||
* "\e#Bold text" - sets bold font before until next '\n' symbol
|
||||
* "\ecCenter-aligned text" - sets center horizontal align until the next '\n' symbol
|
||||
* "\erRight-aligned text" - sets right horizontal align until the next '\n' symbol
|
||||
*/
|
||||
void widget_add_text_scroll_element(
|
||||
Widget* widget,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t width,
|
||||
uint8_t height,
|
||||
const char* text);
|
||||
|
||||
/** Add Button Element
|
||||
*
|
||||
* @param widget Widget instance
|
||||
|
||||
@ -29,6 +29,7 @@ struct WidgetElement {
|
||||
|
||||
// generic model holder
|
||||
void* model;
|
||||
FuriMutex* model_mutex;
|
||||
|
||||
// pointer to widget that hold our element
|
||||
Widget* parent;
|
||||
@ -80,3 +81,10 @@ WidgetElement* widget_element_frame_create(
|
||||
uint8_t width,
|
||||
uint8_t height,
|
||||
uint8_t radius);
|
||||
|
||||
WidgetElement* widget_element_text_scroll_create(
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t width,
|
||||
uint8_t height,
|
||||
const char* text);
|
||||
|
||||
@ -0,0 +1,245 @@
|
||||
#include "widget_element_i.h"
|
||||
#include <m-string.h>
|
||||
#include <gui/elements.h>
|
||||
#include <m-array.h>
|
||||
|
||||
#define WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET (4)
|
||||
|
||||
typedef struct {
|
||||
Font font;
|
||||
Align horizontal;
|
||||
string_t text;
|
||||
} TextScrollLineArray;
|
||||
|
||||
ARRAY_DEF(TextScrollLineArray, TextScrollLineArray, M_POD_OPLIST)
|
||||
|
||||
typedef struct {
|
||||
TextScrollLineArray_t line_array;
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
string_t text;
|
||||
uint8_t scroll_pos_total;
|
||||
uint8_t scroll_pos_current;
|
||||
bool text_formatted;
|
||||
} WidgetElementTextScrollModel;
|
||||
|
||||
static bool
|
||||
widget_element_text_scroll_process_ctrl_symbols(TextScrollLineArray* line, string_t text) {
|
||||
bool processed = false;
|
||||
|
||||
do {
|
||||
if(string_get_char(text, 0) != '\e') break;
|
||||
char ctrl_symbol = string_get_char(text, 1);
|
||||
if(ctrl_symbol == 'c') {
|
||||
line->horizontal = AlignCenter;
|
||||
} else if(ctrl_symbol == 'r') {
|
||||
line->horizontal = AlignRight;
|
||||
} else if(ctrl_symbol == '#') {
|
||||
line->font = FontPrimary;
|
||||
}
|
||||
string_right(text, 2);
|
||||
processed = true;
|
||||
} while(false);
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
void widget_element_text_scroll_add_line(WidgetElement* element, TextScrollLineArray* line) {
|
||||
WidgetElementTextScrollModel* model = element->model;
|
||||
TextScrollLineArray new_line;
|
||||
new_line.font = line->font;
|
||||
new_line.horizontal = line->horizontal;
|
||||
string_init_set(new_line.text, line->text);
|
||||
TextScrollLineArray_push_back(model->line_array, new_line);
|
||||
}
|
||||
|
||||
static void widget_element_text_scroll_fill_lines(Canvas* canvas, WidgetElement* element) {
|
||||
WidgetElementTextScrollModel* model = element->model;
|
||||
TextScrollLineArray line_tmp;
|
||||
bool all_text_processed = false;
|
||||
string_init(line_tmp.text);
|
||||
bool reached_new_line = true;
|
||||
uint16_t total_height = 0;
|
||||
|
||||
while(!all_text_processed) {
|
||||
if(reached_new_line) {
|
||||
// Set default line properties
|
||||
line_tmp.font = FontSecondary;
|
||||
line_tmp.horizontal = AlignLeft;
|
||||
string_reset(line_tmp.text);
|
||||
// Process control symbols
|
||||
while(widget_element_text_scroll_process_ctrl_symbols(&line_tmp, model->text))
|
||||
;
|
||||
}
|
||||
// Set canvas font
|
||||
canvas_set_font(canvas, line_tmp.font);
|
||||
CanvasFontParameters* params = canvas_get_font_params(canvas, line_tmp.font);
|
||||
total_height += params->height;
|
||||
if(total_height > model->height) {
|
||||
model->scroll_pos_total++;
|
||||
}
|
||||
|
||||
uint8_t line_width = 0;
|
||||
uint16_t char_i = 0;
|
||||
while(true) {
|
||||
char next_char = string_get_char(model->text, char_i++);
|
||||
if(next_char == '\0') {
|
||||
string_push_back(line_tmp.text, '\0');
|
||||
widget_element_text_scroll_add_line(element, &line_tmp);
|
||||
total_height += params->leading_default - params->height;
|
||||
all_text_processed = true;
|
||||
break;
|
||||
} else if(next_char == '\n') {
|
||||
string_push_back(line_tmp.text, '\0');
|
||||
widget_element_text_scroll_add_line(element, &line_tmp);
|
||||
string_right(model->text, char_i);
|
||||
total_height += params->leading_default - params->height;
|
||||
reached_new_line = true;
|
||||
break;
|
||||
} else {
|
||||
line_width += canvas_glyph_width(canvas, next_char);
|
||||
if(line_width > model->width) {
|
||||
string_push_back(line_tmp.text, '\0');
|
||||
widget_element_text_scroll_add_line(element, &line_tmp);
|
||||
string_right(model->text, char_i - 1);
|
||||
string_reset(line_tmp.text);
|
||||
total_height += params->leading_default - params->height;
|
||||
reached_new_line = false;
|
||||
break;
|
||||
} else {
|
||||
string_push_back(line_tmp.text, next_char);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(line_tmp.text);
|
||||
}
|
||||
|
||||
static void widget_element_text_scroll_draw(Canvas* canvas, WidgetElement* element) {
|
||||
furi_assert(canvas);
|
||||
furi_assert(element);
|
||||
|
||||
furi_mutex_acquire(element->model_mutex, FuriWaitForever);
|
||||
|
||||
WidgetElementTextScrollModel* model = element->model;
|
||||
if(!model->text_formatted) {
|
||||
widget_element_text_scroll_fill_lines(canvas, element);
|
||||
model->text_formatted = true;
|
||||
}
|
||||
|
||||
uint8_t y = model->y;
|
||||
uint8_t x = model->x;
|
||||
uint16_t curr_line = 0;
|
||||
if(TextScrollLineArray_size(model->line_array)) {
|
||||
TextScrollLineArray_it_t it;
|
||||
for(TextScrollLineArray_it(it, model->line_array); !TextScrollLineArray_end_p(it);
|
||||
TextScrollLineArray_next(it), curr_line++) {
|
||||
if(curr_line < model->scroll_pos_current) continue;
|
||||
TextScrollLineArray* line = TextScrollLineArray_ref(it);
|
||||
CanvasFontParameters* params = canvas_get_font_params(canvas, line->font);
|
||||
if(y + params->descender > model->y + model->height) break;
|
||||
canvas_set_font(canvas, line->font);
|
||||
if(line->horizontal == AlignLeft) {
|
||||
x = model->x;
|
||||
} else if(line->horizontal == AlignCenter) {
|
||||
x = (model->x + model->width) / 2;
|
||||
} else if(line->horizontal == AlignRight) {
|
||||
x = model->x + model->width;
|
||||
}
|
||||
canvas_draw_str_aligned(
|
||||
canvas, x, y, line->horizontal, AlignTop, string_get_cstr(line->text));
|
||||
y += params->leading_default;
|
||||
}
|
||||
// Draw scroll bar
|
||||
if(model->scroll_pos_total > 1) {
|
||||
elements_scrollbar_pos(
|
||||
canvas,
|
||||
model->x + model->width + WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET,
|
||||
model->y,
|
||||
model->height,
|
||||
model->scroll_pos_current,
|
||||
model->scroll_pos_total);
|
||||
}
|
||||
}
|
||||
|
||||
furi_mutex_release(element->model_mutex);
|
||||
}
|
||||
|
||||
static bool widget_element_text_scroll_input(InputEvent* event, WidgetElement* element) {
|
||||
furi_assert(event);
|
||||
furi_assert(element);
|
||||
|
||||
furi_mutex_acquire(element->model_mutex, FuriWaitForever);
|
||||
|
||||
WidgetElementTextScrollModel* model = element->model;
|
||||
bool consumed = false;
|
||||
|
||||
if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->scroll_pos_current > 0) {
|
||||
model->scroll_pos_current--;
|
||||
}
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
if((model->scroll_pos_total > 1) &&
|
||||
(model->scroll_pos_current < model->scroll_pos_total - 1)) {
|
||||
model->scroll_pos_current++;
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
furi_mutex_release(element->model_mutex);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static void widget_element_text_scroll_free(WidgetElement* text_scroll) {
|
||||
furi_assert(text_scroll);
|
||||
|
||||
WidgetElementTextScrollModel* model = text_scroll->model;
|
||||
TextScrollLineArray_it_t it;
|
||||
for(TextScrollLineArray_it(it, model->line_array); !TextScrollLineArray_end_p(it);
|
||||
TextScrollLineArray_next(it)) {
|
||||
TextScrollLineArray* line = TextScrollLineArray_ref(it);
|
||||
string_clear(line->text);
|
||||
}
|
||||
TextScrollLineArray_clear(model->line_array);
|
||||
string_clear(model->text);
|
||||
free(text_scroll->model);
|
||||
furi_mutex_free(text_scroll->model_mutex);
|
||||
free(text_scroll);
|
||||
}
|
||||
|
||||
WidgetElement* widget_element_text_scroll_create(
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t width,
|
||||
uint8_t height,
|
||||
const char* text) {
|
||||
furi_assert(text);
|
||||
|
||||
// Allocate and init model
|
||||
WidgetElementTextScrollModel* model = malloc(sizeof(WidgetElementTextScrollModel));
|
||||
model->x = x;
|
||||
model->y = y;
|
||||
model->width = width - WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET;
|
||||
model->height = height;
|
||||
model->scroll_pos_current = 0;
|
||||
model->scroll_pos_total = 1;
|
||||
TextScrollLineArray_init(model->line_array);
|
||||
string_init_set_str(model->text, text);
|
||||
|
||||
WidgetElement* text_scroll = malloc(sizeof(WidgetElement));
|
||||
text_scroll->parent = NULL;
|
||||
text_scroll->draw = widget_element_text_scroll_draw;
|
||||
text_scroll->input = widget_element_text_scroll_input;
|
||||
text_scroll->free = widget_element_text_scroll_free;
|
||||
text_scroll->model = model;
|
||||
text_scroll->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
|
||||
return text_scroll;
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
#include "decoder_analyzer.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
// FIXME: unused args?
|
||||
bool DecoderAnalyzer::read(uint8_t* /* _data */, uint8_t /* _data_size */) {
|
||||
bool result = false;
|
||||
|
||||
if(ready) {
|
||||
result = true;
|
||||
|
||||
for(size_t i = 0; i < data_size; i++) {
|
||||
printf("%lu ", data[i]);
|
||||
if((i + 1) % 8 == 0) printf("\r\n");
|
||||
}
|
||||
printf("\r\n--------\r\n");
|
||||
|
||||
ready = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DecoderAnalyzer::process_front(bool polarity, uint32_t time) {
|
||||
UNUSED(polarity);
|
||||
if(ready) return;
|
||||
|
||||
data[data_index] = time;
|
||||
|
||||
if(data_index < data_size) {
|
||||
data_index++;
|
||||
} else {
|
||||
data_index = 0;
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
DecoderAnalyzer::DecoderAnalyzer() {
|
||||
data = reinterpret_cast<uint32_t*>(calloc(data_size, sizeof(uint32_t)));
|
||||
furi_check(data);
|
||||
data_index = 0;
|
||||
ready = false;
|
||||
}
|
||||
|
||||
DecoderAnalyzer::~DecoderAnalyzer() {
|
||||
free(data);
|
||||
}
|
||||
|
||||
void DecoderAnalyzer::reset_state() {
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
|
||||
class DecoderAnalyzer {
|
||||
public:
|
||||
bool read(uint8_t* data, uint8_t data_size);
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
|
||||
DecoderAnalyzer();
|
||||
~DecoderAnalyzer();
|
||||
|
||||
private:
|
||||
void reset_state();
|
||||
|
||||
std::atomic<bool> ready;
|
||||
|
||||
static const uint32_t data_size = 2048;
|
||||
uint32_t data_index = 0;
|
||||
uint32_t* data;
|
||||
};
|
||||
@ -1,72 +0,0 @@
|
||||
#include "emmarin.h"
|
||||
#include "decoder_emmarin.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
constexpr uint32_t clocks_in_us = 64;
|
||||
constexpr uint32_t short_time = 255 * clocks_in_us;
|
||||
constexpr uint32_t long_time = 510 * clocks_in_us;
|
||||
constexpr uint32_t jitter_time = 100 * clocks_in_us;
|
||||
|
||||
constexpr uint32_t short_time_low = short_time - jitter_time;
|
||||
constexpr uint32_t short_time_high = short_time + jitter_time;
|
||||
constexpr uint32_t long_time_low = long_time - jitter_time;
|
||||
constexpr uint32_t long_time_high = long_time + jitter_time;
|
||||
|
||||
void DecoderEMMarin::reset_state() {
|
||||
ready = false;
|
||||
read_data = 0;
|
||||
manchester_advance(
|
||||
manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr);
|
||||
}
|
||||
|
||||
bool DecoderEMMarin::read(uint8_t* data, uint8_t data_size) {
|
||||
bool result = false;
|
||||
|
||||
if(ready) {
|
||||
result = true;
|
||||
em_marin.decode(
|
||||
reinterpret_cast<const uint8_t*>(&read_data), sizeof(uint64_t), data, data_size);
|
||||
ready = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DecoderEMMarin::process_front(bool polarity, uint32_t time) {
|
||||
if(ready) return;
|
||||
if(time < short_time_low) return;
|
||||
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
|
||||
if(time > short_time_low && time < short_time_high) {
|
||||
if(polarity) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else {
|
||||
event = ManchesterEventShortLow;
|
||||
}
|
||||
} else if(time > long_time_low && time < long_time_high) {
|
||||
if(polarity) {
|
||||
event = ManchesterEventLongHigh;
|
||||
} else {
|
||||
event = ManchesterEventLongLow;
|
||||
}
|
||||
}
|
||||
|
||||
if(event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok =
|
||||
manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data);
|
||||
|
||||
if(data_ok) {
|
||||
read_data = (read_data << 1) | data;
|
||||
|
||||
ready = em_marin.can_be_decoded(
|
||||
reinterpret_cast<const uint8_t*>(&read_data), sizeof(uint64_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DecoderEMMarin::DecoderEMMarin() {
|
||||
reset_state();
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
#include "protocols/protocol_emmarin.h"
|
||||
class DecoderEMMarin {
|
||||
public:
|
||||
bool read(uint8_t* data, uint8_t data_size);
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
|
||||
DecoderEMMarin();
|
||||
|
||||
private:
|
||||
void reset_state();
|
||||
|
||||
uint64_t read_data = 0;
|
||||
std::atomic<bool> ready;
|
||||
|
||||
ManchesterState manchester_saved_state;
|
||||
ProtocolEMMarin em_marin;
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
#include "decoder_gpio_out.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
void DecoderGpioOut::process_front(bool polarity, uint32_t /* time */) {
|
||||
furi_hal_gpio_write(&gpio_ext_pa7, polarity);
|
||||
}
|
||||
|
||||
DecoderGpioOut::DecoderGpioOut() {
|
||||
furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull);
|
||||
}
|
||||
|
||||
DecoderGpioOut::~DecoderGpioOut() {
|
||||
furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog);
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
|
||||
class DecoderGpioOut {
|
||||
public:
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
|
||||
DecoderGpioOut();
|
||||
~DecoderGpioOut();
|
||||
|
||||
private:
|
||||
void reset_state();
|
||||
};
|
||||
@ -1,98 +0,0 @@
|
||||
#include "decoder_hid26.h"
|
||||
#include <furi_hal.h>
|
||||
|
||||
constexpr uint32_t clocks_in_us = 64;
|
||||
|
||||
constexpr uint32_t jitter_time_us = 20;
|
||||
constexpr uint32_t min_time_us = 64;
|
||||
constexpr uint32_t max_time_us = 80;
|
||||
|
||||
constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us;
|
||||
constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us;
|
||||
constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us;
|
||||
|
||||
bool DecoderHID26::read(uint8_t* data, uint8_t data_size) {
|
||||
bool result = false;
|
||||
furi_assert(data_size >= 3);
|
||||
|
||||
if(ready) {
|
||||
result = true;
|
||||
hid.decode(
|
||||
reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3, data, data_size);
|
||||
ready = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DecoderHID26::process_front(bool polarity, uint32_t time) {
|
||||
if(ready) return;
|
||||
|
||||
if(polarity == true) {
|
||||
last_pulse_time = time;
|
||||
} else {
|
||||
last_pulse_time += time;
|
||||
|
||||
if(last_pulse_time > min_time && last_pulse_time < max_time) {
|
||||
bool pulse;
|
||||
|
||||
if(last_pulse_time < mid_time) {
|
||||
// 6 pulses
|
||||
pulse = false;
|
||||
} else {
|
||||
// 5 pulses
|
||||
pulse = true;
|
||||
}
|
||||
|
||||
if(last_pulse == pulse) {
|
||||
pulse_count++;
|
||||
|
||||
if(pulse) {
|
||||
if(pulse_count > 4) {
|
||||
pulse_count = 0;
|
||||
store_data(1);
|
||||
}
|
||||
} else {
|
||||
if(pulse_count > 5) {
|
||||
pulse_count = 0;
|
||||
store_data(0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(last_pulse) {
|
||||
if(pulse_count > 2) {
|
||||
store_data(1);
|
||||
}
|
||||
} else {
|
||||
if(pulse_count > 3) {
|
||||
store_data(0);
|
||||
}
|
||||
}
|
||||
|
||||
pulse_count = 0;
|
||||
last_pulse = pulse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DecoderHID26::DecoderHID26() {
|
||||
reset_state();
|
||||
}
|
||||
|
||||
void DecoderHID26::store_data(bool data) {
|
||||
stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1);
|
||||
stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1);
|
||||
stored_data[2] = (stored_data[2] << 1) | data;
|
||||
|
||||
if(hid.can_be_decoded(reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3)) {
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DecoderHID26::reset_state() {
|
||||
last_pulse = false;
|
||||
pulse_count = 0;
|
||||
ready = false;
|
||||
last_pulse_time = 0;
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
#include "protocols/protocol_hid_h10301.h"
|
||||
|
||||
class DecoderHID26 {
|
||||
public:
|
||||
bool read(uint8_t* data, uint8_t data_size);
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
DecoderHID26();
|
||||
|
||||
private:
|
||||
uint32_t last_pulse_time = 0;
|
||||
bool last_pulse;
|
||||
uint8_t pulse_count;
|
||||
|
||||
uint32_t stored_data[3] = {0, 0, 0};
|
||||
void store_data(bool data);
|
||||
|
||||
std::atomic<bool> ready;
|
||||
|
||||
void reset_state();
|
||||
ProtocolHID10301 hid;
|
||||
};
|
||||
@ -1,76 +0,0 @@
|
||||
#include "decoder_indala.h"
|
||||
#include <furi_hal.h>
|
||||
|
||||
constexpr uint32_t clocks_in_us = 64;
|
||||
constexpr uint32_t us_per_bit = 255;
|
||||
|
||||
bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
|
||||
bool result = false;
|
||||
|
||||
if(ready) {
|
||||
result = true;
|
||||
if(cursed_data_valid) {
|
||||
indala.decode(
|
||||
reinterpret_cast<const uint8_t*>(&cursed_raw_data),
|
||||
sizeof(uint64_t),
|
||||
data,
|
||||
data_size);
|
||||
} else {
|
||||
indala.decode(
|
||||
reinterpret_cast<const uint8_t*>(&raw_data), sizeof(uint64_t), data, data_size);
|
||||
}
|
||||
reset_state();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DecoderIndala::process_front(bool polarity, uint32_t time) {
|
||||
if(ready) return;
|
||||
|
||||
process_internal(polarity, time, &raw_data);
|
||||
if(ready) return;
|
||||
|
||||
if(polarity) {
|
||||
time = time + 110;
|
||||
} else {
|
||||
time = time - 110;
|
||||
}
|
||||
|
||||
process_internal(!polarity, time, &cursed_raw_data);
|
||||
if(ready) {
|
||||
cursed_data_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DecoderIndala::process_internal(bool polarity, uint32_t time, uint64_t* data) {
|
||||
time /= clocks_in_us;
|
||||
time += (us_per_bit / 2);
|
||||
|
||||
uint32_t bit_count = (time / us_per_bit);
|
||||
|
||||
if(bit_count < 64) {
|
||||
for(uint32_t i = 0; i < bit_count; i++) {
|
||||
*data = (*data << 1) | polarity;
|
||||
|
||||
if((*data >> 32) == 0xa0000000ULL) {
|
||||
if(indala.can_be_decoded(
|
||||
reinterpret_cast<const uint8_t*>(data), sizeof(uint64_t))) {
|
||||
ready = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DecoderIndala::DecoderIndala() {
|
||||
reset_state();
|
||||
}
|
||||
|
||||
void DecoderIndala::reset_state() {
|
||||
raw_data = 0;
|
||||
cursed_raw_data = 0;
|
||||
ready = false;
|
||||
cursed_data_valid = false;
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <atomic>
|
||||
#include "protocols/protocol_indala_40134.h"
|
||||
|
||||
class DecoderIndala {
|
||||
public:
|
||||
bool read(uint8_t* data, uint8_t data_size);
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
|
||||
void process_internal(bool polarity, uint32_t time, uint64_t* data);
|
||||
|
||||
DecoderIndala();
|
||||
|
||||
private:
|
||||
void reset_state();
|
||||
|
||||
uint64_t raw_data;
|
||||
uint64_t cursed_raw_data;
|
||||
|
||||
std::atomic<bool> ready;
|
||||
std::atomic<bool> cursed_data_valid;
|
||||
ProtocolIndala40134 indala;
|
||||
};
|
||||
@ -1,107 +0,0 @@
|
||||
#include "decoder_ioprox.h"
|
||||
#include <furi_hal.h>
|
||||
#include <cli/cli.h>
|
||||
#include <utility>
|
||||
|
||||
constexpr uint32_t clocks_in_us = 64;
|
||||
|
||||
constexpr uint32_t jitter_time_us = 20;
|
||||
constexpr uint32_t min_time_us = 64;
|
||||
constexpr uint32_t max_time_us = 80;
|
||||
constexpr uint32_t baud_time_us = 500;
|
||||
|
||||
constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us;
|
||||
constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us;
|
||||
constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us;
|
||||
constexpr uint32_t baud_time = baud_time_us * clocks_in_us;
|
||||
|
||||
bool DecoderIoProx::read(uint8_t* data, uint8_t data_size) {
|
||||
bool result = false;
|
||||
furi_assert(data_size >= 4);
|
||||
|
||||
if(ready) {
|
||||
result = true;
|
||||
ioprox.decode(raw_data, sizeof(raw_data), data, data_size);
|
||||
ready = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DecoderIoProx::process_front(bool is_rising_edge, uint32_t time) {
|
||||
if(ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always track the time that's gone by.
|
||||
current_period_duration += time;
|
||||
demodulation_sample_duration += time;
|
||||
|
||||
// If a baud time has elapsed, we're at a sample point.
|
||||
if(demodulation_sample_duration >= baud_time) {
|
||||
// Start a new baud period...
|
||||
demodulation_sample_duration = 0;
|
||||
demodulated_value_invalid = false;
|
||||
|
||||
// ... and if we didn't have any baud errors, capture a sample.
|
||||
if(!demodulated_value_invalid) {
|
||||
store_data(current_demodulated_value);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FSK demodulator.
|
||||
//
|
||||
|
||||
// If this isn't a rising edge, this isn't a pulse of interest.
|
||||
// We're done.
|
||||
if(!is_rising_edge) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_valid_low = (current_period_duration > min_time) &&
|
||||
(current_period_duration <= mid_time);
|
||||
bool is_valid_high = (current_period_duration > mid_time) &&
|
||||
(current_period_duration < max_time);
|
||||
|
||||
// If this is between the minimum and our threshold, this is a logical 0.
|
||||
if(is_valid_low) {
|
||||
current_demodulated_value = false;
|
||||
}
|
||||
// Otherwise, if between our threshold and the max time, it's a logical 1.
|
||||
else if(is_valid_high) {
|
||||
current_demodulated_value = true;
|
||||
}
|
||||
// Otherwise, invalidate this sample.
|
||||
else {
|
||||
demodulated_value_invalid = true;
|
||||
}
|
||||
|
||||
// We're starting a new period; track that.
|
||||
current_period_duration = 0;
|
||||
}
|
||||
|
||||
DecoderIoProx::DecoderIoProx() {
|
||||
reset_state();
|
||||
}
|
||||
|
||||
void DecoderIoProx::store_data(bool data) {
|
||||
for(int i = 0; i < 7; ++i) {
|
||||
raw_data[i] = (raw_data[i] << 1) | ((raw_data[i + 1] >> 7) & 1);
|
||||
}
|
||||
raw_data[7] = (raw_data[7] << 1) | data;
|
||||
|
||||
if(ioprox.can_be_decoded(raw_data, sizeof(raw_data))) {
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DecoderIoProx::reset_state() {
|
||||
current_demodulated_value = false;
|
||||
demodulated_value_invalid = false;
|
||||
|
||||
current_period_duration = 0;
|
||||
demodulation_sample_duration = 0;
|
||||
|
||||
ready = false;
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
#include "protocols/protocol_ioprox.h"
|
||||
|
||||
class DecoderIoProx {
|
||||
public:
|
||||
bool read(uint8_t* data, uint8_t data_size);
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
DecoderIoProx();
|
||||
|
||||
private:
|
||||
uint32_t current_period_duration = 0;
|
||||
uint32_t demodulation_sample_duration = 0;
|
||||
|
||||
bool current_demodulated_value = false;
|
||||
bool demodulated_value_invalid = false;
|
||||
|
||||
uint8_t raw_data[8] = {0};
|
||||
void store_data(bool data);
|
||||
|
||||
std::atomic<bool> ready;
|
||||
|
||||
void reset_state();
|
||||
ProtocolIoProx ioprox;
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#define EM_HEADER_POS 55
|
||||
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
|
||||
|
||||
#define EM_FIRST_ROW_POS 50
|
||||
#define EM_ROW_COUNT 10
|
||||
|
||||
#define EM_COLUMN_POS 4
|
||||
#define EM_STOP_POS 0
|
||||
#define EM_STOP_MASK (0x1LLU << EM_STOP_POS)
|
||||
|
||||
#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)
|
||||
#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)
|
||||
@ -1,24 +0,0 @@
|
||||
#include "encoder_emmarin.h"
|
||||
#include "protocols/protocol_emmarin.h"
|
||||
#include <furi.h>
|
||||
|
||||
void EncoderEM::init(const uint8_t* data, const uint8_t data_size) {
|
||||
ProtocolEMMarin em_marin;
|
||||
em_marin.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(uint64_t));
|
||||
|
||||
card_data_index = 0;
|
||||
}
|
||||
|
||||
// data transmitted as manchester encoding
|
||||
// 0 - high2low
|
||||
// 1 - low2high
|
||||
void EncoderEM::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
|
||||
*period = clocks_per_bit;
|
||||
*pulse = clocks_per_bit / 2;
|
||||
*polarity = (card_data >> (63 - card_data_index)) & 1;
|
||||
|
||||
card_data_index++;
|
||||
if(card_data_index >= 64) {
|
||||
card_data_index = 0;
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
#include "encoder_generic.h"
|
||||
|
||||
class EncoderEM : public EncoderGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief init data to emulate
|
||||
*
|
||||
* @param data 1 byte FC, next 4 byte SN
|
||||
* @param data_size must be 5
|
||||
*/
|
||||
void init(const uint8_t* data, const uint8_t data_size) final;
|
||||
|
||||
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
|
||||
|
||||
private:
|
||||
// clock pulses per bit
|
||||
static const uint8_t clocks_per_bit = 64;
|
||||
|
||||
uint64_t card_data;
|
||||
uint8_t card_data_index;
|
||||
};
|
||||
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class EncoderGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief init encoder
|
||||
*
|
||||
* @param data data array
|
||||
* @param data_size data array size
|
||||
*/
|
||||
virtual void init(const uint8_t* data, const uint8_t data_size) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the next timer pulse
|
||||
*
|
||||
* @param polarity pulse polarity true = high2low, false = low2high
|
||||
* @param period overall period time in timer clicks
|
||||
* @param pulse pulse time in timer clicks
|
||||
*/
|
||||
virtual void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) = 0;
|
||||
|
||||
virtual ~EncoderGeneric(){};
|
||||
|
||||
private:
|
||||
};
|
||||
@ -1,46 +0,0 @@
|
||||
#include "encoder_hid_h10301.h"
|
||||
#include "protocols/protocol_hid_h10301.h"
|
||||
#include <furi.h>
|
||||
|
||||
void EncoderHID_H10301::init(const uint8_t* data, const uint8_t data_size) {
|
||||
ProtocolHID10301 hid;
|
||||
hid.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3);
|
||||
|
||||
card_data_index = 0;
|
||||
}
|
||||
|
||||
void EncoderHID_H10301::write_bit(bool bit, uint8_t position) {
|
||||
write_raw_bit(bit, position + 0);
|
||||
write_raw_bit(!bit, position + 1);
|
||||
}
|
||||
|
||||
void EncoderHID_H10301::write_raw_bit(bool bit, uint8_t position) {
|
||||
if(bit) {
|
||||
card_data[position / 32] |= 1UL << (31 - (position % 32));
|
||||
} else {
|
||||
card_data[position / 32] &= ~(1UL << (31 - (position % 32)));
|
||||
}
|
||||
}
|
||||
|
||||
void EncoderHID_H10301::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
|
||||
uint8_t bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1;
|
||||
|
||||
bool advance = fsk->next(bit, period);
|
||||
if(advance) {
|
||||
card_data_index++;
|
||||
if(card_data_index >= (32 * card_data_max)) {
|
||||
card_data_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
*polarity = true;
|
||||
*pulse = *period / 2;
|
||||
}
|
||||
|
||||
EncoderHID_H10301::EncoderHID_H10301() {
|
||||
fsk = new OscFSK(8, 10, 50);
|
||||
}
|
||||
|
||||
EncoderHID_H10301::~EncoderHID_H10301() {
|
||||
delete fsk;
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
#include "encoder_generic.h"
|
||||
#include "osc_fsk.h"
|
||||
|
||||
class EncoderHID_H10301 : public EncoderGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief init data to emulate
|
||||
*
|
||||
* @param data 1 byte FC, next 2 byte SN
|
||||
* @param data_size must be 3
|
||||
*/
|
||||
void init(const uint8_t* data, const uint8_t data_size) final;
|
||||
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
|
||||
EncoderHID_H10301();
|
||||
~EncoderHID_H10301();
|
||||
|
||||
private:
|
||||
static const uint8_t card_data_max = 3;
|
||||
uint32_t card_data[card_data_max];
|
||||
uint8_t card_data_index;
|
||||
void write_bit(bool bit, uint8_t position);
|
||||
void write_raw_bit(bool bit, uint8_t position);
|
||||
|
||||
OscFSK* fsk;
|
||||
};
|
||||
@ -1,36 +0,0 @@
|
||||
#include "encoder_indala_40134.h"
|
||||
#include "protocols/protocol_indala_40134.h"
|
||||
#include <furi.h>
|
||||
|
||||
void EncoderIndala_40134::init(const uint8_t* data, const uint8_t data_size) {
|
||||
ProtocolIndala40134 indala;
|
||||
indala.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data));
|
||||
|
||||
last_bit = card_data & 1;
|
||||
card_data_index = 0;
|
||||
current_polarity = true;
|
||||
}
|
||||
|
||||
void EncoderIndala_40134::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
|
||||
*period = 2;
|
||||
*pulse = 1;
|
||||
*polarity = current_polarity;
|
||||
|
||||
bit_clock_index++;
|
||||
if(bit_clock_index >= clock_per_bit) {
|
||||
bit_clock_index = 0;
|
||||
|
||||
bool current_bit = (card_data >> (63 - card_data_index)) & 1;
|
||||
|
||||
if(current_bit != last_bit) {
|
||||
current_polarity = !current_polarity;
|
||||
}
|
||||
|
||||
last_bit = current_bit;
|
||||
|
||||
card_data_index++;
|
||||
if(card_data_index >= 64) {
|
||||
card_data_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
#include "encoder_generic.h"
|
||||
|
||||
class EncoderIndala_40134 : public EncoderGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief init data to emulate
|
||||
*
|
||||
* @param data indala raw data
|
||||
* @param data_size must be 5
|
||||
*/
|
||||
void init(const uint8_t* data, const uint8_t data_size) final;
|
||||
|
||||
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
|
||||
|
||||
private:
|
||||
uint64_t card_data;
|
||||
uint8_t card_data_index;
|
||||
uint8_t bit_clock_index;
|
||||
bool last_bit;
|
||||
bool current_polarity;
|
||||
static const uint8_t clock_per_bit = 16;
|
||||
};
|
||||
@ -1,32 +0,0 @@
|
||||
#include "encoder_ioprox.h"
|
||||
#include "protocols/protocol_ioprox.h"
|
||||
#include <furi.h>
|
||||
|
||||
void EncoderIoProx::init(const uint8_t* data, const uint8_t data_size) {
|
||||
ProtocolIoProx ioprox;
|
||||
ioprox.encode(data, data_size, card_data, sizeof(card_data));
|
||||
card_data_index = 0;
|
||||
}
|
||||
|
||||
void EncoderIoProx::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
|
||||
uint8_t bit = (card_data[card_data_index / 8] >> (7 - (card_data_index % 8))) & 1;
|
||||
|
||||
bool advance = fsk->next(bit, period);
|
||||
if(advance) {
|
||||
card_data_index++;
|
||||
if(card_data_index >= (8 * card_data_max)) {
|
||||
card_data_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
*polarity = true;
|
||||
*pulse = *period / 2;
|
||||
}
|
||||
|
||||
EncoderIoProx::EncoderIoProx() {
|
||||
fsk = new OscFSK(8, 10, 64);
|
||||
}
|
||||
|
||||
EncoderIoProx::~EncoderIoProx() {
|
||||
delete fsk;
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
#include "encoder_generic.h"
|
||||
#include "osc_fsk.h"
|
||||
|
||||
class EncoderIoProx : public EncoderGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief init data to emulate
|
||||
*
|
||||
* @param data 1 byte FC, 1 byte Version, 2 bytes code
|
||||
* @param data_size must be 4
|
||||
*/
|
||||
void init(const uint8_t* data, const uint8_t data_size) final;
|
||||
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
|
||||
EncoderIoProx();
|
||||
~EncoderIoProx();
|
||||
|
||||
private:
|
||||
static const uint8_t card_data_max = 8;
|
||||
|
||||
uint8_t card_data[card_data_max];
|
||||
uint8_t card_data_index;
|
||||
|
||||
OscFSK* fsk;
|
||||
};
|
||||
@ -1,76 +0,0 @@
|
||||
#include "key_info.h"
|
||||
#include <string.h>
|
||||
|
||||
const char* lfrfid_key_get_type_string(LfrfidKeyType type) {
|
||||
switch(type) {
|
||||
case LfrfidKeyType::KeyEM4100:
|
||||
return "EM4100";
|
||||
break;
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
return "H10301";
|
||||
break;
|
||||
case LfrfidKeyType::KeyI40134:
|
||||
return "I40134";
|
||||
break;
|
||||
case LfrfidKeyType::KeyIoProxXSF:
|
||||
return "IoProxXSF";
|
||||
break;
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type) {
|
||||
switch(type) {
|
||||
case LfrfidKeyType::KeyEM4100:
|
||||
return "EM-Marin";
|
||||
break;
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
return "HID";
|
||||
break;
|
||||
case LfrfidKeyType::KeyI40134:
|
||||
return "Indala";
|
||||
break;
|
||||
case LfrfidKeyType::KeyIoProxXSF:
|
||||
return "Kantech";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type) {
|
||||
bool result = true;
|
||||
|
||||
if(strcmp("EM4100", string) == 0) {
|
||||
*type = LfrfidKeyType::KeyEM4100;
|
||||
} else if(strcmp("H10301", string) == 0) {
|
||||
*type = LfrfidKeyType::KeyH10301;
|
||||
} else if(strcmp("I40134", string) == 0) {
|
||||
*type = LfrfidKeyType::KeyI40134;
|
||||
} else if(strcmp("IoProxXSF", string) == 0) {
|
||||
*type = LfrfidKeyType::KeyIoProxXSF;
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type) {
|
||||
switch(type) {
|
||||
case LfrfidKeyType::KeyEM4100:
|
||||
return 5;
|
||||
break;
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
return 3;
|
||||
break;
|
||||
case LfrfidKeyType::KeyI40134:
|
||||
return 3;
|
||||
break;
|
||||
case LfrfidKeyType::KeyIoProxXSF:
|
||||
return 4;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t LFRFID_KEY_SIZE = 8;
|
||||
static const uint8_t LFRFID_KEY_NAME_SIZE = 22;
|
||||
|
||||
enum class LfrfidKeyType : uint8_t {
|
||||
KeyEM4100,
|
||||
KeyH10301,
|
||||
KeyI40134,
|
||||
KeyIoProxXSF,
|
||||
};
|
||||
|
||||
const char* lfrfid_key_get_type_string(LfrfidKeyType type);
|
||||
const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type);
|
||||
bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type);
|
||||
uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type);
|
||||
@ -1,20 +0,0 @@
|
||||
#include "osc_fsk.h"
|
||||
|
||||
OscFSK::OscFSK(uint16_t _freq_low, uint16_t _freq_hi, uint16_t _osc_phase_max)
|
||||
: freq{_freq_low, _freq_hi}
|
||||
, osc_phase_max(_osc_phase_max) {
|
||||
osc_phase_current = 0;
|
||||
}
|
||||
|
||||
bool OscFSK::next(bool bit, uint16_t* period) {
|
||||
bool advance = false;
|
||||
*period = freq[bit];
|
||||
osc_phase_current += *period;
|
||||
|
||||
if(osc_phase_current > osc_phase_max) {
|
||||
advance = true;
|
||||
osc_phase_current -= osc_phase_max;
|
||||
}
|
||||
|
||||
return advance;
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods.
|
||||
*/
|
||||
class OscFSK {
|
||||
public:
|
||||
/**
|
||||
* Get next period
|
||||
* @param bit bit value
|
||||
* @param period return period
|
||||
* @return bool whether to advance to the next bit
|
||||
*/
|
||||
bool next(bool bit, uint16_t* period);
|
||||
|
||||
/**
|
||||
* FSK ocillator constructor
|
||||
*
|
||||
* @param freq_low bit 0 freq
|
||||
* @param freq_hi bit 1 freq
|
||||
* @param osc_phase_max max oscillator phase
|
||||
*/
|
||||
OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max);
|
||||
|
||||
private:
|
||||
const uint16_t freq[2];
|
||||
const uint16_t osc_phase_max;
|
||||
int32_t osc_phase_current;
|
||||
};
|
||||
@ -1,150 +0,0 @@
|
||||
#include "protocol_emmarin.h"
|
||||
#include <furi.h>
|
||||
|
||||
#define EM_HEADER_POS 55
|
||||
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
|
||||
|
||||
#define EM_FIRST_ROW_POS 50
|
||||
|
||||
#define EM_ROW_COUNT 10
|
||||
#define EM_COLUMN_COUNT 4
|
||||
#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)
|
||||
|
||||
#define EM_COLUMN_POS 4
|
||||
#define EM_STOP_POS 0
|
||||
#define EM_STOP_MASK (0x1LLU << EM_STOP_POS)
|
||||
|
||||
#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)
|
||||
#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)
|
||||
|
||||
typedef uint64_t EMMarinCardData;
|
||||
|
||||
void write_nibble(bool low_nibble, uint8_t data, EMMarinCardData* card_data) {
|
||||
uint8_t parity_sum = 0;
|
||||
uint8_t start = 0;
|
||||
if(!low_nibble) start = 4;
|
||||
|
||||
for(int8_t i = (start + 3); i >= start; i--) {
|
||||
parity_sum += (data >> i) & 1;
|
||||
*card_data = (*card_data << 1) | ((data >> i) & 1);
|
||||
}
|
||||
|
||||
*card_data = (*card_data << 1) | ((parity_sum % 2) & 1);
|
||||
}
|
||||
|
||||
uint8_t ProtocolEMMarin::get_encoded_data_size() {
|
||||
return sizeof(EMMarinCardData);
|
||||
}
|
||||
|
||||
uint8_t ProtocolEMMarin::get_decoded_data_size() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
void ProtocolEMMarin::encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
EMMarinCardData card_data;
|
||||
|
||||
// header
|
||||
card_data = 0b111111111;
|
||||
|
||||
// data
|
||||
for(uint8_t i = 0; i < get_decoded_data_size(); i++) {
|
||||
write_nibble(false, decoded_data[i], &card_data);
|
||||
write_nibble(true, decoded_data[i], &card_data);
|
||||
}
|
||||
|
||||
// column parity and stop bit
|
||||
uint8_t parity_sum;
|
||||
|
||||
for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) {
|
||||
parity_sum = 0;
|
||||
for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) {
|
||||
uint8_t parity_bit = (card_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1;
|
||||
parity_sum += parity_bit;
|
||||
}
|
||||
card_data = (card_data << 1) | ((parity_sum % 2) & 1);
|
||||
}
|
||||
|
||||
// stop bit
|
||||
card_data = (card_data << 1) | 0;
|
||||
|
||||
memcpy(encoded_data, &card_data, get_encoded_data_size());
|
||||
}
|
||||
|
||||
void ProtocolEMMarin::decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
uint8_t decoded_data_index = 0;
|
||||
EMMarinCardData card_data = *(reinterpret_cast<const EMMarinCardData*>(encoded_data));
|
||||
|
||||
// clean result
|
||||
memset(decoded_data, 0, decoded_data_size);
|
||||
|
||||
// header
|
||||
for(uint8_t i = 0; i < 9; i++) {
|
||||
card_data = card_data << 1;
|
||||
}
|
||||
|
||||
// nibbles
|
||||
uint8_t value = 0;
|
||||
for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
|
||||
uint8_t nibble = 0;
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
if(i < 4) nibble = (nibble << 1) | (card_data & (1LLU << 63) ? 1 : 0);
|
||||
card_data = card_data << 1;
|
||||
}
|
||||
value = (value << 4) | nibble;
|
||||
if(r % 2) {
|
||||
decoded_data[decoded_data_index] |= value;
|
||||
decoded_data_index++;
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ProtocolEMMarin::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
const EMMarinCardData* card_data = reinterpret_cast<const EMMarinCardData*>(encoded_data);
|
||||
|
||||
// check header and stop bit
|
||||
if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;
|
||||
|
||||
// check row parity
|
||||
for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {
|
||||
uint8_t parity_sum = 0;
|
||||
|
||||
for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) {
|
||||
parity_sum += (*card_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1;
|
||||
}
|
||||
|
||||
if((parity_sum % 2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check columns parity
|
||||
for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) {
|
||||
uint8_t parity_sum = 0;
|
||||
|
||||
for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {
|
||||
parity_sum += (*card_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1;
|
||||
}
|
||||
|
||||
if((parity_sum % 2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
#include "protocol_generic.h"
|
||||
|
||||
class ProtocolEMMarin : public ProtocolGeneric {
|
||||
public:
|
||||
uint8_t get_encoded_data_size() final;
|
||||
uint8_t get_decoded_data_size() final;
|
||||
|
||||
void encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) final;
|
||||
|
||||
void decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) final;
|
||||
|
||||
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
|
||||
};
|
||||
@ -1,60 +0,0 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
#include "stdbool.h"
|
||||
|
||||
class ProtocolGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief Get the encoded data size
|
||||
*
|
||||
* @return uint8_t size of encoded data in bytes
|
||||
*/
|
||||
virtual uint8_t get_encoded_data_size() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the decoded data size
|
||||
*
|
||||
* @return uint8_t size of decoded data in bytes
|
||||
*/
|
||||
virtual uint8_t get_decoded_data_size() = 0;
|
||||
|
||||
/**
|
||||
* @brief encode decoded data
|
||||
*
|
||||
* @param decoded_data
|
||||
* @param decoded_data_size
|
||||
* @param encoded_data
|
||||
* @param encoded_data_size
|
||||
*/
|
||||
virtual void encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) = 0;
|
||||
|
||||
/**
|
||||
* @brief decode encoded data
|
||||
*
|
||||
* @param encoded_data
|
||||
* @param encoded_data_size
|
||||
* @param decoded_data
|
||||
* @param decoded_data_size
|
||||
*/
|
||||
virtual void decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) = 0;
|
||||
|
||||
/**
|
||||
* @brief fast check that data can be correctly decoded
|
||||
*
|
||||
* @param encoded_data
|
||||
* @param encoded_data_size
|
||||
* @return true - can be correctly decoded
|
||||
* @return false - cannot be correctly decoded
|
||||
*/
|
||||
virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0;
|
||||
|
||||
virtual ~ProtocolGeneric(){};
|
||||
};
|
||||
@ -1,238 +0,0 @@
|
||||
#include "protocol_hid_h10301.h"
|
||||
#include <furi.h>
|
||||
|
||||
typedef uint32_t HID10301CardData;
|
||||
constexpr uint8_t HID10301Count = 3;
|
||||
constexpr uint8_t HID10301BitSize = sizeof(HID10301CardData) * 8;
|
||||
|
||||
static void write_raw_bit(bool bit, uint8_t position, HID10301CardData* card_data) {
|
||||
if(bit) {
|
||||
card_data[position / HID10301BitSize] |=
|
||||
1UL << (HID10301BitSize - (position % HID10301BitSize) - 1);
|
||||
} else {
|
||||
card_data[position / (sizeof(HID10301CardData) * 8)] &=
|
||||
~(1UL << (HID10301BitSize - (position % HID10301BitSize) - 1));
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bit(bool bit, uint8_t position, HID10301CardData* card_data) {
|
||||
write_raw_bit(bit, position + 0, card_data);
|
||||
write_raw_bit(!bit, position + 1, card_data);
|
||||
}
|
||||
|
||||
uint8_t ProtocolHID10301::get_encoded_data_size() {
|
||||
return sizeof(HID10301CardData) * HID10301Count;
|
||||
}
|
||||
|
||||
uint8_t ProtocolHID10301::get_decoded_data_size() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
void ProtocolHID10301::encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
HID10301CardData card_data[HID10301Count] = {0, 0, 0};
|
||||
|
||||
uint32_t fc_cn = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];
|
||||
|
||||
// even parity sum calculation (high 12 bits of data)
|
||||
uint8_t even_parity_sum = 0;
|
||||
for(int8_t i = 12; i < 24; i++) {
|
||||
if(((fc_cn >> i) & 1) == 1) {
|
||||
even_parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
// odd parity sum calculation (low 12 bits of data)
|
||||
uint8_t odd_parity_sum = 1;
|
||||
for(int8_t i = 0; i < 12; i++) {
|
||||
if(((fc_cn >> i) & 1) == 1) {
|
||||
odd_parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x1D preamble
|
||||
write_raw_bit(0, 0, card_data);
|
||||
write_raw_bit(0, 1, card_data);
|
||||
write_raw_bit(0, 2, card_data);
|
||||
write_raw_bit(1, 3, card_data);
|
||||
write_raw_bit(1, 4, card_data);
|
||||
write_raw_bit(1, 5, card_data);
|
||||
write_raw_bit(0, 6, card_data);
|
||||
write_raw_bit(1, 7, card_data);
|
||||
|
||||
// company / OEM code 1
|
||||
write_bit(0, 8, card_data);
|
||||
write_bit(0, 10, card_data);
|
||||
write_bit(0, 12, card_data);
|
||||
write_bit(0, 14, card_data);
|
||||
write_bit(0, 16, card_data);
|
||||
write_bit(0, 18, card_data);
|
||||
write_bit(1, 20, card_data);
|
||||
|
||||
// card format / length 1
|
||||
write_bit(0, 22, card_data);
|
||||
write_bit(0, 24, card_data);
|
||||
write_bit(0, 26, card_data);
|
||||
write_bit(0, 28, card_data);
|
||||
write_bit(0, 30, card_data);
|
||||
write_bit(0, 32, card_data);
|
||||
write_bit(0, 34, card_data);
|
||||
write_bit(0, 36, card_data);
|
||||
write_bit(0, 38, card_data);
|
||||
write_bit(0, 40, card_data);
|
||||
write_bit(1, 42, card_data);
|
||||
|
||||
// even parity bit
|
||||
write_bit((even_parity_sum % 2), 44, card_data);
|
||||
|
||||
// data
|
||||
for(uint8_t i = 0; i < 24; i++) {
|
||||
write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data);
|
||||
}
|
||||
|
||||
// odd parity bit
|
||||
write_bit((odd_parity_sum % 2), 94, card_data);
|
||||
|
||||
memcpy(encoded_data, &card_data, get_encoded_data_size());
|
||||
}
|
||||
|
||||
void ProtocolHID10301::decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
const HID10301CardData* card_data = reinterpret_cast<const HID10301CardData*>(encoded_data);
|
||||
|
||||
// data decoding
|
||||
uint32_t result = 0;
|
||||
|
||||
// decode from word 1
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 9; i >= 0; i--) {
|
||||
switch((*(card_data + 1) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// decode from word 2
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 15; i >= 0; i--) {
|
||||
switch((*(card_data + 2) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t data[3] = {(uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)};
|
||||
|
||||
memcpy(decoded_data, &data, get_decoded_data_size());
|
||||
}
|
||||
|
||||
bool ProtocolHID10301::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
const HID10301CardData* card_data = reinterpret_cast<const HID10301CardData*>(encoded_data);
|
||||
|
||||
// packet preamble
|
||||
// raw data
|
||||
if(*(encoded_data + 3) != 0x1D) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// encoded company/oem
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
// stored in word 0
|
||||
if((*card_data >> 10 & 0x3FFF) != 0x1556) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// encoded format/length
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
// stored in word 0 and word 1
|
||||
if((((*card_data & 0x3FF) << 12) | ((*(card_data + 1) >> 20) & 0xFFF)) != 0x155556) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// data decoding
|
||||
uint32_t result = 0;
|
||||
|
||||
// decode from word 1
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 9; i >= 0; i--) {
|
||||
switch((*(card_data + 1) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// decode from word 2
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 15; i >= 0; i--) {
|
||||
switch((*(card_data + 2) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// trailing parity (odd) test
|
||||
uint8_t parity_sum = 0;
|
||||
for(int8_t i = 0; i < 13; i++) {
|
||||
if(((result >> i) & 1) == 1) {
|
||||
parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if((parity_sum % 2) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// leading parity (even) test
|
||||
parity_sum = 0;
|
||||
for(int8_t i = 13; i < 26; i++) {
|
||||
if(((result >> i) & 1) == 1) {
|
||||
parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if((parity_sum % 2) == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
#include "protocol_generic.h"
|
||||
|
||||
class ProtocolHID10301 : public ProtocolGeneric {
|
||||
public:
|
||||
uint8_t get_encoded_data_size() final;
|
||||
uint8_t get_decoded_data_size() final;
|
||||
|
||||
void encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) final;
|
||||
|
||||
void decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) final;
|
||||
|
||||
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
|
||||
};
|
||||
@ -1,237 +0,0 @@
|
||||
#include "protocol_indala_40134.h"
|
||||
#include <furi.h>
|
||||
|
||||
typedef uint64_t Indala40134CardData;
|
||||
|
||||
static void set_bit(bool bit, uint8_t position, Indala40134CardData* card_data) {
|
||||
position = (sizeof(Indala40134CardData) * 8) - 1 - position;
|
||||
if(bit) {
|
||||
*card_data |= 1ull << position;
|
||||
} else {
|
||||
*card_data &= ~(1ull << position);
|
||||
}
|
||||
}
|
||||
|
||||
static bool get_bit(uint8_t position, const Indala40134CardData* card_data) {
|
||||
position = (sizeof(Indala40134CardData) * 8) - 1 - position;
|
||||
return (*card_data >> position) & 1;
|
||||
}
|
||||
|
||||
uint8_t ProtocolIndala40134::get_encoded_data_size() {
|
||||
return sizeof(Indala40134CardData);
|
||||
}
|
||||
|
||||
uint8_t ProtocolIndala40134::get_decoded_data_size() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
void ProtocolIndala40134::encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
uint32_t fc_and_card = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];
|
||||
Indala40134CardData card_data = 0;
|
||||
|
||||
// preamble
|
||||
set_bit(1, 0, &card_data);
|
||||
set_bit(1, 2, &card_data);
|
||||
set_bit(1, 32, &card_data);
|
||||
|
||||
// factory code
|
||||
set_bit(((fc_and_card >> 23) & 1), 57, &card_data);
|
||||
set_bit(((fc_and_card >> 22) & 1), 49, &card_data);
|
||||
set_bit(((fc_and_card >> 21) & 1), 44, &card_data);
|
||||
set_bit(((fc_and_card >> 20) & 1), 47, &card_data);
|
||||
set_bit(((fc_and_card >> 19) & 1), 48, &card_data);
|
||||
set_bit(((fc_and_card >> 18) & 1), 53, &card_data);
|
||||
set_bit(((fc_and_card >> 17) & 1), 39, &card_data);
|
||||
set_bit(((fc_and_card >> 16) & 1), 58, &card_data);
|
||||
|
||||
// card number
|
||||
set_bit(((fc_and_card >> 15) & 1), 42, &card_data);
|
||||
set_bit(((fc_and_card >> 14) & 1), 45, &card_data);
|
||||
set_bit(((fc_and_card >> 13) & 1), 43, &card_data);
|
||||
set_bit(((fc_and_card >> 12) & 1), 40, &card_data);
|
||||
set_bit(((fc_and_card >> 11) & 1), 52, &card_data);
|
||||
set_bit(((fc_and_card >> 10) & 1), 36, &card_data);
|
||||
set_bit(((fc_and_card >> 9) & 1), 35, &card_data);
|
||||
set_bit(((fc_and_card >> 8) & 1), 51, &card_data);
|
||||
set_bit(((fc_and_card >> 7) & 1), 46, &card_data);
|
||||
set_bit(((fc_and_card >> 6) & 1), 33, &card_data);
|
||||
set_bit(((fc_and_card >> 5) & 1), 37, &card_data);
|
||||
set_bit(((fc_and_card >> 4) & 1), 54, &card_data);
|
||||
set_bit(((fc_and_card >> 3) & 1), 56, &card_data);
|
||||
set_bit(((fc_and_card >> 2) & 1), 59, &card_data);
|
||||
set_bit(((fc_and_card >> 1) & 1), 50, &card_data);
|
||||
set_bit(((fc_and_card >> 0) & 1), 41, &card_data);
|
||||
|
||||
// checksum
|
||||
uint8_t checksum = 0;
|
||||
checksum += ((fc_and_card >> 14) & 1);
|
||||
checksum += ((fc_and_card >> 12) & 1);
|
||||
checksum += ((fc_and_card >> 9) & 1);
|
||||
checksum += ((fc_and_card >> 8) & 1);
|
||||
checksum += ((fc_and_card >> 6) & 1);
|
||||
checksum += ((fc_and_card >> 5) & 1);
|
||||
checksum += ((fc_and_card >> 2) & 1);
|
||||
checksum += ((fc_and_card >> 0) & 1);
|
||||
|
||||
// wiegand parity bits
|
||||
// even parity sum calculation (high 12 bits of data)
|
||||
uint8_t even_parity_sum = 0;
|
||||
for(int8_t i = 12; i < 24; i++) {
|
||||
if(((fc_and_card >> i) & 1) == 1) {
|
||||
even_parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
// odd parity sum calculation (low 12 bits of data)
|
||||
uint8_t odd_parity_sum = 1;
|
||||
for(int8_t i = 0; i < 12; i++) {
|
||||
if(((fc_and_card >> i) & 1) == 1) {
|
||||
odd_parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
// even parity bit
|
||||
set_bit((even_parity_sum % 2), 34, &card_data);
|
||||
|
||||
// odd parity bit
|
||||
set_bit((odd_parity_sum % 2), 38, &card_data);
|
||||
|
||||
// checksum
|
||||
if((checksum & 1) == 1) {
|
||||
set_bit(0, 62, &card_data);
|
||||
set_bit(1, 63, &card_data);
|
||||
} else {
|
||||
set_bit(1, 62, &card_data);
|
||||
set_bit(0, 63, &card_data);
|
||||
}
|
||||
|
||||
memcpy(encoded_data, &card_data, get_encoded_data_size());
|
||||
}
|
||||
|
||||
// factory code
|
||||
static uint8_t get_fc(const Indala40134CardData* card_data) {
|
||||
uint8_t fc = 0;
|
||||
|
||||
fc = fc << 1 | get_bit(57, card_data);
|
||||
fc = fc << 1 | get_bit(49, card_data);
|
||||
fc = fc << 1 | get_bit(44, card_data);
|
||||
fc = fc << 1 | get_bit(47, card_data);
|
||||
fc = fc << 1 | get_bit(48, card_data);
|
||||
fc = fc << 1 | get_bit(53, card_data);
|
||||
fc = fc << 1 | get_bit(39, card_data);
|
||||
fc = fc << 1 | get_bit(58, card_data);
|
||||
|
||||
return fc;
|
||||
}
|
||||
|
||||
// card number
|
||||
static uint16_t get_cn(const Indala40134CardData* card_data) {
|
||||
uint16_t cn = 0;
|
||||
|
||||
cn = cn << 1 | get_bit(42, card_data);
|
||||
cn = cn << 1 | get_bit(45, card_data);
|
||||
cn = cn << 1 | get_bit(43, card_data);
|
||||
cn = cn << 1 | get_bit(40, card_data);
|
||||
cn = cn << 1 | get_bit(52, card_data);
|
||||
cn = cn << 1 | get_bit(36, card_data);
|
||||
cn = cn << 1 | get_bit(35, card_data);
|
||||
cn = cn << 1 | get_bit(51, card_data);
|
||||
cn = cn << 1 | get_bit(46, card_data);
|
||||
cn = cn << 1 | get_bit(33, card_data);
|
||||
cn = cn << 1 | get_bit(37, card_data);
|
||||
cn = cn << 1 | get_bit(54, card_data);
|
||||
cn = cn << 1 | get_bit(56, card_data);
|
||||
cn = cn << 1 | get_bit(59, card_data);
|
||||
cn = cn << 1 | get_bit(50, card_data);
|
||||
cn = cn << 1 | get_bit(41, card_data);
|
||||
|
||||
return cn;
|
||||
}
|
||||
|
||||
void ProtocolIndala40134::decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
const Indala40134CardData* card_data =
|
||||
reinterpret_cast<const Indala40134CardData*>(encoded_data);
|
||||
|
||||
uint8_t fc = get_fc(card_data);
|
||||
uint16_t card = get_cn(card_data);
|
||||
|
||||
decoded_data[0] = fc;
|
||||
decoded_data[1] = card >> 8;
|
||||
decoded_data[2] = card;
|
||||
}
|
||||
|
||||
bool ProtocolIndala40134::can_be_decoded(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) {
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
bool can_be_decoded = false;
|
||||
|
||||
const Indala40134CardData* card_data =
|
||||
reinterpret_cast<const Indala40134CardData*>(encoded_data);
|
||||
|
||||
do {
|
||||
// preambula
|
||||
if((*card_data >> 32) != 0xa0000000UL) break;
|
||||
|
||||
// data
|
||||
const uint32_t fc_and_card = get_fc(card_data) << 16 | get_cn(card_data);
|
||||
|
||||
// checksum
|
||||
const uint8_t checksum = get_bit(62, card_data) << 1 | get_bit(63, card_data);
|
||||
uint8_t checksum_sum = 0;
|
||||
checksum_sum += ((fc_and_card >> 14) & 1);
|
||||
checksum_sum += ((fc_and_card >> 12) & 1);
|
||||
checksum_sum += ((fc_and_card >> 9) & 1);
|
||||
checksum_sum += ((fc_and_card >> 8) & 1);
|
||||
checksum_sum += ((fc_and_card >> 6) & 1);
|
||||
checksum_sum += ((fc_and_card >> 5) & 1);
|
||||
checksum_sum += ((fc_and_card >> 2) & 1);
|
||||
checksum_sum += ((fc_and_card >> 0) & 1);
|
||||
checksum_sum = checksum_sum & 0b1;
|
||||
|
||||
if(checksum_sum == 1 && checksum == 0b01) {
|
||||
} else if(checksum_sum == 0 && checksum == 0b10) {
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
// wiegand parity bits
|
||||
// even parity sum calculation (high 12 bits of data)
|
||||
const bool even_parity = get_bit(34, card_data);
|
||||
uint8_t even_parity_sum = 0;
|
||||
for(int8_t i = 12; i < 24; i++) {
|
||||
if(((fc_and_card >> i) & 1) == 1) {
|
||||
even_parity_sum++;
|
||||
}
|
||||
}
|
||||
if(even_parity_sum % 2 != even_parity) break;
|
||||
|
||||
// odd parity sum calculation (low 12 bits of data)
|
||||
const bool odd_parity = get_bit(38, card_data);
|
||||
uint8_t odd_parity_sum = 1;
|
||||
for(int8_t i = 0; i < 12; i++) {
|
||||
if(((fc_and_card >> i) & 1) == 1) {
|
||||
odd_parity_sum++;
|
||||
}
|
||||
}
|
||||
if(odd_parity_sum % 2 != odd_parity) break;
|
||||
|
||||
can_be_decoded = true;
|
||||
} while(false);
|
||||
|
||||
return can_be_decoded;
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
#include "protocol_generic.h"
|
||||
|
||||
class ProtocolIndala40134 : public ProtocolGeneric {
|
||||
public:
|
||||
uint8_t get_encoded_data_size() final;
|
||||
uint8_t get_decoded_data_size() final;
|
||||
|
||||
void encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) final;
|
||||
|
||||
void decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) final;
|
||||
|
||||
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
|
||||
};
|
||||
@ -1,193 +0,0 @@
|
||||
#include "protocol_ioprox.h"
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
|
||||
/**
|
||||
* Writes a bit into the output buffer.
|
||||
*/
|
||||
static void write_bit(bool bit, uint8_t position, uint8_t* data) {
|
||||
if(bit) {
|
||||
data[position / 8] |= 1UL << (7 - (position % 8));
|
||||
} else {
|
||||
data[position / 8] &= ~(1UL << (7 - (position % 8)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes up to eight contiguous bits into the output buffer.
|
||||
*/
|
||||
static void write_bits(uint8_t byte, uint8_t position, uint8_t* data, uint8_t length) {
|
||||
furi_check(length <= 8);
|
||||
furi_check(length > 0);
|
||||
|
||||
for(uint8_t i = 0; i < length; ++i) {
|
||||
uint8_t shift = 7 - i;
|
||||
write_bit((byte >> shift) & 1, position + i, data);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ProtocolIoProx::get_encoded_data_size() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
uint8_t ProtocolIoProx::get_decoded_data_size() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
void ProtocolIoProx::encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
// Packet to transmit:
|
||||
//
|
||||
// 0 10 20 30 40 50 60
|
||||
// v v v v v v v
|
||||
// 01234567 8 90123456 7 89012345 6 78901234 5 67890123 4 56789012 3 45678901 23
|
||||
// -----------------------------------------------------------------------------
|
||||
// 00000000 0 11110000 1 facility 1 version_ 1 code-one 1 code-two 1 checksum 11
|
||||
|
||||
// Preamble.
|
||||
write_bits(0b00000000, 0, encoded_data, 8);
|
||||
write_bit(0, 8, encoded_data);
|
||||
|
||||
write_bits(0b11110000, 9, encoded_data, 8);
|
||||
write_bit(1, 17, encoded_data);
|
||||
|
||||
// Facility code.
|
||||
write_bits(decoded_data[0], 18, encoded_data, 8);
|
||||
write_bit(1, 26, encoded_data);
|
||||
|
||||
// Version
|
||||
write_bits(decoded_data[1], 27, encoded_data, 8);
|
||||
write_bit(1, 35, encoded_data);
|
||||
|
||||
// Code one
|
||||
write_bits(decoded_data[2], 36, encoded_data, 8);
|
||||
write_bit(1, 44, encoded_data);
|
||||
|
||||
// Code two
|
||||
write_bits(decoded_data[3], 45, encoded_data, 8);
|
||||
write_bit(1, 53, encoded_data);
|
||||
|
||||
// Checksum
|
||||
write_bits(compute_checksum(encoded_data, 8), 54, encoded_data, 8);
|
||||
write_bit(1, 62, encoded_data);
|
||||
write_bit(1, 63, encoded_data);
|
||||
}
|
||||
|
||||
void ProtocolIoProx::decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
// Packet structure:
|
||||
// (Note: the second word seems fixed; but this may not be a guarantee;
|
||||
// it currently has no meaning.)
|
||||
//
|
||||
//0 1 2 3 4 5 6 7
|
||||
//v v v v v v v v
|
||||
//01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF
|
||||
//-----------------------------------------------------------------------
|
||||
//00000000 01111000 01FFFFFF FF1VVVVV VVV1CCCC CCCC1CCC CCCCC1XX XXXXXX11
|
||||
//
|
||||
// F = facility code
|
||||
// V = version
|
||||
// C = code
|
||||
// X = checksum
|
||||
|
||||
// Facility code
|
||||
decoded_data[0] = (encoded_data[2] << 2) | (encoded_data[3] >> 6);
|
||||
|
||||
// Version code.
|
||||
decoded_data[1] = (encoded_data[3] << 3) | (encoded_data[4] >> 5);
|
||||
|
||||
// Code bytes.
|
||||
decoded_data[2] = (encoded_data[4] << 4) | (encoded_data[5] >> 4);
|
||||
decoded_data[3] = (encoded_data[5] << 5) | (encoded_data[6] >> 3);
|
||||
}
|
||||
|
||||
bool ProtocolIoProx::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
// Packet framing
|
||||
//
|
||||
//0 1 2 3 4 5 6 7
|
||||
//v v v v v v v v
|
||||
//01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF
|
||||
//-----------------------------------------------------------------------
|
||||
//00000000 01______ _1______ __1_____ ___1____ ____1___ _____1XX XXXXXX11
|
||||
//
|
||||
// _ = variable data
|
||||
// 0 = preamble 0
|
||||
// 1 = framing 1
|
||||
// X = checksum
|
||||
|
||||
// Validate the packet preamble is there...
|
||||
if(encoded_data[0] != 0b00000000) {
|
||||
return false;
|
||||
}
|
||||
if((encoded_data[1] >> 6) != 0b01) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ... check for known ones...
|
||||
if((encoded_data[2] & 0b01000000) == 0) {
|
||||
return false;
|
||||
}
|
||||
if((encoded_data[3] & 0b00100000) == 0) {
|
||||
return false;
|
||||
}
|
||||
if((encoded_data[4] & 0b00010000) == 0) {
|
||||
return false;
|
||||
}
|
||||
if((encoded_data[5] & 0b00001000) == 0) {
|
||||
return false;
|
||||
}
|
||||
if((encoded_data[6] & 0b00000100) == 0) {
|
||||
return false;
|
||||
}
|
||||
if((encoded_data[7] & 0b00000011) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ... and validate our checksums.
|
||||
uint8_t checksum = compute_checksum(encoded_data, 8);
|
||||
uint8_t checkval = (encoded_data[6] << 6) | (encoded_data[7] >> 2);
|
||||
|
||||
if(checksum != checkval) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t ProtocolIoProx::compute_checksum(const uint8_t* data, const uint8_t data_size) {
|
||||
furi_check(data_size == get_encoded_data_size());
|
||||
|
||||
// Packet structure:
|
||||
//
|
||||
//0 1 2 3 4 5 6 7
|
||||
//v v v v v v v v
|
||||
//01234567 8 9ABCDEF0 1 23456789 A BCDEF012 3 456789AB C DEF01234 5 6789ABCD EF
|
||||
//00000000 0 VVVVVVVV 1 WWWWWWWW 1 XXXXXXXX 1 YYYYYYYY 1 ZZZZZZZZ 1 CHECKSUM 11
|
||||
//
|
||||
// algorithm as observed by the proxmark3 folks
|
||||
// CHECKSUM == 0xFF - (V + W + X + Y + Z)
|
||||
|
||||
uint8_t checksum = 0;
|
||||
|
||||
checksum += (data[1] << 1) | (data[2] >> 7); // VVVVVVVVV
|
||||
checksum += (data[2] << 2) | (data[3] >> 6); // WWWWWWWWW
|
||||
checksum += (data[3] << 3) | (data[4] >> 5); // XXXXXXXXX
|
||||
checksum += (data[4] << 4) | (data[5] >> 4); // YYYYYYYYY
|
||||
checksum += (data[5] << 5) | (data[6] >> 3); // ZZZZZZZZZ
|
||||
|
||||
return 0xFF - checksum;
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
#include "protocol_generic.h"
|
||||
|
||||
class ProtocolIoProx : public ProtocolGeneric {
|
||||
public:
|
||||
uint8_t get_encoded_data_size() final;
|
||||
uint8_t get_decoded_data_size() final;
|
||||
|
||||
void encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) final;
|
||||
|
||||
void decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) final;
|
||||
|
||||
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
|
||||
|
||||
private:
|
||||
/** Computes the IoProx checksum of the provided (decoded) data. */
|
||||
uint8_t compute_checksum(const uint8_t* data, const uint8_t data_size);
|
||||
};
|
||||
@ -1,95 +0,0 @@
|
||||
#include "pulse_joiner.h"
|
||||
#include <furi.h>
|
||||
|
||||
bool PulseJoiner::push_pulse(bool polarity, uint16_t period, uint16_t pulse) {
|
||||
bool result = false;
|
||||
furi_check((pulse_index + 1) < pulse_max);
|
||||
|
||||
if(polarity == false && pulse_index == 0) {
|
||||
// first negative pulse is ommited
|
||||
|
||||
} else {
|
||||
pulses[pulse_index].polarity = polarity;
|
||||
pulses[pulse_index].time = pulse;
|
||||
pulse_index++;
|
||||
}
|
||||
|
||||
if(period > pulse) {
|
||||
pulses[pulse_index].polarity = !polarity;
|
||||
pulses[pulse_index].time = period - pulse;
|
||||
pulse_index++;
|
||||
}
|
||||
|
||||
if(pulse_index >= 4) {
|
||||
// we know that first pulse is always high
|
||||
// so we wait 2 edges, hi2low and next low2hi
|
||||
|
||||
uint8_t edges_count = 0;
|
||||
bool last_polarity = pulses[0].polarity;
|
||||
|
||||
for(uint8_t i = 1; i < pulse_index; i++) {
|
||||
if(pulses[i].polarity != last_polarity) {
|
||||
edges_count++;
|
||||
last_polarity = pulses[i].polarity;
|
||||
}
|
||||
}
|
||||
|
||||
if(edges_count >= 2) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void PulseJoiner::pop_pulse(uint16_t* period, uint16_t* pulse) {
|
||||
furi_check(pulse_index <= (pulse_max + 1));
|
||||
|
||||
uint16_t tmp_period = 0;
|
||||
uint16_t tmp_pulse = 0;
|
||||
uint8_t edges_count = 0;
|
||||
bool last_polarity = pulses[0].polarity;
|
||||
uint8_t next_fist_pulse = 0;
|
||||
|
||||
for(uint8_t i = 0; i < pulse_max; i++) {
|
||||
// count edges
|
||||
if(pulses[i].polarity != last_polarity) {
|
||||
edges_count++;
|
||||
last_polarity = pulses[i].polarity;
|
||||
}
|
||||
|
||||
// wait for 2 edges
|
||||
if(edges_count == 2) {
|
||||
next_fist_pulse = i;
|
||||
break;
|
||||
}
|
||||
|
||||
// sum pulse time
|
||||
if(pulses[i].polarity) {
|
||||
tmp_period += pulses[i].time;
|
||||
tmp_pulse += pulses[i].time;
|
||||
} else {
|
||||
tmp_period += pulses[i].time;
|
||||
}
|
||||
pulse_index--;
|
||||
}
|
||||
|
||||
*period = tmp_period;
|
||||
*pulse = tmp_pulse;
|
||||
|
||||
// remove counted periods and shift data
|
||||
for(uint8_t i = 0; i < pulse_max; i++) {
|
||||
if((next_fist_pulse + i) < pulse_max) {
|
||||
pulses[i].polarity = pulses[next_fist_pulse + i].polarity;
|
||||
pulses[i].time = pulses[next_fist_pulse + i].time;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PulseJoiner::PulseJoiner() {
|
||||
for(uint8_t i = 0; i < pulse_max; i++) {
|
||||
pulses[i] = {false, 0};
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
|
||||
class PulseJoiner {
|
||||
public:
|
||||
/**
|
||||
* @brief Push timer pulse. First negative pulse is ommited.
|
||||
*
|
||||
* @param polarity pulse polarity: true = high2low, false = low2high
|
||||
* @param period overall period time in timer clicks
|
||||
* @param pulse pulse time in timer clicks
|
||||
*
|
||||
* @return true - next pulse can and must be popped immediatly
|
||||
*/
|
||||
bool push_pulse(bool polarity, uint16_t period, uint16_t pulse);
|
||||
|
||||
/**
|
||||
* @brief Get the next timer pulse. Call only if push_pulse returns true.
|
||||
*
|
||||
* @param period overall period time in timer clicks
|
||||
* @param pulse pulse time in timer clicks
|
||||
*/
|
||||
void pop_pulse(uint16_t* period, uint16_t* pulse);
|
||||
|
||||
PulseJoiner();
|
||||
|
||||
private:
|
||||
struct Pulse {
|
||||
bool polarity;
|
||||
uint16_t time;
|
||||
};
|
||||
|
||||
uint8_t pulse_index = 0;
|
||||
static const uint8_t pulse_max = 6;
|
||||
Pulse pulses[pulse_max];
|
||||
};
|
||||
@ -1,65 +0,0 @@
|
||||
#include "rfid_key.h"
|
||||
#include <core/check.h>
|
||||
#include <string.h>
|
||||
|
||||
RfidKey::RfidKey() {
|
||||
clear();
|
||||
}
|
||||
|
||||
RfidKey::~RfidKey() {
|
||||
}
|
||||
|
||||
void RfidKey::set_type(LfrfidKeyType _type) {
|
||||
type = _type;
|
||||
}
|
||||
|
||||
void RfidKey::set_data(const uint8_t* _data, const uint8_t _data_size) {
|
||||
furi_assert(_data_size <= data.size());
|
||||
for(uint8_t i = 0; i < _data_size; i++) {
|
||||
data[i] = _data[i];
|
||||
}
|
||||
}
|
||||
|
||||
void RfidKey::set_name(const char* _name) {
|
||||
strlcpy(name, _name, get_name_length());
|
||||
}
|
||||
|
||||
LfrfidKeyType RfidKey::get_type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
const uint8_t* RfidKey::get_data() {
|
||||
return &data[0];
|
||||
}
|
||||
|
||||
const char* RfidKey::get_type_text() {
|
||||
return lfrfid_key_get_type_string(type);
|
||||
}
|
||||
|
||||
uint8_t RfidKey::get_type_data_count() const {
|
||||
return lfrfid_key_get_type_data_count(type);
|
||||
}
|
||||
|
||||
char* RfidKey::get_name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
uint8_t RfidKey::get_name_length() {
|
||||
return LFRFID_KEY_NAME_SIZE;
|
||||
}
|
||||
|
||||
void RfidKey::clear() {
|
||||
set_name("");
|
||||
set_type(LfrfidKeyType::KeyEM4100);
|
||||
data.fill(0);
|
||||
}
|
||||
|
||||
RfidKey& RfidKey::operator=(const RfidKey& rhs) {
|
||||
if(this == &rhs) return *this;
|
||||
|
||||
set_type(rhs.type);
|
||||
set_name(rhs.name);
|
||||
set_data(&rhs.data[0], get_type_data_count());
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
#include "key_info.h"
|
||||
#include <array>
|
||||
|
||||
class RfidKey {
|
||||
public:
|
||||
RfidKey();
|
||||
~RfidKey();
|
||||
|
||||
void set_type(LfrfidKeyType type);
|
||||
void set_data(const uint8_t* data, const uint8_t data_size);
|
||||
void set_name(const char* name);
|
||||
|
||||
LfrfidKeyType get_type();
|
||||
const uint8_t* get_data();
|
||||
const char* get_type_text();
|
||||
uint8_t get_type_data_count() const;
|
||||
char* get_name();
|
||||
uint8_t get_name_length();
|
||||
void clear();
|
||||
RfidKey& operator=(const RfidKey& rhs);
|
||||
|
||||
private:
|
||||
std::array<uint8_t, LFRFID_KEY_SIZE> data;
|
||||
LfrfidKeyType type;
|
||||
char name[LFRFID_KEY_NAME_SIZE + 1];
|
||||
};
|
||||
@ -1,175 +0,0 @@
|
||||
#include "rfid_reader.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <stm32wbxx_ll_cortex.h>
|
||||
|
||||
/**
|
||||
* @brief private violation assistant for RfidReader
|
||||
*/
|
||||
struct RfidReaderAccessor {
|
||||
static void decode(RfidReader& rfid_reader, bool polarity) {
|
||||
rfid_reader.decode(polarity);
|
||||
}
|
||||
};
|
||||
|
||||
void RfidReader::decode(bool polarity) {
|
||||
uint32_t current_dwt_value = DWT->CYCCNT;
|
||||
uint32_t period = current_dwt_value - last_dwt_value;
|
||||
last_dwt_value = current_dwt_value;
|
||||
|
||||
#ifdef RFID_GPIO_DEBUG
|
||||
decoder_gpio_out.process_front(polarity, period);
|
||||
#endif
|
||||
|
||||
switch(type) {
|
||||
case Type::Normal:
|
||||
decoder_em.process_front(polarity, period);
|
||||
decoder_hid26.process_front(polarity, period);
|
||||
decoder_ioprox.process_front(polarity, period);
|
||||
break;
|
||||
case Type::Indala:
|
||||
decoder_em.process_front(polarity, period);
|
||||
decoder_hid26.process_front(polarity, period);
|
||||
decoder_ioprox.process_front(polarity, period);
|
||||
decoder_indala.process_front(polarity, period);
|
||||
break;
|
||||
}
|
||||
|
||||
detect_ticks++;
|
||||
}
|
||||
|
||||
bool RfidReader::switch_timer_elapsed() {
|
||||
const uint32_t seconds_to_switch = furi_kernel_get_tick_frequency() * 2.0f;
|
||||
return (furi_get_tick() - switch_os_tick_last) > seconds_to_switch;
|
||||
}
|
||||
|
||||
void RfidReader::switch_timer_reset() {
|
||||
switch_os_tick_last = furi_get_tick();
|
||||
}
|
||||
|
||||
void RfidReader::switch_mode() {
|
||||
switch(type) {
|
||||
case Type::Normal:
|
||||
type = Type::Indala;
|
||||
furi_hal_rfid_change_read_config(62500.0f, 0.25f);
|
||||
break;
|
||||
case Type::Indala:
|
||||
type = Type::Normal;
|
||||
furi_hal_rfid_change_read_config(125000.0f, 0.5f);
|
||||
break;
|
||||
}
|
||||
|
||||
switch_timer_reset();
|
||||
}
|
||||
|
||||
static void comparator_trigger_callback(bool level, void* comp_ctx) {
|
||||
RfidReader* _this = static_cast<RfidReader*>(comp_ctx);
|
||||
|
||||
RfidReaderAccessor::decode(*_this, !level);
|
||||
}
|
||||
|
||||
RfidReader::RfidReader() {
|
||||
}
|
||||
|
||||
void RfidReader::start() {
|
||||
type = Type::Normal;
|
||||
|
||||
furi_hal_rfid_pins_read();
|
||||
furi_hal_rfid_tim_read(125000, 0.5);
|
||||
furi_hal_rfid_tim_read_start();
|
||||
start_comparator();
|
||||
|
||||
switch_timer_reset();
|
||||
last_read_count = 0;
|
||||
}
|
||||
|
||||
void RfidReader::start_forced(RfidReader::Type _type) {
|
||||
start();
|
||||
if(_type == Type::Indala) {
|
||||
switch_mode();
|
||||
}
|
||||
}
|
||||
|
||||
void RfidReader::stop() {
|
||||
furi_hal_rfid_pins_reset();
|
||||
furi_hal_rfid_tim_read_stop();
|
||||
furi_hal_rfid_tim_reset();
|
||||
stop_comparator();
|
||||
}
|
||||
|
||||
bool RfidReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) {
|
||||
bool result = false;
|
||||
bool something_read = false;
|
||||
|
||||
// reading
|
||||
if(decoder_em.read(data, data_size)) {
|
||||
*_type = LfrfidKeyType::KeyEM4100;
|
||||
something_read = true;
|
||||
}
|
||||
|
||||
if(decoder_hid26.read(data, data_size)) {
|
||||
*_type = LfrfidKeyType::KeyH10301;
|
||||
something_read = true;
|
||||
}
|
||||
|
||||
if(decoder_ioprox.read(data, data_size)) {
|
||||
*_type = LfrfidKeyType::KeyIoProxXSF;
|
||||
something_read = true;
|
||||
}
|
||||
|
||||
if(decoder_indala.read(data, data_size)) {
|
||||
*_type = LfrfidKeyType::KeyI40134;
|
||||
something_read = true;
|
||||
}
|
||||
|
||||
// validation
|
||||
if(something_read) {
|
||||
switch_timer_reset();
|
||||
|
||||
if(last_read_type == *_type && memcmp(last_read_data, data, data_size) == 0) {
|
||||
last_read_count = last_read_count + 1;
|
||||
|
||||
if(last_read_count > 2) {
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
last_read_type = *_type;
|
||||
memcpy(last_read_data, data, data_size);
|
||||
last_read_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// mode switching
|
||||
if(switch_enable && switch_timer_elapsed()) {
|
||||
switch_mode();
|
||||
last_read_count = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RfidReader::detect() {
|
||||
bool detected = false;
|
||||
if(detect_ticks > 10) {
|
||||
detected = true;
|
||||
}
|
||||
detect_ticks = 0;
|
||||
|
||||
return detected;
|
||||
}
|
||||
|
||||
bool RfidReader::any_read() {
|
||||
return last_read_count > 0;
|
||||
}
|
||||
|
||||
void RfidReader::start_comparator(void) {
|
||||
furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this);
|
||||
last_dwt_value = DWT->CYCCNT;
|
||||
|
||||
furi_hal_rfid_comp_start();
|
||||
}
|
||||
|
||||
void RfidReader::stop_comparator(void) {
|
||||
furi_hal_rfid_comp_stop();
|
||||
furi_hal_rfid_comp_set_callback(NULL, NULL);
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
//#include "decoder_analyzer.h"
|
||||
#include "decoder_gpio_out.h"
|
||||
#include "decoder_emmarin.h"
|
||||
#include "decoder_hid26.h"
|
||||
#include "decoder_indala.h"
|
||||
#include "decoder_ioprox.h"
|
||||
#include "key_info.h"
|
||||
|
||||
//#define RFID_GPIO_DEBUG 1
|
||||
|
||||
class RfidReader {
|
||||
public:
|
||||
enum class Type : uint8_t {
|
||||
Normal,
|
||||
Indala,
|
||||
};
|
||||
|
||||
RfidReader();
|
||||
void start();
|
||||
void start_forced(RfidReader::Type type);
|
||||
void stop();
|
||||
bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size, bool switch_enable = true);
|
||||
|
||||
bool detect();
|
||||
bool any_read();
|
||||
|
||||
private:
|
||||
friend struct RfidReaderAccessor;
|
||||
|
||||
//DecoderAnalyzer decoder_analyzer;
|
||||
#ifdef RFID_GPIO_DEBUG
|
||||
DecoderGpioOut decoder_gpio_out;
|
||||
#endif
|
||||
DecoderEMMarin decoder_em;
|
||||
DecoderHID26 decoder_hid26;
|
||||
DecoderIndala decoder_indala;
|
||||
DecoderIoProx decoder_ioprox;
|
||||
|
||||
uint32_t last_dwt_value;
|
||||
|
||||
void start_comparator(void);
|
||||
void stop_comparator(void);
|
||||
|
||||
void decode(bool polarity);
|
||||
|
||||
uint32_t detect_ticks;
|
||||
|
||||
uint32_t switch_os_tick_last;
|
||||
bool switch_timer_elapsed();
|
||||
void switch_timer_reset();
|
||||
void switch_mode();
|
||||
|
||||
LfrfidKeyType last_read_type;
|
||||
uint8_t last_read_data[LFRFID_KEY_SIZE];
|
||||
uint8_t last_read_count;
|
||||
|
||||
Type type = Type::Normal;
|
||||
};
|
||||
@ -1,56 +0,0 @@
|
||||
#include "rfid_timer_emulator.h"
|
||||
|
||||
RfidTimerEmulator::RfidTimerEmulator() {
|
||||
}
|
||||
|
||||
RfidTimerEmulator::~RfidTimerEmulator() {
|
||||
std::map<LfrfidKeyType, EncoderGeneric*>::iterator it;
|
||||
|
||||
for(it = encoders.begin(); it != encoders.end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
encoders.clear();
|
||||
}
|
||||
|
||||
void RfidTimerEmulator::start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size) {
|
||||
if(encoders.count(type)) {
|
||||
current_encoder = encoders.find(type)->second;
|
||||
|
||||
if(data_size >= lfrfid_key_get_type_data_count(type)) {
|
||||
current_encoder->init(data, data_size);
|
||||
|
||||
furi_hal_rfid_tim_emulate(125000);
|
||||
furi_hal_rfid_pins_emulate();
|
||||
|
||||
furi_hal_rfid_tim_emulate_start(RfidTimerEmulator::timer_update_callback, this);
|
||||
}
|
||||
} else {
|
||||
// not found
|
||||
}
|
||||
}
|
||||
|
||||
void RfidTimerEmulator::stop() {
|
||||
furi_hal_rfid_tim_emulate_stop();
|
||||
furi_hal_rfid_tim_reset();
|
||||
furi_hal_rfid_pins_reset();
|
||||
}
|
||||
|
||||
void RfidTimerEmulator::timer_update_callback(void* ctx) {
|
||||
RfidTimerEmulator* _this = static_cast<RfidTimerEmulator*>(ctx);
|
||||
|
||||
bool result;
|
||||
bool polarity;
|
||||
uint16_t period;
|
||||
uint16_t pulse;
|
||||
|
||||
do {
|
||||
_this->current_encoder->get_next(&polarity, &period, &pulse);
|
||||
result = _this->pulse_joiner.push_pulse(polarity, period, pulse);
|
||||
} while(result == false);
|
||||
|
||||
_this->pulse_joiner.pop_pulse(&period, &pulse);
|
||||
|
||||
furi_hal_rfid_set_emulate_period(period - 1);
|
||||
furi_hal_rfid_set_emulate_pulse(pulse);
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
#include <furi_hal.h>
|
||||
#include "key_info.h"
|
||||
#include "encoder_generic.h"
|
||||
#include "encoder_emmarin.h"
|
||||
#include "encoder_hid_h10301.h"
|
||||
#include "encoder_indala_40134.h"
|
||||
#include "encoder_ioprox.h"
|
||||
#include "pulse_joiner.h"
|
||||
#include <map>
|
||||
|
||||
class RfidTimerEmulator {
|
||||
public:
|
||||
RfidTimerEmulator();
|
||||
~RfidTimerEmulator();
|
||||
void start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size);
|
||||
void stop();
|
||||
|
||||
private:
|
||||
EncoderGeneric* current_encoder = nullptr;
|
||||
|
||||
std::map<LfrfidKeyType, EncoderGeneric*> encoders = {
|
||||
{LfrfidKeyType::KeyEM4100, new EncoderEM()},
|
||||
{LfrfidKeyType::KeyH10301, new EncoderHID_H10301()},
|
||||
{LfrfidKeyType::KeyI40134, new EncoderIndala_40134()},
|
||||
{LfrfidKeyType::KeyIoProxXSF, new EncoderIoProx()},
|
||||
};
|
||||
|
||||
PulseJoiner pulse_joiner;
|
||||
static void timer_update_callback(void* ctx);
|
||||
};
|
||||
@ -1,136 +0,0 @@
|
||||
#include "rfid_worker.h"
|
||||
|
||||
RfidWorker::RfidWorker() {
|
||||
}
|
||||
|
||||
RfidWorker::~RfidWorker() {
|
||||
}
|
||||
|
||||
void RfidWorker::start_read() {
|
||||
reader.start();
|
||||
}
|
||||
|
||||
bool RfidWorker::read() {
|
||||
static const uint8_t data_size = LFRFID_KEY_SIZE;
|
||||
uint8_t data[data_size] = {0};
|
||||
LfrfidKeyType type;
|
||||
|
||||
bool result = reader.read(&type, data, data_size);
|
||||
|
||||
if(result) {
|
||||
key.set_type(type);
|
||||
key.set_data(data, data_size);
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RfidWorker::detect() {
|
||||
return reader.detect();
|
||||
}
|
||||
|
||||
bool RfidWorker::any_read() {
|
||||
return reader.any_read();
|
||||
}
|
||||
|
||||
void RfidWorker::stop_read() {
|
||||
reader.stop();
|
||||
}
|
||||
|
||||
void RfidWorker::start_write() {
|
||||
write_result = WriteResult::Nothing;
|
||||
write_sequence = new TickSequencer();
|
||||
validate_counts = 0;
|
||||
|
||||
write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write, this));
|
||||
write_sequence->do_after_tick(2, std::bind(&RfidWorker::sq_write_start_validate, this));
|
||||
write_sequence->do_every_tick(30, std::bind(&RfidWorker::sq_write_validate, this));
|
||||
write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write_stop_validate, this));
|
||||
}
|
||||
|
||||
RfidWorker::WriteResult RfidWorker::write() {
|
||||
write_sequence->tick();
|
||||
return write_result;
|
||||
}
|
||||
|
||||
void RfidWorker::stop_write() {
|
||||
delete write_sequence;
|
||||
reader.stop();
|
||||
}
|
||||
|
||||
void RfidWorker::start_emulate() {
|
||||
emulator.start(key.get_type(), key.get_data(), key.get_type_data_count());
|
||||
}
|
||||
|
||||
void RfidWorker::stop_emulate() {
|
||||
emulator.stop();
|
||||
}
|
||||
|
||||
void RfidWorker::sq_write() {
|
||||
for(size_t i = 0; i < 5; i++) {
|
||||
switch(key.get_type()) {
|
||||
case LfrfidKeyType::KeyEM4100:
|
||||
writer.start();
|
||||
writer.write_em(key.get_data());
|
||||
writer.stop();
|
||||
break;
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
writer.start();
|
||||
writer.write_hid(key.get_data());
|
||||
writer.stop();
|
||||
break;
|
||||
case LfrfidKeyType::KeyI40134:
|
||||
writer.start();
|
||||
writer.write_indala(key.get_data());
|
||||
writer.stop();
|
||||
break;
|
||||
case LfrfidKeyType::KeyIoProxXSF:
|
||||
writer.start();
|
||||
writer.write_ioprox(key.get_data());
|
||||
writer.stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RfidWorker::sq_write_start_validate() {
|
||||
switch(key.get_type()) {
|
||||
case LfrfidKeyType::KeyEM4100:
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
case LfrfidKeyType::KeyIoProxXSF:
|
||||
reader.start_forced(RfidReader::Type::Normal);
|
||||
break;
|
||||
case LfrfidKeyType::KeyI40134:
|
||||
reader.start_forced(RfidReader::Type::Indala);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RfidWorker::sq_write_validate() {
|
||||
static const uint8_t data_size = LFRFID_KEY_SIZE;
|
||||
uint8_t data[data_size] = {0};
|
||||
LfrfidKeyType type;
|
||||
|
||||
bool result = reader.read(&type, data, data_size);
|
||||
|
||||
if(result && (write_result != WriteResult::Ok)) {
|
||||
if(validate_counts > (5 * 60)) {
|
||||
write_result = WriteResult::NotWritable;
|
||||
}
|
||||
|
||||
if(type == key.get_type()) {
|
||||
if(memcmp(data, key.get_data(), key.get_type_data_count()) == 0) {
|
||||
write_result = WriteResult::Ok;
|
||||
validate_counts = 0;
|
||||
} else {
|
||||
validate_counts++;
|
||||
}
|
||||
} else {
|
||||
validate_counts++;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void RfidWorker::sq_write_stop_validate() {
|
||||
reader.stop();
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
#include "key_info.h"
|
||||
#include "rfid_reader.h"
|
||||
#include "rfid_writer.h"
|
||||
#include "rfid_timer_emulator.h"
|
||||
#include "rfid_key.h"
|
||||
#include "state_sequencer.h"
|
||||
|
||||
class RfidWorker {
|
||||
public:
|
||||
RfidWorker();
|
||||
~RfidWorker();
|
||||
|
||||
void start_read();
|
||||
bool read();
|
||||
bool detect();
|
||||
bool any_read();
|
||||
void stop_read();
|
||||
|
||||
enum class WriteResult : uint8_t {
|
||||
Ok,
|
||||
NotWritable,
|
||||
Nothing,
|
||||
};
|
||||
|
||||
void start_write();
|
||||
WriteResult write();
|
||||
void stop_write();
|
||||
|
||||
void start_emulate();
|
||||
void stop_emulate();
|
||||
|
||||
RfidKey key;
|
||||
|
||||
private:
|
||||
RfidWriter writer;
|
||||
RfidReader reader;
|
||||
RfidTimerEmulator emulator;
|
||||
|
||||
WriteResult write_result;
|
||||
TickSequencer* write_sequence;
|
||||
|
||||
void sq_write();
|
||||
void sq_write_start_validate();
|
||||
void sq_write_validate();
|
||||
uint16_t validate_counts;
|
||||
void sq_write_stop_validate();
|
||||
};
|
||||
@ -1,183 +0,0 @@
|
||||
#include "rfid_writer.h"
|
||||
#include "protocols/protocol_ioprox.h"
|
||||
#include <furi_hal.h>
|
||||
#include "protocols/protocol_emmarin.h"
|
||||
#include "protocols/protocol_hid_h10301.h"
|
||||
#include "protocols/protocol_indala_40134.h"
|
||||
|
||||
/**
|
||||
* @brief all timings are specified in field clocks (field clock = 125 kHz, 8 us)
|
||||
*
|
||||
*/
|
||||
class T55xxTiming {
|
||||
public:
|
||||
constexpr static const uint16_t wait_time = 400;
|
||||
constexpr static const uint8_t start_gap = 30;
|
||||
constexpr static const uint8_t write_gap = 18;
|
||||
constexpr static const uint8_t data_0 = 24;
|
||||
constexpr static const uint8_t data_1 = 56;
|
||||
constexpr static const uint16_t program = 700;
|
||||
};
|
||||
|
||||
class T55xxCmd {
|
||||
public:
|
||||
constexpr static const uint8_t opcode_page_0 = 0b10;
|
||||
constexpr static const uint8_t opcode_page_1 = 0b11;
|
||||
constexpr static const uint8_t opcode_reset = 0b00;
|
||||
};
|
||||
|
||||
RfidWriter::RfidWriter() {
|
||||
}
|
||||
|
||||
RfidWriter::~RfidWriter() {
|
||||
}
|
||||
|
||||
void RfidWriter::start() {
|
||||
furi_hal_rfid_tim_read(125000, 0.5);
|
||||
furi_hal_rfid_pins_read();
|
||||
furi_hal_rfid_tim_read_start();
|
||||
|
||||
// do not ground the antenna
|
||||
furi_hal_rfid_pin_pull_release();
|
||||
}
|
||||
|
||||
void RfidWriter::stop() {
|
||||
furi_hal_rfid_tim_read_stop();
|
||||
furi_hal_rfid_tim_reset();
|
||||
furi_hal_rfid_pins_reset();
|
||||
}
|
||||
|
||||
void RfidWriter::write_gap(uint32_t gap_time) {
|
||||
furi_hal_rfid_tim_read_stop();
|
||||
furi_delay_us(gap_time * 8);
|
||||
furi_hal_rfid_tim_read_start();
|
||||
}
|
||||
|
||||
void RfidWriter::write_bit(bool value) {
|
||||
if(value) {
|
||||
furi_delay_us(T55xxTiming::data_1 * 8);
|
||||
} else {
|
||||
furi_delay_us(T55xxTiming::data_0 * 8);
|
||||
}
|
||||
write_gap(T55xxTiming::write_gap);
|
||||
}
|
||||
|
||||
void RfidWriter::write_byte(uint8_t value) {
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
write_bit((value >> i) & 1);
|
||||
}
|
||||
}
|
||||
|
||||
void RfidWriter::write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data) {
|
||||
furi_delay_us(T55xxTiming::wait_time * 8);
|
||||
|
||||
// start gap
|
||||
write_gap(T55xxTiming::start_gap);
|
||||
|
||||
// opcode
|
||||
switch(page) {
|
||||
case 0:
|
||||
write_bit(1);
|
||||
write_bit(0);
|
||||
break;
|
||||
case 1:
|
||||
write_bit(1);
|
||||
write_bit(1);
|
||||
break;
|
||||
default:
|
||||
furi_check(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// lock bit
|
||||
write_bit(lock_bit);
|
||||
|
||||
// data
|
||||
for(uint8_t i = 0; i < 32; i++) {
|
||||
write_bit((data >> (31 - i)) & 1);
|
||||
}
|
||||
|
||||
// block address
|
||||
write_bit((block >> 2) & 1);
|
||||
write_bit((block >> 1) & 1);
|
||||
write_bit((block >> 0) & 1);
|
||||
|
||||
furi_delay_us(T55xxTiming::program * 8);
|
||||
|
||||
furi_delay_us(T55xxTiming::wait_time * 8);
|
||||
write_reset();
|
||||
}
|
||||
|
||||
void RfidWriter::write_reset() {
|
||||
write_gap(T55xxTiming::start_gap);
|
||||
write_bit(1);
|
||||
write_bit(0);
|
||||
}
|
||||
|
||||
void RfidWriter::write_em(const uint8_t em_data[5]) {
|
||||
ProtocolEMMarin em_card;
|
||||
uint64_t em_encoded_data;
|
||||
em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t));
|
||||
const uint32_t em_config_block_data = 0b00000000000101001000000001000000;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
write_block(0, 0, false, em_config_block_data);
|
||||
write_block(0, 1, false, em_encoded_data);
|
||||
write_block(0, 2, false, em_encoded_data >> 32);
|
||||
write_reset();
|
||||
FURI_CRITICAL_EXIT();
|
||||
}
|
||||
|
||||
void RfidWriter::write_hid(const uint8_t hid_data[3]) {
|
||||
ProtocolHID10301 hid_card;
|
||||
uint32_t card_data[3];
|
||||
hid_card.encode(hid_data, 3, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3);
|
||||
|
||||
const uint32_t hid_config_block_data = 0b00000000000100000111000001100000;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
write_block(0, 0, false, hid_config_block_data);
|
||||
write_block(0, 1, false, card_data[0]);
|
||||
write_block(0, 2, false, card_data[1]);
|
||||
write_block(0, 3, false, card_data[2]);
|
||||
write_reset();
|
||||
FURI_CRITICAL_EXIT();
|
||||
}
|
||||
|
||||
/** Endian fixup. Translates an ioprox block into a t5577 block */
|
||||
static uint32_t ioprox_encode_block(const uint8_t block_data[4]) {
|
||||
uint8_t raw_card_data[] = {block_data[3], block_data[2], block_data[1], block_data[0]};
|
||||
return *reinterpret_cast<uint32_t*>(&raw_card_data);
|
||||
}
|
||||
|
||||
void RfidWriter::write_ioprox(const uint8_t ioprox_data[4]) {
|
||||
ProtocolIoProx ioprox_card;
|
||||
|
||||
uint8_t encoded_data[8];
|
||||
ioprox_card.encode(ioprox_data, 4, encoded_data, sizeof(encoded_data));
|
||||
|
||||
const uint32_t ioprox_config_block_data = 0b00000000000101000111000001000000;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
write_block(0, 0, false, ioprox_config_block_data);
|
||||
write_block(0, 1, false, ioprox_encode_block(&encoded_data[0]));
|
||||
write_block(0, 2, false, ioprox_encode_block(&encoded_data[4]));
|
||||
write_reset();
|
||||
FURI_CRITICAL_EXIT();
|
||||
}
|
||||
|
||||
void RfidWriter::write_indala(const uint8_t indala_data[3]) {
|
||||
ProtocolIndala40134 indala_card;
|
||||
uint32_t card_data[2];
|
||||
indala_card.encode(
|
||||
indala_data, 3, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 2);
|
||||
|
||||
const uint32_t indala_config_block_data = 0b00000000000010000001000001000000;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
write_block(0, 0, false, indala_config_block_data);
|
||||
write_block(0, 1, false, card_data[0]);
|
||||
write_block(0, 2, false, card_data[1]);
|
||||
write_reset();
|
||||
FURI_CRITICAL_EXIT();
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
|
||||
class RfidWriter {
|
||||
public:
|
||||
RfidWriter();
|
||||
~RfidWriter();
|
||||
void start();
|
||||
void stop();
|
||||
void write_em(const uint8_t em_data[5]);
|
||||
void write_hid(const uint8_t hid_data[3]);
|
||||
void write_ioprox(const uint8_t ioprox_data[4]);
|
||||
void write_indala(const uint8_t indala_data[3]);
|
||||
|
||||
private:
|
||||
void write_gap(uint32_t gap_time);
|
||||
void write_bit(bool value);
|
||||
void write_byte(uint8_t value);
|
||||
void write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data);
|
||||
void write_reset();
|
||||
};
|
||||
@ -1,50 +0,0 @@
|
||||
#include "state_sequencer.h"
|
||||
#include "stdio.h"
|
||||
|
||||
TickSequencer::TickSequencer() {
|
||||
}
|
||||
|
||||
TickSequencer::~TickSequencer() {
|
||||
}
|
||||
|
||||
void TickSequencer::tick() {
|
||||
if(tick_count == list_it->first) {
|
||||
tick_count = 0;
|
||||
|
||||
list_it++;
|
||||
if(list_it == list.end()) {
|
||||
list_it = list.begin();
|
||||
}
|
||||
}
|
||||
|
||||
list_it->second();
|
||||
tick_count++;
|
||||
}
|
||||
|
||||
void TickSequencer::reset() {
|
||||
list_it = list.begin();
|
||||
tick_count = 0;
|
||||
}
|
||||
|
||||
void TickSequencer::clear() {
|
||||
list.clear();
|
||||
reset();
|
||||
}
|
||||
|
||||
void TickSequencer::do_every_tick(uint32_t tick_count, std::function<void(void)> fn) {
|
||||
list.push_back(std::make_pair(tick_count, fn));
|
||||
reset();
|
||||
}
|
||||
|
||||
void TickSequencer::do_after_tick(uint32_t tick_count, std::function<void(void)> fn) {
|
||||
if(tick_count > 1) {
|
||||
list.push_back(
|
||||
std::make_pair(tick_count - 1, std::bind(&TickSequencer::do_nothing, this)));
|
||||
}
|
||||
list.push_back(std::make_pair(1, fn));
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void TickSequencer::do_nothing() {
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
#include <list>
|
||||
#include <functional>
|
||||
|
||||
class TickSequencer {
|
||||
public:
|
||||
TickSequencer();
|
||||
~TickSequencer();
|
||||
|
||||
void tick();
|
||||
void reset();
|
||||
void clear();
|
||||
|
||||
void do_every_tick(uint32_t tick_count, std::function<void(void)> fn);
|
||||
void do_after_tick(uint32_t tick_count, std::function<void(void)> fn);
|
||||
|
||||
private:
|
||||
std::list<std::pair<uint32_t, std::function<void(void)> > > list;
|
||||
std::list<std::pair<uint32_t, std::function<void(void)> > >::iterator list_it;
|
||||
|
||||
uint32_t tick_count;
|
||||
|
||||
void do_nothing();
|
||||
};
|
||||
@ -21,6 +21,11 @@
|
||||
#include "scene/lfrfid_app_scene_delete_confirm.h"
|
||||
#include "scene/lfrfid_app_scene_delete_success.h"
|
||||
#include "scene/lfrfid_app_scene_rpc.h"
|
||||
#include "scene/lfrfid_app_scene_extra_actions.h"
|
||||
#include "scene/lfrfid_app_scene_raw_info.h"
|
||||
#include "scene/lfrfid_app_scene_raw_name.h"
|
||||
#include "scene/lfrfid_app_scene_raw_read.h"
|
||||
#include "scene/lfrfid_app_scene_raw_success.h"
|
||||
|
||||
#include <toolbox/path.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
@ -28,24 +33,44 @@
|
||||
#include <rpc/rpc_app.h>
|
||||
|
||||
const char* LfRfidApp::app_folder = ANY_PATH("lfrfid");
|
||||
const char* LfRfidApp::app_sd_folder = EXT_PATH("lfrfid");
|
||||
const char* LfRfidApp::app_extension = ".rfid";
|
||||
const char* LfRfidApp::app_filetype = "Flipper RFID key";
|
||||
|
||||
LfRfidApp::LfRfidApp()
|
||||
: scene_controller{this}
|
||||
, notification{"notification"}
|
||||
, storage{"storage"}
|
||||
, dialogs{"dialogs"}
|
||||
, notification{RECORD_NOTIFICATION}
|
||||
, storage{RECORD_STORAGE}
|
||||
, dialogs{RECORD_DIALOGS}
|
||||
, text_store(40) {
|
||||
string_init(file_name);
|
||||
string_init(raw_file_name);
|
||||
string_init_set_str(file_path, app_folder);
|
||||
|
||||
dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
|
||||
size_t size = protocol_dict_get_max_data_size(dict);
|
||||
new_key_data = (uint8_t*)malloc(size);
|
||||
old_key_data = (uint8_t*)malloc(size);
|
||||
|
||||
lfworker = lfrfid_worker_alloc(dict);
|
||||
}
|
||||
|
||||
LfRfidApp::~LfRfidApp() {
|
||||
string_clear(raw_file_name);
|
||||
string_clear(file_name);
|
||||
string_clear(file_path);
|
||||
protocol_dict_free(dict);
|
||||
|
||||
lfrfid_worker_free(lfworker);
|
||||
|
||||
if(rpc_ctx) {
|
||||
rpc_system_app_set_callback(rpc_ctx, NULL, NULL);
|
||||
rpc_system_app_send_exited(rpc_ctx);
|
||||
}
|
||||
|
||||
free(new_key_data);
|
||||
free(old_key_data);
|
||||
}
|
||||
|
||||
static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) {
|
||||
@ -88,7 +113,7 @@ void LfRfidApp::run(void* _args) {
|
||||
scene_controller.process(100, SceneType::Rpc);
|
||||
} else {
|
||||
string_set_str(file_path, args);
|
||||
load_key_data(file_path, &worker.key, true);
|
||||
load_key_data(file_path, true);
|
||||
view_controller.attach_to_gui(ViewDispatcherTypeFullscreen);
|
||||
scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate());
|
||||
scene_controller.process(100, SceneType::Emulate);
|
||||
@ -114,11 +139,16 @@ void LfRfidApp::run(void* _args) {
|
||||
scene_controller.add_scene(SceneType::SavedInfo, new LfRfidAppSceneSavedInfo());
|
||||
scene_controller.add_scene(SceneType::DeleteConfirm, new LfRfidAppSceneDeleteConfirm());
|
||||
scene_controller.add_scene(SceneType::DeleteSuccess, new LfRfidAppSceneDeleteSuccess());
|
||||
scene_controller.add_scene(SceneType::ExtraActions, new LfRfidAppSceneExtraActions());
|
||||
scene_controller.add_scene(SceneType::RawInfo, new LfRfidAppSceneRawInfo());
|
||||
scene_controller.add_scene(SceneType::RawName, new LfRfidAppSceneRawName());
|
||||
scene_controller.add_scene(SceneType::RawRead, new LfRfidAppSceneRawRead());
|
||||
scene_controller.add_scene(SceneType::RawSuccess, new LfRfidAppSceneRawSuccess());
|
||||
scene_controller.process(100);
|
||||
}
|
||||
}
|
||||
|
||||
bool LfRfidApp::save_key(RfidKey* key) {
|
||||
bool LfRfidApp::save_key() {
|
||||
bool result = false;
|
||||
|
||||
make_app_folder();
|
||||
@ -128,9 +158,9 @@ bool LfRfidApp::save_key(RfidKey* key) {
|
||||
string_left(file_path, filename_start);
|
||||
}
|
||||
|
||||
string_cat_printf(file_path, "/%s%s", key->get_name(), app_extension);
|
||||
string_cat_printf(file_path, "/%s%s", string_get_cstr(file_name), app_extension);
|
||||
|
||||
result = save_key_data(file_path, key);
|
||||
result = save_key_data(file_path);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -143,56 +173,27 @@ bool LfRfidApp::load_key_from_file_select(bool need_restore) {
|
||||
dialogs, file_path, file_path, app_extension, true, &I_125_10px, true);
|
||||
|
||||
if(result) {
|
||||
result = load_key_data(file_path, &worker.key, true);
|
||||
result = load_key_data(file_path, true);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LfRfidApp::delete_key(RfidKey* key) {
|
||||
UNUSED(key);
|
||||
bool LfRfidApp::delete_key() {
|
||||
return storage_simply_remove(storage, string_get_cstr(file_path));
|
||||
}
|
||||
|
||||
bool LfRfidApp::load_key_data(string_t path, RfidKey* key, bool show_dialog) {
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
bool LfRfidApp::load_key_data(string_t path, bool show_dialog) {
|
||||
bool result = false;
|
||||
string_t str_result;
|
||||
string_init(str_result);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, string_get_cstr(path))) break;
|
||||
protocol_id = lfrfid_dict_file_load(dict, string_get_cstr(path));
|
||||
if(protocol_id == PROTOCOL_NO) break;
|
||||
|
||||
// header
|
||||
uint32_t version;
|
||||
if(!flipper_format_read_header(file, str_result, &version)) break;
|
||||
if(string_cmp_str(str_result, app_filetype) != 0) break;
|
||||
if(version != 1) break;
|
||||
|
||||
// key type
|
||||
LfrfidKeyType type;
|
||||
RfidKey loaded_key;
|
||||
|
||||
if(!flipper_format_read_string(file, "Key type", str_result)) break;
|
||||
if(!lfrfid_key_get_string_type(string_get_cstr(str_result), &type)) break;
|
||||
loaded_key.set_type(type);
|
||||
|
||||
// key data
|
||||
uint8_t key_data[loaded_key.get_type_data_count()] = {};
|
||||
if(!flipper_format_read_hex(file, "Data", key_data, loaded_key.get_type_data_count()))
|
||||
break;
|
||||
loaded_key.set_data(key_data, loaded_key.get_type_data_count());
|
||||
|
||||
path_extract_filename(path, str_result, true);
|
||||
loaded_key.set_name(string_get_cstr(str_result));
|
||||
|
||||
*key = loaded_key;
|
||||
path_extract_filename(path, file_name, true);
|
||||
result = true;
|
||||
} while(0);
|
||||
|
||||
flipper_format_free(file);
|
||||
string_clear(str_result);
|
||||
|
||||
if((!result) && (show_dialog)) {
|
||||
dialog_message_show_storage_error(dialogs, "Cannot load\nkey file");
|
||||
}
|
||||
@ -200,27 +201,8 @@ bool LfRfidApp::load_key_data(string_t path, RfidKey* key, bool show_dialog) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LfRfidApp::save_key_data(string_t path, RfidKey* key) {
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
bool result = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_always(file, string_get_cstr(path))) break;
|
||||
if(!flipper_format_write_header_cstr(file, app_filetype, 1)) break;
|
||||
if(!flipper_format_write_comment_cstr(file, "Key type can be EM4100, H10301 or I40134"))
|
||||
break;
|
||||
if(!flipper_format_write_string_cstr(
|
||||
file, "Key type", lfrfid_key_get_type_string(key->get_type())))
|
||||
break;
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
file, "Data size for EM4100 is 5, for H10301 is 3, for I40134 is 3"))
|
||||
break;
|
||||
if(!flipper_format_write_hex(file, "Data", key->get_data(), key->get_type_data_count()))
|
||||
break;
|
||||
result = true;
|
||||
} while(0);
|
||||
|
||||
flipper_format_free(file);
|
||||
bool LfRfidApp::save_key_data(string_t path) {
|
||||
bool result = lfrfid_dict_file_save(dict, protocol_id, string_get_cstr(path));
|
||||
|
||||
if(!result) {
|
||||
dialog_message_show_storage_error(dialogs, "Cannot save\nkey file");
|
||||
|
||||
@ -20,9 +20,15 @@
|
||||
#include <storage/storage.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include "helpers/rfid_worker.h"
|
||||
#include "rpc/rpc_app.h"
|
||||
|
||||
#include <toolbox/protocols/protocol_dict.h>
|
||||
#include <lfrfid/lfrfid_dict_file.h>
|
||||
#include <lfrfid/protocols/lfrfid_protocols.h>
|
||||
#include <lfrfid/lfrfid_worker.h>
|
||||
|
||||
#define LFRFID_KEY_NAME_SIZE 22
|
||||
|
||||
class LfRfidApp {
|
||||
public:
|
||||
enum class EventType : uint8_t {
|
||||
@ -32,7 +38,19 @@ public:
|
||||
Stay,
|
||||
Retry,
|
||||
Exit,
|
||||
EmulateStart,
|
||||
ReadEventSenseStart,
|
||||
ReadEventSenseEnd,
|
||||
ReadEventSenseCardStart,
|
||||
ReadEventSenseCardEnd,
|
||||
ReadEventStartASK,
|
||||
ReadEventStartPSK,
|
||||
ReadEventDone,
|
||||
ReadEventOverrun,
|
||||
ReadEventError,
|
||||
WriteEventOK,
|
||||
WriteEventProtocolCannotBeWritten,
|
||||
WriteEventFobCannotBeWritten,
|
||||
WriteEventTooLongToWrite,
|
||||
RpcLoadFile,
|
||||
RpcSessionClose,
|
||||
};
|
||||
@ -57,12 +75,17 @@ public:
|
||||
DeleteConfirm,
|
||||
DeleteSuccess,
|
||||
Rpc,
|
||||
ExtraActions,
|
||||
RawInfo,
|
||||
RawName,
|
||||
RawRead,
|
||||
RawSuccess,
|
||||
};
|
||||
|
||||
class Event {
|
||||
public:
|
||||
union {
|
||||
int32_t menu_index;
|
||||
int32_t signed_int;
|
||||
} payload;
|
||||
|
||||
EventType type;
|
||||
@ -79,8 +102,6 @@ public:
|
||||
RecordController<Storage> storage;
|
||||
RecordController<DialogsApp> dialogs;
|
||||
|
||||
RfidWorker worker;
|
||||
|
||||
TextStore text_store;
|
||||
|
||||
string_t file_path;
|
||||
@ -90,15 +111,27 @@ public:
|
||||
void run(void* args);
|
||||
|
||||
static const char* app_folder;
|
||||
static const char* app_sd_folder;
|
||||
static const char* app_extension;
|
||||
static const char* app_filetype;
|
||||
|
||||
bool save_key(RfidKey* key);
|
||||
bool save_key();
|
||||
bool load_key_from_file_select(bool need_restore);
|
||||
bool delete_key(RfidKey* key);
|
||||
bool delete_key();
|
||||
|
||||
bool load_key_data(string_t path, RfidKey* key, bool show_dialog);
|
||||
bool save_key_data(string_t path, RfidKey* key);
|
||||
bool load_key_data(string_t path, bool show_dialog);
|
||||
bool save_key_data(string_t path);
|
||||
|
||||
void make_app_folder();
|
||||
|
||||
ProtocolDict* dict;
|
||||
LFRFIDWorker* lfworker;
|
||||
string_t file_name;
|
||||
ProtocolId protocol_id;
|
||||
LFRFIDWorkerReadType read_type;
|
||||
|
||||
uint8_t* old_key_data;
|
||||
uint8_t* new_key_data;
|
||||
|
||||
string_t raw_file_name;
|
||||
};
|
||||
|
||||
575
applications/lfrfid/lfrfid_cli.c
Normal file
575
applications/lfrfid/lfrfid_cli.c
Normal file
@ -0,0 +1,575 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <stdarg.h>
|
||||
#include <cli/cli.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
#include <lib/lfrfid/lfrfid_worker.h>
|
||||
#include <storage/storage.h>
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
|
||||
#include <toolbox/varint.h>
|
||||
|
||||
#include <toolbox/protocols/protocol_dict.h>
|
||||
#include <lfrfid/protocols/lfrfid_protocols.h>
|
||||
#include <lfrfid/lfrfid_raw_file.h>
|
||||
#include <toolbox/pulse_protocols/pulse_glue.h>
|
||||
|
||||
static void lfrfid_cli(Cli* cli, string_t args, void* context);
|
||||
|
||||
// app cli function
|
||||
void lfrfid_on_system_start() {
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_print_usage() {
|
||||
printf("Usage:\r\n");
|
||||
printf("rfid read <optional: normal | indala>\r\n");
|
||||
printf("rfid <write | emulate> <key_type> <key_data>\r\n");
|
||||
printf("rfid raw_read <ask | psk> <filename>\r\n");
|
||||
printf("rfid raw_emulate <filename>\r\n");
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ProtocolId protocol;
|
||||
FuriEventFlag* event;
|
||||
} LFRFIDCliReadContext;
|
||||
|
||||
static void lfrfid_cli_read_callback(LFRFIDWorkerReadResult result, ProtocolId proto, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
LFRFIDCliReadContext* context = ctx;
|
||||
if(result == LFRFIDWorkerReadDone) {
|
||||
context->protocol = proto;
|
||||
FURI_SW_MEMBARRIER();
|
||||
}
|
||||
furi_event_flag_set(context->event, 1 << result);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_read(Cli* cli, string_t args) {
|
||||
string_t type_string;
|
||||
string_init(type_string);
|
||||
LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto;
|
||||
|
||||
if(args_read_string_and_trim(args, type_string)) {
|
||||
if(string_cmp_str(type_string, "normal") == 0 || string_cmp_str(type_string, "ask") == 0) {
|
||||
// ask
|
||||
type = LFRFIDWorkerReadTypeASKOnly;
|
||||
} else if(
|
||||
string_cmp_str(type_string, "indala") == 0 ||
|
||||
string_cmp_str(type_string, "psk") == 0) {
|
||||
// psk
|
||||
type = LFRFIDWorkerReadTypePSKOnly;
|
||||
} else {
|
||||
lfrfid_cli_print_usage();
|
||||
string_clear(type_string);
|
||||
return;
|
||||
}
|
||||
}
|
||||
string_clear(type_string);
|
||||
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
|
||||
LFRFIDCliReadContext context;
|
||||
context.protocol = PROTOCOL_NO;
|
||||
context.event = furi_event_flag_alloc();
|
||||
|
||||
lfrfid_worker_start_thread(worker);
|
||||
|
||||
printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n");
|
||||
|
||||
const uint32_t available_flags = (1 << LFRFIDWorkerReadDone);
|
||||
|
||||
lfrfid_worker_read_start(worker, type, lfrfid_cli_read_callback, &context);
|
||||
|
||||
while(true) {
|
||||
uint32_t flags =
|
||||
furi_event_flag_wait(context.event, available_flags, FuriFlagWaitAny, 100);
|
||||
|
||||
if(flags != FuriFlagErrorTimeout) {
|
||||
if(FURI_BIT(flags, LFRFIDWorkerReadDone)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
}
|
||||
|
||||
lfrfid_worker_stop(worker);
|
||||
lfrfid_worker_stop_thread(worker);
|
||||
lfrfid_worker_free(worker);
|
||||
|
||||
if(context.protocol != PROTOCOL_NO) {
|
||||
printf("%s ", protocol_dict_get_name(dict, context.protocol));
|
||||
|
||||
size_t size = protocol_dict_get_data_size(dict, context.protocol);
|
||||
uint8_t* data = malloc(size);
|
||||
protocol_dict_get_data(dict, context.protocol, data, size);
|
||||
for(size_t i = 0; i < size; i++) {
|
||||
printf("%02X", data[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
free(data);
|
||||
|
||||
string_t info;
|
||||
string_init(info);
|
||||
protocol_dict_render_data(dict, info, context.protocol);
|
||||
if(string_size(info) > 0) {
|
||||
printf("%s\r\n", string_get_cstr(info));
|
||||
}
|
||||
string_clear(info);
|
||||
}
|
||||
|
||||
printf("Reading stopped\r\n");
|
||||
protocol_dict_free(dict);
|
||||
|
||||
furi_event_flag_free(context.event);
|
||||
}
|
||||
|
||||
static bool lfrfid_cli_parse_args(string_t args, ProtocolDict* dict, ProtocolId* protocol) {
|
||||
bool result = false;
|
||||
string_t protocol_name, data_text;
|
||||
string_init(protocol_name);
|
||||
string_init(data_text);
|
||||
size_t data_size = protocol_dict_get_max_data_size(dict);
|
||||
uint8_t* data = malloc(data_size);
|
||||
|
||||
do {
|
||||
// load args
|
||||
if(!args_read_string_and_trim(args, protocol_name) ||
|
||||
!args_read_string_and_trim(args, data_text)) {
|
||||
lfrfid_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
|
||||
// check protocol arg
|
||||
*protocol = protocol_dict_get_protocol_by_name(dict, string_get_cstr(protocol_name));
|
||||
if(*protocol == PROTOCOL_NO) {
|
||||
printf(
|
||||
"Unknown protocol: %s\r\n"
|
||||
"Available protocols:\r\n",
|
||||
string_get_cstr(protocol_name));
|
||||
|
||||
for(ProtocolId i = 0; i < LFRFIDProtocolMax; i++) {
|
||||
printf(
|
||||
"\t%s, %d bytes long\r\n",
|
||||
protocol_dict_get_name(dict, i),
|
||||
protocol_dict_get_data_size(dict, i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
data_size = protocol_dict_get_data_size(dict, *protocol);
|
||||
|
||||
// check data arg
|
||||
if(!args_read_hex_bytes(data_text, data, data_size)) {
|
||||
printf(
|
||||
"%s data needs to be %d bytes long\r\n",
|
||||
protocol_dict_get_name(dict, *protocol),
|
||||
data_size);
|
||||
break;
|
||||
}
|
||||
|
||||
// load data to protocol
|
||||
protocol_dict_set_data(dict, *protocol, data, data_size);
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
free(data);
|
||||
string_clear(protocol_name);
|
||||
string_clear(data_text);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void lfrfid_cli_write_callback(LFRFIDWorkerWriteResult result, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriEventFlag* events = ctx;
|
||||
furi_event_flag_set(events, 1 << result);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_write(Cli* cli, string_t args) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
ProtocolId protocol;
|
||||
|
||||
if(!lfrfid_cli_parse_args(args, dict, &protocol)) {
|
||||
protocol_dict_free(dict);
|
||||
return;
|
||||
}
|
||||
|
||||
LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
|
||||
FuriEventFlag* event = furi_event_flag_alloc();
|
||||
|
||||
lfrfid_worker_start_thread(worker);
|
||||
lfrfid_worker_write_start(worker, protocol, lfrfid_cli_write_callback, event);
|
||||
|
||||
printf("Writing RFID...\r\nPress Ctrl+C to abort\r\n");
|
||||
const uint32_t available_flags = (1 << LFRFIDWorkerWriteOK) |
|
||||
(1 << LFRFIDWorkerWriteProtocolCannotBeWritten) |
|
||||
(1 << LFRFIDWorkerWriteFobCannotBeWritten);
|
||||
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);
|
||||
if(flags != FuriFlagErrorTimeout) {
|
||||
if(FURI_BIT(flags, LFRFIDWorkerWriteOK)) {
|
||||
printf("Written!\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if(FURI_BIT(flags, LFRFIDWorkerWriteProtocolCannotBeWritten)) {
|
||||
printf("This protocol cannot be written.\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if(FURI_BIT(flags, LFRFIDWorkerWriteFobCannotBeWritten)) {
|
||||
printf("Seems this fob cannot be written.\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("Writing stopped\r\n");
|
||||
|
||||
lfrfid_worker_stop(worker);
|
||||
lfrfid_worker_stop_thread(worker);
|
||||
lfrfid_worker_free(worker);
|
||||
protocol_dict_free(dict);
|
||||
furi_event_flag_free(event);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_emulate(Cli* cli, string_t args) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
ProtocolId protocol;
|
||||
|
||||
if(!lfrfid_cli_parse_args(args, dict, &protocol)) {
|
||||
protocol_dict_free(dict);
|
||||
return;
|
||||
}
|
||||
|
||||
LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
|
||||
|
||||
lfrfid_worker_start_thread(worker);
|
||||
lfrfid_worker_emulate_start(worker, protocol);
|
||||
|
||||
printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n");
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
printf("Emulation stopped\r\n");
|
||||
|
||||
lfrfid_worker_stop(worker);
|
||||
lfrfid_worker_stop_thread(worker);
|
||||
lfrfid_worker_free(worker);
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_raw_analyze(Cli* cli, string_t args) {
|
||||
UNUSED(cli);
|
||||
string_t filepath, info_string;
|
||||
string_init(filepath);
|
||||
string_init(info_string);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage);
|
||||
|
||||
do {
|
||||
float frequency = 0;
|
||||
float duty_cycle = 0;
|
||||
|
||||
if(!args_read_probably_quoted_string_and_trim(args, filepath)) {
|
||||
lfrfid_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
|
||||
if(!lfrfid_raw_file_open_read(file, string_get_cstr(filepath))) {
|
||||
printf("Failed to open file\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!lfrfid_raw_file_read_header(file, &frequency, &duty_cycle)) {
|
||||
printf("Invalid header\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
bool file_end = false;
|
||||
uint32_t total_warns = 0;
|
||||
uint32_t total_duration = 0;
|
||||
uint32_t total_pulse = 0;
|
||||
ProtocolId total_protocol = PROTOCOL_NO;
|
||||
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
protocol_dict_decoders_start(dict);
|
||||
|
||||
while(!file_end) {
|
||||
uint32_t pulse = 0;
|
||||
uint32_t duration = 0;
|
||||
if(lfrfid_raw_file_read_pair(file, &duration, &pulse, &file_end)) {
|
||||
bool warn = false;
|
||||
|
||||
if(pulse > duration || pulse <= 0 || duration <= 0) {
|
||||
total_warns += 1;
|
||||
warn = true;
|
||||
}
|
||||
|
||||
string_printf(info_string, "[%ld %ld]", pulse, duration);
|
||||
printf("%-16s", string_get_cstr(info_string));
|
||||
string_printf(info_string, "[%ld %ld]", pulse, duration - pulse);
|
||||
printf("%-16s", string_get_cstr(info_string));
|
||||
|
||||
if(warn) {
|
||||
printf(" <<----");
|
||||
}
|
||||
|
||||
if(total_protocol == PROTOCOL_NO) {
|
||||
total_protocol = protocol_dict_decoders_feed(dict, true, pulse);
|
||||
if(total_protocol == PROTOCOL_NO) {
|
||||
total_protocol =
|
||||
protocol_dict_decoders_feed(dict, false, duration - pulse);
|
||||
}
|
||||
|
||||
if(total_protocol != PROTOCOL_NO) {
|
||||
printf(" <FOUND %s>", protocol_dict_get_name(dict, total_protocol));
|
||||
}
|
||||
}
|
||||
|
||||
printf("\r\n");
|
||||
|
||||
total_pulse += pulse;
|
||||
total_duration += duration;
|
||||
|
||||
if(total_protocol != PROTOCOL_NO) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
printf("Failed to read pair\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf(" Frequency: %f\r\n", (double)frequency);
|
||||
printf(" Duty Cycle: %f\r\n", (double)duty_cycle);
|
||||
printf(" Warns: %ld\r\n", total_warns);
|
||||
printf(" Pulse sum: %ld\r\n", total_pulse);
|
||||
printf("Duration sum: %ld\r\n", total_duration);
|
||||
printf(" Average: %f\r\n", (double)((float)total_pulse / (float)total_duration));
|
||||
printf(" Protocol: ");
|
||||
|
||||
if(total_protocol != PROTOCOL_NO) {
|
||||
size_t data_size = protocol_dict_get_data_size(dict, total_protocol);
|
||||
uint8_t* data = malloc(data_size);
|
||||
protocol_dict_get_data(dict, total_protocol, data, data_size);
|
||||
|
||||
printf("%s [", protocol_dict_get_name(dict, total_protocol));
|
||||
for(size_t i = 0; i < data_size; i++) {
|
||||
printf("%02X", data[i]);
|
||||
if(i < data_size - 1) {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
printf("]\r\n");
|
||||
|
||||
protocol_dict_render_data(dict, info_string, total_protocol);
|
||||
printf("%s\r\n", string_get_cstr(info_string));
|
||||
|
||||
free(data);
|
||||
} else {
|
||||
printf("not found\r\n");
|
||||
}
|
||||
|
||||
protocol_dict_free(dict);
|
||||
} while(false);
|
||||
|
||||
string_clear(filepath);
|
||||
string_clear(info_string);
|
||||
lfrfid_raw_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_raw_read_callback(LFRFIDWorkerReadRawResult result, void* context) {
|
||||
furi_assert(context);
|
||||
FuriEventFlag* event = context;
|
||||
furi_event_flag_set(event, 1 << result);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_raw_read(Cli* cli, string_t args) {
|
||||
UNUSED(cli);
|
||||
|
||||
string_t filepath, type_string;
|
||||
string_init(filepath);
|
||||
string_init(type_string);
|
||||
LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto;
|
||||
|
||||
do {
|
||||
if(args_read_string_and_trim(args, type_string)) {
|
||||
if(string_cmp_str(type_string, "normal") == 0 ||
|
||||
string_cmp_str(type_string, "ask") == 0) {
|
||||
// ask
|
||||
type = LFRFIDWorkerReadTypeASKOnly;
|
||||
} else if(
|
||||
string_cmp_str(type_string, "indala") == 0 ||
|
||||
string_cmp_str(type_string, "psk") == 0) {
|
||||
// psk
|
||||
type = LFRFIDWorkerReadTypePSKOnly;
|
||||
} else {
|
||||
lfrfid_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!args_read_probably_quoted_string_and_trim(args, filepath)) {
|
||||
lfrfid_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
|
||||
FuriEventFlag* event = furi_event_flag_alloc();
|
||||
|
||||
lfrfid_worker_start_thread(worker);
|
||||
|
||||
bool overrun = false;
|
||||
|
||||
const uint32_t available_flags = (1 << LFRFIDWorkerReadRawFileError) |
|
||||
(1 << LFRFIDWorkerReadRawOverrun);
|
||||
|
||||
lfrfid_worker_read_raw_start(
|
||||
worker, string_get_cstr(filepath), type, lfrfid_cli_raw_read_callback, event);
|
||||
while(true) {
|
||||
uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);
|
||||
|
||||
if(flags != FuriFlagErrorTimeout) {
|
||||
if(FURI_BIT(flags, LFRFIDWorkerReadRawFileError)) {
|
||||
printf("File is not RFID raw file\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if(FURI_BIT(flags, LFRFIDWorkerReadRawOverrun)) {
|
||||
if(!overrun) {
|
||||
printf("Overrun\r\n");
|
||||
overrun = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
}
|
||||
|
||||
if(overrun) {
|
||||
printf("An overrun occurred during read\r\n");
|
||||
}
|
||||
|
||||
lfrfid_worker_stop(worker);
|
||||
|
||||
lfrfid_worker_stop_thread(worker);
|
||||
lfrfid_worker_free(worker);
|
||||
protocol_dict_free(dict);
|
||||
|
||||
furi_event_flag_free(event);
|
||||
|
||||
} while(false);
|
||||
|
||||
string_clear(filepath);
|
||||
string_clear(type_string);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_raw_emulate_callback(LFRFIDWorkerEmulateRawResult result, void* context) {
|
||||
furi_assert(context);
|
||||
FuriEventFlag* event = context;
|
||||
furi_event_flag_set(event, 1 << result);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_raw_emulate(Cli* cli, string_t args) {
|
||||
UNUSED(cli);
|
||||
|
||||
string_t filepath;
|
||||
string_init(filepath);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
do {
|
||||
if(!args_read_probably_quoted_string_and_trim(args, filepath)) {
|
||||
lfrfid_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
|
||||
if(!storage_file_exists(storage, string_get_cstr(filepath))) {
|
||||
printf("File not found: \"%s\"\r\n", string_get_cstr(filepath));
|
||||
break;
|
||||
}
|
||||
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
LFRFIDWorker* worker = lfrfid_worker_alloc(dict);
|
||||
FuriEventFlag* event = furi_event_flag_alloc();
|
||||
|
||||
lfrfid_worker_start_thread(worker);
|
||||
|
||||
bool overrun = false;
|
||||
|
||||
const uint32_t available_flags = (1 << LFRFIDWorkerEmulateRawFileError) |
|
||||
(1 << LFRFIDWorkerEmulateRawOverrun);
|
||||
|
||||
lfrfid_worker_emulate_raw_start(
|
||||
worker, string_get_cstr(filepath), lfrfid_cli_raw_emulate_callback, event);
|
||||
while(true) {
|
||||
uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100);
|
||||
|
||||
if(flags != FuriFlagErrorTimeout) {
|
||||
if(FURI_BIT(flags, LFRFIDWorkerEmulateRawFileError)) {
|
||||
printf("File is not RFID raw file\r\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if(FURI_BIT(flags, LFRFIDWorkerEmulateRawOverrun)) {
|
||||
if(!overrun) {
|
||||
printf("Overrun\r\n");
|
||||
overrun = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) break;
|
||||
}
|
||||
|
||||
if(overrun) {
|
||||
printf("An overrun occurred during emulation\r\n");
|
||||
}
|
||||
|
||||
lfrfid_worker_stop(worker);
|
||||
|
||||
lfrfid_worker_stop_thread(worker);
|
||||
lfrfid_worker_free(worker);
|
||||
protocol_dict_free(dict);
|
||||
|
||||
furi_event_flag_free(event);
|
||||
|
||||
} while(false);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
string_clear(filepath);
|
||||
}
|
||||
|
||||
static void lfrfid_cli(Cli* cli, string_t args, void* context) {
|
||||
UNUSED(context);
|
||||
string_t cmd;
|
||||
string_init(cmd);
|
||||
|
||||
if(!args_read_string_and_trim(args, cmd)) {
|
||||
string_clear(cmd);
|
||||
lfrfid_cli_print_usage();
|
||||
return;
|
||||
}
|
||||
|
||||
if(string_cmp_str(cmd, "read") == 0) {
|
||||
lfrfid_cli_read(cli, args);
|
||||
} else if(string_cmp_str(cmd, "write") == 0) {
|
||||
lfrfid_cli_write(cli, args);
|
||||
} else if(string_cmp_str(cmd, "emulate") == 0) {
|
||||
lfrfid_cli_emulate(cli, args);
|
||||
} else if(string_cmp_str(cmd, "raw_read") == 0) {
|
||||
lfrfid_cli_raw_read(cli, args);
|
||||
} else if(string_cmp_str(cmd, "raw_emulate") == 0) {
|
||||
lfrfid_cli_raw_emulate(cli, args);
|
||||
} else if(string_cmp_str(cmd, "raw_analyze") == 0) {
|
||||
lfrfid_cli_raw_analyze(cli, args);
|
||||
} else {
|
||||
lfrfid_cli_print_usage();
|
||||
}
|
||||
|
||||
string_clear(cmd);
|
||||
}
|
||||
@ -1,177 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <stdarg.h>
|
||||
#include <cli/cli.h>
|
||||
#include <lib/toolbox/args.h>
|
||||
|
||||
#include "helpers/rfid_reader.h"
|
||||
#include "helpers/rfid_timer_emulator.h"
|
||||
|
||||
static void lfrfid_cli(Cli* cli, string_t args, void* context);
|
||||
|
||||
// app cli function
|
||||
extern "C" void lfrfid_on_system_start() {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = static_cast<Cli*>(furi_record_open("cli"));
|
||||
cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL);
|
||||
furi_record_close("cli");
|
||||
#else
|
||||
UNUSED(lfrfid_cli);
|
||||
#endif
|
||||
}
|
||||
|
||||
void lfrfid_cli_print_usage() {
|
||||
printf("Usage:\r\n");
|
||||
printf("rfid read <optional: normal | indala>\r\n");
|
||||
printf("rfid <write | emulate> <key_type> <key_data>\r\n");
|
||||
printf("\t<key_type> choose from:\r\n");
|
||||
printf("\tEM4100, EM-Marin (5 bytes key_data)\r\n");
|
||||
printf("\tH10301, HID26 (3 bytes key_data)\r\n");
|
||||
printf("\tI40134, Indala (3 bytes key_data)\r\n");
|
||||
printf("\tIoProxXSF, IoProx (4 bytes key_data)\r\n");
|
||||
printf("\t<key_data> are hex-formatted\r\n");
|
||||
};
|
||||
|
||||
static bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) {
|
||||
bool result = false;
|
||||
|
||||
if(string_cmp_str(data, "EM4100") == 0 || string_cmp_str(data, "EM-Marin") == 0) {
|
||||
result = true;
|
||||
*type = LfrfidKeyType::KeyEM4100;
|
||||
} else if(string_cmp_str(data, "H10301") == 0 || string_cmp_str(data, "HID26") == 0) {
|
||||
result = true;
|
||||
*type = LfrfidKeyType::KeyH10301;
|
||||
} else if(string_cmp_str(data, "I40134") == 0 || string_cmp_str(data, "Indala") == 0) {
|
||||
result = true;
|
||||
*type = LfrfidKeyType::KeyI40134;
|
||||
} else if(string_cmp_str(data, "IoProxXSF") == 0 || string_cmp_str(data, "IoProx") == 0) {
|
||||
result = true;
|
||||
*type = LfrfidKeyType::KeyIoProxXSF;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void lfrfid_cli_read(Cli* cli, string_t args) {
|
||||
RfidReader reader;
|
||||
string_t type_string;
|
||||
string_init(type_string);
|
||||
bool simple_mode = true;
|
||||
LfrfidKeyType type;
|
||||
RfidReader::Type reader_type = RfidReader::Type::Normal;
|
||||
static const uint8_t data_size = LFRFID_KEY_SIZE;
|
||||
uint8_t data[data_size] = {0};
|
||||
|
||||
if(args_read_string_and_trim(args, type_string)) {
|
||||
simple_mode = false;
|
||||
|
||||
if(string_cmp_str(type_string, "normal") == 0) {
|
||||
reader_type = RfidReader::Type::Normal;
|
||||
} else if(string_cmp_str(type_string, "indala") == 0) {
|
||||
reader_type = RfidReader::Type::Indala;
|
||||
} else {
|
||||
lfrfid_cli_print_usage();
|
||||
string_clear(type_string);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(simple_mode) {
|
||||
reader.start();
|
||||
} else {
|
||||
reader.start_forced(reader_type);
|
||||
}
|
||||
|
||||
printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n");
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
if(reader.read(&type, data, data_size, simple_mode)) {
|
||||
printf("%s", lfrfid_key_get_type_string(type));
|
||||
printf(" ");
|
||||
|
||||
for(uint8_t i = 0; i < lfrfid_key_get_type_data_count(type); i++) {
|
||||
printf("%02X", data[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
break;
|
||||
}
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
|
||||
printf("Reading stopped\r\n");
|
||||
reader.stop();
|
||||
|
||||
string_clear(type_string);
|
||||
}
|
||||
|
||||
static void lfrfid_cli_write(Cli* cli, string_t args) {
|
||||
UNUSED(cli);
|
||||
UNUSED(args);
|
||||
// TODO implement rfid write
|
||||
printf("Not Implemented :(\r\n");
|
||||
}
|
||||
|
||||
static void lfrfid_cli_emulate(Cli* cli, string_t args) {
|
||||
string_t data;
|
||||
string_init(data);
|
||||
RfidTimerEmulator emulator;
|
||||
|
||||
static const uint8_t data_size = LFRFID_KEY_SIZE;
|
||||
uint8_t key_data[data_size] = {0};
|
||||
uint8_t key_data_size = 0;
|
||||
LfrfidKeyType type;
|
||||
|
||||
if(!args_read_string_and_trim(args, data)) {
|
||||
lfrfid_cli_print_usage();
|
||||
string_clear(data);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!lfrfid_cli_get_key_type(data, &type)) {
|
||||
lfrfid_cli_print_usage();
|
||||
string_clear(data);
|
||||
return;
|
||||
}
|
||||
|
||||
key_data_size = lfrfid_key_get_type_data_count(type);
|
||||
|
||||
if(!args_read_hex_bytes(args, key_data, key_data_size)) {
|
||||
lfrfid_cli_print_usage();
|
||||
string_clear(data);
|
||||
return;
|
||||
}
|
||||
|
||||
emulator.start(type, key_data, key_data_size);
|
||||
|
||||
printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n");
|
||||
while(!cli_cmd_interrupt_received(cli)) {
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
printf("Emulation stopped\r\n");
|
||||
emulator.stop();
|
||||
|
||||
string_clear(data);
|
||||
}
|
||||
|
||||
static void lfrfid_cli(Cli* cli, string_t args, void* context) {
|
||||
UNUSED(context);
|
||||
string_t cmd;
|
||||
string_init(cmd);
|
||||
|
||||
if(!args_read_string_and_trim(args, cmd)) {
|
||||
string_clear(cmd);
|
||||
lfrfid_cli_print_usage();
|
||||
return;
|
||||
}
|
||||
|
||||
if(string_cmp_str(cmd, "read") == 0) {
|
||||
lfrfid_cli_read(cli, args);
|
||||
} else if(string_cmp_str(cmd, "write") == 0) {
|
||||
lfrfid_cli_write(cli, args);
|
||||
} else if(string_cmp_str(cmd, "emulate") == 0) {
|
||||
lfrfid_cli_emulate(cli, args);
|
||||
} else {
|
||||
lfrfid_cli_print_usage();
|
||||
}
|
||||
|
||||
string_clear(cmd);
|
||||
}
|
||||
@ -5,7 +5,6 @@
|
||||
|
||||
void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore */) {
|
||||
string_init(string_data);
|
||||
string_init(string_decrypted);
|
||||
string_init(string_header);
|
||||
|
||||
auto container = app->view_controller.get<ContainerVM>();
|
||||
@ -21,49 +20,26 @@ void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore
|
||||
auto line_1 = container->add<StringElement>();
|
||||
auto line_2 = container->add<StringElement>();
|
||||
auto line_3 = container->add<StringElement>();
|
||||
auto line_4 = container->add<StringElement>();
|
||||
|
||||
RfidKey& key = app->worker.key;
|
||||
const uint8_t* data = key.get_data();
|
||||
|
||||
for(uint8_t i = 0; i < key.get_type_data_count(); i++) {
|
||||
size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id);
|
||||
uint8_t* data = (uint8_t*)malloc(size);
|
||||
protocol_dict_get_data(app->dict, app->protocol_id, data, size);
|
||||
for(uint8_t i = 0; i < MIN(size, (size_t)8); i++) {
|
||||
if(i != 0) {
|
||||
string_cat_printf(string_data, " ");
|
||||
}
|
||||
|
||||
string_cat_printf(string_data, "%02X", data[i]);
|
||||
}
|
||||
free(data);
|
||||
|
||||
string_printf(string_header, "Delete %s?", key.get_name());
|
||||
string_printf(string_header, "Delete %s?", string_get_cstr(app->file_name));
|
||||
line_1->set_text(
|
||||
string_get_cstr(string_header), 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary);
|
||||
string_get_cstr(string_header), 64, 0, 128 - 2, AlignCenter, AlignTop, FontPrimary);
|
||||
line_2->set_text(
|
||||
string_get_cstr(string_data), 64, 29, 0, AlignCenter, AlignBottom, FontSecondary);
|
||||
|
||||
switch(key.get_type()) {
|
||||
case LfrfidKeyType::KeyEM4100:
|
||||
string_printf(
|
||||
string_decrypted, "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4])));
|
||||
|
||||
break;
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
case LfrfidKeyType::KeyI40134:
|
||||
string_printf(
|
||||
string_decrypted, "FC: %u ID: %u", data[0], (uint16_t)((data[1] << 8) | (data[2])));
|
||||
break;
|
||||
case LfrfidKeyType::KeyIoProxXSF:
|
||||
string_printf(
|
||||
string_decrypted,
|
||||
"FC: %u VC: %u ID: %u",
|
||||
data[0],
|
||||
data[1],
|
||||
(uint16_t)((data[2] << 8) | (data[3])));
|
||||
break;
|
||||
}
|
||||
string_get_cstr(string_data), 64, 19, 0, AlignCenter, AlignTop, FontSecondary);
|
||||
line_3->set_text(
|
||||
string_get_cstr(string_decrypted), 64, 39, 0, AlignCenter, AlignBottom, FontSecondary);
|
||||
|
||||
line_4->set_text(
|
||||
lfrfid_key_get_type_string(key.get_type()),
|
||||
protocol_dict_get_name(app->dict, app->protocol_id),
|
||||
64,
|
||||
49,
|
||||
0,
|
||||
@ -78,7 +54,7 @@ bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* eve
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == LfRfidApp::EventType::Next) {
|
||||
app->delete_key(&app->worker.key);
|
||||
app->delete_key();
|
||||
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteSuccess);
|
||||
consumed = true;
|
||||
} else if(event->type == LfRfidApp::EventType::Stay) {
|
||||
@ -94,7 +70,6 @@ bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* eve
|
||||
void LfRfidAppSceneDeleteConfirm::on_exit(LfRfidApp* app) {
|
||||
app->view_controller.get<ContainerVM>()->clean();
|
||||
string_clear(string_data);
|
||||
string_clear(string_decrypted);
|
||||
string_clear(string_header);
|
||||
}
|
||||
|
||||
|
||||
@ -13,5 +13,4 @@ private:
|
||||
|
||||
string_t string_header;
|
||||
string_t string_data;
|
||||
string_t string_decrypted;
|
||||
};
|
||||
|
||||
@ -3,28 +3,21 @@
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void LfRfidAppSceneEmulate::on_enter(LfRfidApp* app, bool /* need_restore */) {
|
||||
string_init(data_string);
|
||||
|
||||
DOLPHIN_DEED(DolphinDeedRfidEmulate);
|
||||
const uint8_t* data = app->worker.key.get_data();
|
||||
|
||||
for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) {
|
||||
string_cat_printf(data_string, "%02X", data[i]);
|
||||
}
|
||||
|
||||
auto popup = app->view_controller.get<PopupVM>();
|
||||
|
||||
popup->set_header("Emulating", 89, 30, AlignCenter, AlignTop);
|
||||
if(strlen(app->worker.key.get_name())) {
|
||||
popup->set_text(app->worker.key.get_name(), 89, 43, AlignCenter, AlignTop);
|
||||
if(string_size(app->file_name)) {
|
||||
popup->set_text(string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop);
|
||||
} else {
|
||||
popup->set_text(string_get_cstr(data_string), 89, 43, AlignCenter, AlignTop);
|
||||
popup->set_text(
|
||||
protocol_dict_get_name(app->dict, app->protocol_id), 89, 43, AlignCenter, AlignTop);
|
||||
}
|
||||
popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61);
|
||||
|
||||
app->view_controller.switch_to<PopupVM>();
|
||||
app->worker.start_emulate();
|
||||
|
||||
lfrfid_worker_start_thread(app->lfworker);
|
||||
lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id);
|
||||
notification_message(app->notification, &sequence_blink_start_magenta);
|
||||
}
|
||||
|
||||
@ -37,7 +30,7 @@ bool LfRfidAppSceneEmulate::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
||||
|
||||
void LfRfidAppSceneEmulate::on_exit(LfRfidApp* app) {
|
||||
app->view_controller.get<PopupVM>()->clean();
|
||||
app->worker.stop_emulate();
|
||||
string_clear(data_string);
|
||||
lfrfid_worker_stop(app->lfworker);
|
||||
lfrfid_worker_stop_thread(app->lfworker);
|
||||
notification_message(app->notification, &sequence_blink_stop);
|
||||
}
|
||||
|
||||
@ -6,7 +6,4 @@ public:
|
||||
void on_enter(LfRfidApp* app, bool need_restore) final;
|
||||
bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
|
||||
void on_exit(LfRfidApp* app) final;
|
||||
|
||||
private:
|
||||
string_t data_string;
|
||||
};
|
||||
|
||||
63
applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp
Normal file
63
applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
#include "lfrfid_app_scene_extra_actions.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuASK,
|
||||
SubmenuPSK,
|
||||
SubmenuRAW,
|
||||
} SubmenuIndex;
|
||||
|
||||
void LfRfidAppSceneExtraActions::on_enter(LfRfidApp* app, bool need_restore) {
|
||||
auto submenu = app->view_controller.get<SubmenuVM>();
|
||||
|
||||
submenu->add_item("Read ASK (Animal, Ordinary Card)", SubmenuASK, submenu_callback, app);
|
||||
submenu->add_item("Read PSK (Indala)", SubmenuPSK, submenu_callback, app);
|
||||
|
||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||
submenu->add_item("Read RAW RFID data", SubmenuRAW, submenu_callback, app);
|
||||
}
|
||||
|
||||
if(need_restore) {
|
||||
submenu->set_selected_item(submenu_item_selected);
|
||||
}
|
||||
|
||||
app->view_controller.switch_to<SubmenuVM>();
|
||||
}
|
||||
|
||||
bool LfRfidAppSceneExtraActions::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == LfRfidApp::EventType::MenuSelected) {
|
||||
submenu_item_selected = event->payload.signed_int;
|
||||
switch(event->payload.signed_int) {
|
||||
case SubmenuASK:
|
||||
app->read_type = LFRFIDWorkerReadTypeASKOnly;
|
||||
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read);
|
||||
break;
|
||||
case SubmenuPSK:
|
||||
app->read_type = LFRFIDWorkerReadTypePSKOnly;
|
||||
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read);
|
||||
break;
|
||||
case SubmenuRAW:
|
||||
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawName);
|
||||
break;
|
||||
}
|
||||
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void LfRfidAppSceneExtraActions::on_exit(LfRfidApp* app) {
|
||||
app->view_controller.get<SubmenuVM>()->clean();
|
||||
}
|
||||
|
||||
void LfRfidAppSceneExtraActions::submenu_callback(void* context, uint32_t index) {
|
||||
LfRfidApp* app = static_cast<LfRfidApp*>(context);
|
||||
LfRfidApp::Event event;
|
||||
|
||||
event.type = LfRfidApp::EventType::MenuSelected;
|
||||
event.payload.signed_int = index;
|
||||
|
||||
app->view_controller.send_event(&event);
|
||||
}
|
||||
13
applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h
Normal file
13
applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "../lfrfid_app.h"
|
||||
|
||||
class LfRfidAppSceneExtraActions : public GenericScene<LfRfidApp> {
|
||||
public:
|
||||
void on_enter(LfRfidApp* app, bool need_restore) final;
|
||||
bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
|
||||
void on_exit(LfRfidApp* app) final;
|
||||
|
||||
private:
|
||||
static void submenu_callback(void* context, uint32_t index);
|
||||
uint32_t submenu_item_selected = 0;
|
||||
};
|
||||
77
applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp
Normal file
77
applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
#include "lfrfid_app_scene_raw_info.h"
|
||||
#include "../view/elements/button_element.h"
|
||||
#include "../view/elements/icon_element.h"
|
||||
#include "../view/elements/string_element.h"
|
||||
|
||||
static void ok_callback(void* context) {
|
||||
LfRfidApp* app = static_cast<LfRfidApp*>(context);
|
||||
LfRfidApp::Event event;
|
||||
event.type = LfRfidApp::EventType::Next;
|
||||
app->view_controller.send_event(&event);
|
||||
}
|
||||
|
||||
static void back_callback(void* context) {
|
||||
LfRfidApp* app = static_cast<LfRfidApp*>(context);
|
||||
LfRfidApp::Event event;
|
||||
event.type = LfRfidApp::EventType::Back;
|
||||
app->view_controller.send_event(&event);
|
||||
}
|
||||
|
||||
void LfRfidAppSceneRawInfo::on_enter(LfRfidApp* app, bool /* need_restore */) {
|
||||
string_init(string_info);
|
||||
|
||||
auto container = app->view_controller.get<ContainerVM>();
|
||||
|
||||
bool sd_exist = storage_sd_status(app->storage) == FSE_OK;
|
||||
if(!sd_exist) {
|
||||
auto icon = container->add<IconElement>();
|
||||
icon->set_icon(0, 0, &I_SDQuestion_35x43);
|
||||
auto line = container->add<StringElement>();
|
||||
line->set_text(
|
||||
"No SD card found.\nThis function will not\nwork without\nSD card.",
|
||||
81,
|
||||
4,
|
||||
0,
|
||||
AlignCenter,
|
||||
AlignTop,
|
||||
FontSecondary);
|
||||
|
||||
auto button = container->add<ButtonElement>();
|
||||
button->set_type(ButtonElement::Type::Left, "Back");
|
||||
button->set_callback(app, back_callback);
|
||||
} else {
|
||||
string_printf(
|
||||
string_info,
|
||||
"RAW RFID data reader\r\n"
|
||||
"1) Put the Flipper on your card\r\n"
|
||||
"2) Press OK\r\n"
|
||||
"3) Wait until data is read");
|
||||
|
||||
auto line = container->add<StringElement>();
|
||||
line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary);
|
||||
|
||||
auto button = container->add<ButtonElement>();
|
||||
button->set_type(ButtonElement::Type::Center, "OK");
|
||||
button->set_callback(app, ok_callback);
|
||||
}
|
||||
|
||||
app->view_controller.switch_to<ContainerVM>();
|
||||
}
|
||||
|
||||
bool LfRfidAppSceneRawInfo::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
||||
bool consumed = false;
|
||||
if(event->type == LfRfidApp::EventType::Next) {
|
||||
app->scene_controller.switch_to_scene({LfRfidApp::SceneType::RawRead});
|
||||
consumed = true;
|
||||
} else if(event->type == LfRfidApp::EventType::Back) {
|
||||
app->scene_controller.search_and_switch_to_previous_scene(
|
||||
{LfRfidApp::SceneType::ExtraActions});
|
||||
consumed = true;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void LfRfidAppSceneRawInfo::on_exit(LfRfidApp* app) {
|
||||
app->view_controller.get<ContainerVM>()->clean();
|
||||
string_clear(string_info);
|
||||
}
|
||||
12
applications/lfrfid/scene/lfrfid_app_scene_raw_info.h
Normal file
12
applications/lfrfid/scene/lfrfid_app_scene_raw_info.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "../lfrfid_app.h"
|
||||
|
||||
class LfRfidAppSceneRawInfo : public GenericScene<LfRfidApp> {
|
||||
public:
|
||||
void on_enter(LfRfidApp* app, bool need_restore) final;
|
||||
bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
|
||||
void on_exit(LfRfidApp* app) final;
|
||||
|
||||
private:
|
||||
string_t string_info;
|
||||
};
|
||||
46
applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp
Normal file
46
applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
#include "lfrfid_app_scene_raw_name.h"
|
||||
#include "m-string.h"
|
||||
#include <lib/toolbox/random_name.h>
|
||||
#include <lib/toolbox/path.h>
|
||||
|
||||
void LfRfidAppSceneRawName::on_enter(LfRfidApp* app, bool /* need_restore */) {
|
||||
const char* key_name = string_get_cstr(app->raw_file_name);
|
||||
|
||||
bool key_name_empty = (string_size(app->raw_file_name) == 0);
|
||||
if(key_name_empty) {
|
||||
app->text_store.set("RfidRecord");
|
||||
} else {
|
||||
app->text_store.set("%s", key_name);
|
||||
}
|
||||
|
||||
auto text_input = app->view_controller.get<TextInputVM>();
|
||||
text_input->set_header_text("Name the raw file");
|
||||
|
||||
text_input->set_result_callback(
|
||||
save_callback, app, app->text_store.text, LFRFID_KEY_NAME_SIZE, key_name_empty);
|
||||
|
||||
app->view_controller.switch_to<TextInputVM>();
|
||||
}
|
||||
|
||||
bool LfRfidAppSceneRawName::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == LfRfidApp::EventType::Next) {
|
||||
string_set_str(app->raw_file_name, app->text_store.text);
|
||||
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawInfo);
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void LfRfidAppSceneRawName::on_exit(LfRfidApp* app) {
|
||||
app->view_controller.get<TextInputVM>()->clean();
|
||||
}
|
||||
|
||||
void LfRfidAppSceneRawName::save_callback(void* context) {
|
||||
LfRfidApp* app = static_cast<LfRfidApp*>(context);
|
||||
LfRfidApp::Event event;
|
||||
event.type = LfRfidApp::EventType::Next;
|
||||
app->view_controller.send_event(&event);
|
||||
}
|
||||
12
applications/lfrfid/scene/lfrfid_app_scene_raw_name.h
Normal file
12
applications/lfrfid/scene/lfrfid_app_scene_raw_name.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "../lfrfid_app.h"
|
||||
|
||||
class LfRfidAppSceneRawName : public GenericScene<LfRfidApp> {
|
||||
public:
|
||||
void on_enter(LfRfidApp* app, bool need_restore) final;
|
||||
bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
|
||||
void on_exit(LfRfidApp* app) final;
|
||||
|
||||
private:
|
||||
static void save_callback(void* context);
|
||||
};
|
||||
107
applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp
Normal file
107
applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp
Normal file
@ -0,0 +1,107 @@
|
||||
#include "lfrfid_app_scene_raw_read.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define RAW_READ_TIME 5000
|
||||
|
||||
static void lfrfid_read_callback(LFRFIDWorkerReadRawResult result, void* ctx) {
|
||||
LfRfidApp* app = static_cast<LfRfidApp*>(ctx);
|
||||
LfRfidApp::Event event;
|
||||
|
||||
switch(result) {
|
||||
case LFRFIDWorkerReadRawFileError:
|
||||
event.type = LfRfidApp::EventType::ReadEventError;
|
||||
break;
|
||||
case LFRFIDWorkerReadRawOverrun:
|
||||
event.type = LfRfidApp::EventType::ReadEventOverrun;
|
||||
break;
|
||||
}
|
||||
|
||||
app->view_controller.send_event(&event);
|
||||
}
|
||||
|
||||
static void timer_callback(void* ctx) {
|
||||
LfRfidApp* app = static_cast<LfRfidApp*>(ctx);
|
||||
LfRfidApp::Event event;
|
||||
event.type = LfRfidApp::EventType::ReadEventDone;
|
||||
app->view_controller.send_event(&event);
|
||||
}
|
||||
|
||||
void LfRfidAppSceneRawRead::on_enter(LfRfidApp* app, bool /* need_restore */) {
|
||||
string_init(string_file_name);
|
||||
auto popup = app->view_controller.get<PopupVM>();
|
||||
popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61);
|
||||
app->view_controller.switch_to<PopupVM>();
|
||||
lfrfid_worker_start_thread(app->lfworker);
|
||||
app->make_app_folder();
|
||||
|
||||
timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, app);
|
||||
furi_timer_start(timer, RAW_READ_TIME);
|
||||
string_printf(
|
||||
string_file_name, "%s/%s.ask.raw", app->app_sd_folder, string_get_cstr(app->raw_file_name));
|
||||
popup->set_header("Reading\nRAW RFID\nASK", 89, 30, AlignCenter, AlignTop);
|
||||
lfrfid_worker_read_raw_start(
|
||||
app->lfworker,
|
||||
string_get_cstr(string_file_name),
|
||||
LFRFIDWorkerReadTypeASKOnly,
|
||||
lfrfid_read_callback,
|
||||
app);
|
||||
|
||||
notification_message(app->notification, &sequence_blink_start_cyan);
|
||||
|
||||
is_psk = false;
|
||||
error = false;
|
||||
}
|
||||
|
||||
bool LfRfidAppSceneRawRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
||||
UNUSED(app);
|
||||
bool consumed = true;
|
||||
auto popup = app->view_controller.get<PopupVM>();
|
||||
|
||||
switch(event->type) {
|
||||
case LfRfidApp::EventType::ReadEventError:
|
||||
error = true;
|
||||
popup->set_header("Reading\nRAW RFID\nFile error", 89, 30, AlignCenter, AlignTop);
|
||||
notification_message(app->notification, &sequence_blink_start_red);
|
||||
furi_timer_stop(timer);
|
||||
break;
|
||||
case LfRfidApp::EventType::ReadEventDone:
|
||||
if(!error) {
|
||||
if(is_psk) {
|
||||
notification_message(app->notification, &sequence_success);
|
||||
app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawSuccess);
|
||||
} else {
|
||||
popup->set_header("Reading\nRAW RFID\nPSK", 89, 30, AlignCenter, AlignTop);
|
||||
notification_message(app->notification, &sequence_blink_start_yellow);
|
||||
lfrfid_worker_stop(app->lfworker);
|
||||
string_printf(
|
||||
string_file_name,
|
||||
"%s/%s.psk.raw",
|
||||
app->app_sd_folder,
|
||||
string_get_cstr(app->raw_file_name));
|
||||
lfrfid_worker_read_raw_start(
|
||||
app->lfworker,
|
||||
string_get_cstr(string_file_name),
|
||||
LFRFIDWorkerReadTypePSKOnly,
|
||||
lfrfid_read_callback,
|
||||
app);
|
||||
furi_timer_start(timer, RAW_READ_TIME);
|
||||
is_psk = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void LfRfidAppSceneRawRead::on_exit(LfRfidApp* app) {
|
||||
notification_message(app->notification, &sequence_blink_stop);
|
||||
app->view_controller.get<PopupVM>()->clean();
|
||||
lfrfid_worker_stop(app->lfworker);
|
||||
lfrfid_worker_stop_thread(app->lfworker);
|
||||
furi_timer_free(timer);
|
||||
string_clear(string_file_name);
|
||||
}
|
||||
15
applications/lfrfid/scene/lfrfid_app_scene_raw_read.h
Normal file
15
applications/lfrfid/scene/lfrfid_app_scene_raw_read.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include "../lfrfid_app.h"
|
||||
|
||||
class LfRfidAppSceneRawRead : public GenericScene<LfRfidApp> {
|
||||
public:
|
||||
void on_enter(LfRfidApp* app, bool need_restore) final;
|
||||
bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final;
|
||||
void on_exit(LfRfidApp* app) final;
|
||||
|
||||
private:
|
||||
string_t string_file_name;
|
||||
FuriTimer* timer;
|
||||
bool is_psk;
|
||||
bool error;
|
||||
};
|
||||
45
applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp
Normal file
45
applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include "lfrfid_app_scene_raw_success.h"
|
||||
#include "../view/elements/button_element.h"
|
||||
#include "../view/elements/icon_element.h"
|
||||
#include "../view/elements/string_element.h"
|
||||
|
||||
void LfRfidAppSceneRawSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) {
|
||||
string_init(string_info);
|
||||
|
||||
string_printf(string_info, "RAW RFID read success!\r\n");
|
||||
string_cat_printf(string_info, "Now you can analyze files\r\n");
|
||||
string_cat_printf(string_info, "Or send them to developers");
|
||||
|
||||
auto container = app->view_controller.get<ContainerVM>();
|
||||
|
||||
auto line = container->add<StringElement>();
|
||||
line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary);
|
||||
|
||||
auto button = container->add<ButtonElement>();
|
||||
button->set_type(ButtonElement::Type::Center, "OK");
|
||||
button->set_callback(app, LfRfidAppSceneRawSuccess::ok_callback);
|
||||
|
||||
app->view_controller.switch_to<ContainerVM>();
|
||||
}
|
||||
|
||||
bool LfRfidAppSceneRawSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
|
||||
bool consumed = false;
|
||||
if(event->type == LfRfidApp::EventType::Next) {
|
||||
app->scene_controller.search_and_switch_to_previous_scene(
|
||||
{LfRfidApp::SceneType::ExtraActions});
|
||||
consumed = true;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void LfRfidAppSceneRawSuccess::on_exit(LfRfidApp* app) {
|
||||
app->view_controller.get<ContainerVM>()->clean();
|
||||
string_clear(string_info);
|
||||
}
|
||||
|
||||
void LfRfidAppSceneRawSuccess::ok_callback(void* context) {
|
||||
LfRfidApp* app = static_cast<LfRfidApp*>(context);
|
||||
LfRfidApp::Event event;
|
||||
event.type = LfRfidApp::EventType::Next;
|
||||
app->view_controller.send_event(&event);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user