diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8358d1706aa6..9fbcb16ebeb9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ on: pull_request: env: - TARGETS: f7 + TARGETS: f7 f18 DEFAULT_TARGET: f7 FBT_TOOLCHAIN_PATH: /runner/_work @@ -52,10 +52,8 @@ jobs: - name: 'Make artifacts directory' run: | - rm -rf artifacts - rm -rf map_analyser_files - mkdir artifacts - mkdir map_analyser_files + rm -rf artifacts map_analyser_files + mkdir artifacts map_analyser_files - name: 'Bundle scripts' if: ${{ !github.event.pull_request.head.repo.fork }} @@ -66,28 +64,21 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET copro_dist updater_package \ - ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} - done - - - name: 'Move upload files' - if: ${{ !github.event.pull_request.head.repo.fork }} - run: | - set -e - for TARGET in ${TARGETS}; do + TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET_HW copro_dist updater_package \ + ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} mv dist/${TARGET}-*/* artifacts/ + tar czpf "artifacts/flipper-z-${TARGET}-resources-${SUFFIX}.tgz" \ + -C assets resources + ./fbt TARGET_HW=$TARGET_HW fap_dist + tar czpf "artifacts/flipper-z-${TARGET}-debugapps-${SUFFIX}.tgz" \ + -C dist/${TARGET}-*/apps/Debug . done - name: "Check for uncommitted changes" run: | git diff --exit-code - - name: 'Bundle resources' - if: ${{ !github.event.pull_request.head.repo.fork }} - run: | - tar czpf "artifacts/flipper-z-any-resources-${SUFFIX}.tgz" -C assets resources - - name: 'Bundle core2 firmware' if: ${{ !github.event.pull_request.head.repo.fork }} run: | @@ -96,45 +87,44 @@ jobs: - name: 'Copy map analyser files' if: ${{ !github.event.pull_request.head.repo.fork }} run: | - cp build/f7-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map - cp build/f7-firmware-*/firmware.elf map_analyser_files/firmware.elf + cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map + cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf map_analyser_files/firmware.elf cp ${{ github.event_path }} map_analyser_files/event.json - - name: 'Upload map analyser files to storage' - if: ${{ !github.event.pull_request.head.repo.fork }} - uses: prewk/s3-cp-action@v2 - with: - aws_s3_endpoint: "${{ secrets.MAP_REPORT_AWS_ENDPOINT }}" - aws_access_key_id: "${{ secrets.MAP_REPORT_AWS_ACCESS_KEY }}" - aws_secret_access_key: "${{ secrets.MAP_REPORT_AWS_SECRET_KEY }}" - source: "./map_analyser_files/" - dest: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" - flags: "--recursive --acl public-read" - - - name: 'Trigger map file reporter' + - name: 'Analyse map file' if: ${{ !github.event.pull_request.head.repo.fork }} - uses: peter-evans/repository-dispatch@v2 - with: - repository: flipperdevices/flipper-map-reporter - token: ${{ secrets.REPOSITORY_DISPATCH_TOKEN }} - event-type: map-file-analyse - client-payload: '{"random_hash": "${{steps.names.outputs.random_hash}}", "event_type": "${{steps.names.outputs.event_type}}"}' + run: | + source scripts/toolchain/fbtenv.sh + get_size() + { + SECTION="$1"; + arm-none-eabi-size \ + -A map_analyser_files/firmware.elf \ + | grep "^$SECTION" | awk '{print $2}' + } + export BSS_SIZE="$(get_size ".bss")" + export TEXT_SIZE="$(get_size ".text")" + export RODATA_SIZE="$(get_size ".rodata")" + export DATA_SIZE="$(get_size ".data")" + export FREE_FLASH_SIZE="$(get_size ".free_flash")" + python3 -m pip install mariadb==1.1.6 cxxfilt==0.3.0 + python3 scripts/map_parser.py map_analyser_files/firmware.elf.map map_analyser_files/firmware.elf.map.all + python3 scripts/map_mariadb_insert.py \ + ${{ secrets.AMAP_MARIADB_USER }} \ + ${{ secrets.AMAP_MARIADB_PASSWORD }} \ + ${{ secrets.AMAP_MARIADB_HOST }} \ + ${{ secrets.AMAP_MARIADB_PORT }} \ + ${{ secrets.AMAP_MARIADB_DATABASE }} \ + map_analyser_files/firmware.elf.map.all - name: 'Upload artifacts to update server' if: ${{ !github.event.pull_request.head.repo.fork }} run: | - mkdir -p ~/.ssh - ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts - echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; - chmod 600 ./deploy_key; - rsync -avzP --delete --mkpath \ - -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ - artifacts/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:"${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${BRANCH_NAME}/"; - rm ./deploy_key; - - - name: 'Trigger update server reindex' - if: ${{ !github.event.pull_request.head.repo.fork }} - run: curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} + FILES=$(for CUR in $(ls artifacts/); do echo "-F files=@artifacts/$CUR"; done) + curl --fail -L -H "Token: ${{ secrets.INDEXER_TOKEN }}" \ + -F "branch=${BRANCH_NAME}" \ + ${FILES[@]} \ + "${{ secrets.INDEXER_URL }}"/firmware/uploadfiles - name: 'Find Previous Comment' if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} diff --git a/.github/workflows/reindex.yml b/.github/workflows/reindex.yml index ea850e7051b7..5645f609badf 100644 --- a/.github/workflows/reindex.yml +++ b/.github/workflows/reindex.yml @@ -11,4 +11,5 @@ jobs: steps: - name: Trigger reindex run: | - curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} + curl --fail -L -H "Token: ${{ secrets.INDEXER_TOKEN }}" \ + "${{ secrets.INDEXER_URL }}"/firmware/reindex diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 81f0e0d050e7..9c6c6b2db6f0 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -29,12 +29,14 @@ jobs: - name: 'Flash unit tests firmware' id: flashing if: success() + timeout-minutes: 10 run: | ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 - name: 'Wait for flipper and format ext' id: format_ext if: steps.flashing.outcome == 'success' + timeout-minutes: 5 run: | source scripts/toolchain/fbtenv.sh python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} @@ -43,6 +45,7 @@ jobs: - name: 'Copy assets and unit data, reboot and wait for flipper' id: copy if: steps.format_ext.outcome == 'success' + timeout-minutes: 5 run: | source scripts/toolchain/fbtenv.sh python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/resources /ext @@ -53,7 +56,7 @@ jobs: - name: 'Run units and validate results' id: run_units if: steps.copy.outcome == 'success' - timeout-minutes: 2.5 + timeout-minutes: 7 run: | source scripts/toolchain/fbtenv.sh python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml index bd837297974c..27a181c46120 100644 --- a/.github/workflows/updater_test.yml +++ b/.github/workflows/updater_test.yml @@ -30,6 +30,7 @@ jobs: - name: 'Flashing target firmware' id: first_full_flash + timeout-minutes: 10 run: | source scripts/toolchain/fbtenv.sh ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 @@ -37,6 +38,7 @@ jobs: - name: 'Validating updater' id: second_full_flash + timeout-minutes: 10 if: success() run: | source scripts/toolchain/fbtenv.sh diff --git a/.gitignore b/.gitignore index bf17a94e28d0..d60dcec3f5db 100644 --- a/.gitignore +++ b/.gitignore @@ -30,27 +30,25 @@ bindings/ .mxproject Brewfile.lock.json -# Visual Studio Code -/.vscode/ - # Kate .kateproject .kateconfig -# legendary cmake's -build -CMakeLists.txt - -# bundle output -dist - # kde .directory # SCons .sconsign.dblite + + +# Visual Studio Code +/.vscode + +# bundle output +/dist + # SCons build dir -build/ +/build # Toolchain /toolchain @@ -64,3 +62,5 @@ PVS-Studio.log *.PVS-Studio.* .gdbinit + +/fbt_options_local.py \ No newline at end of file diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json index 28e67d456dc8..3c01506a8c11 100644 --- a/.vscode/example/tasks.json +++ b/.vscode/example/tasks.json @@ -4,13 +4,13 @@ "version": "2.0.0", "tasks": [ { - "label": "[Release] Build", + "label": "[Release] Build Firmware", "group": "build", "type": "shell", "command": "./fbt COMPACT=1 DEBUG=0" }, { - "label": "[Debug] Build", + "label": "[Debug] Build Firmware", "group": "build", "type": "shell", "command": "./fbt" @@ -123,17 +123,29 @@ "type": "shell", "command": "./fbt COMPACT=1 DEBUG=0 fap_dist" }, + { + "label": "[Debug] Build App", + "group": "build", + "type": "shell", + "command": "./fbt build APPSRC=${relativeFileDirname}" + }, + { + "label": "[Release] Build App", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 build APPSRC=${relativeFileDirname}" + }, { "label": "[Debug] Launch App on Flipper", "group": "build", "type": "shell", - "command": "./fbt launch_app APPSRC=${relativeFileDirname}" + "command": "./fbt launch APPSRC=${relativeFileDirname}" }, { "label": "[Release] Launch App on Flipper", "group": "build", "type": "shell", - "command": "./fbt COMPACT=1 DEBUG=0 launch_app APPSRC=${relativeFileDirname}" + "command": "./fbt COMPACT=1 DEBUG=0 launch APPSRC=${relativeFileDirname}" }, { "label": "[Debug] Launch App on Flipper with Serial Console", diff --git a/CODING_STYLE.md b/CODING_STYLE.md index c62009eff5fd..002c67f246ae 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -48,7 +48,7 @@ Almost everything in flipper firmware is built around this concept. # C coding style - Tab is 4 spaces -- Use `fbt format` to reformat source code and check style guide before commit +- Use `./fbt format` to reformat source code and check style guide before commit ## Naming diff --git a/SConstruct b/SConstruct index e2568287dff4..b51154f70322 100644 --- a/SConstruct +++ b/SConstruct @@ -171,7 +171,7 @@ distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_ fap_deploy = distenv.PhonyTarget( "fap_deploy", - "${PYTHON3} ${ROOT_DIR}/scripts/storage.py send ${SOURCE} /ext/apps", + "${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send ${SOURCE} /ext/apps", source=Dir("#/assets/resources/apps"), ) @@ -323,7 +323,9 @@ distenv.PhonyTarget( ) # Start Flipper CLI via PySerial's miniterm -distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py") +distenv.PhonyTarget( + "cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}" +) # Find blackmagic probe diff --git a/applications/ReadMe.md b/applications/ReadMe.md index e50d8e46a5f7..10e54ce22573 100644 --- a/applications/ReadMe.md +++ b/applications/ReadMe.md @@ -26,7 +26,6 @@ Applications for main Flipper menu. - `archive` - Archive and file manager - `bad_usb` - Bad USB application -- `fap_loader` - External applications loader - `gpio` - GPIO application: includes USART bridge and GPIO control - `ibutton` - iButton application, onewire keys and more - `infrared` - Infrared application, controls your IR devices diff --git a/applications/debug/application.fam b/applications/debug/application.fam index a33b3693dfea..cdbf8fe18b6c 100644 --- a/applications/debug/application.fam +++ b/applications/debug/application.fam @@ -12,5 +12,6 @@ App( "display_test", "text_box_test", "file_browser_test", + "speaker_debug", ], ) diff --git a/applications/debug/crash_test/application.fam b/applications/debug/crash_test/application.fam new file mode 100644 index 000000000000..55f62f86d89f --- /dev/null +++ b/applications/debug/crash_test/application.fam @@ -0,0 +1,10 @@ +App( + appid="crash_test", + name="Crash Test", + apptype=FlipperAppType.DEBUG, + entry_point="crash_test_app", + cdefines=["APP_CRASH_TEST"], + requires=["gui"], + stack_size=1 * 1024, + fap_category="Debug", +) diff --git a/applications/debug/crash_test/crash_test.c b/applications/debug/crash_test/crash_test.c new file mode 100644 index 000000000000..92f1668be9ff --- /dev/null +++ b/applications/debug/crash_test/crash_test.c @@ -0,0 +1,128 @@ +#include +#include + +#include +#include +#include + +#define TAG "CrashTest" + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Submenu* submenu; +} CrashTest; + +typedef enum { + CrashTestViewSubmenu, +} CrashTestView; + +typedef enum { + CrashTestSubmenuCheck, + CrashTestSubmenuCheckMessage, + CrashTestSubmenuAssert, + CrashTestSubmenuAssertMessage, + CrashTestSubmenuCrash, + CrashTestSubmenuHalt, +} CrashTestSubmenu; + +static void crash_test_submenu_callback(void* context, uint32_t index) { + CrashTest* instance = (CrashTest*)context; + UNUSED(instance); + + switch(index) { + case CrashTestSubmenuCheck: + furi_check(false); + break; + case CrashTestSubmenuCheckMessage: + furi_check(false, "Crash test: furi_check with message"); + break; + case CrashTestSubmenuAssert: + furi_assert(false); + break; + case CrashTestSubmenuAssertMessage: + furi_assert(false, "Crash test: furi_assert with message"); + break; + case CrashTestSubmenuCrash: + furi_crash("Crash test: furi_crash"); + break; + case CrashTestSubmenuHalt: + furi_halt("Crash test: furi_halt"); + break; + default: + furi_crash("Programming error"); + } +} + +static uint32_t crash_test_exit_callback(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +CrashTest* crash_test_alloc() { + CrashTest* instance = malloc(sizeof(CrashTest)); + + View* view = NULL; + + instance->gui = furi_record_open(RECORD_GUI); + instance->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(instance->view_dispatcher); + view_dispatcher_attach_to_gui( + instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); + + // Menu + instance->submenu = submenu_alloc(); + view = submenu_get_view(instance->submenu); + view_set_previous_callback(view, crash_test_exit_callback); + view_dispatcher_add_view(instance->view_dispatcher, CrashTestViewSubmenu, view); + submenu_add_item( + instance->submenu, "Check", CrashTestSubmenuCheck, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, + "Check with message", + CrashTestSubmenuCheckMessage, + crash_test_submenu_callback, + instance); + submenu_add_item( + instance->submenu, "Assert", CrashTestSubmenuAssert, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, + "Assert with message", + CrashTestSubmenuAssertMessage, + crash_test_submenu_callback, + instance); + submenu_add_item( + instance->submenu, "Crash", CrashTestSubmenuCrash, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, "Halt", CrashTestSubmenuHalt, crash_test_submenu_callback, instance); + + return instance; +} + +void crash_test_free(CrashTest* instance) { + view_dispatcher_remove_view(instance->view_dispatcher, CrashTestViewSubmenu); + submenu_free(instance->submenu); + + view_dispatcher_free(instance->view_dispatcher); + furi_record_close(RECORD_GUI); + + free(instance); +} + +int32_t crash_test_run(CrashTest* instance) { + view_dispatcher_switch_to_view(instance->view_dispatcher, CrashTestViewSubmenu); + view_dispatcher_run(instance->view_dispatcher); + return 0; +} + +int32_t crash_test_app(void* p) { + UNUSED(p); + + CrashTest* instance = crash_test_alloc(); + + int32_t ret = crash_test_run(instance); + + crash_test_free(instance); + + return ret; +} diff --git a/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c index c7f3bf24fdc7..ac2e2b806116 100644 --- a/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c +++ b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c @@ -6,6 +6,11 @@ static void comparator_trigger_callback(bool level, void* comp_ctx) { furi_hal_gpio_write(&gpio_ext_pa7, !level); } +void lfrfid_debug_view_tune_callback(void* context) { + LfRfidDebug* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, 0xBA); +} + void lfrfid_debug_scene_tune_on_enter(void* context) { LfRfidDebug* app = context; @@ -14,9 +19,9 @@ void lfrfid_debug_scene_tune_on_enter(void* context) { furi_hal_rfid_comp_set_callback(comparator_trigger_callback, app); furi_hal_rfid_comp_start(); - furi_hal_rfid_pins_read(); - furi_hal_rfid_tim_read(125000, 0.5); - furi_hal_rfid_tim_read_start(); + furi_hal_rfid_tim_read_start(125000, 0.5); + + lfrfid_debug_view_tune_set_callback(app->tune_view, lfrfid_debug_view_tune_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewTune); } @@ -43,6 +48,5 @@ void lfrfid_debug_scene_tune_on_exit(void* context) { furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); furi_hal_rfid_tim_read_stop(); - furi_hal_rfid_tim_reset(); furi_hal_rfid_pins_reset(); } diff --git a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c index fd221c4e9d36..9e48a7e27fec 100644 --- a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c +++ b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c @@ -13,6 +13,8 @@ typedef struct { uint32_t ARR; uint32_t CCR; int pos; + void (*update_callback)(void* context); + void* update_context; } LfRfidTuneViewModel; static void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) { @@ -151,6 +153,18 @@ static bool lfrfid_debug_view_tune_input_callback(InputEvent* event, void* conte consumed = false; break; } + + if(event->key == InputKeyLeft || event->key == InputKeyRight) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + if(model->update_callback) { + model->update_callback(model->update_context); + } + }, + false); + } } return consumed; @@ -161,19 +175,7 @@ LfRfidTuneView* lfrfid_debug_view_tune_alloc() { tune_view->view = view_alloc(); view_set_context(tune_view->view, tune_view); view_allocate_model(tune_view->view, ViewModelTypeLocking, sizeof(LfRfidTuneViewModel)); - - with_view_model( - tune_view->view, - LfRfidTuneViewModel * model, - { - model->dirty = true; - model->fine = false; - model->ARR = 511; - model->CCR = 255; - model->pos = 0; - }, - true); - + lfrfid_debug_view_tune_clean(tune_view); view_set_draw_callback(tune_view->view, lfrfid_debug_view_tune_draw_callback); view_set_input_callback(tune_view->view, lfrfid_debug_view_tune_input_callback); @@ -199,6 +201,8 @@ void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view) { model->ARR = 511; model->CCR = 255; model->pos = 0; + model->update_callback = NULL; + model->update_context = NULL; }, true); } @@ -232,3 +236,17 @@ uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view) { return result; } + +void lfrfid_debug_view_tune_set_callback( + LfRfidTuneView* tune_view, + void (*callback)(void* context), + void* context) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + model->update_callback = callback; + model->update_context = context; + }, + false); +} diff --git a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h index fd6d0b1fe9a5..be54b63f9a81 100644 --- a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h +++ b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h @@ -16,3 +16,8 @@ bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view); uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view); uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view); + +void lfrfid_debug_view_tune_set_callback( + LfRfidTuneView* tune_view, + void (*callback)(void* context), + void* context); diff --git a/applications/debug/speaker_debug/application.fam b/applications/debug/speaker_debug/application.fam new file mode 100644 index 000000000000..68d8b188bdd8 --- /dev/null +++ b/applications/debug/speaker_debug/application.fam @@ -0,0 +1,11 @@ +App( + appid="speaker_debug", + name="Speaker Debug", + apptype=FlipperAppType.DEBUG, + entry_point="speaker_debug_app", + requires=["gui", "notification"], + stack_size=2 * 1024, + order=10, + fap_category="Debug", + fap_libs=["music_worker"], +) diff --git a/applications/debug/speaker_debug/speaker_debug.c b/applications/debug/speaker_debug/speaker_debug.c new file mode 100644 index 000000000000..e01d5b8ec449 --- /dev/null +++ b/applications/debug/speaker_debug/speaker_debug.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include + +#define TAG "SpeakerDebug" +#define CLI_COMMAND "speaker_debug" + +typedef enum { + SpeakerDebugAppMessageTypeStop, +} SpeakerDebugAppMessageType; + +typedef struct { + SpeakerDebugAppMessageType type; +} SpeakerDebugAppMessage; + +typedef struct { + MusicWorker* music_worker; + FuriMessageQueue* message_queue; + Cli* cli; +} SpeakerDebugApp; + +static SpeakerDebugApp* speaker_app_alloc() { + SpeakerDebugApp* app = (SpeakerDebugApp*)malloc(sizeof(SpeakerDebugApp)); + app->music_worker = music_worker_alloc(); + app->message_queue = furi_message_queue_alloc(8, sizeof(SpeakerDebugAppMessage)); + app->cli = furi_record_open(RECORD_CLI); + return app; +} + +static void speaker_app_free(SpeakerDebugApp* app) { + music_worker_free(app->music_worker); + furi_message_queue_free(app->message_queue); + furi_record_close(RECORD_CLI); + free(app); +} + +static void speaker_app_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + + SpeakerDebugApp* app = (SpeakerDebugApp*)context; + SpeakerDebugAppMessage message; + FuriString* cmd = furi_string_alloc(); + + if(!args_read_string_and_trim(args, cmd)) { + furi_string_free(cmd); + printf("Usage:\r\n"); + printf("\t" CLI_COMMAND " stop\r\n"); + return; + } + + if(furi_string_cmp(cmd, "stop") == 0) { + message.type = SpeakerDebugAppMessageTypeStop; + FuriStatus status = furi_message_queue_put(app->message_queue, &message, 100); + if(status != FuriStatusOk) { + printf("Failed to send message\r\n"); + } else { + printf("Stopping\r\n"); + } + } else { + printf("Usage:\r\n"); + printf("\t" CLI_COMMAND " stop\r\n"); + } + + furi_string_free(cmd); +} + +static bool speaker_app_music_play(SpeakerDebugApp* app, const char* rtttl) { + if(music_worker_is_playing(app->music_worker)) { + music_worker_stop(app->music_worker); + } + + if(!music_worker_load_rtttl_from_string(app->music_worker, rtttl)) { + FURI_LOG_E(TAG, "Failed to load RTTTL"); + return false; + } + + music_worker_set_volume(app->music_worker, 1.0f); + music_worker_start(app->music_worker); + + return true; +} + +static void speaker_app_music_stop(SpeakerDebugApp* app) { + if(music_worker_is_playing(app->music_worker)) { + music_worker_stop(app->music_worker); + } +} + +static void speaker_app_run(SpeakerDebugApp* app, const char* arg) { + if(!arg || !speaker_app_music_play(app, arg)) { + FURI_LOG_E(TAG, "Provided RTTTL is invalid"); + return; + } + + cli_add_command(app->cli, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app); + + SpeakerDebugAppMessage message; + FuriStatus status; + while(true) { + status = furi_message_queue_get(app->message_queue, &message, FuriWaitForever); + + if(status == FuriStatusOk) { + if(message.type == SpeakerDebugAppMessageTypeStop) { + speaker_app_music_stop(app); + break; + } + } + } + + cli_delete_command(app->cli, CLI_COMMAND); +} + +int32_t speaker_debug_app(void* arg) { + SpeakerDebugApp* app = speaker_app_alloc(); + speaker_app_run(app, arg); + speaker_app_free(app); + return 0; +} diff --git a/applications/debug/subghz_test/application.fam b/applications/debug/subghz_test/application.fam new file mode 100644 index 000000000000..1b3e19d73f9c --- /dev/null +++ b/applications/debug/subghz_test/application.fam @@ -0,0 +1,14 @@ +App( + appid="subghz_test", + name="Sub-Ghz test", + apptype=FlipperAppType.DEBUG, + targets=["f7"], + entry_point="subghz_test_app", + requires=["gui"], + stack_size=4 * 1024, + order=50, + fap_icon="subghz_test_10px.png", + fap_category="Debug", + fap_icon_assets="images", + fap_version="0.1", +) diff --git a/applications/debug/subghz_test/helpers/subghz_test_event.h b/applications/debug/subghz_test/helpers/subghz_test_event.h new file mode 100644 index 000000000000..a0a851976ad8 --- /dev/null +++ b/applications/debug/subghz_test/helpers/subghz_test_event.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum { + //SubGhzTestCustomEvent + SubGhzTestCustomEventStartId = 100, + SubGhzTestCustomEventSceneShowOnlyRX, +} SubGhzTestCustomEvent; diff --git a/applications/main/subghz/helpers/subghz_testing.c b/applications/debug/subghz_test/helpers/subghz_test_frequency.c similarity index 95% rename from applications/main/subghz/helpers/subghz_testing.c rename to applications/debug/subghz_test/helpers/subghz_test_frequency.c index 8afa868e0126..ed1ba704e4de 100644 --- a/applications/main/subghz/helpers/subghz_testing.c +++ b/applications/debug/subghz_test/helpers/subghz_test_frequency.c @@ -1,4 +1,4 @@ -#include "subghz_testing.h" +#include "subghz_test_frequency.h" const uint32_t subghz_frequencies_testing[] = { /* 300 - 348 */ diff --git a/applications/main/subghz/helpers/subghz_testing.h b/applications/debug/subghz_test/helpers/subghz_test_frequency.h similarity index 84% rename from applications/main/subghz/helpers/subghz_testing.h rename to applications/debug/subghz_test/helpers/subghz_test_frequency.h index 29ce578a0106..7dd1423f968e 100644 --- a/applications/main/subghz/helpers/subghz_testing.h +++ b/applications/debug/subghz_test/helpers/subghz_test_frequency.h @@ -1,5 +1,5 @@ #pragma once -#include "../subghz_i.h" +#include "../subghz_test_app_i.h" extern const uint32_t subghz_frequencies_testing[]; extern const uint32_t subghz_frequencies_count_testing; diff --git a/applications/debug/subghz_test/helpers/subghz_test_types.h b/applications/debug/subghz_test/helpers/subghz_test_types.h new file mode 100644 index 000000000000..03be6459ec13 --- /dev/null +++ b/applications/debug/subghz_test/helpers/subghz_test_types.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#define SUBGHZ_TEST_VERSION_APP "0.1" +#define SUBGHZ_TEST_DEVELOPED "SkorP" +#define SUBGHZ_TEST_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" + +typedef enum { + SubGhzTestViewVariableItemList, + SubGhzTestViewSubmenu, + SubGhzTestViewStatic, + SubGhzTestViewCarrier, + SubGhzTestViewPacket, + SubGhzTestViewWidget, + SubGhzTestViewPopup, +} SubGhzTestView; diff --git a/applications/debug/subghz_test/images/DolphinCommon_56x48.png b/applications/debug/subghz_test/images/DolphinCommon_56x48.png new file mode 100644 index 000000000000..089aaed83507 Binary files /dev/null and b/applications/debug/subghz_test/images/DolphinCommon_56x48.png differ diff --git a/applications/debug/subghz_test/protocol/math.c b/applications/debug/subghz_test/protocol/math.c new file mode 100644 index 000000000000..24202ad1c628 --- /dev/null +++ b/applications/debug/subghz_test/protocol/math.c @@ -0,0 +1,244 @@ +#include "math.h" + +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) { + uint64_t reverse_key = 0; + for(uint8_t i = 0; i < bit_count; i++) { + reverse_key = reverse_key << 1 | bit_read(key, i); + } + return reverse_key; +} + +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) { + uint8_t parity = 0; + for(uint8_t i = 0; i < bit_count; i++) { + parity += bit_read(key, i); + } + return parity & 0x01; +} + +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 4; // LSBs are unused + uint8_t poly = polynomial << 4; + uint8_t bit; + + while(size--) { + remainder ^= *message++; + for(bit = 0; bit < 8; bit++) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 4 & 0x0f; // discard the LSBs +} + +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 1; // LSB is unused + uint8_t poly = polynomial << 1; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 1 & 0x7f; // discard the LSB +} + +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8); + polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8); + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte] << 8; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x8000) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int i = 7; i >= 0; --i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + // Process message from last byte to first byte (reflected) + for(int byte = size - 1; byte >= 0; --byte) { + uint8_t data = message[byte]; + // Process individual bits of each byte (reflected) + for(uint8_t i = 0; i < 8; ++i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) { + sum ^= key; + } + + // roll the key left (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped lsb as MSB) + if(key & 0x80) + key = (key << 1) ^ gen; + else + key = (key << 1); + } + } + return sum; +} + +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + size_t size, + uint16_t gen, + uint16_t key) { + uint16_t sum = 0; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int8_t i = 7; i >= 0; --i) { + // if data bit is set then xor with key + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) { + uint32_t result = 0; + for(size_t i = 0; i < size; ++i) { + result += message[i]; + } + return (uint8_t)result; +} + +uint8_t subghz_protocol_blocks_parity8(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; +} + +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { + result ^= subghz_protocol_blocks_parity8(message[i]); + } + return result; +} + +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { + result ^= message[i]; + } + return result; +} \ No newline at end of file diff --git a/applications/debug/subghz_test/protocol/math.h b/applications/debug/subghz_test/protocol/math.h new file mode 100644 index 000000000000..dcea3da5faf0 --- /dev/null +++ b/applications/debug/subghz_test/protocol/math.h @@ -0,0 +1,222 @@ +#pragma once + +#include +#include +#include + +#define bit_read(value, bit) (((value) >> (bit)) & 0x01) +#define bit_set(value, bit) \ + ({ \ + __typeof__(value) _one = (1); \ + (value) |= (_one << (bit)); \ + }) +#define bit_clear(value, bit) \ + ({ \ + __typeof__(value) _one = (1); \ + (value) &= ~(_one << (bit)); \ + }) +#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) +#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) + +#ifdef __cplusplus +extern "C" { +#endif + +/** Flip the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return Reverse data + */ +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count); + +/** Get parity the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return parity + */ +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count); + +/** CRC-4 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** CRC-7 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 + + * x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal + * bit-by-bit parity XOR) + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** "Little-endian" Cyclic Redundancy Check CRC-8 LE Input and output are + * reflected, i.e. least significant bit is shifted in first + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is + * shifted in first. Note that poly and init already need to be reflected + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** CRC-16 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** Digest-8 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key); + +/** Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key); + +/** Digest-16 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + size_t size, + uint16_t gen, + uint16_t key); + +/** Compute Addition of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value + */ +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size); + +/** Compute bit parity of a single byte (8 bits) + * + * @param byte single byte to check + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity8(uint8_t byte); + +/** Compute bit parity of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size); + +/** Compute XOR (byte-wide parity) of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value, per bit-position 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/protocols/princeton_for_testing.c b/applications/debug/subghz_test/protocol/princeton_for_testing.c similarity index 99% rename from lib/subghz/protocols/princeton_for_testing.c rename to applications/debug/subghz_test/protocol/princeton_for_testing.c index 478d14cdfcaf..334a8241bbe3 100644 --- a/lib/subghz/protocols/princeton_for_testing.c +++ b/applications/debug/subghz_test/protocol/princeton_for_testing.c @@ -1,7 +1,7 @@ #include "princeton_for_testing.h" #include -#include "../blocks/math.h" +#include "math.h" /* * Help diff --git a/lib/subghz/protocols/princeton_for_testing.h b/applications/debug/subghz_test/protocol/princeton_for_testing.h similarity index 97% rename from lib/subghz/protocols/princeton_for_testing.h rename to applications/debug/subghz_test/protocol/princeton_for_testing.h index 07a37ec5f7f5..7b4201d38a9e 100644 --- a/lib/subghz/protocols/princeton_for_testing.h +++ b/applications/debug/subghz_test/protocol/princeton_for_testing.h @@ -1,6 +1,8 @@ #pragma once -#include "base.h" +//#include "base.h" +#include +#include /** SubGhzDecoderPrinceton anonymous type */ typedef struct SubGhzDecoderPrinceton SubGhzDecoderPrinceton; diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene.c b/applications/debug/subghz_test/scenes/subghz_test_scene.c new file mode 100644 index 000000000000..ff439ef0f8db --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene.c @@ -0,0 +1,30 @@ +#include "../subghz_test_app_i.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const subghz_test_scene_on_enter_handlers[])(void*) = { +#include "subghz_test_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const subghz_test_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "subghz_test_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const subghz_test_scene_on_exit_handlers[])(void* context) = { +#include "subghz_test_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers subghz_test_scene_handlers = { + .on_enter_handlers = subghz_test_scene_on_enter_handlers, + .on_event_handlers = subghz_test_scene_on_event_handlers, + .on_exit_handlers = subghz_test_scene_on_exit_handlers, + .scene_num = SubGhzTestSceneNum, +}; diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene.h b/applications/debug/subghz_test/scenes/subghz_test_scene.h new file mode 100644 index 000000000000..0e6e06481b53 --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) SubGhzTestScene##id, +typedef enum { +#include "subghz_test_scene_config.h" + SubGhzTestSceneNum, +} SubGhzTestScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers subghz_test_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "subghz_test_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "subghz_test_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "subghz_test_scene_config.h" +#undef ADD_SCENE diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_about.c b/applications/debug/subghz_test/scenes/subghz_test_scene_about.c new file mode 100644 index 000000000000..64263d738877 --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_about.c @@ -0,0 +1,66 @@ +#include "../subghz_test_app_i.h" + +void subghz_test_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) { + SubGhzTestApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void subghz_test_scene_about_on_enter(void* context) { + SubGhzTestApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", SUBGHZ_TEST_VERSION_APP); + furi_string_cat_printf(temp_str, "Developed by: %s\n", SUBGHZ_TEST_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", SUBGHZ_TEST_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, + "This application is designed\nto test the functionality of the\nbuilt-in CC1101 module.\n\n"); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! Sub-Ghz Test \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewWidget); +} + +bool subghz_test_scene_about_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void subghz_test_scene_about_on_exit(void* context) { + SubGhzTestApp* app = context; + + // Clear views + widget_reset(app->widget); +} diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_carrier.c b/applications/debug/subghz_test/scenes/subghz_test_scene_carrier.c new file mode 100644 index 000000000000..41ff5c8c6e19 --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_carrier.c @@ -0,0 +1,29 @@ +#include "../subghz_test_app_i.h" + +void subghz_test_scene_carrier_callback(SubGhzTestCarrierEvent event, void* context) { + furi_assert(context); + SubGhzTestApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void subghz_test_scene_carrier_on_enter(void* context) { + SubGhzTestApp* app = context; + subghz_test_carrier_set_callback( + app->subghz_test_carrier, subghz_test_scene_carrier_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewCarrier); +} + +bool subghz_test_scene_carrier_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzTestCarrierEventOnlyRx) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx); + return true; + } + } + return false; +} + +void subghz_test_scene_carrier_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_config.h b/applications/debug/subghz_test/scenes/subghz_test_scene_config.h new file mode 100644 index 000000000000..80a42c3761a0 --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_config.h @@ -0,0 +1,6 @@ +ADD_SCENE(subghz_test, start, Start) +ADD_SCENE(subghz_test, about, About) +ADD_SCENE(subghz_test, carrier, Carrier) +ADD_SCENE(subghz_test, packet, Packet) +ADD_SCENE(subghz_test, static, Static) +ADD_SCENE(subghz_test, show_only_rx, ShowOnlyRx) diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_packet.c b/applications/debug/subghz_test/scenes/subghz_test_scene_packet.c new file mode 100644 index 000000000000..b43a4d0cb39b --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_packet.c @@ -0,0 +1,29 @@ +#include "../subghz_test_app_i.h" + +void subghz_test_scene_packet_callback(SubGhzTestPacketEvent event, void* context) { + furi_assert(context); + SubGhzTestApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void subghz_test_scene_packet_on_enter(void* context) { + SubGhzTestApp* app = context; + subghz_test_packet_set_callback( + app->subghz_test_packet, subghz_test_scene_packet_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewPacket); +} + +bool subghz_test_scene_packet_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzTestPacketEventOnlyRx) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx); + return true; + } + } + return false; +} + +void subghz_test_scene_packet_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_show_only_rx.c b/applications/debug/subghz_test/scenes/subghz_test_scene_show_only_rx.c new file mode 100644 index 000000000000..3d5a54355c3d --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_show_only_rx.c @@ -0,0 +1,49 @@ +#include "../subghz_test_app_i.h" +#include + +void subghz_test_scene_show_only_rx_popup_callback(void* context) { + SubGhzTestApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SubGhzTestCustomEventSceneShowOnlyRX); +} + +void subghz_test_scene_show_only_rx_on_enter(void* context) { + SubGhzTestApp* app = context; + + // Setup view + Popup* popup = app->popup; + + const char* header_text = "Transmission is blocked"; + const char* message_text = "Transmission on\nthis frequency is\nrestricted in\nyour region"; + if(!furi_hal_region_is_provisioned()) { + header_text = "Firmware update needed"; + message_text = "Please update\nfirmware before\nusing this feature\nflipp.dev/upd"; + } + + popup_set_header(popup, header_text, 63, 3, AlignCenter, AlignTop); + popup_set_text(popup, message_text, 0, 17, AlignLeft, AlignTop); + popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + + popup_set_timeout(popup, 1500); + popup_set_context(popup, app); + popup_set_callback(popup, subghz_test_scene_show_only_rx_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewPopup); +} + +bool subghz_test_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzTestCustomEventSceneShowOnlyRX) { + scene_manager_previous_scene(app->scene_manager); + return true; + } + } + return false; +} + +void subghz_test_scene_show_only_rx_on_exit(void* context) { + SubGhzTestApp* app = context; + Popup* popup = app->popup; + + popup_reset(popup); +} diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_start.c b/applications/debug/subghz_test/scenes/subghz_test_scene_start.c new file mode 100644 index 000000000000..cf3b08163d2d --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_start.c @@ -0,0 +1,77 @@ +#include "../subghz_test_app_i.h" + +typedef enum { + SubmenuIndexSubGhzTestCarrier, + SubmenuIndexSubGhzTestPacket, + SubmenuIndexSubGhzTestStatic, + SubmenuIndexSubGhzTestAbout, +} SubmenuIndex; + +void subghz_test_scene_start_submenu_callback(void* context, uint32_t index) { + SubGhzTestApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void subghz_test_scene_start_on_enter(void* context) { + SubGhzTestApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Carrier", + SubmenuIndexSubGhzTestCarrier, + subghz_test_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "Packet", + SubmenuIndexSubGhzTestPacket, + subghz_test_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "Static", + SubmenuIndexSubGhzTestStatic, + subghz_test_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "About", + SubmenuIndexSubGhzTestAbout, + subghz_test_scene_start_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, SubGhzTestSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewSubmenu); +} + +bool subghz_test_scene_start_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSubGhzTestAbout) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneAbout); + consumed = true; + } else if(event.event == SubmenuIndexSubGhzTestCarrier) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneCarrier); + consumed = true; + } else if(event.event == SubmenuIndexSubGhzTestPacket) { + scene_manager_next_scene(app->scene_manager, SubGhzTestScenePacket); + consumed = true; + } else if(event.event == SubmenuIndexSubGhzTestStatic) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneStatic); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, SubGhzTestSceneStart, event.event); + } + + return consumed; +} + +void subghz_test_scene_start_on_exit(void* context) { + SubGhzTestApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_static.c b/applications/debug/subghz_test/scenes/subghz_test_scene_static.c new file mode 100644 index 000000000000..a008d2438ffd --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_static.c @@ -0,0 +1,29 @@ +#include "../subghz_test_app_i.h" + +void subghz_test_scene_static_callback(SubGhzTestStaticEvent event, void* context) { + furi_assert(context); + SubGhzTestApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void subghz_test_scene_static_on_enter(void* context) { + SubGhzTestApp* app = context; + subghz_test_static_set_callback( + app->subghz_test_static, subghz_test_scene_static_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewStatic); +} + +bool subghz_test_scene_static_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzTestStaticEventOnlyRx) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx); + return true; + } + } + return false; +} + +void subghz_test_scene_static_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/debug/subghz_test/subghz_test_10px.png b/applications/debug/subghz_test/subghz_test_10px.png new file mode 100644 index 000000000000..10dac0ecaac6 Binary files /dev/null and b/applications/debug/subghz_test/subghz_test_10px.png differ diff --git a/applications/debug/subghz_test/subghz_test_app.c b/applications/debug/subghz_test/subghz_test_app.c new file mode 100644 index 000000000000..704941fb28e0 --- /dev/null +++ b/applications/debug/subghz_test/subghz_test_app.c @@ -0,0 +1,139 @@ +#include "subghz_test_app_i.h" + +#include +#include + +static bool subghz_test_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + SubGhzTestApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool subghz_test_app_back_event_callback(void* context) { + furi_assert(context); + SubGhzTestApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void subghz_test_app_tick_event_callback(void* context) { + furi_assert(context); + SubGhzTestApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +SubGhzTestApp* subghz_test_app_alloc() { + SubGhzTestApp* app = malloc(sizeof(SubGhzTestApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&subghz_test_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, subghz_test_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, subghz_test_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, subghz_test_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // SubMenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, SubGhzTestViewSubmenu, submenu_get_view(app->submenu)); + + // Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, SubGhzTestViewWidget, widget_get_view(app->widget)); + + // Popup + app->popup = popup_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, SubGhzTestViewPopup, popup_get_view(app->popup)); + + // Carrier Test Module + app->subghz_test_carrier = subghz_test_carrier_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + SubGhzTestViewCarrier, + subghz_test_carrier_get_view(app->subghz_test_carrier)); + + // Packet Test + app->subghz_test_packet = subghz_test_packet_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + SubGhzTestViewPacket, + subghz_test_packet_get_view(app->subghz_test_packet)); + + // Static send + app->subghz_test_static = subghz_test_static_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + SubGhzTestViewStatic, + subghz_test_static_get_view(app->subghz_test_static)); + + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneStart); + + return app; +} + +void subghz_test_app_free(SubGhzTestApp* app) { + furi_assert(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewSubmenu); + submenu_free(app->submenu); + + // Widget + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewWidget); + widget_free(app->widget); + + // Popup + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewPopup); + popup_free(app->popup); + + // Carrier Test + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewCarrier); + subghz_test_carrier_free(app->subghz_test_carrier); + + // Packet Test + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewPacket); + subghz_test_packet_free(app->subghz_test_packet); + + // Static + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewStatic); + subghz_test_static_free(app->subghz_test_static); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Close records + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t subghz_test_app(void* p) { + UNUSED(p); + SubGhzTestApp* subghz_test_app = subghz_test_app_alloc(); + + view_dispatcher_run(subghz_test_app->view_dispatcher); + + subghz_test_app_free(subghz_test_app); + + return 0; +} diff --git a/applications/debug/subghz_test/subghz_test_app_i.c b/applications/debug/subghz_test/subghz_test_app_i.c new file mode 100644 index 000000000000..0ec6635a0ebd --- /dev/null +++ b/applications/debug/subghz_test/subghz_test_app_i.c @@ -0,0 +1,5 @@ +#include "subghz_test_app_i.h" + +#include + +#define TAG "SubGhzTest" diff --git a/applications/debug/subghz_test/subghz_test_app_i.h b/applications/debug/subghz_test/subghz_test_app_i.h new file mode 100644 index 000000000000..c96f9c4ee4c0 --- /dev/null +++ b/applications/debug/subghz_test/subghz_test_app_i.h @@ -0,0 +1,32 @@ +#pragma once + +#include "helpers/subghz_test_types.h" +#include "helpers/subghz_test_event.h" + +#include "scenes/subghz_test_scene.h" +#include +#include +#include +#include +#include +#include +#include + +#include "views/subghz_test_static.h" +#include "views/subghz_test_carrier.h" +#include "views/subghz_test_packet.h" + +typedef struct SubGhzTestApp SubGhzTestApp; + +struct SubGhzTestApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + Submenu* submenu; + Widget* widget; + Popup* popup; + SubGhzTestStatic* subghz_test_static; + SubGhzTestCarrier* subghz_test_carrier; + SubGhzTestPacket* subghz_test_packet; +}; diff --git a/applications/main/subghz/views/subghz_test_carrier.c b/applications/debug/subghz_test/views/subghz_test_carrier.c similarity index 97% rename from applications/main/subghz/views/subghz_test_carrier.c rename to applications/debug/subghz_test/views/subghz_test_carrier.c index e533a6aac4e9..53e309b7c98c 100644 --- a/applications/main/subghz/views/subghz_test_carrier.c +++ b/applications/debug/subghz_test/views/subghz_test_carrier.c @@ -1,6 +1,7 @@ #include "subghz_test_carrier.h" -#include "../subghz_i.h" -#include "../helpers/subghz_testing.h" +#include "../subghz_test_app_i.h" +#include "../helpers/subghz_test_frequency.h" +#include #include #include @@ -138,7 +139,7 @@ void subghz_test_carrier_enter(void* context) { SubGhzTestCarrier* subghz_test_carrier = context; furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); diff --git a/applications/main/subghz/views/subghz_test_carrier.h b/applications/debug/subghz_test/views/subghz_test_carrier.h similarity index 100% rename from applications/main/subghz/views/subghz_test_carrier.h rename to applications/debug/subghz_test/views/subghz_test_carrier.h diff --git a/applications/main/subghz/views/subghz_test_packet.c b/applications/debug/subghz_test/views/subghz_test_packet.c similarity index 97% rename from applications/main/subghz/views/subghz_test_packet.c rename to applications/debug/subghz_test/views/subghz_test_packet.c index 43502180cea4..bab83ab5b579 100644 --- a/applications/main/subghz/views/subghz_test_packet.c +++ b/applications/debug/subghz_test/views/subghz_test_packet.c @@ -1,13 +1,14 @@ #include "subghz_test_packet.h" -#include "../subghz_i.h" -#include "../helpers/subghz_testing.h" +#include "../subghz_test_app_i.h" +#include "../helpers/subghz_test_frequency.h" +#include #include #include #include #include #include -#include +#include "../protocol/princeton_for_testing.h" #define SUBGHZ_TEST_PACKET_COUNT 500 @@ -194,7 +195,7 @@ void subghz_test_packet_enter(void* context) { SubGhzTestPacket* instance = context; furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); with_view_model( instance->view, diff --git a/applications/main/subghz/views/subghz_test_packet.h b/applications/debug/subghz_test/views/subghz_test_packet.h similarity index 100% rename from applications/main/subghz/views/subghz_test_packet.h rename to applications/debug/subghz_test/views/subghz_test_packet.h diff --git a/applications/main/subghz/views/subghz_test_static.c b/applications/debug/subghz_test/views/subghz_test_static.c similarity index 96% rename from applications/main/subghz/views/subghz_test_static.c rename to applications/debug/subghz_test/views/subghz_test_static.c index 6abefda763e8..6764fd5ca9fe 100644 --- a/applications/main/subghz/views/subghz_test_static.c +++ b/applications/debug/subghz_test/views/subghz_test_static.c @@ -1,13 +1,14 @@ #include "subghz_test_static.h" -#include "../subghz_i.h" -#include "../helpers/subghz_testing.h" +#include "../subghz_test_app_i.h" +#include "../helpers/subghz_test_frequency.h" +#include #include #include #include #include #include -#include +#include "../protocol/princeton_for_testing.h" #define TAG "SubGhzTestStatic" @@ -141,7 +142,7 @@ void subghz_test_static_enter(void* context) { SubGhzTestStatic* instance = context; furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_cc1101_g0, false); diff --git a/applications/main/subghz/views/subghz_test_static.h b/applications/debug/subghz_test/views/subghz_test_static.h similarity index 100% rename from applications/main/subghz/views/subghz_test_static.h rename to applications/debug/subghz_test/views/subghz_test_static.h diff --git a/applications/debug/uart_echo/uart_echo.c b/applications/debug/uart_echo/uart_echo.c index dc1327529214..4bede9ab45fd 100644 --- a/applications/debug/uart_echo/uart_echo.c +++ b/applications/debug/uart_echo/uart_echo.c @@ -10,6 +10,8 @@ #define LINES_ON_SCREEN 6 #define COLUMNS_ON_SCREEN 21 +#define TAG "UartEcho" +#define DEFAULT_BAUD_RATE 230400 typedef struct UartDumpModel UartDumpModel; @@ -179,7 +181,7 @@ static int32_t uart_echo_worker(void* context) { return 0; } -static UartEchoApp* uart_echo_app_alloc() { +static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) { UartEchoApp* app = malloc(sizeof(UartEchoApp)); app->rx_stream = furi_stream_buffer_alloc(2048, 1); @@ -220,7 +222,7 @@ static UartEchoApp* uart_echo_app_alloc() { // Enable uart listener furi_hal_console_disable(); - furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200); + furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate); furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app); return app; @@ -263,8 +265,18 @@ static void uart_echo_app_free(UartEchoApp* app) { } int32_t uart_echo_app(void* p) { - UNUSED(p); - UartEchoApp* app = uart_echo_app_alloc(); + uint32_t baudrate = DEFAULT_BAUD_RATE; + if(p) { + const char* baudrate_str = p; + if(sscanf(baudrate_str, "%lu", &baudrate) != 1) { + FURI_LOG_E(TAG, "Invalid baudrate: %s", baudrate_str); + baudrate = DEFAULT_BAUD_RATE; + } + } + + FURI_LOG_I(TAG, "Using baudrate: %lu", baudrate); + + UartEchoApp* app = uart_echo_app_alloc(baudrate); view_dispatcher_run(app->view_dispatcher); uart_echo_app_free(app); return 0; diff --git a/applications/debug/unit_tests/application.fam b/applications/debug/unit_tests/application.fam index 949bb3fc292b..6f150e285ab9 100644 --- a/applications/debug/unit_tests/application.fam +++ b/applications/debug/unit_tests/application.fam @@ -4,6 +4,7 @@ App( entry_point="unit_tests_on_system_start", cdefines=["APP_UNIT_TESTS"], provides=["delay_test"], + requires=["nfc"], order=100, ) diff --git a/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c b/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c new file mode 100644 index 000000000000..2d5bad4c8a55 --- /dev/null +++ b/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c @@ -0,0 +1,32 @@ +#include + +#include "../minunit.h" + +MU_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields) { + mu_assert( + sizeof(DialogsFileBrowserOptions) == 28, + "Changes to `DialogsFileBrowserOptions` should also be reflected in `dialog_file_browser_set_basic_options`"); + + DialogsFileBrowserOptions options; + dialog_file_browser_set_basic_options(&options, ".fap", NULL); + // note: this assertions can safely be changed, their primary purpose is to remind the maintainer + // to update `dialog_file_browser_set_basic_options` by including all structure fields in it + mu_assert_string_eq(".fap", options.extension); + mu_assert_null(options.base_path); + mu_assert(options.skip_assets, "`skip_assets` should default to `true"); + mu_assert(options.hide_dot_files, "`hide_dot_files` should default to `true"); + mu_assert_null(options.icon); + mu_assert(options.hide_ext, "`hide_ext` should default to `true"); + mu_assert_null(options.item_loader_callback); + mu_assert_null(options.item_loader_context); +} + +MU_TEST_SUITE(dialogs_file_browser_options) { + MU_RUN_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields); +} + +int run_minunit_test_dialogs_file_browser_options() { + MU_RUN_SUITE(dialogs_file_browser_options); + + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/infrared/infrared_test.c b/applications/debug/unit_tests/infrared/infrared_test.c index 2bcb95da8e5b..b2acad470e69 100644 --- a/applications/debug/unit_tests/infrared/infrared_test.c +++ b/applications/debug/unit_tests/infrared/infrared_test.c @@ -425,6 +425,7 @@ MU_TEST(infrared_test_decoder_mixed) { infrared_test_run_decoder(InfraredProtocolSamsung32, 1); infrared_test_run_decoder(InfraredProtocolSIRC, 3); infrared_test_run_decoder(InfraredProtocolKaseikyo, 1); + infrared_test_run_decoder(InfraredProtocolRCA, 1); } MU_TEST(infrared_test_decoder_nec) { @@ -499,6 +500,15 @@ MU_TEST(infrared_test_decoder_kaseikyo) { infrared_test_run_decoder(InfraredProtocolKaseikyo, 6); } +MU_TEST(infrared_test_decoder_rca) { + infrared_test_run_decoder(InfraredProtocolRCA, 1); + infrared_test_run_decoder(InfraredProtocolRCA, 2); + infrared_test_run_decoder(InfraredProtocolRCA, 3); + infrared_test_run_decoder(InfraredProtocolRCA, 4); + infrared_test_run_decoder(InfraredProtocolRCA, 5); + infrared_test_run_decoder(InfraredProtocolRCA, 6); +} + MU_TEST(infrared_test_encoder_decoder_all) { infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1); infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1); @@ -509,6 +519,7 @@ MU_TEST(infrared_test_encoder_decoder_all) { infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1); infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1); infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1); + infrared_test_run_encoder_decoder(InfraredProtocolRCA, 1); } MU_TEST_SUITE(infrared_test) { @@ -527,6 +538,7 @@ MU_TEST_SUITE(infrared_test) { MU_RUN_TEST(infrared_test_decoder_samsung32); MU_RUN_TEST(infrared_test_decoder_necext1); MU_RUN_TEST(infrared_test_decoder_kaseikyo); + MU_RUN_TEST(infrared_test_decoder_rca); MU_RUN_TEST(infrared_test_decoder_mixed); MU_RUN_TEST(infrared_test_encoder_decoder_all); } diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 54bdd59097e9..8500d46c9b3a 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -1,46 +1,36 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "../minunit.h" +#include -#define TAG "NfcTest" +#include -#define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/") -#define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc" -#define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc" -#define NFC_TEST_DICT_PATH EXT_PATH("unit_tests/mf_classic_dict.nfc") -#define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_dev_test.nfc") +#include "../minunit.h" -static const char* nfc_test_file_type = "Flipper NFC test"; -static const uint32_t nfc_test_file_version = 1; +#define TAG "NfcTest" -#define NFC_TEST_DATA_MAX_LEN 18 -#define NFC_TETS_TIMINGS_MAX_LEN 1350 +#define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_device_test.nfc") +#define MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_dict.nfc") typedef struct { Storage* storage; - NfcaSignal* signal; - uint32_t test_data_len; - uint8_t test_data[NFC_TEST_DATA_MAX_LEN]; - uint32_t test_timings_len; - uint32_t test_timings[NFC_TETS_TIMINGS_MAX_LEN]; } NfcTest; static NfcTest* nfc_test = NULL; static void nfc_test_alloc() { nfc_test = malloc(sizeof(NfcTest)); - nfc_test->signal = nfca_signal_alloc(); nfc_test->storage = furi_record_open(RECORD_STORAGE); } @@ -48,480 +38,492 @@ static void nfc_test_free() { furi_assert(nfc_test); furi_record_close(RECORD_STORAGE); - nfca_signal_free(nfc_test->signal); free(nfc_test); nfc_test = NULL; } -static bool nfc_test_read_signal_from_file(const char* file_name) { - bool success = false; - - FlipperFormat* file = flipper_format_file_alloc(nfc_test->storage); - FuriString* file_type; - file_type = furi_string_alloc(); - uint32_t file_version = 0; - - do { - if(!flipper_format_file_open_existing(file, file_name)) break; - if(!flipper_format_read_header(file, file_type, &file_version)) break; - if(furi_string_cmp_str(file_type, nfc_test_file_type) || - file_version != nfc_test_file_version) - break; - if(!flipper_format_read_uint32(file, "Data length", &nfc_test->test_data_len, 1)) break; - if(nfc_test->test_data_len > NFC_TEST_DATA_MAX_LEN) break; - if(!flipper_format_read_hex( - file, "Plain data", nfc_test->test_data, nfc_test->test_data_len)) - break; - if(!flipper_format_read_uint32(file, "Timings length", &nfc_test->test_timings_len, 1)) - break; - if(nfc_test->test_timings_len > NFC_TETS_TIMINGS_MAX_LEN) break; - if(!flipper_format_read_uint32( - file, "Timings", nfc_test->test_timings, nfc_test->test_timings_len)) - break; - success = true; - } while(false); - - furi_string_free(file_type); - flipper_format_free(file); - - return success; -} - -static bool nfc_test_digital_signal_test_encode( - const char* file_name, - uint32_t encode_max_time, - uint32_t timing_tolerance, - uint32_t timings_sum_tolerance) { - furi_assert(nfc_test); +static void nfc_test_save_and_load(NfcDevice* nfc_device_ref) { + NfcDevice* nfc_device_dut = nfc_device_alloc(); + + mu_assert( + nfc_device_save(nfc_device_ref, NFC_TEST_NFC_DEV_PATH), "nfc_device_save() failed\r\n"); + + mu_assert( + nfc_device_load(nfc_device_dut, NFC_TEST_NFC_DEV_PATH), "nfc_device_load() failed\r\n"); - bool success = false; - uint32_t time = 0; - uint32_t dut_timings_sum = 0; - uint32_t ref_timings_sum = 0; - uint8_t parity[10] = {}; - - do { - // Read test data - if(!nfc_test_read_signal_from_file(file_name)) { - FURI_LOG_E(TAG, "Failed to read signal from file"); - break; - } - - // Encode signal - FURI_CRITICAL_ENTER(); - time = DWT->CYCCNT; - nfca_signal_encode( - nfc_test->signal, nfc_test->test_data, nfc_test->test_data_len * 8, parity); - digital_signal_prepare_arr(nfc_test->signal->tx_signal); - time = (DWT->CYCCNT - time) / furi_hal_cortex_instructions_per_microsecond(); - FURI_CRITICAL_EXIT(); - - // Check timings - if(time > encode_max_time) { - FURI_LOG_E( - TAG, "Encoding time: %ld us while accepted value: %ld us", time, encode_max_time); - break; - } - - // Check data - if(nfc_test->signal->tx_signal->edge_cnt != nfc_test->test_timings_len) { - FURI_LOG_E(TAG, "Not equal timings buffers length"); - break; - } - - uint32_t timings_diff = 0; - uint32_t* ref = nfc_test->test_timings; - uint32_t* dut = nfc_test->signal->tx_signal->reload_reg_buff; - bool timing_check_success = true; - for(size_t i = 0; i < nfc_test->test_timings_len; i++) { - timings_diff = dut[i] > ref[i] ? dut[i] - ref[i] : ref[i] - dut[i]; - dut_timings_sum += dut[i]; - ref_timings_sum += ref[i]; - if(timings_diff > timing_tolerance) { - FURI_LOG_E( - TAG, "Too big difference in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]); - timing_check_success = false; - break; - } - } - if(!timing_check_success) break; - uint32_t sum_diff = dut_timings_sum > ref_timings_sum ? dut_timings_sum - ref_timings_sum : - ref_timings_sum - dut_timings_sum; - if(sum_diff > timings_sum_tolerance) { - FURI_LOG_E( - TAG, - "Too big difference in timings sum. Ref: %ld, DUT: %ld", - ref_timings_sum, - dut_timings_sum); - break; - } - - FURI_LOG_I(TAG, "Encoding time: %ld us. Acceptable time: %ld us", time, encode_max_time); - FURI_LOG_I( - TAG, - "Timings sum difference: %ld [1/64MHZ]. Acceptable difference: %ld [1/64MHz]", - sum_diff, - timings_sum_tolerance); - success = true; - } while(false); - - return success; -} - -MU_TEST(nfc_digital_signal_test) { mu_assert( - nfc_test_digital_signal_test_encode( - NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_SHORT_FILE, 500, 1, 37), - "NFC short digital signal test failed\r\n"); + nfc_device_is_equal(nfc_device_ref, nfc_device_dut), + "nfc_device_data_dut != nfc_device_data_ref\r\n"); + mu_assert( - nfc_test_digital_signal_test_encode( - NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_LONG_FILE, 2000, 1, 37), - "NFC long digital signal test failed\r\n"); + storage_simply_remove(nfc_test->storage, NFC_TEST_NFC_DEV_PATH), + "storage_simply_remove() failed\r\n"); + + nfc_device_free(nfc_device_dut); } -MU_TEST(mf_classic_dict_test) { - MfClassicDict* instance = NULL; - uint64_t key = 0; - FuriString* temp_str; - temp_str = furi_string_alloc(); +static void iso14443_3a_file_test(uint8_t uid_len) { + NfcDevice* nfc_device = nfc_device_alloc(); - instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest); - mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n"); + Iso14443_3aData* data = iso14443_3a_alloc(); + data->uid_len = uid_len; + furi_hal_random_fill_buf(data->uid, uid_len); + furi_hal_random_fill_buf(data->atqa, 2); + furi_hal_random_fill_buf(&data->sak, 1); - mu_assert( - mf_classic_dict_get_total_keys(instance) == 0, - "mf_classic_dict_get_total_keys == 0 assert failed\r\n"); + nfc_device_set_data(nfc_device, NfcProtocolIso14443_3a, data); + nfc_test_save_and_load(nfc_device); - furi_string_set(temp_str, "2196FAD8115B"); - mu_assert( - mf_classic_dict_add_key_str(instance, temp_str), - "mf_classic_dict_add_key == true assert failed\r\n"); + iso14443_3a_free(data); + nfc_device_free(nfc_device); +} - mu_assert( - mf_classic_dict_get_total_keys(instance) == 1, - "mf_classic_dict_get_total_keys == 1 assert failed\r\n"); +static void nfc_file_test_with_generator(NfcDataGeneratorType type) { + NfcDevice* nfc_device_ref = nfc_device_alloc(); - mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n"); + nfc_data_generator_fill_data(type, nfc_device_ref); + nfc_test_save_and_load(nfc_device_ref); - mu_assert( - mf_classic_dict_get_key_at_index_str(instance, temp_str, 0), - "mf_classic_dict_get_key_at_index_str == true assert failed\r\n"); - mu_assert( - furi_string_cmp(temp_str, "2196FAD8115B") == 0, - "string_cmp(temp_str, \"2196FAD8115B\") == 0 assert failed\r\n"); + nfc_device_free(nfc_device_ref); +} - mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n"); +MU_TEST(iso14443_3a_4b_file_test) { + iso14443_3a_file_test(4); +} - mu_assert( - mf_classic_dict_get_key_at_index(instance, &key, 0), - "mf_classic_dict_get_key_at_index == true assert failed\r\n"); - mu_assert(key == 0x2196FAD8115B, "key == 0x2196FAD8115B assert failed\r\n"); +MU_TEST(iso14443_3a_7b_file_test) { + iso14443_3a_file_test(7); +} - mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n"); +MU_TEST(mf_ultralight_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralight); +} - mu_assert( - mf_classic_dict_delete_index(instance, 0), - "mf_classic_dict_delete_index == true assert failed\r\n"); +MU_TEST(mf_ultralight_ev1_11_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_11); +} - mf_classic_dict_free(instance); - furi_string_free(temp_str); +MU_TEST(mf_ultralight_ev1_h11_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_H11); } -MU_TEST(mf_classic_dict_load_test) { - Storage* storage = furi_record_open(RECORD_STORAGE); - mu_assert(storage != NULL, "storage != NULL assert failed\r\n"); +MU_TEST(mf_ultralight_ev1_21_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_21); +} - // Delete unit test dict file if exists - if(storage_file_exists(storage, NFC_TEST_DICT_PATH)) { - mu_assert( - storage_simply_remove(storage, NFC_TEST_DICT_PATH), - "remove == true assert failed\r\n"); - } +MU_TEST(mf_ultralight_ev1_h21_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_H21); +} - // Create unit test dict file - Stream* file_stream = file_stream_alloc(storage); - mu_assert(file_stream != NULL, "file_stream != NULL assert failed\r\n"); - mu_assert( - file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS), - "file_stream_open == true assert failed\r\n"); +MU_TEST(mf_ultralight_ntag_203_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG203); +} - // Write unit test dict file - char key_str[] = "a0a1a2a3a4a5"; - mu_assert( - stream_write_cstring(file_stream, key_str) == strlen(key_str), - "write == true assert failed\r\n"); - // Close unit test dict file - mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n"); - - // Load unit test dict file - MfClassicDict* instance = NULL; - instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest); - mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n"); - uint32_t total_keys = mf_classic_dict_get_total_keys(instance); - mu_assert(total_keys == 1, "total_keys == 1 assert failed\r\n"); - - // Read key - uint64_t key_ref = 0xa0a1a2a3a4a5; - uint64_t key_dut = 0; - FuriString* temp_str = furi_string_alloc(); - mu_assert( - mf_classic_dict_get_next_key_str(instance, temp_str), - "get_next_key_str == true assert failed\r\n"); - mu_assert(furi_string_cmp_str(temp_str, key_str) == 0, "invalid key loaded\r\n"); - mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n"); - mu_assert( - mf_classic_dict_get_next_key(instance, &key_dut), - "get_next_key == true assert failed\r\n"); - mu_assert(key_dut == key_ref, "invalid key loaded\r\n"); - furi_string_free(temp_str); - mf_classic_dict_free(instance); +MU_TEST(mf_ultralight_ntag_213_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG213); +} - // Check that MfClassicDict added new line to the end of the file - mu_assert( - file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING), - "file_stream_open == true assert failed\r\n"); - mu_assert(stream_seek(file_stream, -1, StreamOffsetFromEnd), "seek == true assert failed\r\n"); - uint8_t last_char = 0; - mu_assert(stream_read(file_stream, &last_char, 1) == 1, "read == true assert failed\r\n"); - mu_assert(last_char == '\n', "last_char == '\\n' assert failed\r\n"); - mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n"); - - // Delete unit test dict file - mu_assert( - storage_simply_remove(storage, NFC_TEST_DICT_PATH), "remove == true assert failed\r\n"); - stream_free(file_stream); - furi_record_close(RECORD_STORAGE); +MU_TEST(mf_ultralight_ntag_215_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG215); } -MU_TEST(nfca_file_test) { - NfcDevice* nfc = nfc_device_alloc(); - mu_assert(nfc != NULL, "nfc_device_data != NULL assert failed\r\n"); - nfc->format = NfcDeviceSaveFormatUid; +MU_TEST(mf_ultralight_ntag_216_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG216); +} - // Fill the UID, sak, ATQA and type - uint8_t uid[7] = {0x04, 0x01, 0x23, 0x45, 0x67, 0x89, 0x00}; - memcpy(nfc->dev_data.nfc_data.uid, uid, 7); - nfc->dev_data.nfc_data.uid_len = 7; +MU_TEST(mf_ultralight_ntag_i2c_1k_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2C1k); +} - nfc->dev_data.nfc_data.sak = 0x08; - nfc->dev_data.nfc_data.atqa[0] = 0x00; - nfc->dev_data.nfc_data.atqa[1] = 0x04; - nfc->dev_data.nfc_data.type = FuriHalNfcTypeA; +MU_TEST(mf_ultralight_ntag_i2c_2k_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2C2k); +} - // Save the NFC device data to the file - mu_assert( - nfc_device_save(nfc, NFC_TEST_NFC_DEV_PATH), "nfc_device_save == true assert failed\r\n"); - nfc_device_free(nfc); +MU_TEST(mf_ultralight_ntag_i2c_plus_1k_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2CPlus1k); +} - // Load the NFC device data from the file - NfcDevice* nfc_validate = nfc_device_alloc(); - mu_assert( - nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, true), - "nfc_device_load == true assert failed\r\n"); +MU_TEST(mf_ultralight_ntag_i2c_plus_2k_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2CPlus2k); +} - // Check the UID, sak, ATQA and type - mu_assert(memcmp(nfc_validate->dev_data.nfc_data.uid, uid, 7) == 0, "uid assert failed\r\n"); - mu_assert(nfc_validate->dev_data.nfc_data.sak == 0x08, "sak == 0x08 assert failed\r\n"); - mu_assert( - nfc_validate->dev_data.nfc_data.atqa[0] == 0x00, "atqa[0] == 0x00 assert failed\r\n"); - mu_assert( - nfc_validate->dev_data.nfc_data.atqa[1] == 0x04, "atqa[1] == 0x04 assert failed\r\n"); +MU_TEST(mf_classic_mini_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassicMini); +} + +MU_TEST(mf_classic_1k_4b_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic1k_4b); +} + +MU_TEST(mf_classic_1k_7b_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic1k_7b); +} + +MU_TEST(mf_classic_4k_4b_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic4k_4b); +} + +MU_TEST(mf_classic_4k_7b_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic4k_7b); +} + +MU_TEST(iso14443_3a_reader) { + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + Iso14443_3aData iso14443_3a_listener_data = { + .uid_len = 7, + .uid = {0x04, 0x51, 0x5C, 0xFA, 0x6F, 0x73, 0x81}, + .atqa = {0x44, 0x00}, + .sak = 0x00, + }; + NfcListener* iso3_listener = + nfc_listener_alloc(listener, NfcProtocolIso14443_3a, &iso14443_3a_listener_data); + nfc_listener_start(iso3_listener, NULL, NULL); + + Iso14443_3aData iso14443_3a_poller_data = {}; mu_assert( - nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA, - "type == FuriHalNfcTypeA assert failed\r\n"); - nfc_device_free(nfc_validate); -} - -static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) { - NfcDevice* nfc_dev = nfc_device_alloc(); - mu_assert(nfc_dev != NULL, "nfc_device_data != NULL assert failed\r\n"); - nfc_dev->format = NfcDeviceSaveFormatMifareClassic; - - // Create a test file - nfc_generate_mf_classic(&nfc_dev->dev_data, uid_len, type); - - // Get the uid from generated MFC - uint8_t uid[7] = {0}; - memcpy(uid, nfc_dev->dev_data.nfc_data.uid, uid_len); - uint8_t sak = nfc_dev->dev_data.nfc_data.sak; - uint8_t atqa[2] = {}; - memcpy(atqa, nfc_dev->dev_data.nfc_data.atqa, 2); - - MfClassicData* mf_data = &nfc_dev->dev_data.mf_classic_data; - // Check the manufacturer block (should be uid[uid_len] + BCC (for 4byte only) + SAK + ATQA0 + ATQA1 + 0xFF[rest]) - uint8_t manufacturer_block[16] = {0}; - memcpy(manufacturer_block, nfc_dev->dev_data.mf_classic_data.block[0].value, 16); + iso14443_3a_poller_read(poller, &iso14443_3a_poller_data) == Iso14443_3aErrorNone, + "iso14443_3a_poller_read() failed"); + + nfc_listener_stop(iso3_listener); mu_assert( - memcmp(manufacturer_block, uid, uid_len) == 0, - "manufacturer_block uid doesn't match the file\r\n"); + iso14443_3a_is_equal(&iso14443_3a_poller_data, &iso14443_3a_listener_data), + "Data not matches"); - uint8_t position = 0; - if(uid_len == 4) { - position = uid_len; + nfc_listener_free(iso3_listener); + nfc_free(listener); + nfc_free(poller); +} - uint8_t bcc = 0; +static void mf_ultralight_reader_test(const char* path) { + FURI_LOG_I(TAG, "Testing file: %s", path); + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); - for(int i = 0; i < uid_len; i++) { - bcc ^= uid[i]; - } + NfcDevice* nfc_device = nfc_device_alloc(); + mu_assert(nfc_device_load(nfc_device, path), "nfc_device_load() failed\r\n"); - mu_assert(manufacturer_block[position] == bcc, "manufacturer_block bcc assert failed\r\n"); - } else { - position = uid_len - 1; - } + NfcListener* mfu_listener = nfc_listener_alloc( + listener, + NfcProtocolMfUltralight, + nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)); + nfc_listener_start(mfu_listener, NULL, NULL); - mu_assert(manufacturer_block[position + 1] == sak, "manufacturer_block sak assert failed\r\n"); + MfUltralightData* mfu_data = mf_ultralight_alloc(); + MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data); + mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed"); - mu_assert( - manufacturer_block[position + 2] == atqa[0], "manufacturer_block atqa0 assert failed\r\n"); + nfc_listener_stop(mfu_listener); + nfc_listener_free(mfu_listener); mu_assert( - manufacturer_block[position + 3] == atqa[1], "manufacturer_block atqa1 assert failed\r\n"); + mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)), + "Data not matches"); - for(uint8_t i = position + 4; i < 16; i++) { - mu_assert( - manufacturer_block[i] == 0xFF, "manufacturer_block[i] == 0xFF assert failed\r\n"); - } + mf_ultralight_free(mfu_data); + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); +} - // Reference sector trailers (should be 0xFF[6] + 0xFF + 0x07 + 0x80 + 0x69 + 0xFF[6]) - uint8_t sector_trailer[16] = { - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0x07, - 0x80, - 0x69, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0xFF}; - // Reference block data - uint8_t block_data[16] = {}; - memset(block_data, 0xff, sizeof(block_data)); - uint16_t total_blocks = mf_classic_get_total_block_num(type); - for(size_t i = 1; i < total_blocks; i++) { - if(mf_classic_is_sector_trailer(i)) { - mu_assert( - memcmp(mf_data->block[i].value, sector_trailer, 16) == 0, - "Failed sector trailer compare"); - } else { - mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare"); - } - } - // Save the NFC device data to the file - mu_assert( - nfc_device_save(nfc_dev, NFC_TEST_NFC_DEV_PATH), - "nfc_device_save == true assert failed\r\n"); - // Verify that key cache is saved - FuriString* key_cache_name = furi_string_alloc(); - furi_string_set_str(key_cache_name, "/ext/nfc/.cache/"); - for(size_t i = 0; i < uid_len; i++) { - furi_string_cat_printf(key_cache_name, "%02X", uid[i]); - } - furi_string_cat_printf(key_cache_name, ".keys"); - mu_assert( - storage_common_stat(nfc_dev->storage, furi_string_get_cstr(key_cache_name), NULL) == - FSE_OK, - "Key cache file save failed"); - nfc_device_free(nfc_dev); - - // Load the NFC device data from the file - NfcDevice* nfc_validate = nfc_device_alloc(); - mu_assert(nfc_validate, "Nfc device alloc assert"); - mu_assert( - nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, false), - "nfc_device_load == true assert failed\r\n"); +MU_TEST(mf_ultralight_11_reader) { + mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ultralight_11.nfc")); +} - // Check the UID, sak, ATQA and type - mu_assert( - memcmp(nfc_validate->dev_data.nfc_data.uid, uid, uid_len) == 0, - "uid compare assert failed\r\n"); - mu_assert(nfc_validate->dev_data.nfc_data.sak == sak, "sak compare assert failed\r\n"); +MU_TEST(mf_ultralight_21_reader) { + mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ultralight_21.nfc")); +} + +MU_TEST(ntag_215_reader) { + mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ntag215.nfc")); +} + +MU_TEST(ntag_216_reader) { + mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ntag216.nfc")); +} + +MU_TEST(ntag_213_locked_reader) { + FURI_LOG_I(TAG, "Testing Ntag215 locked file"); + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDeviceData* nfc_device = nfc_device_alloc(); mu_assert( - memcmp(nfc_validate->dev_data.nfc_data.atqa, atqa, 2) == 0, - "atqa compare assert failed\r\n"); + nfc_device_load(nfc_device, EXT_PATH("unit_tests/nfc/Ntag213_locked.nfc")), + "nfc_device_load() failed\r\n"); + + NfcListener* mfu_listener = nfc_listener_alloc( + listener, + NfcProtocolMfUltralight, + nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)); + nfc_listener_start(mfu_listener, NULL, NULL); + + MfUltralightData* mfu_data = mf_ultralight_alloc(); + MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data); + mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed"); + + nfc_listener_stop(mfu_listener); + nfc_listener_free(mfu_listener); + + MfUltralightConfigPages* config = NULL; + const MfUltralightData* mfu_ref_data = + nfc_device_get_data(nfc_device, NfcProtocolMfUltralight); mu_assert( - nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA, - "type == FuriHalNfcTypeA assert failed\r\n"); + mf_ultralight_get_config_page(mfu_ref_data, &config), + "mf_ultralight_get_config_page() failed"); + uint16_t pages_locked = config->auth0; + + mu_assert(mfu_data->pages_read == pages_locked, "Unexpected pages read"); + + mf_ultralight_free(mfu_data); + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); +} + +static void mf_ultralight_write() { + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDevice* nfc_device = nfc_device_alloc(); + nfc_data_generator_fill_data(NfcDataGeneratorTypeMfUltralightEV1_21, nfc_device); + + NfcListener* mfu_listener = nfc_listener_alloc( + listener, + NfcProtocolMfUltralight, + nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)); + nfc_listener_start(mfu_listener, NULL, NULL); + + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + // Initial read + MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data); + mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed"); - // Check the manufacturer block mu_assert( - memcmp(nfc_validate->dev_data.mf_classic_data.block[0].value, manufacturer_block, 16) == 0, - "manufacturer_block assert failed\r\n"); - // Check other blocks - for(size_t i = 1; i < total_blocks; i++) { - if(mf_classic_is_sector_trailer(i)) { - mu_assert( - memcmp(mf_data->block[i].value, sector_trailer, 16) == 0, - "Failed sector trailer compare"); - } else { - mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare"); - } - } - nfc_device_free(nfc_validate); - - // Check saved key cache - NfcDevice* nfc_keys = nfc_device_alloc(); - mu_assert(nfc_validate, "Nfc device alloc assert"); - nfc_keys->dev_data.nfc_data.uid_len = uid_len; - memcpy(nfc_keys->dev_data.nfc_data.uid, uid, uid_len); - mu_assert(nfc_device_load_key_cache(nfc_keys), "Failed to load key cache"); - uint8_t total_sec = mf_classic_get_total_sectors_num(type); - uint8_t default_key[6] = {}; - memset(default_key, 0xff, 6); - for(size_t i = 0; i < total_sec; i++) { - MfClassicSectorTrailer* sec_tr = - mf_classic_get_sector_trailer_by_sector(&nfc_keys->dev_data.mf_classic_data, i); - mu_assert(memcmp(sec_tr->key_a, default_key, 6) == 0, "Failed key compare"); - mu_assert(memcmp(sec_tr->key_b, default_key, 6) == 0, "Failed key compare"); + mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)), + "Data not matches"); + + // Write random data + for(size_t i = 5; i < 15; i++) { + MfUltralightPage page = {}; + FURI_LOG_D(TAG, "Writing page %d", i); + furi_hal_random_fill_buf(page.data, sizeof(MfUltralightPage)); + mfu_data->page[i] = page; + error = mf_ultralight_poller_write_page(poller, i, &page); + mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed"); } - // Delete key cache file - mu_assert( - storage_common_remove(nfc_keys->storage, furi_string_get_cstr(key_cache_name)) == FSE_OK, - "Failed to remove key cache file"); - furi_string_free(key_cache_name); - nfc_device_free(nfc_keys); -} + // Verification read + error = mf_ultralight_poller_read_card(poller, mfu_data); + mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed"); + + nfc_listener_stop(mfu_listener); + const MfUltralightData* mfu_listener_data = + nfc_listener_get_data(mfu_listener, NfcProtocolMfUltralight); -MU_TEST(mf_mini_file_test) { - mf_classic_generator_test(4, MfClassicTypeMini); + mu_assert(mf_ultralight_is_equal(mfu_data, mfu_listener_data), "Data not matches"); + + nfc_listener_free(mfu_listener); + mf_ultralight_free(mfu_data); + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); } -MU_TEST(mf_classic_1k_4b_file_test) { - mf_classic_generator_test(4, MfClassicType1k); +static void mf_classic_reader() { + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDevice* nfc_device = nfc_device_alloc(); + nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device); + NfcListener* mfc_listener = nfc_listener_alloc( + listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic)); + nfc_listener_start(mfc_listener, NULL, NULL); + + MfClassicBlock block = {}; + MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; + + mf_classic_poller_read_block(poller, 0, &key, MfClassicKeyTypeA, &block); + + nfc_listener_stop(mfc_listener); + nfc_listener_free(mfc_listener); + + const MfClassicData* mfc_data = nfc_device_get_data(nfc_device, NfcProtocolMfClassic); + mu_assert(memcmp(&mfc_data->block[0], &block, sizeof(MfClassicBlock)) == 0, "Data mismatch"); + + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); } -MU_TEST(mf_classic_4k_4b_file_test) { - mf_classic_generator_test(4, MfClassicType4k); +static void mf_classic_write() { + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDevice* nfc_device = nfc_device_alloc(); + nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device); + NfcListener* mfc_listener = nfc_listener_alloc( + listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic)); + nfc_listener_start(mfc_listener, NULL, NULL); + + MfClassicBlock block_write = {}; + MfClassicBlock block_read = {}; + furi_hal_random_fill_buf(block_write.data, sizeof(MfClassicBlock)); + MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; + + mf_classic_poller_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write); + mf_classic_poller_read_block(poller, 1, &key, MfClassicKeyTypeA, &block_read); + + nfc_listener_stop(mfc_listener); + nfc_listener_free(mfc_listener); + + mu_assert(memcmp(&block_read, &block_write, sizeof(MfClassicBlock)) == 0, "Data mismatch"); + + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); } -MU_TEST(mf_classic_1k_7b_file_test) { - mf_classic_generator_test(7, MfClassicType1k); +static void mf_classic_value_block() { + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDevice* nfc_device = nfc_device_alloc(); + nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device); + NfcListener* mfc_listener = nfc_listener_alloc( + listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic)); + nfc_listener_start(mfc_listener, NULL, NULL); + + MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; + + int32_t value = 228; + MfClassicBlock block_write = {}; + mf_classic_value_to_block(value, 1, &block_write); + + MfClassicError error = MfClassicErrorNone; + error = mf_classic_poller_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write); + mu_assert(error == MfClassicErrorNone, "Write failed"); + + int32_t data = 200; + int32_t new_value = 0; + error = mf_classic_poller_change_value(poller, 1, &key, MfClassicKeyTypeA, data, &new_value); + mu_assert(error == MfClassicErrorNone, "Value increment failed"); + mu_assert(new_value == value + data, "Value not match"); + + error = mf_classic_poller_change_value(poller, 1, &key, MfClassicKeyTypeA, -data, &new_value); + mu_assert(error == MfClassicErrorNone, "Value decrement failed"); + mu_assert(new_value == value, "Value not match"); + + nfc_listener_stop(mfc_listener); + nfc_listener_free(mfc_listener); + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); } -MU_TEST(mf_classic_4k_7b_file_test) { - mf_classic_generator_test(7, MfClassicType4k); +MU_TEST(mf_classic_dict_test) { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_common_stat(storage, MF_CLASSIC_DICT_UNIT_TEST_PATH, NULL) == FSE_OK) { + mu_assert( + storage_simply_remove(storage, MF_CLASSIC_DICT_UNIT_TEST_PATH), + "Remove test dict failed"); + } + + MfDict* dict = mf_dict_alloc(MfDictTypeUnitTest); + mu_assert(dict != NULL, "mf_dict_alloc() failed"); + + size_t dict_keys_total = mf_dict_get_total_keys(dict); + mu_assert(dict_keys_total == 0, "mf_dict_keys_total() failed"); + + const uint32_t test_key_num = 30; + MfClassicKey* key_arr_ref = malloc(test_key_num * sizeof(MfClassicKey)); + for(size_t i = 0; i < test_key_num; i++) { + furi_hal_random_fill_buf(key_arr_ref[i].data, sizeof(MfClassicKey)); + mu_assert(mf_dict_add_key(dict, &key_arr_ref[i]), "add key failed"); + + size_t dict_keys_total = mf_dict_get_total_keys(dict); + mu_assert(dict_keys_total == (i + 1), "mf_dict_keys_total() failed"); + } + + mf_dict_free(dict); + + dict = mf_dict_alloc(MfDictTypeUnitTest); + mu_assert(dict != NULL, "mf_dict_alloc() failed"); + + dict_keys_total = mf_dict_get_total_keys(dict); + mu_assert(dict_keys_total == test_key_num, "mf_dict_keys_total() failed"); + + MfClassicKey key_dut = {}; + size_t key_idx = 0; + while(mf_dict_get_next_key(dict, &key_dut)) { + mu_assert( + memcmp(key_arr_ref[key_idx].data, key_dut.data, sizeof(MfClassicKey)) == 0, + "Loaded key data mismatch"); + key_idx++; + } + + uint32_t delete_keys_idx[] = {1, 3, 9, 11, 19, 27}; + + for(size_t i = 0; i < COUNT_OF(delete_keys_idx); i++) { + MfClassicKey* key = &key_arr_ref[delete_keys_idx[i]]; + mu_assert(mf_dict_is_key_present(dict, key), "mf_dict_is_key_present() failed"); + mu_assert(mf_dict_delete_key(dict, key), "mf_dict_delete_key() failed"); + } + + dict_keys_total = mf_dict_get_total_keys(dict); + mu_assert( + dict_keys_total == test_key_num - COUNT_OF(delete_keys_idx), + "mf_dict_keys_total() failed"); + + mf_dict_free(dict); + free(key_arr_ref); + + mu_assert( + storage_simply_remove(storage, MF_CLASSIC_DICT_UNIT_TEST_PATH), "Remove test dict failed"); } MU_TEST_SUITE(nfc) { nfc_test_alloc(); - MU_RUN_TEST(nfca_file_test); - MU_RUN_TEST(mf_mini_file_test); + MU_RUN_TEST(iso14443_3a_reader); + MU_RUN_TEST(mf_ultralight_11_reader); + MU_RUN_TEST(mf_ultralight_21_reader); + MU_RUN_TEST(ntag_215_reader); + MU_RUN_TEST(ntag_216_reader); + MU_RUN_TEST(ntag_213_locked_reader); + + MU_RUN_TEST(mf_ultralight_write); + + MU_RUN_TEST(iso14443_3a_4b_file_test); + MU_RUN_TEST(iso14443_3a_7b_file_test); + + MU_RUN_TEST(mf_ultralight_file_test); + MU_RUN_TEST(mf_ultralight_ev1_11_file_test); + MU_RUN_TEST(mf_ultralight_ev1_h11_file_test); + MU_RUN_TEST(mf_ultralight_ev1_21_file_test); + MU_RUN_TEST(mf_ultralight_ev1_h21_file_test); + MU_RUN_TEST(mf_ultralight_ntag_203_file_test); + MU_RUN_TEST(mf_ultralight_ntag_213_file_test); + MU_RUN_TEST(mf_ultralight_ntag_215_file_test); + MU_RUN_TEST(mf_ultralight_ntag_216_file_test); + MU_RUN_TEST(mf_ultralight_ntag_i2c_1k_file_test); + MU_RUN_TEST(mf_ultralight_ntag_i2c_2k_file_test); + MU_RUN_TEST(mf_ultralight_ntag_i2c_plus_1k_file_test); + MU_RUN_TEST(mf_ultralight_ntag_i2c_plus_2k_file_test); + + MU_RUN_TEST(mf_classic_mini_file_test); MU_RUN_TEST(mf_classic_1k_4b_file_test); - MU_RUN_TEST(mf_classic_4k_4b_file_test); MU_RUN_TEST(mf_classic_1k_7b_file_test); + MU_RUN_TEST(mf_classic_4k_4b_file_test); MU_RUN_TEST(mf_classic_4k_7b_file_test); - MU_RUN_TEST(nfc_digital_signal_test); + MU_RUN_TEST(mf_classic_reader); + + MU_RUN_TEST(mf_classic_write); + MU_RUN_TEST(mf_classic_value_block); + MU_RUN_TEST(mf_classic_dict_test); - MU_RUN_TEST(mf_classic_dict_load_test); nfc_test_free(); } diff --git a/applications/debug/unit_tests/nfc/nfc_test_old.c b/applications/debug/unit_tests/nfc/nfc_test_old.c new file mode 100644 index 000000000000..83e44fe9db6d --- /dev/null +++ b/applications/debug/unit_tests/nfc/nfc_test_old.c @@ -0,0 +1,575 @@ +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + +// #include +// #include + +// #include "../minunit.h" + +// #define TAG "NfcTest" + +// #define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/") +// #define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc" +// #define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc" +// #define NFC_TEST_DICT_PATH EXT_PATH("unit_tests/mf_classic_dict.nfc") +// #define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_dev_test.nfc") + +// static const char* nfc_test_file_type = "Flipper NFC test"; +// static const uint32_t nfc_test_file_version = 1; + +// #define NFC_TEST_DATA_MAX_LEN 18 +// #define NFC_TETS_TIMINGS_MAX_LEN 1350 + +// // Maximum allowed time for buffer preparation to fit 500us nt message timeout +// #define NFC_TEST_4_BYTE_BUILD_BUFFER_TIM_MAX (150) +// #define NFC_TEST_16_BYTE_BUILD_BUFFER_TIM_MAX (640) +// #define NFC_TEST_4_BYTE_BUILD_SIGNAL_TIM_MAX (110) +// #define NFC_TEST_16_BYTE_BUILD_SIGNAL_TIM_MAX (440) + +// typedef struct { +// Storage* storage; +// NfcaSignal* signal; +// uint32_t test_data_len; +// uint8_t test_data[NFC_TEST_DATA_MAX_LEN]; +// uint32_t test_timings_len; +// uint32_t test_timings[NFC_TETS_TIMINGS_MAX_LEN]; +// } NfcTest; + +// static NfcTest* nfc_test = NULL; + +// static void nfc_test_alloc() { +// nfc_test = malloc(sizeof(NfcTest)); +// nfc_test->signal = nfca_signal_alloc(); +// nfc_test->storage = furi_record_open(RECORD_STORAGE); +// } + +// static void nfc_test_free() { +// furi_assert(nfc_test); + +// furi_record_close(RECORD_STORAGE); +// nfca_signal_free(nfc_test->signal); +// free(nfc_test); +// nfc_test = NULL; +// } + +// static bool nfc_test_read_signal_from_file(const char* file_name) { +// bool success = false; + +// FlipperFormat* file = flipper_format_file_alloc(nfc_test->storage); +// FuriString* file_type; +// file_type = furi_string_alloc(); +// uint32_t file_version = 0; + +// do { +// if(!flipper_format_file_open_existing(file, file_name)) break; +// if(!flipper_format_read_header(file, file_type, &file_version)) break; +// if(furi_string_cmp_str(file_type, nfc_test_file_type) || +// file_version != nfc_test_file_version) +// break; +// if(!flipper_format_read_uint32(file, "Data length", &nfc_test->test_data_len, 1)) break; +// if(nfc_test->test_data_len > NFC_TEST_DATA_MAX_LEN) break; +// if(!flipper_format_read_hex( +// file, "Plain data", nfc_test->test_data, nfc_test->test_data_len)) +// break; +// if(!flipper_format_read_uint32(file, "Timings length", &nfc_test->test_timings_len, 1)) +// break; +// if(nfc_test->test_timings_len > NFC_TETS_TIMINGS_MAX_LEN) break; +// if(!flipper_format_read_uint32( +// file, "Timings", nfc_test->test_timings, nfc_test->test_timings_len)) +// break; +// success = true; +// } while(false); + +// furi_string_free(file_type); +// flipper_format_free(file); + +// return success; +// } + +// static bool nfc_test_digital_signal_test_encode( +// const char* file_name, +// uint32_t build_signal_max_time_us, +// uint32_t build_buffer_max_time_us, +// uint32_t timing_tolerance, +// uint32_t timings_sum_tolerance) { +// furi_assert(nfc_test); + +// bool success = false; +// uint32_t dut_timings_sum = 0; +// uint32_t ref_timings_sum = 0; +// uint8_t parity[10] = {}; + +// do { +// // Read test data +// if(!nfc_test_read_signal_from_file(file_name)) { +// FURI_LOG_E(TAG, "Failed to read signal from file"); +// break; +// } + +// // Encode signal +// FURI_CRITICAL_ENTER(); +// uint32_t time_start = DWT->CYCCNT; + +// nfca_signal_encode( +// nfc_test->signal, nfc_test->test_data, nfc_test->test_data_len * 8, parity); + +// uint32_t time_signal = +// (DWT->CYCCNT - time_start) / furi_hal_cortex_instructions_per_microsecond(); + +// time_start = DWT->CYCCNT; + +// digital_signal_prepare_arr(nfc_test->signal->tx_signal); + +// uint32_t time_buffer = +// (DWT->CYCCNT - time_start) / furi_hal_cortex_instructions_per_microsecond(); +// FURI_CRITICAL_EXIT(); + +// // Check timings +// if(time_signal > build_signal_max_time_us) { +// FURI_LOG_E( +// TAG, +// "Build signal time: %ld us while accepted value: %ld us", +// time_signal, +// build_signal_max_time_us); +// break; +// } +// if(time_buffer > build_buffer_max_time_us) { +// FURI_LOG_E( +// TAG, +// "Build buffer time: %ld us while accepted value: %ld us", +// time_buffer, +// build_buffer_max_time_us); +// break; +// } + +// // Check data +// if(nfc_test->signal->tx_signal->edge_cnt != nfc_test->test_timings_len) { +// FURI_LOG_E(TAG, "Not equal timings buffers length"); +// break; +// } + +// uint32_t timings_diff = 0; +// uint32_t* ref = nfc_test->test_timings; +// uint32_t* dut = nfc_test->signal->tx_signal->reload_reg_buff; +// bool timing_check_success = true; +// for(size_t i = 0; i < nfc_test->test_timings_len; i++) { +// timings_diff = dut[i] > ref[i] ? dut[i] - ref[i] : ref[i] - dut[i]; +// dut_timings_sum += dut[i]; +// ref_timings_sum += ref[i]; +// if(timings_diff > timing_tolerance) { +// FURI_LOG_E( +// TAG, "Too big difference in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]); +// timing_check_success = false; +// break; +// } +// } +// if(!timing_check_success) break; +// uint32_t sum_diff = dut_timings_sum > ref_timings_sum ? dut_timings_sum - ref_timings_sum : +// ref_timings_sum - dut_timings_sum; +// if(sum_diff > timings_sum_tolerance) { +// FURI_LOG_E( +// TAG, +// "Too big difference in timings sum. Ref: %ld, DUT: %ld", +// ref_timings_sum, +// dut_timings_sum); +// break; +// } + +// FURI_LOG_I( +// TAG, +// "Build signal time: %ld us. Acceptable time: %ld us", +// time_signal, +// build_signal_max_time_us); +// FURI_LOG_I( +// TAG, +// "Build buffer time: %ld us. Acceptable time: %ld us", +// time_buffer, +// build_buffer_max_time_us); +// FURI_LOG_I( +// TAG, +// "Timings sum difference: %ld [1/64MHZ]. Acceptable difference: %ld [1/64MHz]", +// sum_diff, +// timings_sum_tolerance); +// success = true; +// } while(false); + +// return success; +// } + +// MU_TEST(nfc_digital_signal_test) { +// mu_assert( +// nfc_test_digital_signal_test_encode( +// NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_SHORT_FILE, +// NFC_TEST_4_BYTE_BUILD_SIGNAL_TIM_MAX, +// NFC_TEST_4_BYTE_BUILD_BUFFER_TIM_MAX, +// 1, +// 37), +// "NFC short digital signal test failed\r\n"); +// mu_assert( +// nfc_test_digital_signal_test_encode( +// NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_LONG_FILE, +// NFC_TEST_16_BYTE_BUILD_SIGNAL_TIM_MAX, +// NFC_TEST_16_BYTE_BUILD_BUFFER_TIM_MAX, +// 1, +// 37), +// "NFC long digital signal test failed\r\n"); +// } + +// MU_TEST(mf_classic_dict_test) { +// MfClassicDict* instance = NULL; +// uint64_t key = 0; +// FuriString* temp_str; +// temp_str = furi_string_alloc(); + +// instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest); +// mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n"); + +// mu_assert( +// mf_classic_dict_get_total_keys(instance) == 0, +// "mf_classic_dict_get_total_keys == 0 assert failed\r\n"); + +// furi_string_set(temp_str, "2196FAD8115B"); +// mu_assert( +// mf_classic_dict_add_key_str(instance, temp_str), +// "mf_classic_dict_add_key == true assert failed\r\n"); + +// mu_assert( +// mf_classic_dict_get_total_keys(instance) == 1, +// "mf_classic_dict_get_total_keys == 1 assert failed\r\n"); + +// mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n"); + +// mu_assert( +// mf_classic_dict_get_key_at_index_str(instance, temp_str, 0), +// "mf_classic_dict_get_key_at_index_str == true assert failed\r\n"); +// mu_assert( +// furi_string_cmp(temp_str, "2196FAD8115B") == 0, +// "string_cmp(temp_str, \"2196FAD8115B\") == 0 assert failed\r\n"); + +// mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n"); + +// mu_assert( +// mf_classic_dict_get_key_at_index(instance, &key, 0), +// "mf_classic_dict_get_key_at_index == true assert failed\r\n"); +// mu_assert(key == 0x2196FAD8115B, "key == 0x2196FAD8115B assert failed\r\n"); + +// mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n"); + +// mu_assert( +// mf_classic_dict_delete_index(instance, 0), +// "mf_classic_dict_delete_index == true assert failed\r\n"); + +// mf_classic_dict_free(instance); +// furi_string_free(temp_str); +// } + +// MU_TEST(mf_classic_dict_load_test) { +// Storage* storage = furi_record_open(RECORD_STORAGE); +// mu_assert(storage != NULL, "storage != NULL assert failed\r\n"); + +// // Delete unit test dict file if exists +// if(storage_file_exists(storage, NFC_TEST_DICT_PATH)) { +// mu_assert( +// storage_simply_remove(storage, NFC_TEST_DICT_PATH), +// "remove == true assert failed\r\n"); +// } + +// // Create unit test dict file +// Stream* file_stream = file_stream_alloc(storage); +// mu_assert(file_stream != NULL, "file_stream != NULL assert failed\r\n"); +// mu_assert( +// file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS), +// "file_stream_open == true assert failed\r\n"); + +// // Write unit test dict file +// char key_str[] = "a0a1a2a3a4a5"; +// mu_assert( +// stream_write_cstring(file_stream, key_str) == strlen(key_str), +// "write == true assert failed\r\n"); +// // Close unit test dict file +// mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n"); + +// // Load unit test dict file +// MfClassicDict* instance = NULL; +// instance = mf_classic_dict_alloc(MfClassicDictTypeUnitTest); +// mu_assert(instance != NULL, "mf_classic_dict_alloc\r\n"); +// uint32_t total_keys = mf_classic_dict_get_total_keys(instance); +// mu_assert(total_keys == 1, "total_keys == 1 assert failed\r\n"); + +// // Read key +// uint64_t key_ref = 0xa0a1a2a3a4a5; +// uint64_t key_dut = 0; +// FuriString* temp_str = furi_string_alloc(); +// mu_assert( +// mf_classic_dict_get_next_key_str(instance, temp_str), +// "get_next_key_str == true assert failed\r\n"); +// mu_assert(furi_string_cmp_str(temp_str, key_str) == 0, "invalid key loaded\r\n"); +// mu_assert(mf_classic_dict_rewind(instance), "mf_classic_dict_rewind == 1 assert failed\r\n"); +// mu_assert( +// mf_classic_dict_get_next_key(instance, &key_dut), +// "get_next_key == true assert failed\r\n"); +// mu_assert(key_dut == key_ref, "invalid key loaded\r\n"); +// furi_string_free(temp_str); +// mf_classic_dict_free(instance); + +// // Check that MfClassicDict added new line to the end of the file +// mu_assert( +// file_stream_open(file_stream, NFC_TEST_DICT_PATH, FSAM_READ, FSOM_OPEN_EXISTING), +// "file_stream_open == true assert failed\r\n"); +// mu_assert(stream_seek(file_stream, -1, StreamOffsetFromEnd), "seek == true assert failed\r\n"); +// uint8_t last_char = 0; +// mu_assert(stream_read(file_stream, &last_char, 1) == 1, "read == true assert failed\r\n"); +// mu_assert(last_char == '\n', "last_char == '\\n' assert failed\r\n"); +// mu_assert(file_stream_close(file_stream), "file_stream_close == true assert failed\r\n"); + +// // Delete unit test dict file +// mu_assert( +// storage_simply_remove(storage, NFC_TEST_DICT_PATH), "remove == true assert failed\r\n"); +// stream_free(file_stream); +// furi_record_close(RECORD_STORAGE); +// } + +// MU_TEST(nfca_file_test) { +// NfcDevice* nfc = nfc_device_alloc(); +// mu_assert(nfc != NULL, "nfc_device_data != NULL assert failed\r\n"); +// nfc->format = NfcDeviceSaveFormatUid; + +// // Fill the UID, sak, ATQA and type +// uint8_t uid[7] = {0x04, 0x01, 0x23, 0x45, 0x67, 0x89, 0x00}; +// memcpy(nfc->dev_data.nfc_data.uid, uid, 7); +// nfc->dev_data.nfc_data.uid_len = 7; + +// nfc->dev_data.nfc_data.sak = 0x08; +// nfc->dev_data.nfc_data.atqa[0] = 0x00; +// nfc->dev_data.nfc_data.atqa[1] = 0x04; +// nfc->dev_data.nfc_data.type = FuriHalNfcTypeA; + +// // Save the NFC device data to the file +// mu_assert( +// nfc_device_save(nfc, NFC_TEST_NFC_DEV_PATH), "nfc_device_save == true assert failed\r\n"); +// nfc_device_free(nfc); + +// // Load the NFC device data from the file +// NfcDevice* nfc_validate = nfc_device_alloc(); +// mu_assert( +// nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, true), +// "nfc_device_load == true assert failed\r\n"); + +// // Check the UID, sak, ATQA and type +// mu_assert(memcmp(nfc_validate->dev_data.nfc_data.uid, uid, 7) == 0, "uid assert failed\r\n"); +// mu_assert(nfc_validate->dev_data.nfc_data.sak == 0x08, "sak == 0x08 assert failed\r\n"); +// mu_assert( +// nfc_validate->dev_data.nfc_data.atqa[0] == 0x00, "atqa[0] == 0x00 assert failed\r\n"); +// mu_assert( +// nfc_validate->dev_data.nfc_data.atqa[1] == 0x04, "atqa[1] == 0x04 assert failed\r\n"); +// mu_assert( +// nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA, +// "type == FuriHalNfcTypeA assert failed\r\n"); +// nfc_device_free(nfc_validate); +// } + +// static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) { +// NfcDevice* nfc_dev = nfc_device_alloc(); +// mu_assert(nfc_dev != NULL, "nfc_device_data != NULL assert failed\r\n"); +// nfc_dev->format = NfcDeviceSaveFormatMifareClassic; + +// // Create a test file +// nfc_generate_mf_classic(&nfc_dev->dev_data, uid_len, type); + +// // Get the uid from generated MFC +// uint8_t uid[7] = {0}; +// memcpy(uid, nfc_dev->dev_data.nfc_data.uid, uid_len); +// uint8_t sak = nfc_dev->dev_data.nfc_data.sak; +// uint8_t atqa[2] = {}; +// memcpy(atqa, nfc_dev->dev_data.nfc_data.atqa, 2); + +// MfClassicData* mf_data = &nfc_dev->dev_data.mf_classic_data; +// // Check the manufacturer block (should be uid[uid_len] + BCC (for 4byte only) + SAK + ATQA0 + ATQA1 + 0xFF[rest]) +// uint8_t manufacturer_block[16] = {0}; +// memcpy(manufacturer_block, nfc_dev->dev_data.mf_classic_data.block[0].value, 16); +// mu_assert( +// memcmp(manufacturer_block, uid, uid_len) == 0, +// "manufacturer_block uid doesn't match the file\r\n"); + +// uint8_t position = 0; +// if(uid_len == 4) { +// position = uid_len; + +// uint8_t bcc = 0; + +// for(int i = 0; i < uid_len; i++) { +// bcc ^= uid[i]; +// } + +// mu_assert(manufacturer_block[position] == bcc, "manufacturer_block bcc assert failed\r\n"); +// } else { +// position = uid_len - 1; +// } + +// mu_assert(manufacturer_block[position + 1] == sak, "manufacturer_block sak assert failed\r\n"); + +// mu_assert( +// manufacturer_block[position + 2] == atqa[0], "manufacturer_block atqa0 assert failed\r\n"); + +// mu_assert( +// manufacturer_block[position + 3] == atqa[1], "manufacturer_block atqa1 assert failed\r\n"); + +// for(uint8_t i = position + 4; i < 16; i++) { +// mu_assert( +// manufacturer_block[i] == 0xFF, "manufacturer_block[i] == 0xFF assert failed\r\n"); +// } + +// // Reference sector trailers (should be 0xFF[6] + 0xFF + 0x07 + 0x80 + 0x69 + 0xFF[6]) +// uint8_t sector_trailer[16] = { +// 0xFF, +// 0xFF, +// 0xFF, +// 0xFF, +// 0xFF, +// 0xFF, +// 0xFF, +// 0x07, +// 0x80, +// 0x69, +// 0xFF, +// 0xFF, +// 0xFF, +// 0xFF, +// 0xFF, +// 0xFF}; +// // Reference block data +// uint8_t block_data[16] = {}; +// memset(block_data, 0xff, sizeof(block_data)); +// uint16_t total_blocks = mf_classic_get_total_block_num(type); +// for(size_t i = 1; i < total_blocks; i++) { +// if(mf_classic_is_sector_trailer(i)) { +// mu_assert( +// memcmp(mf_data->block[i].value, sector_trailer, 16) == 0, +// "Failed sector trailer compare"); +// } else { +// mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare"); +// } +// } +// // Save the NFC device data to the file +// mu_assert( +// nfc_device_save(nfc_dev, NFC_TEST_NFC_DEV_PATH), +// "nfc_device_save == true assert failed\r\n"); +// // Verify that key cache is saved +// FuriString* key_cache_name = furi_string_alloc(); +// furi_string_set_str(key_cache_name, "/ext/nfc/.cache/"); +// for(size_t i = 0; i < uid_len; i++) { +// furi_string_cat_printf(key_cache_name, "%02X", uid[i]); +// } +// furi_string_cat_printf(key_cache_name, ".keys"); +// mu_assert( +// storage_common_stat(nfc_dev->storage, furi_string_get_cstr(key_cache_name), NULL) == +// FSE_OK, +// "Key cache file save failed"); +// nfc_device_free(nfc_dev); + +// // Load the NFC device data from the file +// NfcDevice* nfc_validate = nfc_device_alloc(); +// mu_assert(nfc_validate, "Nfc device alloc assert"); +// mu_assert( +// nfc_device_load(nfc_validate, NFC_TEST_NFC_DEV_PATH, false), +// "nfc_device_load == true assert failed\r\n"); + +// // Check the UID, sak, ATQA and type +// mu_assert( +// memcmp(nfc_validate->dev_data.nfc_data.uid, uid, uid_len) == 0, +// "uid compare assert failed\r\n"); +// mu_assert(nfc_validate->dev_data.nfc_data.sak == sak, "sak compare assert failed\r\n"); +// mu_assert( +// memcmp(nfc_validate->dev_data.nfc_data.atqa, atqa, 2) == 0, +// "atqa compare assert failed\r\n"); +// mu_assert( +// nfc_validate->dev_data.nfc_data.type == FuriHalNfcTypeA, +// "type == FuriHalNfcTypeA assert failed\r\n"); + +// // Check the manufacturer block +// mu_assert( +// memcmp(nfc_validate->dev_data.mf_classic_data.block[0].value, manufacturer_block, 16) == 0, +// "manufacturer_block assert failed\r\n"); +// // Check other blocks +// for(size_t i = 1; i < total_blocks; i++) { +// if(mf_classic_is_sector_trailer(i)) { +// mu_assert( +// memcmp(mf_data->block[i].value, sector_trailer, 16) == 0, +// "Failed sector trailer compare"); +// } else { +// mu_assert(memcmp(mf_data->block[i].value, block_data, 16) == 0, "Failed data compare"); +// } +// } +// nfc_device_free(nfc_validate); + +// // Check saved key cache +// NfcDevice* nfc_keys = nfc_device_alloc(); +// mu_assert(nfc_validate, "Nfc device alloc assert"); +// nfc_keys->dev_data.nfc_data.uid_len = uid_len; +// memcpy(nfc_keys->dev_data.nfc_data.uid, uid, uid_len); +// mu_assert(nfc_device_load_key_cache(nfc_keys), "Failed to load key cache"); +// uint8_t total_sec = mf_classic_get_total_sectors_num(type); +// uint8_t default_key[6] = {}; +// memset(default_key, 0xff, 6); +// for(size_t i = 0; i < total_sec; i++) { +// MfClassicSectorTrailer* sec_tr = +// mf_classic_get_sector_trailer_by_sector(&nfc_keys->dev_data.mf_classic_data, i); +// mu_assert(memcmp(sec_tr->key_a, default_key, 6) == 0, "Failed key compare"); +// mu_assert(memcmp(sec_tr->key_b, default_key, 6) == 0, "Failed key compare"); +// } + +// // Delete key cache file +// mu_assert( +// storage_common_remove(nfc_keys->storage, furi_string_get_cstr(key_cache_name)) == FSE_OK, +// "Failed to remove key cache file"); +// furi_string_free(key_cache_name); +// nfc_device_free(nfc_keys); +// } + +// MU_TEST(mf_mini_file_test) { +// mf_classic_generator_test(4, MfClassicTypeMini); +// } + +// MU_TEST(mf_classic_1k_4b_file_test) { +// mf_classic_generator_test(4, MfClassicType1k); +// } + +// MU_TEST(mf_classic_4k_4b_file_test) { +// mf_classic_generator_test(4, MfClassicType4k); +// } + +// MU_TEST(mf_classic_1k_7b_file_test) { +// mf_classic_generator_test(7, MfClassicType1k); +// } + +// MU_TEST(mf_classic_4k_7b_file_test) { +// mf_classic_generator_test(7, MfClassicType4k); +// } + +// MU_TEST_SUITE(nfc) { +// nfc_test_alloc(); + +// MU_RUN_TEST(nfca_file_test); +// MU_RUN_TEST(mf_mini_file_test); +// MU_RUN_TEST(mf_classic_1k_4b_file_test); +// MU_RUN_TEST(mf_classic_4k_4b_file_test); +// MU_RUN_TEST(mf_classic_1k_7b_file_test); +// MU_RUN_TEST(mf_classic_4k_7b_file_test); +// MU_RUN_TEST(nfc_digital_signal_test); +// MU_RUN_TEST(mf_classic_dict_test); +// MU_RUN_TEST(mf_classic_dict_load_test); + +// nfc_test_free(); +// } + +// int run_minunit_test_nfc() { +// MU_RUN_SUITE(nfc); +// return MU_EXIT_CODE; +// } \ No newline at end of file diff --git a/applications/debug/unit_tests/nfc/nfc_transport.c b/applications/debug/unit_tests/nfc/nfc_transport.c new file mode 100644 index 000000000000..c0201483bcd2 --- /dev/null +++ b/applications/debug/unit_tests/nfc/nfc_transport.c @@ -0,0 +1,457 @@ +#ifdef APP_UNIT_TESTS + +#include +#include +#include + +#include + +#define NFC_MAX_BUFFER_SIZE (256) + +typedef enum { + NfcTransportLogLevelWarning, + NfcTransportLogLevelInfo, +} NfcTransportLogLevel; + +FuriMessageQueue* poller_queue = NULL; +FuriMessageQueue* listener_queue = NULL; + +typedef enum { + NfcMessageTypeTx, + NfcMessageTypeTimeout, + NfcMessageTypeAbort, +} NfcMessageType; + +typedef struct { + uint16_t data_bits; + uint8_t data[NFC_MAX_BUFFER_SIZE]; +} NfcMessageData; + +typedef struct { + NfcMessageType type; + NfcMessageData data; +} NfcMessage; + +typedef enum { + NfcStateIdle, + NfcStateReady, + NfcStateReset, +} NfcState; + +typedef enum { + Iso14443_3aColResStatusIdle, + Iso14443_3aColResStatusInProgress, + Iso14443_3aColResStatusDone, +} Iso14443_3aColResStatus; + +typedef struct { + Iso14443_3aSensResp sens_resp; + Iso14443_3aSddResp sdd_resp[2]; + Iso14443_3aSelResp sel_resp[2]; +} Iso14443_3aColResData; + +struct Nfc { + NfcState state; + + Iso14443_3aColResStatus col_res_status; + Iso14443_3aColResData col_res_data; + + NfcEventCallback callback; + void* context; + + FuriThread* worker_thread; +}; + +static void nfc_test_print( + NfcTransportLogLevel log_level, + const char* message, + uint8_t* buffer, + uint16_t bits) { + FuriString* str = furi_string_alloc(); + size_t bytes = (bits + 7) / 8; + + for(size_t i = 0; i < bytes; i++) { + furi_string_cat_printf(str, " %02X", buffer[i]); + } + if(log_level == NfcTransportLogLevelWarning) { + FURI_LOG_W(message, "%s", furi_string_get_cstr(str)); + } else { + FURI_LOG_I(message, "%s", furi_string_get_cstr(str)); + } + + furi_string_free(str); +} + +static void nfc_prepare_col_res_data( + Nfc* instance, + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak) { + memcpy(instance->col_res_data.sens_resp.sens_resp, atqa, 2); + + if(uid_len == 7) { + instance->col_res_data.sdd_resp[0].nfcid[0] = 0x88; + memcpy(&instance->col_res_data.sdd_resp[0].nfcid[1], uid, 3); + uint8_t bss = 0; + for(size_t i = 0; i < 4; i++) { + bss ^= instance->col_res_data.sdd_resp[0].nfcid[i]; + } + instance->col_res_data.sdd_resp[0].bss = bss; + instance->col_res_data.sel_resp[0].sak = 0x04; + + memcpy(instance->col_res_data.sdd_resp[1].nfcid, &uid[3], 4); + bss = 0; + for(size_t i = 0; i < 4; i++) { + bss ^= instance->col_res_data.sdd_resp[1].nfcid[i]; + } + instance->col_res_data.sdd_resp[1].bss = bss; + instance->col_res_data.sel_resp[1].sak = sak; + + } else { + furi_crash("Not supporting not 7 bytes"); + } +} + +Nfc* nfc_alloc() { + Nfc* instance = malloc(sizeof(Nfc)); + + return instance; +} + +void nfc_free(Nfc* instance) { + furi_assert(instance); + + free(instance); +} + +void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) { + UNUSED(instance); + UNUSED(mode); + UNUSED(tech); +} + +void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) { + UNUSED(instance); + UNUSED(fdt_poll_fc); +} + +void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) { + UNUSED(instance); + UNUSED(fdt_listen_fc); +} + +void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) { + UNUSED(instance); + UNUSED(mask_rx_time_fc); +} + +void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) { + UNUSED(instance); + UNUSED(fdt_poll_poll_us); +} + +void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) { + UNUSED(instance); + UNUSED(guard_time_us); +} + +NfcError nfc_iso14443a_listener_set_col_res_data( + Nfc* instance, + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak) { + furi_assert(instance); + furi_assert(uid); + furi_assert(atqa); + + nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak); + + return NfcErrorNone; +} + +static int32_t nfc_worker_poller(void* context) { + Nfc* instance = context; + furi_assert(instance->callback); + + instance->state = NfcStateReady; + NfcCommand command = NfcCommandContinue; + NfcEvent event = {}; + + while(true) { + event.type = NfcEventTypePollerReady; + command = instance->callback(event, instance->context); + if(command == NfcCommandStop) { + break; + } + } + + instance->state = NfcStateIdle; + + return 0; +} + +void nfc_start_poller(Nfc* instance, NfcEventCallback callback, void* context) { + furi_assert(instance); + furi_assert(instance->worker_thread == NULL); + + furi_assert(poller_queue == NULL); + // Check that poller is started after listener + furi_assert(listener_queue); + + instance->callback = callback; + instance->context = context; + + poller_queue = furi_message_queue_alloc(4, sizeof(NfcMessage)); + + instance->worker_thread = furi_thread_alloc(); + furi_thread_set_name(instance->worker_thread, "NfcWorkerPoller"); + furi_thread_set_context(instance->worker_thread, instance); + furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHigh); + furi_thread_set_stack_size(instance->worker_thread, 8 * 1024); + furi_thread_set_callback(instance->worker_thread, nfc_worker_poller); + furi_thread_start(instance->worker_thread); +} + +static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, uint16_t rx_bits) { + furi_assert(instance->col_res_status != Iso14443_3aColResStatusDone); + BitBuffer* tx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE); + + bool processed = false; + + if((rx_bits == 7) && (rx_data[0] == 0x52)) { + instance->col_res_status = Iso14443_3aColResStatusInProgress; + bit_buffer_copy_bytes( + tx_buffer, + instance->col_res_data.sens_resp.sens_resp, + sizeof(instance->col_res_data.sens_resp.sens_resp)); + nfc_listener_tx(instance, tx_buffer); + processed = true; + } else if(rx_bits == 2 * 8) { + if((rx_data[0] == 0x93) && (rx_data[1] == 0x20)) { + bit_buffer_copy_bytes( + tx_buffer, + (const uint8_t*)&instance->col_res_data.sdd_resp[0], + sizeof(Iso14443_3aSddResp)); + nfc_listener_tx(instance, tx_buffer); + processed = true; + } else if((rx_data[0] == 0x95) && (rx_data[1] == 0x20)) { + bit_buffer_copy_bytes( + tx_buffer, + (const uint8_t*)&instance->col_res_data.sdd_resp[1], + sizeof(Iso14443_3aSddResp)); + nfc_listener_tx(instance, tx_buffer); + processed = true; + } + } else if(rx_bits == 9 * 8) { + if((rx_data[0] == 0x93) && (rx_data[1] == 0x70)) { + bit_buffer_set_size_bytes(tx_buffer, 1); + bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[0].sak); + iso14443_crc_append(Iso14443CrcTypeA, tx_buffer); + nfc_listener_tx(instance, tx_buffer); + processed = true; + } else if((rx_data[0] == 0x95) && (rx_data[1] == 0x70)) { + bit_buffer_set_size_bytes(tx_buffer, 1); + bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[1].sak); + iso14443_crc_append(Iso14443CrcTypeA, tx_buffer); + nfc_listener_tx(instance, tx_buffer); + instance->col_res_status = Iso14443_3aColResStatusDone; + NfcEvent event = {.type = NfcEventTypeListenerActivated}; + instance->callback(event, instance->context); + + processed = true; + } + } + + if(!processed) { + NfcMessage message = {.type = NfcMessageTypeTimeout}; + furi_message_queue_put(poller_queue, &message, FuriWaitForever); + } + + bit_buffer_free(tx_buffer); +} + +static int32_t nfc_worker_listener(void* context) { + Nfc* instance = context; + furi_assert(instance->callback); + + NfcMessage message = {}; + + NfcEventData event_data = {}; + event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE); + NfcEvent nfc_event = {.data = event_data}; + + while(true) { + furi_message_queue_get(listener_queue, &message, FuriWaitForever); + bit_buffer_copy_bits(event_data.buffer, message.data.data, message.data.data_bits); + if((message.data.data[0] == 0x52) && (message.data.data_bits == 7)) { + instance->col_res_status = Iso14443_3aColResStatusIdle; + } + + if(message.type == NfcMessageTypeAbort) { + break; + } else if(message.type == NfcMessageTypeTx) { + nfc_test_print( + NfcTransportLogLevelInfo, "RDR", message.data.data, message.data.data_bits); + if(instance->col_res_status != Iso14443_3aColResStatusDone) { + nfc_worker_listener_pass_col_res( + instance, message.data.data, message.data.data_bits); + } else { + instance->state = NfcStateReady; + nfc_event.type = NfcEventTypeRxEnd; + instance->callback(nfc_event, instance->context); + } + } + } + + instance->state = NfcStateIdle; + instance->col_res_status = Iso14443_3aColResStatusIdle; + memset(&instance->col_res_data, 0, sizeof(instance->col_res_data)); + bit_buffer_free(nfc_event.data.buffer); + + return 0; +} + +void nfc_start_listener(Nfc* instance, NfcEventCallback callback, void* context) { + furi_assert(instance); + furi_assert(instance->worker_thread == NULL); + + furi_assert(listener_queue == NULL); + // Check that poller didn't start + furi_assert(poller_queue == NULL); + + instance->callback = callback; + instance->context = context; + + listener_queue = furi_message_queue_alloc(4, sizeof(NfcMessage)); + + instance->worker_thread = furi_thread_alloc(); + furi_thread_set_name(instance->worker_thread, "NfcWorkerListener"); + furi_thread_set_context(instance->worker_thread, instance); + furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHigh); + furi_thread_set_stack_size(instance->worker_thread, 8 * 1024); + furi_thread_set_callback(instance->worker_thread, nfc_worker_listener); + furi_thread_start(instance->worker_thread); +} + +void nfc_listener_abort(Nfc* instance) { + furi_assert(instance); + + NfcMessage message = {.type = NfcMessageTypeAbort}; + furi_message_queue_put(listener_queue, &message, FuriWaitForever); + + furi_thread_join(instance->worker_thread); + + furi_message_queue_free(listener_queue); + listener_queue = NULL; + + furi_thread_free(instance->worker_thread); + instance->worker_thread = NULL; +} + +void nfc_stop(Nfc* instance) { + furi_assert(instance); + furi_assert(instance->worker_thread); + + furi_thread_join(instance->worker_thread); + + furi_message_queue_free(poller_queue); + poller_queue = NULL; + + furi_thread_free(instance->worker_thread); + instance->worker_thread = NULL; +} + +// Called from worker thread + +NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(poller_queue); + furi_assert(listener_queue); + furi_assert(tx_buffer); + + NfcMessage message = {}; + message.type = NfcMessageTypeTx; + message.data.data_bits = bit_buffer_get_size(tx_buffer); + bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer)); + + furi_message_queue_put(poller_queue, &message, FuriWaitForever); + + return NfcErrorNone; +} + +NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) { + return nfc_listener_tx(instance, tx_buffer); +} + +NfcError + nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + furi_assert(poller_queue); + furi_assert(listener_queue); + UNUSED(fwt); + + NfcError error = NfcErrorNone; + + NfcMessage message = {}; + message.type = NfcMessageTypeTx; + message.data.data_bits = bit_buffer_get_size(tx_buffer); + bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer)); + // Tx + furi_assert(furi_message_queue_put(listener_queue, &message, FuriWaitForever) == FuriStatusOk); + // Rx + FuriStatus status = furi_message_queue_get(poller_queue, &message, 50); + + if(status == FuriStatusErrorTimeout) { + error = NfcErrorTimeout; + } else if(message.type == NfcMessageTypeTx) { + bit_buffer_copy_bits(rx_buffer, message.data.data, message.data.data_bits); + nfc_test_print( + NfcTransportLogLevelWarning, "TAG", message.data.data, message.data.data_bits); + } else if(message.type == NfcMessageTypeTimeout) { + error = NfcErrorTimeout; + } + + return error; +} + +NfcError nfc_iso14443a_poller_trx_custom_parity( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt); +} + +// Technology specific API + +NfcError nfc_iso14443a_poller_trx_short_frame( + Nfc* instance, + NfcIso14443aShortFrame frame, + BitBuffer* rx_buffer, + uint32_t fwt) { + UNUSED(frame); + + BitBuffer* tx_buffer = bit_buffer_alloc(32); + bit_buffer_set_size(tx_buffer, 7); + bit_buffer_set_byte(tx_buffer, 0, 0x52); + + NfcError error = nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt); + + bit_buffer_free(tx_buffer); + + return error; +} + +NfcError nfc_iso14443a_poller_trx_sdd_frame( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt); +} + +#endif diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index f1ab926538fa..6bdaa641e8d6 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #define TAG "SubGhz TEST" #define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes") @@ -49,12 +51,15 @@ static void subghz_test_init(void) { subghz_environment_set_protocol_registry( environment_handler, (void*)&subghz_protocol_registry); + subghz_devices_init(); + receiver_handler = subghz_receiver_alloc_init(environment_handler); subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable); subghz_receiver_set_rx_callback(receiver_handler, subghz_test_rx_callback, NULL); } static void subghz_test_deinit(void) { + subghz_devices_deinit(); subghz_receiver_free(receiver_handler); subghz_environment_free(environment_handler); } @@ -68,7 +73,7 @@ static bool subghz_decoder_test(const char* path, const char* name_decoder) { if(decoder) { file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); - if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) { + if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path, NULL)) { // the worker needs a file in order to open and read part of the file furi_delay_ms(100); @@ -108,7 +113,7 @@ static bool subghz_decode_random_test(const char* path) { uint32_t test_start = furi_get_tick(); file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); - if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) { + if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path, NULL)) { // the worker needs a file in order to open and read part of the file furi_delay_ms(100); @@ -318,7 +323,7 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) { SubGhzHalAsyncTxTest test = {0}; test.type = type; furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); furi_hal_subghz_set_frequency_and_path(433920000); if(!furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test)) { diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index ac71ca397ee9..9d7631bfee3b 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -27,6 +27,7 @@ int run_minunit_test_nfc(); int run_minunit_test_bit_lib(); int run_minunit_test_float_tools(); int run_minunit_test_bt(); +int run_minunit_test_dialogs_file_browser_options(); typedef int (*UnitTestEntry)(); @@ -55,6 +56,8 @@ const UnitTest unit_tests[] = { {.name = "bit_lib", .entry = run_minunit_test_bit_lib}, {.name = "float_tools", .entry = run_minunit_test_float_tools}, {.name = "bt", .entry = run_minunit_test_bt}, + {.name = "dialogs_file_browser_options", + .entry = run_minunit_test_dialogs_file_browser_options}, }; void minunit_print_progress() { diff --git a/applications/drivers/application.fam b/applications/drivers/application.fam new file mode 100644 index 000000000000..dc70e630c9f6 --- /dev/null +++ b/applications/drivers/application.fam @@ -0,0 +1,6 @@ +# Placeholder +App( + appid="drivers", + name="Drivers device", + apptype=FlipperAppType.METAPACKAGE, +) diff --git a/applications/drivers/subghz/application.fam b/applications/drivers/subghz/application.fam new file mode 100644 index 000000000000..2ee114833bb5 --- /dev/null +++ b/applications/drivers/subghz/application.fam @@ -0,0 +1,9 @@ +App( + appid="radio_device_cc1101_ext", + apptype=FlipperAppType.PLUGIN, + targets=["f7"], + entry_point="subghz_device_cc1101_ext_ep", + requires=["subghz"], + sdk_headers=["cc1101_ext/cc1101_ext_interconnect.h"], + fap_libs=["hwdrivers"], +) diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c new file mode 100644 index 000000000000..594a74a01506 --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -0,0 +1,796 @@ +#include "cc1101_ext.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define TAG "SubGhz_Device_CC1101_Ext" + +#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO &gpio_ext_pb2 + +/* DMA Channels definition */ +#define SUBGHZ_DEVICE_CC1101_EXT_DMA DMA2 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL LL_DMA_CHANNEL_3 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL LL_DMA_CHANNEL_4 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL LL_DMA_CHANNEL_5 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ FuriHalInterruptIdDma2Ch3 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF \ + SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF \ + SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF \ + SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL + +/** Low level buffer dimensions and guard times */ +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256) +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF \ + (SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL / 2) +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME 999 + +/** SubGhz state */ +typedef enum { + SubGhzDeviceCC1101ExtStateInit, /**< Init pending */ + SubGhzDeviceCC1101ExtStateIdle, /**< Idle, energy save mode */ + SubGhzDeviceCC1101ExtStateAsyncRx, /**< Async RX started */ + SubGhzDeviceCC1101ExtStateAsyncTx, /**< Async TX started, DMA and timer is on */ + SubGhzDeviceCC1101ExtStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ +} SubGhzDeviceCC1101ExtState; + +/** SubGhz regulation, receive transmission on the current frequency for the + * region */ +typedef enum { + SubGhzDeviceCC1101ExtRegulationOnlyRx, /**only Rx*/ + SubGhzDeviceCC1101ExtRegulationTxRx, /**TxRx*/ +} SubGhzDeviceCC1101ExtRegulation; + +typedef struct { + uint32_t* buffer; + LevelDuration carry_ld; + SubGhzDeviceCC1101ExtCallback callback; + void* callback_context; + uint32_t gpio_tx_buff[2]; + uint32_t debug_gpio_buff[2]; +} SubGhzDeviceCC1101ExtAsyncTx; + +typedef struct { + uint32_t capture_delta_duration; + SubGhzDeviceCC1101ExtCaptureCallback capture_callback; + void* capture_callback_context; +} SubGhzDeviceCC1101ExtAsyncRx; + +typedef struct { + volatile SubGhzDeviceCC1101ExtState state; + volatile SubGhzDeviceCC1101ExtRegulation regulation; + const GpioPin* async_mirror_pin; + FuriHalSpiBusHandle* spi_bus_handle; + const GpioPin* g0_pin; + SubGhzDeviceCC1101ExtAsyncTx async_tx; + SubGhzDeviceCC1101ExtAsyncRx async_rx; +} SubGhzDeviceCC1101Ext; + +static SubGhzDeviceCC1101Ext* subghz_device_cc1101_ext = NULL; + +static bool subghz_device_cc1101_ext_check_init() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateInit); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; + + bool ret = false; + CC1101Status cc1101_status = {0}; + + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(100 * 1000); + do { + // Reset + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init( + subghz_device_cc1101_ext->spi_bus_handle->miso, + GpioModeInput, + GpioPullUp, + GpioSpeedLow); + + cc1101_status = cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + cc1101_status = cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + // Prepare GD0 for power on self test + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeInput, GpioPullUp, GpioSpeedLow); + + // GD0 low + cc1101_status = cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + while(furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin) != false) { + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + } + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + + // GD0 high + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeInput, GpioPullDown, GpioSpeedLow); + cc1101_status = cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, + CC1101_IOCFG0, + CC1101IocfgHW | CC1101_IOCFG_INV); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + while(furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin) != true) { + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + } + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + + // Reset GD0 to floating state + cc1101_status = cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + // Go to sleep + cc1101_status = cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + ret = true; + } while(false); + + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + + if(ret) { + FURI_LOG_I(TAG, "Init OK"); + } else { + FURI_LOG_E(TAG, "Init failed"); + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } + return ret; +} + +bool subghz_device_cc1101_ext_alloc() { + furi_assert(subghz_device_cc1101_ext == NULL); + subghz_device_cc1101_ext = malloc(sizeof(SubGhzDeviceCC1101Ext)); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateInit; + subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationTxRx; + subghz_device_cc1101_ext->async_mirror_pin = NULL; + subghz_device_cc1101_ext->spi_bus_handle = &furi_hal_spi_bus_handle_external; + subghz_device_cc1101_ext->g0_pin = SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO; + + subghz_device_cc1101_ext->async_rx.capture_delta_duration = 0; + + furi_hal_spi_bus_handle_init(subghz_device_cc1101_ext->spi_bus_handle); + return subghz_device_cc1101_ext_check_init(); +} + +void subghz_device_cc1101_ext_free() { + furi_assert(subghz_device_cc1101_ext != NULL); + furi_hal_spi_bus_handle_deinit(subghz_device_cc1101_ext->spi_bus_handle); + free(subghz_device_cc1101_ext); + subghz_device_cc1101_ext = NULL; +} + +void subghz_device_cc1101_ext_set_async_mirror_pin(const GpioPin* pin) { + subghz_device_cc1101_ext->async_mirror_pin = pin; +} + +const GpioPin* subghz_device_cc1101_ext_get_data_gpio() { + return subghz_device_cc1101_ext->g0_pin; +} + +bool subghz_device_cc1101_ext_is_connect() { + bool ret = false; + + if(subghz_device_cc1101_ext == NULL) { // not initialized + ret = subghz_device_cc1101_ext_alloc(); + subghz_device_cc1101_ext_free(); + } else { // initialized + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint8_t partnumber = cc1101_get_partnumber(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + ret = (partnumber != 0) && (partnumber != 0xFF); + } + + return ret; +} + +void subghz_device_cc1101_ext_sleep() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + + cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle); + + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_dump_state() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + printf( + "[subghz_device_cc1101_ext] cc1101 chip %d, version %d\r\n", + cc1101_get_partnumber(subghz_device_cc1101_ext->spi_bus_handle), + cc1101_get_version(subghz_device_cc1101_ext->spi_bus_handle)); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) { + //load config + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + uint32_t i = 0; + uint8_t pa[8] = {0}; + while(preset_data[i]) { + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, preset_data[i], preset_data[i + 1]); + i += 2; + } + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + + //load pa table + memcpy(&pa[0], &preset_data[i + 2], 8); + subghz_device_cc1101_ext_load_patable(pa); + + //show debug + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + i = 0; + FURI_LOG_D(TAG, "Loading custom preset"); + while(preset_data[i]) { + FURI_LOG_D(TAG, "Reg[%lu]: %02X=%02X", i, preset_data[i], preset_data[i + 1]); + i += 2; + } + for(uint8_t y = i; y < i + 10; y++) { + FURI_LOG_D(TAG, "PA[%u]: %02X", y, preset_data[y]); + } + } +} + +void subghz_device_cc1101_ext_load_registers(const uint8_t* data) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + uint32_t i = 0; + while(data[i]) { + cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, data[i], data[i + 1]); + i += 2; + } + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_load_patable(const uint8_t data[8]) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_set_pa_table(subghz_device_cc1101_ext->spi_bus_handle, data); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_write_packet(const uint8_t* data, uint8_t size) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_flush_tx(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, CC1101_FIFO, size); + cc1101_write_fifo(subghz_device_cc1101_ext->spi_bus_handle, data, size); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_flush_rx() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_flush_rx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_flush_tx() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_flush_tx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +bool subghz_device_cc1101_ext_rx_pipe_not_empty() { + CC1101RxBytes status[1]; + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_read_reg( + subghz_device_cc1101_ext->spi_bus_handle, + (CC1101_STATUS_RXBYTES) | CC1101_BURST, + (uint8_t*)status); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + // TODO: you can add a buffer overflow flag if needed + if(status->NUM_RXBYTES > 0) { + return true; + } else { + return false; + } +} + +bool subghz_device_cc1101_ext_is_rx_data_crc_valid() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint8_t data[1]; + cc1101_read_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + if(((data[0] >> 7) & 0x01)) { + return true; + } else { + return false; + } +} + +void subghz_device_cc1101_ext_read_packet(uint8_t* data, uint8_t* size) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_read_fifo(subghz_device_cc1101_ext->spi_bus_handle, data, size); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_shutdown() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + // Reset and shutdown + cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_reset() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_idle() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_rx() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_switch_to_rx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +bool subghz_device_cc1101_ext_tx() { + if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false; + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_switch_to_tx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + return true; +} + +float subghz_device_cc1101_ext_get_rssi() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + int32_t rssi_dec = cc1101_get_rssi(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + + float rssi = rssi_dec; + if(rssi_dec >= 128) { + rssi = ((rssi - 256.0f) / 2.0f) - 74.0f; + } else { + rssi = (rssi / 2.0f) - 74.0f; + } + + return rssi; +} + +uint8_t subghz_device_cc1101_ext_get_lqi() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint8_t data[1]; + cc1101_read_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + return data[0] & 0x7F; +} + +bool subghz_device_cc1101_ext_is_frequency_valid(uint32_t value) { + if(!(value >= 299999755 && value <= 348000335) && + !(value >= 386999938 && value <= 464000000) && + !(value >= 778999847 && value <= 928000000)) { + return false; + } + + return true; +} + +uint32_t subghz_device_cc1101_ext_set_frequency(uint32_t value) { + if(furi_hal_region_is_frequency_allowed(value)) { + subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationTxRx; + } else { + subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationOnlyRx; + } + + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint32_t real_frequency = + cc1101_set_frequency(subghz_device_cc1101_ext->spi_bus_handle, value); + cc1101_calibrate(subghz_device_cc1101_ext->spi_bus_handle); + + while(true) { + CC1101Status status = cc1101_get_status(subghz_device_cc1101_ext->spi_bus_handle); + if(status.STATE == CC1101StateIDLE) break; + } + + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + return real_frequency; +} + +static bool subghz_device_cc1101_ext_start_debug() { + bool ret = false; + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) { + furi_hal_gpio_init( + subghz_device_cc1101_ext->async_mirror_pin, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh); + ret = true; + } + return ret; +} + +static bool subghz_device_cc1101_ext_stop_debug() { + bool ret = false; + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) { + furi_hal_gpio_init( + subghz_device_cc1101_ext->async_mirror_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + ret = true; + } + return ret; +} + +static void subghz_device_cc1101_ext_capture_ISR() { + if(!furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin)) { + if(subghz_device_cc1101_ext->async_rx.capture_callback) { + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) + furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); + + subghz_device_cc1101_ext->async_rx.capture_callback( + true, + LL_TIM_GetCounter(TIM17), + (void*)subghz_device_cc1101_ext->async_rx.capture_callback_context); + } + } else { + if(subghz_device_cc1101_ext->async_rx.capture_callback) { + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) + furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, true); + + subghz_device_cc1101_ext->async_rx.capture_callback( + false, + LL_TIM_GetCounter(TIM17), + (void*)subghz_device_cc1101_ext->async_rx.capture_callback_context); + } + } + LL_TIM_SetCounter(TIM17, 6); +} + +void subghz_device_cc1101_ext_start_async_rx( + SubGhzDeviceCC1101ExtCaptureCallback callback, + void* context) { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncRx; + + subghz_device_cc1101_ext->async_rx.capture_callback = callback; + subghz_device_cc1101_ext->async_rx.capture_callback_context = context; + + furi_hal_bus_enable(FuriHalBusTIM17); + + // Configure TIM + LL_TIM_SetPrescaler(TIM17, 64 - 1); + LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(TIM17, 0xFFFF); + LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); + + // Timer: advanced + LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM17); + LL_TIM_DisableDMAReq_TRIG(TIM17); + LL_TIM_DisableIT_TRIG(TIM17); + + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_remove_int_callback(subghz_device_cc1101_ext->g0_pin); + furi_hal_gpio_add_int_callback( + subghz_device_cc1101_ext->g0_pin, + subghz_device_cc1101_ext_capture_ISR, + subghz_device_cc1101_ext->async_rx.capture_callback); + + // Start timer + LL_TIM_SetCounter(TIM17, 0); + LL_TIM_EnableCounter(TIM17); + + // Start debug + subghz_device_cc1101_ext_start_debug(); + + // Switch to RX + subghz_device_cc1101_ext_rx(); + + //Clear the variable after the end of the session + subghz_device_cc1101_ext->async_rx.capture_delta_duration = 0; +} + +void subghz_device_cc1101_ext_stop_async_rx() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncRx); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; + + // Shutdown radio + subghz_device_cc1101_ext_idle(); + + FURI_CRITICAL_ENTER(); + furi_hal_bus_disable(FuriHalBusTIM17); + + // Stop debug + subghz_device_cc1101_ext_stop_debug(); + + FURI_CRITICAL_EXIT(); + furi_hal_gpio_remove_int_callback(subghz_device_cc1101_ext->g0_pin); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t samples) { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); + while(samples > 0) { + bool is_odd = samples % 2; + LevelDuration ld; + if(level_duration_is_reset(subghz_device_cc1101_ext->async_tx.carry_ld)) { + ld = subghz_device_cc1101_ext->async_tx.callback( + subghz_device_cc1101_ext->async_tx.callback_context); + } else { + ld = subghz_device_cc1101_ext->async_tx.carry_ld; + subghz_device_cc1101_ext->async_tx.carry_ld = level_duration_reset(); + } + + if(level_duration_is_wait(ld)) { + *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; + buffer++; + samples--; + } else if(level_duration_is_reset(ld)) { + *buffer = 0; + buffer++; + samples--; + LL_DMA_DisableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_DisableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_TIM_EnableIT_UPDATE(TIM17); + break; + } else { + bool level = level_duration_get_level(ld); + + // Inject guard time if level is incorrect + if(is_odd != level) { + *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; + buffer++; + samples--; + + // Special case: prevent buffer overflow if sample is last + if(samples == 0) { + subghz_device_cc1101_ext->async_tx.carry_ld = ld; + break; + } + } + + uint32_t duration = level_duration_get_duration(ld); + furi_assert(duration > 0); + *buffer = duration - 1; + buffer++; + samples--; + } + } +} + +static void subghz_device_cc1101_ext_async_tx_dma_isr() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); + +#if SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL == LL_DMA_CHANNEL_3 + if(LL_DMA_IsActiveFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { + LL_DMA_ClearFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA); + subghz_device_cc1101_ext_async_tx_refill( + subghz_device_cc1101_ext->async_tx.buffer, + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF); + } + if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { + LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA); + subghz_device_cc1101_ext_async_tx_refill( + subghz_device_cc1101_ext->async_tx.buffer + + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF, + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF); + } +#else +#error Update this code. Would you kindly? +#endif +} + +static void subghz_device_cc1101_ext_async_tx_timer_isr() { + if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { + if(LL_TIM_GetAutoReload(TIM17) == 0) { + LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) + furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); + LL_TIM_DisableCounter(TIM17); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; + } + LL_TIM_ClearFlag_UPDATE(TIM17); + } +} + +bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context) { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); + furi_assert(callback); + + //If transmission is prohibited by regional settings + if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false; + + subghz_device_cc1101_ext->async_tx.callback = callback; + subghz_device_cc1101_ext->async_tx.callback_context = context; + + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTx; + + subghz_device_cc1101_ext->async_tx.buffer = + malloc(SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); + + //Signal generation with mem-to-mem DMA + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + // Configure DMA update timer + LL_DMA_SetMemoryAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, (uint32_t)subghz_device_cc1101_ext->async_tx.buffer); + LL_DMA_SetPeriphAddress(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, (uint32_t) & (TIM17->ARR)); + LL_DMA_ConfigTransfer( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_MODE_NORMAL); + LL_DMA_SetDataLength( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); + LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMAMUX_REQ_TIM17_UP); + + LL_DMA_EnableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_EnableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + + furi_hal_interrupt_set_isr( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, subghz_device_cc1101_ext_async_tx_dma_isr, NULL); + + furi_hal_bus_enable(FuriHalBusTIM17); + + // Configure TIM + LL_TIM_SetPrescaler(TIM17, 64 - 1); + LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(TIM17, 0xFFFF); + LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM17); + + furi_hal_interrupt_set_isr( + FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL); + + subghz_device_cc1101_ext_async_tx_refill( + subghz_device_cc1101_ext->async_tx.buffer, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); + + // Configure tx gpio dma + const GpioPin* gpio = subghz_device_cc1101_ext->g0_pin; + + subghz_device_cc1101_ext->async_tx.gpio_tx_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; + subghz_device_cc1101_ext->async_tx.gpio_tx_buff[1] = gpio->pin; + + LL_DMA_SetMemoryAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, + (uint32_t)subghz_device_cc1101_ext->async_tx.gpio_tx_buff); + LL_DMA_SetPeriphAddress(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, (uint32_t) & (gpio->port->BSRR)); + LL_DMA_ConfigTransfer( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, 2); + LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, LL_DMAMUX_REQ_TIM17_UP); + LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF); + + // Start debug + if(subghz_device_cc1101_ext_start_debug()) { + gpio = subghz_device_cc1101_ext->async_mirror_pin; + subghz_device_cc1101_ext->async_tx.debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; + subghz_device_cc1101_ext->async_tx.debug_gpio_buff[1] = gpio->pin; + + LL_DMA_SetMemoryAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, + (uint32_t)subghz_device_cc1101_ext->async_tx.debug_gpio_buff); + LL_DMA_SetPeriphAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, (uint32_t) & (gpio->port->BSRR)); + LL_DMA_ConfigTransfer( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_LOW); + LL_DMA_SetDataLength(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, 2); + LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, LL_DMAMUX_REQ_TIM17_UP); + LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF); + } + + // Start counter + LL_TIM_EnableDMAReq_UPDATE(TIM17); + LL_TIM_GenerateEvent_UPDATE(TIM17); + + subghz_device_cc1101_ext_tx(); + + LL_TIM_SetCounter(TIM17, 0); + LL_TIM_EnableCounter(TIM17); + + return true; +} + +bool subghz_device_cc1101_ext_is_async_tx_complete() { + return subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd; +} + +void subghz_device_cc1101_ext_stop_async_tx() { + furi_assert( + subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx || + subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd); + + // Shutdown radio + subghz_device_cc1101_ext_idle(); + + // Deinitialize Timer + FURI_CRITICAL_ENTER(); + furi_hal_bus_disable(FuriHalBusTIM17); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); + + // Deinitialize DMA + LL_DMA_DeInit(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF); + furi_hal_interrupt_set_isr(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, NULL, NULL); + + // Deinitialize GPIO + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + // Stop debug + if(subghz_device_cc1101_ext_stop_debug()) { + LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF); + } + + FURI_CRITICAL_EXIT(); + + free(subghz_device_cc1101_ext->async_tx.buffer); + + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; +} diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.h b/applications/drivers/subghz/cc1101_ext/cc1101_ext.h new file mode 100644 index 000000000000..d972fcb66185 --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.h @@ -0,0 +1,206 @@ +/** + * @file furi_hal_subghz.h + * SubGhz HAL API + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Mirror RX/TX async modulation signal to specified pin + * + * @warning Configures pin to output mode. Make sure it is not connected + * directly to power or ground. + * + * @param[in] pin pointer to the gpio pin structure or NULL to disable + */ +void subghz_device_cc1101_ext_set_async_mirror_pin(const GpioPin* pin); + +/** Get data GPIO + * + * @return pointer to the gpio pin structure + */ +const GpioPin* subghz_device_cc1101_ext_get_data_gpio(); + +/** Initialize device + * + * @return true if success + */ +bool subghz_device_cc1101_ext_alloc(); + +/** Deinitialize device + */ +void subghz_device_cc1101_ext_free(); + +/** Check and switch to power save mode Used by internal API-HAL + * initialization routine Can be used to reinitialize device to safe state and + * send it to sleep + */ +bool subghz_device_cc1101_ext_is_connect(); + +/** Send device to sleep mode + */ +void subghz_device_cc1101_ext_sleep(); + +/** Dump info to stdout + */ +void subghz_device_cc1101_ext_dump_state(); + +/** Load custom registers from preset + * + * @param preset_data registers to load + */ +void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data); + +/** Load registers + * + * @param data Registers data + */ +void subghz_device_cc1101_ext_load_registers(const uint8_t* data); + +/** Load PATABLE + * + * @param data 8 uint8_t values + */ +void subghz_device_cc1101_ext_load_patable(const uint8_t data[8]); + +/** Write packet to FIFO + * + * @param data bytes array + * @param size size + */ +void subghz_device_cc1101_ext_write_packet(const uint8_t* data, uint8_t size); + +/** Check if receive pipe is not empty + * + * @return true if not empty + */ +bool subghz_device_cc1101_ext_rx_pipe_not_empty(); + +/** Check if received data crc is valid + * + * @return true if valid + */ +bool subghz_device_cc1101_ext_is_rx_data_crc_valid(); + +/** Read packet from FIFO + * + * @param data pointer + * @param size size + */ +void subghz_device_cc1101_ext_read_packet(uint8_t* data, uint8_t* size); + +/** Flush rx FIFO buffer + */ +void subghz_device_cc1101_ext_flush_rx(); + +/** Flush tx FIFO buffer + */ +void subghz_device_cc1101_ext_flush_tx(); + +/** Shutdown Issue SPWD command + * @warning registers content will be lost + */ +void subghz_device_cc1101_ext_shutdown(); + +/** Reset Issue reset command + * @warning registers content will be lost + */ +void subghz_device_cc1101_ext_reset(); + +/** Switch to Idle + */ +void subghz_device_cc1101_ext_idle(); + +/** Switch to Receive + */ +void subghz_device_cc1101_ext_rx(); + +/** Switch to Transmit + * + * @return true if the transfer is allowed by belonging to the region + */ +bool subghz_device_cc1101_ext_tx(); + +/** Get RSSI value in dBm + * + * @return RSSI value + */ +float subghz_device_cc1101_ext_get_rssi(); + +/** Get LQI + * + * @return LQI value + */ +uint8_t subghz_device_cc1101_ext_get_lqi(); + +/** Check if frequency is in valid range + * + * @param value frequency in Hz + * + * @return true if frequency is valid, otherwise false + */ +bool subghz_device_cc1101_ext_is_frequency_valid(uint32_t value); + +/** Set frequency + * + * @param value frequency in Hz + * + * @return real frequency in Hz + */ +uint32_t subghz_device_cc1101_ext_set_frequency(uint32_t value); + +/* High Level API */ + +/** Signal Timings Capture callback */ +typedef void (*SubGhzDeviceCC1101ExtCaptureCallback)(bool level, uint32_t duration, void* context); + +/** Enable signal timings capture Initializes GPIO and TIM2 for timings capture + * + * @param callback SubGhzDeviceCC1101ExtCaptureCallback + * @param context callback context + */ +void subghz_device_cc1101_ext_start_async_rx( + SubGhzDeviceCC1101ExtCaptureCallback callback, + void* context); + +/** Disable signal timings capture Resets GPIO and TIM2 + */ +void subghz_device_cc1101_ext_stop_async_rx(); + +/** Async TX callback type + * @param context callback context + * @return LevelDuration + */ +typedef LevelDuration (*SubGhzDeviceCC1101ExtCallback)(void* context); + +/** Start async TX Initializes GPIO, TIM2 and DMA1 for signal output + * + * @param callback SubGhzDeviceCC1101ExtCallback + * @param context callback context + * + * @return true if the transfer is allowed by belonging to the region + */ +bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context); + +/** Wait for async transmission to complete + * + * @return true if TX complete + */ +bool subghz_device_cc1101_ext_is_async_tx_complete(); + +/** Stop async transmission and cleanup resources Resets GPIO, TIM2, and DMA1 + */ +void subghz_device_cc1101_ext_stop_async_tx(); + +#ifdef __cplusplus +} +#endif diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c new file mode 100644 index 000000000000..51f5a0d1ddbe --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c @@ -0,0 +1,110 @@ +#include "cc1101_ext_interconnect.h" +#include "cc1101_ext.h" +#include + +#define TAG "SubGhzDeviceCC1101Ext" + +static bool subghz_device_cc1101_ext_interconnect_is_frequency_valid(uint32_t frequency) { + bool ret = subghz_device_cc1101_ext_is_frequency_valid(frequency); + if(!ret) { + furi_crash("SubGhz: Incorrect frequency."); + } + return ret; +} + +static uint32_t subghz_device_cc1101_ext_interconnect_set_frequency(uint32_t frequency) { + subghz_device_cc1101_ext_interconnect_is_frequency_valid(frequency); + return subghz_device_cc1101_ext_set_frequency(frequency); +} + +static bool subghz_device_cc1101_ext_interconnect_start_async_tx(void* callback, void* context) { + return subghz_device_cc1101_ext_start_async_tx( + (SubGhzDeviceCC1101ExtCallback)callback, context); +} + +static void subghz_device_cc1101_ext_interconnect_start_async_rx(void* callback, void* context) { + subghz_device_cc1101_ext_start_async_rx( + (SubGhzDeviceCC1101ExtCaptureCallback)callback, context); +} + +static void subghz_device_cc1101_ext_interconnect_load_preset( + FuriHalSubGhzPreset preset, + uint8_t* preset_data) { + switch(preset) { + case FuriHalSubGhzPresetOok650Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_ook_650khz_async_regs); + break; + case FuriHalSubGhzPresetOok270Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_ook_270khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev238Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev476Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs); + break; + case FuriHalSubGhzPresetMSK99_97KbAsync: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_msk_99_97kb_async_regs); + break; + case FuriHalSubGhzPresetGFSK9_99KbAsync: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_gfsk_9_99kb_async_regs); + break; + + default: + subghz_device_cc1101_ext_load_custom_preset(preset_data); + } +} + +const SubGhzDeviceInterconnect subghz_device_cc1101_ext_interconnect = { + .begin = subghz_device_cc1101_ext_alloc, + .end = subghz_device_cc1101_ext_free, + .is_connect = subghz_device_cc1101_ext_is_connect, + .reset = subghz_device_cc1101_ext_reset, + .sleep = subghz_device_cc1101_ext_sleep, + .idle = subghz_device_cc1101_ext_idle, + .load_preset = subghz_device_cc1101_ext_interconnect_load_preset, + .set_frequency = subghz_device_cc1101_ext_interconnect_set_frequency, + .is_frequency_valid = subghz_device_cc1101_ext_is_frequency_valid, + .set_async_mirror_pin = subghz_device_cc1101_ext_set_async_mirror_pin, + .get_data_gpio = subghz_device_cc1101_ext_get_data_gpio, + + .set_tx = subghz_device_cc1101_ext_tx, + .flush_tx = subghz_device_cc1101_ext_flush_tx, + .start_async_tx = subghz_device_cc1101_ext_interconnect_start_async_tx, + .is_async_complete_tx = subghz_device_cc1101_ext_is_async_tx_complete, + .stop_async_tx = subghz_device_cc1101_ext_stop_async_tx, + + .set_rx = subghz_device_cc1101_ext_rx, + .flush_rx = subghz_device_cc1101_ext_flush_rx, + .start_async_rx = subghz_device_cc1101_ext_interconnect_start_async_rx, + .stop_async_rx = subghz_device_cc1101_ext_stop_async_rx, + + .get_rssi = subghz_device_cc1101_ext_get_rssi, + .get_lqi = subghz_device_cc1101_ext_get_lqi, + + .rx_pipe_not_empty = subghz_device_cc1101_ext_rx_pipe_not_empty, + .is_rx_data_crc_valid = subghz_device_cc1101_ext_is_rx_data_crc_valid, + .read_packet = subghz_device_cc1101_ext_read_packet, + .write_packet = subghz_device_cc1101_ext_write_packet, +}; + +const SubGhzDevice subghz_device_cc1101_ext = { + .name = SUBGHZ_DEVICE_CC1101_EXT_NAME, + .interconnect = &subghz_device_cc1101_ext_interconnect, +}; + +static const FlipperAppPluginDescriptor subghz_device_cc1101_ext_descriptor = { + .appid = SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID, + .ep_api_version = SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION, + .entry_point = &subghz_device_cc1101_ext, +}; + +const FlipperAppPluginDescriptor* subghz_device_cc1101_ext_ep() { + return &subghz_device_cc1101_ext_descriptor; +} \ No newline at end of file diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h new file mode 100644 index 000000000000..cf1ff3ee07f2 --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h @@ -0,0 +1,8 @@ +#pragma once +#include + +#define SUBGHZ_DEVICE_CC1101_EXT_NAME "cc1101_ext" + +typedef struct SubGhzDeviceCC1101Ext SubGhzDeviceCC1101Ext; + +const FlipperAppPluginDescriptor* subghz_device_cc1101_ext_ep(); diff --git a/applications/examples/example_thermo/README.md b/applications/examples/example_thermo/README.md index 08240a1f8fa6..d298de64309e 100644 --- a/applications/examples/example_thermo/README.md +++ b/applications/examples/example_thermo/README.md @@ -18,7 +18,7 @@ Before launching the application, connect the sensor to Flipper's external GPIO In order to launch this demo, follow the steps below: 1. Make sure your Flipper has an SD card installed. 2. Connect your Flipper to the computer via a USB cable. -3. Run `./fbt launch_app APPSRC=example_thermo` in your terminal emulator of choice. +3. Run `./fbt launch APPSRC=example_thermo` in your terminal emulator of choice. ## Changing the data pin It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below: diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index 0ee5cefa1de0..b4c12cbc38a4 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -60,7 +60,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) { AvrIspWorkerRW* instance = context; /* start PWM on &gpio_ext_pa4 */ - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + } FURI_LOG_D(TAG, "Start"); @@ -122,7 +124,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) { } FURI_LOG_D(TAG, "Stop"); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + } return 0; } @@ -136,7 +140,12 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { instance->chip_arr_ind = avr_isp_chip_arr_size + 1; /* start PWM on &gpio_ext_pa4 */ - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + bool was_pwm_enabled = false; + if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + } else { + was_pwm_enabled = true; + } do { if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { @@ -200,7 +209,9 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { } while(0); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4) && !was_pwm_enabled) { + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + } if(instance->callback) { if(instance->chip_arr_ind > avr_isp_chip_arr_size) { diff --git a/applications/external/hid_app/assets/Space_60x18.png b/applications/external/hid_app/assets/Space_60x18.png new file mode 100644 index 000000000000..e29f50ae9220 Binary files /dev/null and b/applications/external/hid_app/assets/Space_60x18.png differ diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index a4f64589d76b..a969a933a12e 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -7,6 +7,7 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexKeynote, + HidSubmenuIndexKeynoteVertical, HidSubmenuIndexKeyboard, HidSubmenuIndexMedia, HidSubmenuIndexTikTok, @@ -20,6 +21,11 @@ static void hid_submenu_callback(void* context, uint32_t index) { Hid* app = context; if(index == HidSubmenuIndexKeynote) { app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, false); + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); + } else if(index == HidSubmenuIndexKeynoteVertical) { + app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, true); view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); } else if(index == HidSubmenuIndexKeyboard) { app->view_id = HidViewKeyboard; @@ -105,6 +111,12 @@ Hid* hid_alloc(HidTransport transport) { app->device_type_submenu = submenu_alloc(); submenu_add_item( app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, + "Keynote Vertical", + HidSubmenuIndexKeynoteVertical, + hid_submenu_callback, + app); submenu_add_item( app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app); submenu_add_item( @@ -377,7 +389,7 @@ int32_t hid_usb_app(void* p) { bt_hid_connection_status_changed_callback(BtStatusConnected, app); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(app->view_dispatcher); @@ -417,7 +429,7 @@ int32_t hid_ble_app(void* p) { furi_hal_bt_start_advertising(); bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(app->view_dispatcher); diff --git a/applications/external/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c index 5e5eeb790948..543363bf67b7 100644 --- a/applications/external/hid_app/views/hid_keynote.c +++ b/applications/external/hid_app/views/hid_keynote.c @@ -111,6 +111,91 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); } +static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidKeynoteModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote"); + } else { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote"); + } + + canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit"); + + const uint8_t x_2 = 23; + const uint8_t x_1 = 2; + const uint8_t x_3 = 44; + + const uint8_t y_1 = 44; + const uint8_t y_2 = 65; + + // Up + canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); + if(model->up_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_1 + 6, CanvasDirectionBottomToTop); + canvas_set_color(canvas, ColorBlack); + + // Down + canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18); + if(model->down_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_2 + 10, CanvasDirectionTopToBottom); + canvas_set_color(canvas, ColorBlack); + + // Left + canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); + if(model->left_pressed) { + elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_1 + 7, y_2 + 8, CanvasDirectionRightToLeft); + canvas_set_color(canvas, ColorBlack); + + // Right + canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18); + if(model->right_pressed) { + elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_3 + 11, y_2 + 8, CanvasDirectionLeftToRight); + canvas_set_color(canvas, ColorBlack); + + // Ok + canvas_draw_icon(canvas, 2, 86, &I_Space_60x18); + if(model->ok_pressed) { + elements_slightly_rounded_box(canvas, 5, 88, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 90, &I_Ok_btn_9x9); + elements_multiline_text_aligned(canvas, 26, 98, AlignLeft, AlignBottom, "Space"); + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 2, 107, &I_Space_60x18); + if(model->back_pressed) { + elements_slightly_rounded_box(canvas, 5, 109, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 111, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 26, 119, AlignLeft, AlignBottom, "Back"); +} + static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) { with_view_model( hid_keynote->view, @@ -212,3 +297,16 @@ void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) { with_view_model( hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true); } + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical) { + furi_assert(hid_keynote); + + if(vertical) { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_vertical_callback); + view_set_orientation(hid_keynote->view, ViewOrientationVerticalFlip); + + } else { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); + view_set_orientation(hid_keynote->view, ViewOrientationHorizontal); + } +} diff --git a/applications/external/hid_app/views/hid_keynote.h b/applications/external/hid_app/views/hid_keynote.h index 4d4a0a9b1a05..84bfed4ce415 100644 --- a/applications/external/hid_app/views/hid_keynote.h +++ b/applications/external/hid_app/views/hid_keynote.h @@ -12,3 +12,5 @@ void hid_keynote_free(HidKeynote* hid_keynote); View* hid_keynote_get_view(HidKeynote* hid_keynote); void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected); + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical); diff --git a/applications/external/mfkey32/mfkey32.c b/applications/external/mfkey32/mfkey32.c index d4b2d3e4ab00..5e790b01f6e4 100644 --- a/applications/external/mfkey32/mfkey32.c +++ b/applications/external/mfkey32/mfkey32.c @@ -1112,7 +1112,7 @@ void mfkey32(ProgramState* program_state) { } if(keyarray_size > 0) { // TODO: Should we use DolphinDeedNfcMfcAdd? - DOLPHIN_DEED(DolphinDeedNfcMfcAdd); + dolphin_deed(DolphinDeedNfcMfcAdd); } napi_mf_classic_nonce_array_free(nonce_arr); napi_mf_classic_dict_free(user_dict); diff --git a/applications/external/music_player/application.fam b/applications/external/music_player/application.fam index 3414c0a482d3..c9cd5e44de7e 100644 --- a/applications/external/music_player/application.fam +++ b/applications/external/music_player/application.fam @@ -7,18 +7,10 @@ App( "gui", "dialogs", ], - provides=["music_player_start"], stack_size=2 * 1024, order=20, fap_icon="icons/music_10px.png", fap_category="Media", fap_icon_assets="icons", -) - -App( - appid="music_player_start", - apptype=FlipperAppType.STARTUP, - entry_point="music_player_on_system_start", - requires=["music_player"], - order=30, + fap_libs=["music_worker"], ) diff --git a/applications/external/music_player/music_player.c b/applications/external/music_player/music_player.c index 2380d7d17beb..8b0b758c1c15 100644 --- a/applications/external/music_player/music_player.c +++ b/applications/external/music_player/music_player.c @@ -1,4 +1,4 @@ -#include "music_player_worker.h" +#include #include #include @@ -34,7 +34,7 @@ typedef struct { ViewPort* view_port; Gui* gui; - MusicPlayerWorker* worker; + MusicWorker* worker; } MusicPlayer; static const float MUSIC_PLAYER_VOLUMES[] = {0, .25, .5, .75, 1}; @@ -218,7 +218,7 @@ static void input_callback(InputEvent* input_event, void* ctx) { } } -static void music_player_worker_callback( +static void music_worker_callback( uint8_t semitone, uint8_t dots, uint8_t duration, @@ -250,7 +250,7 @@ static void music_player_worker_callback( void music_player_clear(MusicPlayer* instance) { memset(instance->model->duration_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); memset(instance->model->semitone_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); - music_player_worker_clear(instance->worker); + music_worker_clear(instance->worker); } MusicPlayer* music_player_alloc() { @@ -263,10 +263,9 @@ MusicPlayer* music_player_alloc() { instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - instance->worker = music_player_worker_alloc(); - music_player_worker_set_volume( - instance->worker, MUSIC_PLAYER_VOLUMES[instance->model->volume]); - music_player_worker_set_callback(instance->worker, music_player_worker_callback, instance); + instance->worker = music_worker_alloc(); + music_worker_set_volume(instance->worker, MUSIC_PLAYER_VOLUMES[instance->model->volume]); + music_worker_set_callback(instance->worker, music_worker_callback, instance); music_player_clear(instance); @@ -286,7 +285,7 @@ void music_player_free(MusicPlayer* instance) { furi_record_close(RECORD_GUI); view_port_free(instance->view_port); - music_player_worker_free(instance->worker); + music_worker_free(instance->worker); furi_message_queue_free(instance->input_queue); @@ -330,12 +329,12 @@ int32_t music_player_app(void* p) { } } - if(!music_player_worker_load(music_player->worker, furi_string_get_cstr(file_path))) { + if(!music_worker_load(music_player->worker, furi_string_get_cstr(file_path))) { FURI_LOG_E(TAG, "Unable to load file"); break; } - music_player_worker_start(music_player->worker); + music_worker_start(music_player->worker); InputEvent input; while(furi_message_queue_get(music_player->input_queue, &input, FuriWaitForever) == @@ -349,11 +348,11 @@ int32_t music_player_app(void* p) { } else if(input.key == InputKeyUp) { if(music_player->model->volume < COUNT_OF(MUSIC_PLAYER_VOLUMES) - 1) music_player->model->volume++; - music_player_worker_set_volume( + music_worker_set_volume( music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]); } else if(input.key == InputKeyDown) { if(music_player->model->volume > 0) music_player->model->volume--; - music_player_worker_set_volume( + music_worker_set_volume( music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]); } @@ -361,7 +360,7 @@ int32_t music_player_app(void* p) { view_port_update(music_player->view_port); } - music_player_worker_stop(music_player->worker); + music_worker_stop(music_player->worker); if(p && strlen(p)) break; // Exit instead of going to browser if launched with arg music_player_clear(music_player); } while(1); diff --git a/applications/external/music_player/music_player_cli.c b/applications/external/music_player/music_player_cli.c deleted file mode 100644 index 90060d7ee00b..000000000000 --- a/applications/external/music_player/music_player_cli.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include "music_player_worker.h" - -static void music_player_cli(Cli* cli, FuriString* args, void* context) { - UNUSED(context); - MusicPlayerWorker* music_player_worker = music_player_worker_alloc(); - Storage* storage = furi_record_open(RECORD_STORAGE); - - do { - if(storage_common_stat(storage, furi_string_get_cstr(args), NULL) == FSE_OK) { - if(!music_player_worker_load(music_player_worker, furi_string_get_cstr(args))) { - printf("Failed to open file %s\r\n", furi_string_get_cstr(args)); - break; - } - } else { - if(!music_player_worker_load_rtttl_from_string( - music_player_worker, furi_string_get_cstr(args))) { - printf("Argument is not a file or RTTTL\r\n"); - break; - } - } - - printf("Press CTRL+C to stop\r\n"); - music_player_worker_set_volume(music_player_worker, 1.0f); - music_player_worker_start(music_player_worker); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(50); - } - music_player_worker_stop(music_player_worker); - } while(0); - - furi_record_close(RECORD_STORAGE); - music_player_worker_free(music_player_worker); -} - -void music_player_on_system_start() { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - - cli_add_command(cli, "music_player", CliCommandFlagDefault, music_player_cli, NULL); - - furi_record_close(RECORD_CLI); -#else - UNUSED(music_player_cli); -#endif -} diff --git a/applications/external/music_player/music_player_worker.h b/applications/external/music_player/music_player_worker.h deleted file mode 100644 index 00320b11fe5d..000000000000 --- a/applications/external/music_player/music_player_worker.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include - -typedef void (*MusicPlayerWorkerCallback)( - uint8_t semitone, - uint8_t dots, - uint8_t duration, - float position, - void* context); - -typedef struct MusicPlayerWorker MusicPlayerWorker; - -MusicPlayerWorker* music_player_worker_alloc(); - -void music_player_worker_clear(MusicPlayerWorker* instance); - -void music_player_worker_free(MusicPlayerWorker* instance); - -bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string); - -void music_player_worker_set_callback( - MusicPlayerWorker* instance, - MusicPlayerWorkerCallback callback, - void* context); - -void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume); - -void music_player_worker_start(MusicPlayerWorker* instance); - -void music_player_worker_stop(MusicPlayerWorker* instance); diff --git a/applications/external/nfc_magic/lib/magic/magic.c b/applications/external/nfc_magic/lib/magic/classic_gen1.c similarity index 64% rename from applications/external/nfc_magic/lib/magic/magic.c rename to applications/external/nfc_magic/lib/magic/classic_gen1.c index 9a71daaa0ffa..8d87d63161d2 100644 --- a/applications/external/nfc_magic/lib/magic/magic.c +++ b/applications/external/nfc_magic/lib/magic/classic_gen1.c @@ -1,11 +1,10 @@ -#include "magic.h" +#include "classic_gen1.h" #include #define TAG "Magic" #define MAGIC_CMD_WUPA (0x40) -#define MAGIC_CMD_WIPE (0x41) #define MAGIC_CMD_ACCESS (0x43) #define MAGIC_MIFARE_READ_CMD (0x30) @@ -15,7 +14,7 @@ #define MAGIC_BUFFER_SIZE (32) -bool magic_wupa() { +bool magic_gen1_wupa() { bool magic_activated = false; uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; @@ -23,19 +22,6 @@ bool magic_wupa() { FuriHalNfcReturn ret = 0; do { - // Setup nfc poller - furi_hal_nfc_exit_sleep(); - furi_hal_nfc_ll_txrx_on(); - furi_hal_nfc_ll_poll(); - ret = furi_hal_nfc_ll_set_mode( - FuriHalNfcModePollNfca, FuriHalNfcBitrate106, FuriHalNfcBitrate106); - if(ret != FuriHalNfcReturnOk) break; - - furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER); - furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER); - furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); - furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCA); - // Start communication tx_data[0] = MAGIC_CMD_WUPA; ret = furi_hal_nfc_ll_txrx_bits( @@ -53,15 +39,10 @@ bool magic_wupa() { magic_activated = true; } while(false); - if(!magic_activated) { - furi_hal_nfc_ll_txrx_off(); - furi_hal_nfc_start_sleep(); - } - return magic_activated; } -bool magic_data_access_cmd() { +bool magic_gen1_data_access_cmd() { bool write_cmd_success = false; uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; @@ -86,15 +67,10 @@ bool magic_data_access_cmd() { write_cmd_success = true; } while(false); - if(!write_cmd_success) { - furi_hal_nfc_ll_txrx_off(); - furi_hal_nfc_start_sleep(); - } - return write_cmd_success; } -bool magic_read_block(uint8_t block_num, MfClassicBlock* data) { +bool magic_gen1_read_block(uint8_t block_num, MfClassicBlock* data) { furi_assert(data); bool read_success = false; @@ -122,15 +98,10 @@ bool magic_read_block(uint8_t block_num, MfClassicBlock* data) { read_success = true; } while(false); - if(!read_success) { - furi_hal_nfc_ll_txrx_off(); - furi_hal_nfc_start_sleep(); - } - return read_success; } -bool magic_write_blk(uint8_t block_num, MfClassicBlock* data) { +bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data) { furi_assert(data); bool write_success = false; @@ -170,44 +141,5 @@ bool magic_write_blk(uint8_t block_num, MfClassicBlock* data) { write_success = true; } while(false); - if(!write_success) { - furi_hal_nfc_ll_txrx_off(); - furi_hal_nfc_start_sleep(); - } - return write_success; } - -bool magic_wipe() { - bool wipe_success = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - tx_data[0] = MAGIC_CMD_WIPE; - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 8, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, - furi_hal_nfc_ll_ms2fc(2000)); - - if(ret != FuriHalNfcReturnIncompleteByte) break; - if(rx_len != 4) break; - if(rx_data[0] != MAGIC_ACK) break; - - wipe_success = true; - } while(false); - - return wipe_success; -} - -void magic_deactivate() { - furi_hal_nfc_ll_txrx_off(); - furi_hal_nfc_sleep(); -} diff --git a/applications/external/nfc_magic/lib/magic/classic_gen1.h b/applications/external/nfc_magic/lib/magic/classic_gen1.h new file mode 100644 index 000000000000..98de1230239b --- /dev/null +++ b/applications/external/nfc_magic/lib/magic/classic_gen1.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +bool magic_gen1_wupa(); + +bool magic_gen1_read_block(uint8_t block_num, MfClassicBlock* data); + +bool magic_gen1_data_access_cmd(); + +bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data); diff --git a/applications/external/nfc_magic/lib/magic/common.c b/applications/external/nfc_magic/lib/magic/common.c new file mode 100644 index 000000000000..0ea3cb218dd2 --- /dev/null +++ b/applications/external/nfc_magic/lib/magic/common.c @@ -0,0 +1,33 @@ +#include "common.h" + +#include + +#define REQA (0x26) +#define CL1_PREFIX (0x93) +#define SELECT (0x70) + +#define MAGIC_BUFFER_SIZE (32) + +bool magic_activate() { + FuriHalNfcReturn ret = 0; + + // Setup nfc poller + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_ll_txrx_on(); + furi_hal_nfc_ll_poll(); + ret = furi_hal_nfc_ll_set_mode( + FuriHalNfcModePollNfca, FuriHalNfcBitrate106, FuriHalNfcBitrate106); + if(ret != FuriHalNfcReturnOk) return false; + + furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER); + furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER); + furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); + furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCA); + + return true; +} + +void magic_deactivate() { + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_sleep(); +} \ No newline at end of file diff --git a/applications/external/nfc_magic/lib/magic/common.h b/applications/external/nfc_magic/lib/magic/common.h new file mode 100644 index 000000000000..bef166c8f220 --- /dev/null +++ b/applications/external/nfc_magic/lib/magic/common.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +typedef enum { + MagicTypeClassicGen1, + MagicTypeClassicDirectWrite, + MagicTypeClassicAPDU, + MagicTypeUltralightGen1, + MagicTypeUltralightDirectWrite, + MagicTypeUltralightC_Gen1, + MagicTypeUltralightC_DirectWrite, + MagicTypeGen4, +} MagicType; + +bool magic_activate(); + +void magic_deactivate(); \ No newline at end of file diff --git a/applications/external/nfc_magic/lib/magic/gen4.c b/applications/external/nfc_magic/lib/magic/gen4.c new file mode 100644 index 000000000000..31be649a04ec --- /dev/null +++ b/applications/external/nfc_magic/lib/magic/gen4.c @@ -0,0 +1,199 @@ +#include "gen4.h" + +#include +#include + +#define TAG "Magic" + +#define MAGIC_CMD_PREFIX (0xCF) + +#define MAGIC_CMD_GET_CFG (0xC6) +#define MAGIC_CMD_WRITE (0xCD) +#define MAGIC_CMD_READ (0xCE) +#define MAGIC_CMD_SET_CFG (0xF0) +#define MAGIC_CMD_FUSE_CFG (0xF1) +#define MAGIC_CMD_SET_PWD (0xFE) + +#define MAGIC_BUFFER_SIZE (40) + +const uint8_t MAGIC_DEFAULT_CONFIG[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x78, 0x00, 0x91, 0x02, 0xDA, 0xBC, 0x19, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x04, 0x00, 0x08, 0x00 +}; + +const uint8_t MAGIC_DEFAULT_BLOCK0[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const uint8_t MAGIC_EMPTY_BLOCK[16] = { 0 }; + +const uint8_t MAGIC_DEFAULT_SECTOR_TRAILER[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static bool magic_gen4_is_block_num_trailer(uint8_t n) { + n++; + if (n < 32 * 4) { + return (n % 4 == 0); + } + + return (n % 16 == 0); +} + +bool magic_gen4_get_cfg(uint32_t pwd, uint8_t* config) { + bool is_valid_config_len = false; + uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; + uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; + uint16_t rx_len = 0; + FuriHalNfcReturn ret = 0; + + do { + // Start communication + tx_data[0] = MAGIC_CMD_PREFIX; + tx_data[1] = (uint8_t)(pwd >> 24); + tx_data[2] = (uint8_t)(pwd >> 16); + tx_data[3] = (uint8_t)(pwd >> 8); + tx_data[4] = (uint8_t)pwd; + tx_data[5] = MAGIC_CMD_GET_CFG; + ret = furi_hal_nfc_ll_txrx( + tx_data, + 6, + rx_data, + sizeof(rx_data), + &rx_len, + FURI_HAL_NFC_TXRX_DEFAULT, + furi_hal_nfc_ll_ms2fc(20)); + if(ret != FuriHalNfcReturnOk) break; + if(rx_len != 30 && rx_len != 32) break; + memcpy(config, rx_data, rx_len); + is_valid_config_len = true; + } while(false); + + return is_valid_config_len; +} + +bool magic_gen4_set_cfg(uint32_t pwd, const uint8_t* config, uint8_t config_length, bool fuse) { + bool write_success = false; + uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; + uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; + uint16_t rx_len = 0; + FuriHalNfcReturn ret = 0; + + do { + // Start communication + tx_data[0] = MAGIC_CMD_PREFIX; + tx_data[1] = (uint8_t)(pwd >> 24); + tx_data[2] = (uint8_t)(pwd >> 16); + tx_data[3] = (uint8_t)(pwd >> 8); + tx_data[4] = (uint8_t)pwd; + tx_data[5] = fuse ? MAGIC_CMD_FUSE_CFG : MAGIC_CMD_SET_CFG; + memcpy(tx_data + 6, config, config_length); + ret = furi_hal_nfc_ll_txrx( + tx_data, + 6 + config_length, + rx_data, + sizeof(rx_data), + &rx_len, + FURI_HAL_NFC_TXRX_DEFAULT, + furi_hal_nfc_ll_ms2fc(20)); + if(ret != FuriHalNfcReturnOk) break; + if(rx_len != 2) break; + write_success = true; + } while(false); + + return write_success; +} + +bool magic_gen4_set_pwd(uint32_t old_pwd, uint32_t new_pwd) { + bool change_success = false; + uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; + uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; + uint16_t rx_len = 0; + FuriHalNfcReturn ret = 0; + + do { + // Start communication + tx_data[0] = MAGIC_CMD_PREFIX; + tx_data[1] = (uint8_t)(old_pwd >> 24); + tx_data[2] = (uint8_t)(old_pwd >> 16); + tx_data[3] = (uint8_t)(old_pwd >> 8); + tx_data[4] = (uint8_t)old_pwd; + tx_data[5] = MAGIC_CMD_SET_PWD; + tx_data[6] = (uint8_t)(new_pwd >> 24); + tx_data[7] = (uint8_t)(new_pwd >> 16); + tx_data[8] = (uint8_t)(new_pwd >> 8); + tx_data[9] = (uint8_t)new_pwd; + ret = furi_hal_nfc_ll_txrx( + tx_data, + 10, + rx_data, + sizeof(rx_data), + &rx_len, + FURI_HAL_NFC_TXRX_DEFAULT, + furi_hal_nfc_ll_ms2fc(20)); + FURI_LOG_I(TAG, "ret %d, len %d", ret, rx_len); + if(ret != FuriHalNfcReturnOk) break; + if(rx_len != 2) break; + change_success = true; + } while(false); + + return change_success; +} + +bool magic_gen4_write_blk(uint32_t pwd, uint8_t block_num, const uint8_t* data) { + bool write_success = false; + uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; + uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; + uint16_t rx_len = 0; + FuriHalNfcReturn ret = 0; + + do { + // Start communication + tx_data[0] = MAGIC_CMD_PREFIX; + tx_data[1] = (uint8_t)(pwd >> 24); + tx_data[2] = (uint8_t)(pwd >> 16); + tx_data[3] = (uint8_t)(pwd >> 8); + tx_data[4] = (uint8_t)pwd; + tx_data[5] = MAGIC_CMD_WRITE; + tx_data[6] = block_num; + memcpy(tx_data + 7, data, 16); + ret = furi_hal_nfc_ll_txrx( + tx_data, + 23, + rx_data, + sizeof(rx_data), + &rx_len, + FURI_HAL_NFC_TXRX_DEFAULT, + furi_hal_nfc_ll_ms2fc(200)); + if(ret != FuriHalNfcReturnOk) break; + if(rx_len != 2) break; + write_success = true; + } while(false); + + return write_success; +} + +bool magic_gen4_wipe(uint32_t pwd) { + if(!magic_gen4_set_cfg(pwd, MAGIC_DEFAULT_CONFIG, sizeof(MAGIC_DEFAULT_CONFIG), false)) { + FURI_LOG_E(TAG, "Set config failed"); + return false; + } + if(!magic_gen4_write_blk(pwd, 0, MAGIC_DEFAULT_BLOCK0)) { + FURI_LOG_E(TAG, "Block 0 write failed"); + return false; + } + for(size_t i = 1; i < 64; i++) { + const uint8_t* block = magic_gen4_is_block_num_trailer(i) ? MAGIC_DEFAULT_SECTOR_TRAILER : MAGIC_EMPTY_BLOCK; + if(!magic_gen4_write_blk(pwd, i, block)) { + FURI_LOG_E(TAG, "Block %d write failed", i); + return false; + } + } + for(size_t i = 65; i < 256; i++) { + if(!magic_gen4_write_blk(pwd, i, MAGIC_EMPTY_BLOCK)) { + FURI_LOG_E(TAG, "Block %d write failed", i); + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/applications/external/nfc_magic/lib/magic/gen4.h b/applications/external/nfc_magic/lib/magic/gen4.h new file mode 100644 index 000000000000..c515af820b00 --- /dev/null +++ b/applications/external/nfc_magic/lib/magic/gen4.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#define MAGIC_GEN4_DEFAULT_PWD 0x00000000 +#define MAGIC_GEN4_CONFIG_LEN 32 + +#define NFCID1_SINGLE_SIZE 4 +#define NFCID1_DOUBLE_SIZE 7 +#define NFCID1_TRIPLE_SIZE 10 + +typedef enum { + MagicGen4UIDLengthSingle = 0x00, + MagicGen4UIDLengthDouble = 0x01, + MagicGen4UIDLengthTriple = 0x02 +} MagicGen4UIDLength; + +typedef enum { + MagicGen4UltralightModeUL_EV1 = 0x00, + MagicGen4UltralightModeNTAG = 0x01, + MagicGen4UltralightModeUL_C = 0x02, + MagicGen4UltralightModeUL = 0x03 +} MagicGen4UltralightMode; + +typedef enum { + // for writing original (shadow) data + MagicGen4ShadowModePreWrite = 0x00, + // written data can be read once before restored to original + MagicGen4ShadowModeRestore = 0x01, + // written data is discarded + MagicGen4ShadowModeIgnore = 0x02, + // apparently for UL? + MagicGen4ShadowModeHighSpeedIgnore = 0x03 +} MagicGen4ShadowMode; + +bool magic_gen4_get_cfg(uint32_t pwd, uint8_t* config); + +bool magic_gen4_set_cfg(uint32_t pwd, const uint8_t* config, uint8_t config_length, bool fuse); + +bool magic_gen4_set_pwd(uint32_t old_pwd, uint32_t new_pwd); + +bool magic_gen4_read_blk(uint32_t pwd, uint8_t block_num, uint8_t* data); + +bool magic_gen4_write_blk(uint32_t pwd, uint8_t block_num, const uint8_t* data); + +bool magic_gen4_wipe(uint32_t pwd); + +void magic_gen4_deactivate(); diff --git a/applications/external/nfc_magic/lib/magic/magic.h b/applications/external/nfc_magic/lib/magic/magic.h deleted file mode 100644 index 64c60a0a705a..000000000000 --- a/applications/external/nfc_magic/lib/magic/magic.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -bool magic_wupa(); - -bool magic_read_block(uint8_t block_num, MfClassicBlock* data); - -bool magic_data_access_cmd(); - -bool magic_write_blk(uint8_t block_num, MfClassicBlock* data); - -bool magic_wipe(); - -void magic_deactivate(); diff --git a/applications/external/nfc_magic/lib/magic/types.c b/applications/external/nfc_magic/lib/magic/types.c new file mode 100644 index 000000000000..77c6c0a4eff1 --- /dev/null +++ b/applications/external/nfc_magic/lib/magic/types.c @@ -0,0 +1,23 @@ +#include "types.h" + +const char* nfc_magic_type(MagicType type) { + if(type == MagicTypeClassicGen1) { + return "Classic Gen 1A/B"; + } else if(type == MagicTypeClassicDirectWrite) { + return "Classic DirectWrite"; + } else if(type == MagicTypeClassicAPDU) { + return "Classic APDU"; + } else if(type == MagicTypeUltralightGen1) { + return "Ultralight Gen 1"; + } else if(type == MagicTypeUltralightDirectWrite) { + return "Ultralight DirectWrite"; + } else if(type == MagicTypeUltralightC_Gen1) { + return "Ultralight-C Gen 1"; + } else if(type == MagicTypeUltralightC_DirectWrite) { + return "Ultralight-C DirectWrite"; + } else if(type == MagicTypeGen4) { + return "Gen 4 GTU"; + } else { + return "Unknown"; + } +} diff --git a/applications/external/nfc_magic/lib/magic/types.h b/applications/external/nfc_magic/lib/magic/types.h new file mode 100644 index 000000000000..dbf5540637c2 --- /dev/null +++ b/applications/external/nfc_magic/lib/magic/types.h @@ -0,0 +1,5 @@ +#pragma once + +#include "common.h" + +const char* nfc_magic_type(MagicType type); \ No newline at end of file diff --git a/applications/external/nfc_magic/nfc_magic.c b/applications/external/nfc_magic/nfc_magic.c index 1805f35ed052..68c9a65b5fd5 100644 --- a/applications/external/nfc_magic/nfc_magic.c +++ b/applications/external/nfc_magic/nfc_magic.c @@ -48,8 +48,9 @@ NfcMagic* nfc_magic_alloc() { nfc_magic->view_dispatcher, nfc_magic_tick_event_callback, 100); // Nfc device - nfc_magic->nfc_dev = nfc_device_alloc(); - furi_string_set(nfc_magic->nfc_dev->folder, NFC_APP_FOLDER); + nfc_magic->dev = malloc(sizeof(NfcMagicDevice)); + nfc_magic->source_dev = nfc_device_alloc(); + furi_string_set(nfc_magic->source_dev->folder, NFC_APP_FOLDER); // Open GUI record nfc_magic->gui = furi_record_open(RECORD_GUI); @@ -81,6 +82,13 @@ NfcMagic* nfc_magic_alloc() { NfcMagicViewTextInput, text_input_get_view(nfc_magic->text_input)); + // Byte Input + nfc_magic->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + nfc_magic->view_dispatcher, + NfcMagicViewByteInput, + byte_input_get_view(nfc_magic->byte_input)); + // Custom Widget nfc_magic->widget = widget_alloc(); view_dispatcher_add_view( @@ -93,7 +101,8 @@ void nfc_magic_free(NfcMagic* nfc_magic) { furi_assert(nfc_magic); // Nfc device - nfc_device_free(nfc_magic->nfc_dev); + free(nfc_magic->dev); + nfc_device_free(nfc_magic->source_dev); // Submenu view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewMenu); @@ -107,10 +116,14 @@ void nfc_magic_free(NfcMagic* nfc_magic) { view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewLoading); loading_free(nfc_magic->loading); - // TextInput + // Text Input view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewTextInput); text_input_free(nfc_magic->text_input); + // Byte Input + view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput); + byte_input_free(nfc_magic->byte_input); + // Custom Widget view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); widget_free(nfc_magic->widget); @@ -164,6 +177,7 @@ int32_t nfc_magic_app(void* p) { view_dispatcher_run(nfc_magic->view_dispatcher); + magic_deactivate(); nfc_magic_free(nfc_magic); return 0; diff --git a/applications/external/nfc_magic/nfc_magic.h b/applications/external/nfc_magic/nfc_magic.h index 1abf1371ed07..f9cf395d8265 100644 --- a/applications/external/nfc_magic/nfc_magic.h +++ b/applications/external/nfc_magic/nfc_magic.h @@ -1,3 +1,5 @@ #pragma once +typedef struct NfcMagicDevice NfcMagicDevice; + typedef struct NfcMagic NfcMagic; diff --git a/applications/external/nfc_magic/nfc_magic_i.h b/applications/external/nfc_magic/nfc_magic_i.h index 378912e5b09f..88bc5706fa86 100644 --- a/applications/external/nfc_magic/nfc_magic_i.h +++ b/applications/external/nfc_magic/nfc_magic_i.h @@ -3,7 +3,10 @@ #include "nfc_magic.h" #include "nfc_magic_worker.h" -#include "lib/magic/magic.h" +#include "lib/magic/common.h" +#include "lib/magic/types.h" +#include "lib/magic/classic_gen1.h" +#include "lib/magic/gen4.h" #include #include @@ -15,6 +18,7 @@ #include #include #include +#include #include #include @@ -39,14 +43,23 @@ enum NfcMagicCustomEvent { NfcMagicCustomEventTextInputDone, }; +struct NfcMagicDevice { + MagicType type; + uint32_t cuid; + uint8_t uid_len; + uint32_t password; +}; + struct NfcMagic { NfcMagicWorker* worker; ViewDispatcher* view_dispatcher; Gui* gui; NotificationApp* notifications; SceneManager* scene_manager; - // NfcMagicDevice* dev; - NfcDevice* nfc_dev; + struct NfcMagicDevice* dev; + NfcDevice* source_dev; + + uint32_t new_password; FuriString* text_box_store; @@ -55,6 +68,7 @@ struct NfcMagic { Popup* popup; Loading* loading; TextInput* text_input; + ByteInput* byte_input; Widget* widget; }; @@ -63,6 +77,7 @@ typedef enum { NfcMagicViewPopup, NfcMagicViewLoading, NfcMagicViewTextInput, + NfcMagicViewByteInput, NfcMagicViewWidget, } NfcMagicView; diff --git a/applications/external/nfc_magic/nfc_magic_worker.c b/applications/external/nfc_magic/nfc_magic_worker.c index 92eb793a71d8..eb715fe0ddaa 100644 --- a/applications/external/nfc_magic/nfc_magic_worker.c +++ b/applications/external/nfc_magic/nfc_magic_worker.c @@ -1,6 +1,9 @@ #include "nfc_magic_worker_i.h" -#include "lib/magic/magic.h" +#include "nfc_magic_i.h" +#include "lib/magic/common.h" +#include "lib/magic/classic_gen1.h" +#include "lib/magic/gen4.h" #define TAG "NfcMagicWorker" @@ -43,15 +46,20 @@ void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker) { void nfc_magic_worker_start( NfcMagicWorker* nfc_magic_worker, NfcMagicWorkerState state, + NfcMagicDevice* magic_dev, NfcDeviceData* dev_data, + uint32_t new_password, NfcMagicWorkerCallback callback, void* context) { furi_assert(nfc_magic_worker); + furi_assert(magic_dev); furi_assert(dev_data); nfc_magic_worker->callback = callback; nfc_magic_worker->context = context; + nfc_magic_worker->magic_dev = magic_dev; nfc_magic_worker->dev_data = dev_data; + nfc_magic_worker->new_password = new_password; nfc_magic_worker_change_state(nfc_magic_worker, state); furi_thread_start(nfc_magic_worker->thread); } @@ -63,6 +71,8 @@ int32_t nfc_magic_worker_task(void* context) { nfc_magic_worker_check(nfc_magic_worker); } else if(nfc_magic_worker->state == NfcMagicWorkerStateWrite) { nfc_magic_worker_write(nfc_magic_worker); + } else if(nfc_magic_worker->state == NfcMagicWorkerStateRekey) { + nfc_magic_worker_rekey(nfc_magic_worker); } else if(nfc_magic_worker->state == NfcMagicWorkerStateWipe) { nfc_magic_worker_wipe(nfc_magic_worker); } @@ -74,73 +84,308 @@ int32_t nfc_magic_worker_task(void* context) { void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { bool card_found_notified = false; + bool done = false; FuriHalNfcDevData nfc_data = {}; - MfClassicData* src_data = &nfc_magic_worker->dev_data->mf_classic_data; + NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev; + NfcDeviceData* dev_data = nfc_magic_worker->dev_data; + NfcProtocol dev_protocol = dev_data->protocol; while(nfc_magic_worker->state == NfcMagicWorkerStateWrite) { - if(furi_hal_nfc_detect(&nfc_data, 200)) { - if(!card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); - card_found_notified = true; - } - furi_hal_nfc_sleep(); - if(!magic_wupa()) { - FURI_LOG_E(TAG, "No card response to WUPA (not a magic card)"); - nfc_magic_worker->callback( - NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); - break; - } - furi_hal_nfc_sleep(); - } - if(magic_wupa()) { - if(!magic_data_access_cmd()) { - FURI_LOG_E(TAG, "No card response to data access command (not a magic card)"); - nfc_magic_worker->callback( - NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); - break; - } - for(size_t i = 0; i < 64; i++) { - FURI_LOG_D(TAG, "Writing block %d", i); - if(!magic_write_blk(i, &src_data->block[i])) { - FURI_LOG_E(TAG, "Failed to write %d block", i); - nfc_magic_worker->callback(NfcMagicWorkerEventFail, nfc_magic_worker->context); + do { + if(magic_dev->type == MagicTypeClassicGen1) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + magic_deactivate(); + magic_activate(); + if(!magic_gen1_wupa()) { + FURI_LOG_E(TAG, "No card response to WUPA (not a magic card)"); + nfc_magic_worker->callback( + NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); + done = true; + break; + } + magic_deactivate(); + } + magic_activate(); + if(magic_gen1_wupa()) { + magic_gen1_data_access_cmd(); + + MfClassicData* mfc_data = &dev_data->mf_classic_data; + for(size_t i = 0; i < 64; i++) { + FURI_LOG_D(TAG, "Writing block %d", i); + if(!magic_gen1_write_blk(i, &mfc_data->block[i])) { + FURI_LOG_E(TAG, "Failed to write %d block", i); + done = true; + nfc_magic_worker->callback( + NfcMagicWorkerEventFail, nfc_magic_worker->context); + break; + } + } + + done = true; + nfc_magic_worker->callback( + NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + break; + } + } else if(magic_dev->type == MagicTypeGen4) { + if(furi_hal_nfc_detect(&nfc_data, 200)) { + uint8_t gen4_config[28]; + uint32_t password = magic_dev->password; + + uint32_t cuid; + if(dev_protocol == NfcDeviceProtocolMifareClassic) { + gen4_config[0] = 0x00; + gen4_config[27] = 0x00; + } else if(dev_protocol == NfcDeviceProtocolMifareUl) { + MfUltralightData* mf_ul_data = &dev_data->mf_ul_data; + gen4_config[0] = 0x01; + switch(mf_ul_data->type) { + case MfUltralightTypeUL11: + case MfUltralightTypeUL21: + // UL-C? + // UL? + default: + gen4_config[27] = MagicGen4UltralightModeUL_EV1; + break; + case MfUltralightTypeNTAG203: + case MfUltralightTypeNTAG213: + case MfUltralightTypeNTAG215: + case MfUltralightTypeNTAG216: + case MfUltralightTypeNTAGI2C1K: + case MfUltralightTypeNTAGI2C2K: + case MfUltralightTypeNTAGI2CPlus1K: + case MfUltralightTypeNTAGI2CPlus2K: + gen4_config[27] = MagicGen4UltralightModeNTAG; + break; + } + } + + if(dev_data->nfc_data.uid_len == 4) { + gen4_config[1] = MagicGen4UIDLengthSingle; + } else if(dev_data->nfc_data.uid_len == 7) { + gen4_config[1] = MagicGen4UIDLengthDouble; + } else { + FURI_LOG_E(TAG, "Unexpected UID length %d", dev_data->nfc_data.uid_len); + nfc_magic_worker->callback( + NfcMagicWorkerEventFail, nfc_magic_worker->context); + done = true; + break; + } + + gen4_config[2] = (uint8_t)(password >> 24); + gen4_config[3] = (uint8_t)(password >> 16); + gen4_config[4] = (uint8_t)(password >> 8); + gen4_config[5] = (uint8_t)password; + + if(dev_protocol == NfcDeviceProtocolMifareUl) { + gen4_config[6] = MagicGen4ShadowModeHighSpeedIgnore; + } else { + gen4_config[6] = MagicGen4ShadowModeIgnore; + } + gen4_config[7] = 0x00; + memset(gen4_config + 8, 0, 16); + gen4_config[24] = dev_data->nfc_data.atqa[0]; + gen4_config[25] = dev_data->nfc_data.atqa[1]; + gen4_config[26] = dev_data->nfc_data.sak; + + furi_hal_nfc_sleep(); + furi_hal_nfc_activate_nfca(200, &cuid); + if(!magic_gen4_set_cfg(password, gen4_config, sizeof(gen4_config), false)) { + nfc_magic_worker->callback( + NfcMagicWorkerEventFail, nfc_magic_worker->context); + done = true; + break; + } + if(dev_protocol == NfcDeviceProtocolMifareClassic) { + MfClassicData* mfc_data = &dev_data->mf_classic_data; + size_t block_count = 64; + if(mfc_data->type == MfClassicType4k) block_count = 256; + for(size_t i = 0; i < block_count; i++) { + FURI_LOG_D(TAG, "Writing block %d", i); + if(!magic_gen4_write_blk(password, i, mfc_data->block[i].value)) { + FURI_LOG_E(TAG, "Failed to write %d block", i); + nfc_magic_worker->callback( + NfcMagicWorkerEventFail, nfc_magic_worker->context); + done = true; + break; + } + } + } else if(dev_protocol == NfcDeviceProtocolMifareUl) { + MfUltralightData* mf_ul_data = &dev_data->mf_ul_data; + for(size_t i = 0; (i * 4) < mf_ul_data->data_read; i++) { + size_t data_offset = i * 4; + FURI_LOG_D( + TAG, + "Writing page %zu (%zu/%u)", + i, + data_offset, + mf_ul_data->data_read); + uint8_t* block = mf_ul_data->data + data_offset; + if(!magic_gen4_write_blk(password, i, block)) { + FURI_LOG_E(TAG, "Failed to write %zu page", i); + nfc_magic_worker->callback( + NfcMagicWorkerEventFail, nfc_magic_worker->context); + done = true; + break; + } + } + + uint8_t buffer[16] = {0}; + + for(size_t i = 0; i < 8; i++) { + memcpy(buffer, &mf_ul_data->signature[i * 4], 4); //-V1086 + if(!magic_gen4_write_blk(password, 0xF2 + i, buffer)) { + FURI_LOG_E(TAG, "Failed to write signature block %d", i); + nfc_magic_worker->callback( + NfcMagicWorkerEventFail, nfc_magic_worker->context); + done = true; + break; + } + } + + buffer[0] = mf_ul_data->version.header; + buffer[1] = mf_ul_data->version.vendor_id; + buffer[2] = mf_ul_data->version.prod_type; + buffer[3] = mf_ul_data->version.prod_subtype; + if(!magic_gen4_write_blk(password, 0xFA, buffer)) { + FURI_LOG_E(TAG, "Failed to write version block 0"); + nfc_magic_worker->callback( + NfcMagicWorkerEventFail, nfc_magic_worker->context); + done = true; + break; + } + + buffer[0] = mf_ul_data->version.prod_ver_major; + buffer[1] = mf_ul_data->version.prod_ver_minor; + buffer[2] = mf_ul_data->version.storage_size; + buffer[3] = mf_ul_data->version.protocol_type; + if(!magic_gen4_write_blk(password, 0xFB, buffer)) { + FURI_LOG_E(TAG, "Failed to write version block 1"); + nfc_magic_worker->callback( + NfcMagicWorkerEventFail, nfc_magic_worker->context); + done = true; + break; + } + } + + nfc_magic_worker->callback( + NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + done = true; break; } } - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - break; - } else { - if(card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); - card_found_notified = false; - } + } while(false); + + if(done) break; + + if(card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); + card_found_notified = false; } + furi_delay_ms(300); } magic_deactivate(); } void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) { + FuriHalNfcDevData nfc_data = {}; + NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev; bool card_found_notified = false; + uint8_t gen4_config[MAGIC_GEN4_CONFIG_LEN]; while(nfc_magic_worker->state == NfcMagicWorkerStateCheck) { - if(magic_wupa()) { + magic_activate(); + if(magic_gen1_wupa()) { + magic_dev->type = MagicTypeClassicGen1; if(!card_found_notified) { nfc_magic_worker->callback( NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); card_found_notified = true; } + if(furi_hal_nfc_detect(&nfc_data, 200)) { + magic_dev->cuid = nfc_data.cuid; + magic_dev->uid_len = nfc_data.uid_len; + } else { + // wrong BCC + magic_dev->uid_len = 4; + } nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); break; } else { + magic_deactivate(); + magic_activate(); + if(furi_hal_nfc_detect(&nfc_data, 200)) { + magic_dev->cuid = nfc_data.cuid; + magic_dev->uid_len = nfc_data.uid_len; + if(magic_gen4_get_cfg(magic_dev->password, gen4_config)) { + magic_dev->type = MagicTypeGen4; + if(!card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); + card_found_notified = true; + } + + nfc_magic_worker->callback( + NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + } else { + nfc_magic_worker->callback( + NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); + card_found_notified = true; + } + break; + } else { + if(card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); + card_found_notified = false; + } + } + } + + magic_deactivate(); + furi_delay_ms(300); + } + + magic_deactivate(); +} + +void nfc_magic_worker_rekey(NfcMagicWorker* nfc_magic_worker) { + NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev; + bool card_found_notified = false; + + if(magic_dev->type != MagicTypeGen4) { + nfc_magic_worker->callback(NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); + return; + } + + while(nfc_magic_worker->state == NfcMagicWorkerStateRekey) { + magic_activate(); + uint32_t cuid; + furi_hal_nfc_activate_nfca(200, &cuid); + if(cuid != magic_dev->cuid) { if(card_found_notified) { nfc_magic_worker->callback( NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); card_found_notified = false; } + continue; + } + + nfc_magic_worker->callback(NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); + card_found_notified = true; + + if(magic_gen4_set_pwd(magic_dev->password, nfc_magic_worker->new_password)) { + magic_dev->password = nfc_magic_worker->new_password; + nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + break; + } + + if(card_found_notified) { //-V547 + nfc_magic_worker->callback( + NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); + card_found_notified = false; } furi_delay_ms(300); } @@ -148,8 +393,17 @@ void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) { } void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) { + NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev; + bool card_found_notified = false; + bool card_wiped = false; + MfClassicBlock block; memset(&block, 0, sizeof(MfClassicBlock)); + MfClassicBlock empty_block; + memset(&empty_block, 0, sizeof(MfClassicBlock)); + MfClassicBlock trailer_block; + memset(&trailer_block, 0xff, sizeof(MfClassicBlock)); + block.value[0] = 0x01; block.value[1] = 0x02; block.value[2] = 0x03; @@ -158,15 +412,69 @@ void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) { block.value[5] = 0x08; block.value[6] = 0x04; + trailer_block.value[7] = 0x07; + trailer_block.value[8] = 0x80; + trailer_block.value[9] = 0x69; + while(nfc_magic_worker->state == NfcMagicWorkerStateWipe) { - magic_deactivate(); - furi_delay_ms(300); - if(!magic_wupa()) continue; - if(!magic_wipe()) continue; - if(!magic_data_access_cmd()) continue; - if(!magic_write_blk(0, &block)) continue; - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - break; + do { + magic_deactivate(); + furi_delay_ms(300); + if(!magic_activate()) break; + if(magic_dev->type == MagicTypeClassicGen1) { + if(!magic_gen1_wupa()) break; + if(!card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); + card_found_notified = true; + } + + if(!magic_gen1_data_access_cmd()) break; + if(!magic_gen1_write_blk(0, &block)) break; + + for(size_t i = 1; i < 64; i++) { + FURI_LOG_D(TAG, "Wiping block %d", i); + bool success = false; + if((i | 0x03) == i) { + success = magic_gen1_write_blk(i, &trailer_block); + } else { + success = magic_gen1_write_blk(i, &empty_block); + } + + if(!success) { + FURI_LOG_E(TAG, "Failed to write %d block", i); + nfc_magic_worker->callback( + NfcMagicWorkerEventFail, nfc_magic_worker->context); + break; + } + } + + card_wiped = true; + nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + } else if(magic_dev->type == MagicTypeGen4) { + uint32_t cuid; + if(!furi_hal_nfc_activate_nfca(200, &cuid)) break; + if(cuid != magic_dev->cuid) break; + if(!card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); + card_found_notified = true; + } + + if(!magic_gen4_wipe(magic_dev->password)) break; + + card_wiped = true; + nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + } + } while(false); + + if(card_wiped) break; + + if(card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); + card_found_notified = false; + } } magic_deactivate(); } diff --git a/applications/external/nfc_magic/nfc_magic_worker.h b/applications/external/nfc_magic/nfc_magic_worker.h index 9d29bb3a8be9..51ff4ee438d4 100644 --- a/applications/external/nfc_magic/nfc_magic_worker.h +++ b/applications/external/nfc_magic/nfc_magic_worker.h @@ -1,6 +1,7 @@ #pragma once #include +#include "nfc_magic.h" typedef struct NfcMagicWorker NfcMagicWorker; @@ -9,6 +10,7 @@ typedef enum { NfcMagicWorkerStateCheck, NfcMagicWorkerStateWrite, + NfcMagicWorkerStateRekey, NfcMagicWorkerStateWipe, NfcMagicWorkerStateStop, @@ -33,6 +35,8 @@ void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker); void nfc_magic_worker_start( NfcMagicWorker* nfc_magic_worker, NfcMagicWorkerState state, + NfcMagicDevice* magic_dev, NfcDeviceData* dev_data, + uint32_t new_password, NfcMagicWorkerCallback callback, void* context); diff --git a/applications/external/nfc_magic/nfc_magic_worker_i.h b/applications/external/nfc_magic/nfc_magic_worker_i.h index 0cde2e7125bf..a354f8047634 100644 --- a/applications/external/nfc_magic/nfc_magic_worker_i.h +++ b/applications/external/nfc_magic/nfc_magic_worker_i.h @@ -3,11 +3,14 @@ #include #include "nfc_magic_worker.h" +#include "lib/magic/common.h" struct NfcMagicWorker { FuriThread* thread; + NfcMagicDevice* magic_dev; NfcDeviceData* dev_data; + uint32_t new_password; NfcMagicWorkerCallback callback; void* context; @@ -21,4 +24,6 @@ void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker); void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker); +void nfc_magic_worker_rekey(NfcMagicWorker* nfc_magic_worker); + void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker); diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_actions.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_actions.c new file mode 100644 index 000000000000..675262a9b2ff --- /dev/null +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_actions.c @@ -0,0 +1,50 @@ +#include "../nfc_magic_i.h" +enum SubmenuIndex { + SubmenuIndexWrite, + SubmenuIndexWipe, +}; + +void nfc_magic_scene_actions_submenu_callback(void* context, uint32_t index) { + NfcMagic* nfc_magic = context; + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, index); +} + +void nfc_magic_scene_actions_on_enter(void* context) { + NfcMagic* nfc_magic = context; + + Submenu* submenu = nfc_magic->submenu; + submenu_add_item( + submenu, "Write", SubmenuIndexWrite, nfc_magic_scene_actions_submenu_callback, nfc_magic); + submenu_add_item( + submenu, "Wipe", SubmenuIndexWipe, nfc_magic_scene_actions_submenu_callback, nfc_magic); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneActions)); + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu); +} + +bool nfc_magic_scene_actions_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect); + consumed = true; + } else if(event.event == SubmenuIndexWipe) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe); + consumed = true; + } + scene_manager_set_scene_state(nfc_magic->scene_manager, NfcMagicSceneActions, event.event); + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc_magic->scene_manager, NfcMagicSceneStart); + } + + return consumed; +} + +void nfc_magic_scene_actions_on_exit(void* context) { + NfcMagic* nfc_magic = context; + submenu_reset(nfc_magic->submenu); +} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_check.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_check.c index d51797242832..90b43d7d3a2e 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_check.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_check.c @@ -42,7 +42,9 @@ void nfc_magic_scene_check_on_enter(void* context) { nfc_magic_worker_start( nfc_magic->worker, NfcMagicWorkerStateCheck, - &nfc_magic->nfc_dev->dev_data, + nfc_magic->dev, + &nfc_magic->source_dev->dev_data, + nfc_magic->new_password, nfc_magic_check_worker_callback, nfc_magic); nfc_magic_blink_start(nfc_magic); diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_config.h b/applications/external/nfc_magic/scenes/nfc_magic_scene_config.h index 557e26914e61..2f9860d96f2a 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_config.h +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_config.h @@ -1,4 +1,8 @@ ADD_SCENE(nfc_magic, start, Start) +ADD_SCENE(nfc_magic, key_input, KeyInput) +ADD_SCENE(nfc_magic, actions, Actions) +ADD_SCENE(nfc_magic, gen4_actions, Gen4Actions) +ADD_SCENE(nfc_magic, new_key_input, NewKeyInput) ADD_SCENE(nfc_magic, file_select, FileSelect) ADD_SCENE(nfc_magic, write_confirm, WriteConfirm) ADD_SCENE(nfc_magic, wrong_card, WrongCard) @@ -8,5 +12,7 @@ ADD_SCENE(nfc_magic, success, Success) ADD_SCENE(nfc_magic, check, Check) ADD_SCENE(nfc_magic, not_magic, NotMagic) ADD_SCENE(nfc_magic, magic_info, MagicInfo) +ADD_SCENE(nfc_magic, rekey, Rekey) +ADD_SCENE(nfc_magic, rekey_fail, RekeyFail) ADD_SCENE(nfc_magic, wipe, Wipe) ADD_SCENE(nfc_magic, wipe_fail, WipeFail) diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c index d78422eeb663..04b7024ffbd5 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c @@ -1,22 +1,60 @@ #include "../nfc_magic_i.h" -static bool nfc_magic_scene_file_select_is_file_suitable(NfcDevice* nfc_dev) { - return (nfc_dev->format == NfcDeviceSaveFormatMifareClassic) && - (nfc_dev->dev_data.mf_classic_data.type == MfClassicType1k) && - (nfc_dev->dev_data.nfc_data.uid_len == 4); +static bool nfc_magic_scene_file_select_is_file_suitable(NfcMagic* nfc_magic) { + NfcDevice* nfc_dev = nfc_magic->source_dev; + if(nfc_dev->format == NfcDeviceSaveFormatMifareClassic) { + switch(nfc_magic->dev->type) { + case MagicTypeClassicGen1: + case MagicTypeClassicDirectWrite: + case MagicTypeClassicAPDU: + if((nfc_dev->dev_data.mf_classic_data.type != MfClassicType1k) || + (nfc_dev->dev_data.nfc_data.uid_len != nfc_magic->dev->uid_len)) { + return false; + } + return true; + + case MagicTypeGen4: + return true; + default: + return false; + } + } else if( + (nfc_dev->format == NfcDeviceSaveFormatMifareUl) && + (nfc_dev->dev_data.nfc_data.uid_len == 7)) { + switch(nfc_magic->dev->type) { + case MagicTypeUltralightGen1: + case MagicTypeUltralightDirectWrite: + case MagicTypeUltralightC_Gen1: + case MagicTypeUltralightC_DirectWrite: + case MagicTypeGen4: + switch(nfc_dev->dev_data.mf_ul_data.type) { + case MfUltralightTypeNTAGI2C1K: + case MfUltralightTypeNTAGI2C2K: + case MfUltralightTypeNTAGI2CPlus1K: + case MfUltralightTypeNTAGI2CPlus2K: + return false; + default: + return true; + } + default: + return false; + } + } + + return false; } void nfc_magic_scene_file_select_on_enter(void* context) { NfcMagic* nfc_magic = context; // Process file_select return - nfc_device_set_loading_callback(nfc_magic->nfc_dev, nfc_magic_show_loading_popup, nfc_magic); + nfc_device_set_loading_callback( + nfc_magic->source_dev, nfc_magic_show_loading_popup, nfc_magic); - if(!furi_string_size(nfc_magic->nfc_dev->load_path)) { - furi_string_set_str(nfc_magic->nfc_dev->load_path, NFC_APP_FOLDER); + if(!furi_string_size(nfc_magic->source_dev->load_path)) { + furi_string_set_str(nfc_magic->source_dev->load_path, NFC_APP_FOLDER); } - - if(nfc_file_select(nfc_magic->nfc_dev)) { - if(nfc_magic_scene_file_select_is_file_suitable(nfc_magic->nfc_dev)) { + if(nfc_file_select(nfc_magic->source_dev)) { + if(nfc_magic_scene_file_select_is_file_suitable(nfc_magic)) { scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWriteConfirm); } else { scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWrongCard); @@ -34,5 +72,5 @@ bool nfc_magic_scene_file_select_on_event(void* context, SceneManagerEvent event void nfc_magic_scene_file_select_on_exit(void* context) { NfcMagic* nfc_magic = context; - nfc_device_set_loading_callback(nfc_magic->nfc_dev, NULL, nfc_magic); + nfc_device_set_loading_callback(nfc_magic->source_dev, NULL, nfc_magic); } diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_gen4_actions.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_gen4_actions.c new file mode 100644 index 000000000000..ceaa33e29f00 --- /dev/null +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_gen4_actions.c @@ -0,0 +1,70 @@ +#include "../nfc_magic_i.h" +enum SubmenuIndex { + SubmenuIndexWrite, + SubmenuIndexChangePassword, + SubmenuIndexWipe, +}; + +void nfc_magic_scene_gen4_actions_submenu_callback(void* context, uint32_t index) { + NfcMagic* nfc_magic = context; + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, index); +} + +void nfc_magic_scene_gen4_actions_on_enter(void* context) { + NfcMagic* nfc_magic = context; + + Submenu* submenu = nfc_magic->submenu; + submenu_add_item( + submenu, + "Write", + SubmenuIndexWrite, + nfc_magic_scene_gen4_actions_submenu_callback, + nfc_magic); + submenu_add_item( + submenu, + "Change password", + SubmenuIndexChangePassword, + nfc_magic_scene_gen4_actions_submenu_callback, + nfc_magic); + submenu_add_item( + submenu, + "Wipe", + SubmenuIndexWipe, + nfc_magic_scene_gen4_actions_submenu_callback, + nfc_magic); + + submenu_set_selected_item( + submenu, + scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneGen4Actions)); + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu); +} + +bool nfc_magic_scene_gen4_actions_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect); + consumed = true; + } else if(event.event == SubmenuIndexChangePassword) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNewKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexWipe) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe); + consumed = true; + } + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneGen4Actions, event.event); + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc_magic->scene_manager, NfcMagicSceneStart); + } + + return consumed; +} + +void nfc_magic_scene_gen4_actions_on_exit(void* context) { + NfcMagic* nfc_magic = context; + submenu_reset(nfc_magic->submenu); +} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_key_input.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_key_input.c new file mode 100644 index 000000000000..58b487a09bbe --- /dev/null +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_key_input.c @@ -0,0 +1,45 @@ +#include "../nfc_magic_i.h" + +void nfc_magic_scene_key_input_byte_input_callback(void* context) { + NfcMagic* nfc_magic = context; + + view_dispatcher_send_custom_event( + nfc_magic->view_dispatcher, NfcMagicCustomEventByteInputDone); +} + +void nfc_magic_scene_key_input_on_enter(void* context) { + NfcMagic* nfc_magic = context; + + // Setup view + ByteInput* byte_input = nfc_magic->byte_input; + byte_input_set_header_text(byte_input, "Enter the password in hex"); + byte_input_set_result_callback( + byte_input, + nfc_magic_scene_key_input_byte_input_callback, + NULL, + nfc_magic, + (uint8_t*)&nfc_magic->dev->password, + 4); + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput); +} + +bool nfc_magic_scene_key_input_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcMagicCustomEventByteInputDone) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck); + consumed = true; + } + } + return consumed; +} + +void nfc_magic_scene_key_input_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + // Clear view + byte_input_set_result_callback(nfc_magic->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc_magic->byte_input, ""); +} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c index e9b226b3abb3..c147ac4383f6 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c @@ -1,4 +1,5 @@ #include "../nfc_magic_i.h" +#include "../lib/magic/types.h" void nfc_magic_scene_magic_info_widget_callback( GuiButtonType result, @@ -13,14 +14,18 @@ void nfc_magic_scene_magic_info_widget_callback( void nfc_magic_scene_magic_info_on_enter(void* context) { NfcMagic* nfc_magic = context; Widget* widget = nfc_magic->widget; + const char* card_type = nfc_magic_type(nfc_magic->dev->type); notification_message(nfc_magic->notifications, &sequence_success); widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); widget_add_string_element( widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Magic card detected"); + widget_add_string_element(widget, 3, 17, AlignLeft, AlignTop, FontSecondary, card_type); widget_add_button_element( widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_magic_info_widget_callback, nfc_magic); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_magic_scene_magic_info_widget_callback, nfc_magic); // Setup and start worker view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); @@ -33,6 +38,15 @@ bool nfc_magic_scene_magic_info_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeLeft) { consumed = scene_manager_previous_scene(nfc_magic->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + MagicType type = nfc_magic->dev->type; + if(type == MagicTypeGen4) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneGen4Actions); + consumed = true; + } else { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneActions); + consumed = true; + } } } return consumed; diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_new_key_input.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_new_key_input.c new file mode 100644 index 000000000000..b5247f6c555c --- /dev/null +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_new_key_input.c @@ -0,0 +1,45 @@ +#include "../nfc_magic_i.h" + +void nfc_magic_scene_new_key_input_byte_input_callback(void* context) { + NfcMagic* nfc_magic = context; + + view_dispatcher_send_custom_event( + nfc_magic->view_dispatcher, NfcMagicCustomEventByteInputDone); +} + +void nfc_magic_scene_new_key_input_on_enter(void* context) { + NfcMagic* nfc_magic = context; + + // Setup view + ByteInput* byte_input = nfc_magic->byte_input; + byte_input_set_header_text(byte_input, "Enter the password in hex"); + byte_input_set_result_callback( + byte_input, + nfc_magic_scene_new_key_input_byte_input_callback, + NULL, + nfc_magic, + (uint8_t*)&nfc_magic->new_password, + 4); + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput); +} + +bool nfc_magic_scene_new_key_input_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcMagicCustomEventByteInputDone) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneRekey); + consumed = true; + } + } + return consumed; +} + +void nfc_magic_scene_new_key_input_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + // Clear view + byte_input_set_result_callback(nfc_magic->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc_magic->byte_input, ""); +} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c index b87f7f383e86..b4f579f444d5 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c @@ -13,11 +13,10 @@ void nfc_magic_scene_not_magic_on_enter(void* context) { notification_message(nfc_magic->notifications, &sequence_error); - // widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); widget_add_string_element( widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); widget_add_string_multiline_element( - widget, 4, 17, AlignLeft, AlignTop, FontSecondary, "Not a magic\ncard"); + widget, 4, 17, AlignLeft, AlignTop, FontSecondary, "Not magic or unsupported\ncard"); widget_add_button_element( widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_not_magic_widget_callback, nfc_magic); diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey.c new file mode 100644 index 000000000000..259dc78eaa54 --- /dev/null +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey.c @@ -0,0 +1,95 @@ +#include "../nfc_magic_i.h" + +enum { + NfcMagicSceneRekeyStateCardSearch, + NfcMagicSceneRekeyStateCardFound, +}; + +bool nfc_magic_rekey_worker_callback(NfcMagicWorkerEvent event, void* context) { + furi_assert(context); + + NfcMagic* nfc_magic = context; + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, event); + + return true; +} + +static void nfc_magic_scene_rekey_setup_view(NfcMagic* nfc_magic) { + Popup* popup = nfc_magic->popup; + popup_reset(popup); + uint32_t state = scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneRekey); + + if(state == NfcMagicSceneRekeyStateCardSearch) { + popup_set_text( + nfc_magic->popup, + "Apply the\nsame card\nto the back", + 128, + 32, + AlignRight, + AlignCenter); + popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_icon(popup, 12, 23, &I_Loading_24); + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + } + + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); +} + +void nfc_magic_scene_rekey_on_enter(void* context) { + NfcMagic* nfc_magic = context; + + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch); + nfc_magic_scene_rekey_setup_view(nfc_magic); + + // Setup and start worker + nfc_magic_worker_start( + nfc_magic->worker, + NfcMagicWorkerStateRekey, + nfc_magic->dev, + &nfc_magic->source_dev->dev_data, + nfc_magic->new_password, + nfc_magic_rekey_worker_callback, + nfc_magic); + nfc_magic_blink_start(nfc_magic); +} + +bool nfc_magic_scene_rekey_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcMagicWorkerEventSuccess) { + nfc_magic->dev->password = nfc_magic->new_password; + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneSuccess); + consumed = true; + } else if(event.event == NfcMagicWorkerEventFail) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneRekeyFail); + consumed = true; + } else if(event.event == NfcMagicWorkerEventCardDetected) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardFound); + nfc_magic_scene_rekey_setup_view(nfc_magic); + consumed = true; + } else if(event.event == NfcMagicWorkerEventNoCardDetected) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch); + nfc_magic_scene_rekey_setup_view(nfc_magic); + consumed = true; + } + } + return consumed; +} + +void nfc_magic_scene_rekey_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + nfc_magic_worker_stop(nfc_magic->worker); + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch); + // Clear view + popup_reset(nfc_magic->popup); + + nfc_magic_blink_stop(nfc_magic); +} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey_fail.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey_fail.c new file mode 100644 index 000000000000..d30ee57bcfc9 --- /dev/null +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey_fail.c @@ -0,0 +1,50 @@ +#include "../nfc_magic_i.h" + +void nfc_magic_scene_rekey_fail_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcMagic* nfc_magic = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); + } +} + +void nfc_magic_scene_rekey_fail_on_enter(void* context) { + NfcMagic* nfc_magic = context; + Widget* widget = nfc_magic->widget; + + notification_message(nfc_magic->notifications, &sequence_error); + + widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Can't change password!"); + + widget_add_button_element( + widget, GuiButtonTypeLeft, "Finish", nfc_magic_scene_rekey_fail_widget_callback, nfc_magic); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); +} + +bool nfc_magic_scene_rekey_fail_on_event(void* context, SceneManagerEvent event) { + NfcMagic* nfc_magic = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc_magic->scene_manager, NfcMagicSceneStart); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc_magic->scene_manager, NfcMagicSceneStart); + } + return consumed; +} + +void nfc_magic_scene_rekey_fail_on_exit(void* context) { + NfcMagic* nfc_magic = context; + + widget_reset(nfc_magic->widget); +} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_start.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_start.c index a70eb8accfe7..b5861629e432 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_start.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_start.c @@ -1,8 +1,7 @@ #include "../nfc_magic_i.h" enum SubmenuIndex { SubmenuIndexCheck, - SubmenuIndexWriteGen1A, - SubmenuIndexWipe, + SubmenuIndexAuthenticateGen4, }; void nfc_magic_scene_start_submenu_callback(void* context, uint32_t index) { @@ -22,12 +21,10 @@ void nfc_magic_scene_start_on_enter(void* context) { nfc_magic); submenu_add_item( submenu, - "Write Gen1A", - SubmenuIndexWriteGen1A, + "Authenticate Gen4", + SubmenuIndexAuthenticateGen4, nfc_magic_scene_start_submenu_callback, nfc_magic); - submenu_add_item( - submenu, "Wipe", SubmenuIndexWipe, nfc_magic_scene_start_submenu_callback, nfc_magic); submenu_set_selected_item( submenu, scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneStart)); @@ -40,23 +37,13 @@ bool nfc_magic_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexCheck) { + nfc_magic->dev->password = MAGIC_GEN4_DEFAULT_PWD; scene_manager_set_scene_state( nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexCheck); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck); consumed = true; - } else if(event.event == SubmenuIndexWriteGen1A) { - // Explicitly save state in each branch so that the - // correct option is reselected if the user cancels - // loading a file. - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWriteGen1A); - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect); - consumed = true; - } else if(event.event == SubmenuIndexWipe) { - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWipe); - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe); - consumed = true; + } else if(event.event == SubmenuIndexAuthenticateGen4) { + scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneKeyInput); } } diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c index 1ca194286ad6..29640f89c6b5 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c @@ -22,7 +22,12 @@ static void nfc_magic_scene_wipe_setup_view(NfcMagic* nfc_magic) { if(state == NfcMagicSceneWipeStateCardSearch) { popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50); popup_set_text( - nfc_magic->popup, "Apply card to\nthe back", 128, 32, AlignRight, AlignCenter); + nfc_magic->popup, + "Apply the\nsame card\nto the back", + 128, + 32, + AlignRight, + AlignCenter); } else { popup_set_icon(popup, 12, 23, &I_Loading_24); popup_set_header(popup, "Wiping\nDon't move...", 52, 32, AlignLeft, AlignCenter); @@ -42,7 +47,9 @@ void nfc_magic_scene_wipe_on_enter(void* context) { nfc_magic_worker_start( nfc_magic->worker, NfcMagicWorkerStateWipe, - &nfc_magic->nfc_dev->dev_data, + nfc_magic->dev, + &nfc_magic->source_dev->dev_data, + nfc_magic->new_password, nfc_magic_wipe_worker_callback, nfc_magic); nfc_magic_blink_start(nfc_magic); diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_write.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_write.c index c3e6f962a344..45c54557f156 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_write.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_write.c @@ -21,7 +21,12 @@ static void nfc_magic_scene_write_setup_view(NfcMagic* nfc_magic) { if(state == NfcMagicSceneWriteStateCardSearch) { popup_set_text( - nfc_magic->popup, "Apply card to\nthe back", 128, 32, AlignRight, AlignCenter); + nfc_magic->popup, + "Apply the\nsame card\nto the back", + 128, + 32, + AlignRight, + AlignCenter); popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50); } else { popup_set_icon(popup, 12, 23, &I_Loading_24); @@ -42,7 +47,9 @@ void nfc_magic_scene_write_on_enter(void* context) { nfc_magic_worker_start( nfc_magic->worker, NfcMagicWorkerStateWrite, - &nfc_magic->nfc_dev->dev_data, + nfc_magic->dev, + &nfc_magic->source_dev->dev_data, + nfc_magic->new_password, nfc_magic_write_worker_callback, nfc_magic); nfc_magic_blink_start(nfc_magic); diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c index 4b80896932da..857d50c1f7d7 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c @@ -26,7 +26,7 @@ void nfc_magic_scene_wrong_card_on_enter(void* context) { AlignLeft, AlignTop, FontSecondary, - "Writing is supported\nonly for 4 bytes UID\nMifare Classic 1k"); + "Writing this file is\nnot supported for\nthis magic card."); widget_add_button_element( widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wrong_card_widget_callback, nfc_magic); diff --git a/applications/external/nfc_rfid_detector/application.fam b/applications/external/nfc_rfid_detector/application.fam new file mode 100644 index 000000000000..70c91bc84372 --- /dev/null +++ b/applications/external/nfc_rfid_detector/application.fam @@ -0,0 +1,13 @@ +App( + appid="nfc_rfid_detector", + name="NFC/RFID detector", + apptype=FlipperAppType.EXTERNAL, + targets=["f7"], + entry_point="nfc_rfid_detector_app", + requires=["gui"], + stack_size=4 * 1024, + order=50, + fap_icon="nfc_rfid_detector_10px.png", + fap_category="Tools", + fap_icon_assets="images", +) diff --git a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h new file mode 100644 index 000000000000..bbffe2938e44 --- /dev/null +++ b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum { + //NfcRfidDetectorCustomEvent + NfcRfidDetectorCustomEventStartId = 100, + +} NfcRfidDetectorCustomEvent; diff --git a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h new file mode 100644 index 000000000000..5d44b09b7f28 --- /dev/null +++ b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#define NFC_RFID_DETECTOR_VERSION_APP "0.1" +#define NFC_RFID_DETECTOR_DEVELOPED "SkorP" +#define NFC_RFID_DETECTOR_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" + +typedef enum { + NfcRfidDetectorViewVariableItemList, + NfcRfidDetectorViewSubmenu, + NfcRfidDetectorViewFieldPresence, + NfcRfidDetectorViewWidget, +} NfcRfidDetectorView; diff --git a/assets/icons/NFC/Restoring_38x32.png b/applications/external/nfc_rfid_detector/images/Modern_reader_18x34.png similarity index 84% rename from assets/icons/NFC/Restoring_38x32.png rename to applications/external/nfc_rfid_detector/images/Modern_reader_18x34.png index 9e058869f103..b19c0f30c9f3 100644 Binary files a/assets/icons/NFC/Restoring_38x32.png and b/applications/external/nfc_rfid_detector/images/Modern_reader_18x34.png differ diff --git a/applications/external/nfc_rfid_detector/images/Move_flipper_26x39.png b/applications/external/nfc_rfid_detector/images/Move_flipper_26x39.png new file mode 100644 index 000000000000..ff4af9ff0598 Binary files /dev/null and b/applications/external/nfc_rfid_detector/images/Move_flipper_26x39.png differ diff --git a/applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png b/applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png new file mode 100644 index 000000000000..9d8a6f2abf9b Binary files /dev/null and b/applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png differ diff --git a/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png b/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png new file mode 100644 index 000000000000..35c205049bc5 Binary files /dev/null and b/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png differ diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_10px.png b/applications/external/nfc_rfid_detector/nfc_rfid_detector_10px.png new file mode 100644 index 000000000000..7e875e028d5a Binary files /dev/null and b/applications/external/nfc_rfid_detector/nfc_rfid_detector_10px.png differ diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c new file mode 100644 index 000000000000..cba8b60856a3 --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c @@ -0,0 +1,108 @@ +#include "nfc_rfid_detector_app_i.h" + +#include +#include + +static bool nfc_rfid_detector_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool nfc_rfid_detector_app_back_event_callback(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void nfc_rfid_detector_app_tick_event_callback(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +NfcRfidDetectorApp* nfc_rfid_detector_app_alloc() { + NfcRfidDetectorApp* app = malloc(sizeof(NfcRfidDetectorApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&nfc_rfid_detector_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, nfc_rfid_detector_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, nfc_rfid_detector_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, nfc_rfid_detector_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // SubMenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcRfidDetectorViewSubmenu, submenu_get_view(app->submenu)); + + // Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcRfidDetectorViewWidget, widget_get_view(app->widget)); + + // Field Presence + app->nfc_rfid_detector_field_presence = nfc_rfid_detector_view_field_presence_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + NfcRfidDetectorViewFieldPresence, + nfc_rfid_detector_view_field_presence_get_view(app->nfc_rfid_detector_field_presence)); + + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneStart); + + return app; +} + +void nfc_rfid_detector_app_free(NfcRfidDetectorApp* app) { + furi_assert(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu); + submenu_free(app->submenu); + + // Widget + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewWidget); + widget_free(app->widget); + + // Field Presence + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence); + nfc_rfid_detector_view_field_presence_free(app->nfc_rfid_detector_field_presence); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Close records + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t nfc_rfid_detector_app(void* p) { + UNUSED(p); + NfcRfidDetectorApp* nfc_rfid_detector_app = nfc_rfid_detector_app_alloc(); + + view_dispatcher_run(nfc_rfid_detector_app->view_dispatcher); + + nfc_rfid_detector_app_free(nfc_rfid_detector_app); + + return 0; +} diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c new file mode 100644 index 000000000000..c59d40d50c52 --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c @@ -0,0 +1,40 @@ +#include "nfc_rfid_detector_app_i.h" + +#include + +#define TAG "NfcRfidDetector" + +void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app) { + furi_assert(app); + + // start the field presence rfid detection + furi_hal_rfid_field_detect_start(); + + // start the field presence nfc detection + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_field_detect_start(); +} + +void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app) { + furi_assert(app); + + // stop the field presence rfid detection + furi_hal_rfid_field_detect_stop(); + + // stop the field presence nfc detection + furi_hal_nfc_start_sleep(); +} + +bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app) { + furi_assert(app); + + // check if the field presence is nfc + return furi_hal_nfc_field_is_present(); +} + +bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency) { + furi_assert(app); + + // check if the field presence is rfid + return furi_hal_rfid_field_is_present(frequency); +} \ No newline at end of file diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h new file mode 100644 index 000000000000..72cb126d4a3d --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h @@ -0,0 +1,30 @@ +#pragma once + +#include "helpers/nfc_rfid_detector_types.h" +#include "helpers/nfc_rfid_detector_event.h" + +#include "scenes/nfc_rfid_detector_scene.h" +#include +#include +#include +#include +#include +#include +#include "views/nfc_rfid_detector_view_field_presence.h" + +typedef struct NfcRfidDetectorApp NfcRfidDetectorApp; + +struct NfcRfidDetectorApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + Submenu* submenu; + Widget* widget; + NfcRfidDetectorFieldPresence* nfc_rfid_detector_field_presence; +}; + +void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app); +void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app); +bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app); +bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency); \ No newline at end of file diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c new file mode 100644 index 000000000000..d75eb2884fe3 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c @@ -0,0 +1,31 @@ +#include "../nfc_rfid_detector_app_i.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const nfc_rfid_detector_scene_on_enter_handlers[])(void*) = { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const nfc_rfid_detector_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = + { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const nfc_rfid_detector_scene_on_exit_handlers[])(void* context) = { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers nfc_rfid_detector_scene_handlers = { + .on_enter_handlers = nfc_rfid_detector_scene_on_enter_handlers, + .on_event_handlers = nfc_rfid_detector_scene_on_event_handlers, + .on_exit_handlers = nfc_rfid_detector_scene_on_exit_handlers, + .scene_num = NfcRfidDetectorSceneNum, +}; diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h new file mode 100644 index 000000000000..74d324b4d3b7 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) NfcRfidDetectorScene##id, +typedef enum { +#include "nfc_rfid_detector_scene_config.h" + NfcRfidDetectorSceneNum, +} NfcRfidDetectorScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers nfc_rfid_detector_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c new file mode 100644 index 000000000000..ddcb8aac0fe7 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c @@ -0,0 +1,69 @@ +#include "../nfc_rfid_detector_app_i.h" + +void nfc_rfid_detector_scene_about_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcRfidDetectorApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void nfc_rfid_detector_scene_about_on_enter(void* context) { + NfcRfidDetectorApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", NFC_RFID_DETECTOR_VERSION_APP); + furi_string_cat_printf(temp_str, "Developed by: %s\n", NFC_RFID_DETECTOR_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", NFC_RFID_DETECTOR_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, + "This application allows\nyou to determine what\ntype of electromagnetic\nfield the reader is using.\nFor LF RFID you can also\nsee the carrier frequency\n\n"); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! NFC/RFID detector \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewWidget); +} + +bool nfc_rfid_detector_scene_about_on_event(void* context, SceneManagerEvent event) { + NfcRfidDetectorApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void nfc_rfid_detector_scene_about_on_exit(void* context) { + NfcRfidDetectorApp* app = context; + + // Clear views + widget_reset(app->widget); +} diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h new file mode 100644 index 000000000000..ab49ad5c2cfb --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(nfc_rfid_detector, start, Start) +ADD_SCENE(nfc_rfid_detector, about, About) +ADD_SCENE(nfc_rfid_detector, field_presence, FieldPresence) diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c new file mode 100644 index 000000000000..ec53b5a0a60f --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c @@ -0,0 +1,60 @@ +#include "../nfc_rfid_detector_app_i.h" +#include "../views/nfc_rfid_detector_view_field_presence.h" + +void nfc_rfid_detector_scene_field_presence_callback( + NfcRfidDetectorCustomEvent event, + void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +static const NotificationSequence notification_app_display_on = { + + &message_display_backlight_on, + NULL, +}; + +static void nfc_rfid_detector_scene_field_presence_update(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + + uint32_t frequency = 0; + bool nfc_field = nfc_rfid_detector_app_field_presence_is_nfc(app); + bool rfid_field = nfc_rfid_detector_app_field_presence_is_rfid(app, &frequency); + + if(nfc_field || rfid_field) + notification_message(app->notifications, ¬ification_app_display_on); + + nfc_rfid_detector_view_field_presence_update( + app->nfc_rfid_detector_field_presence, nfc_field, rfid_field, frequency); +} + +void nfc_rfid_detector_scene_field_presence_on_enter(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + + // Start detection of field presence + nfc_rfid_detector_app_field_presence_start(app); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence); +} + +bool nfc_rfid_detector_scene_field_presence_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + nfc_rfid_detector_scene_field_presence_update(app); + } + + return consumed; +} + +void nfc_rfid_detector_scene_field_presence_on_exit(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + // Stop detection of field presence + nfc_rfid_detector_app_field_presence_stop(app); +} diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c new file mode 100644 index 000000000000..7b71bd973513 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c @@ -0,0 +1,58 @@ +#include "../nfc_rfid_detector_app_i.h" + +typedef enum { + SubmenuIndexNfcRfidDetectorFieldPresence, + SubmenuIndexNfcRfidDetectorAbout, +} SubmenuIndex; + +void nfc_rfid_detector_scene_start_submenu_callback(void* context, uint32_t index) { + NfcRfidDetectorApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_rfid_detector_scene_start_on_enter(void* context) { + UNUSED(context); + NfcRfidDetectorApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Detect field type", + SubmenuIndexNfcRfidDetectorFieldPresence, + nfc_rfid_detector_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "About", + SubmenuIndexNfcRfidDetectorAbout, + nfc_rfid_detector_scene_start_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcRfidDetectorSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu); +} + +bool nfc_rfid_detector_scene_start_on_event(void* context, SceneManagerEvent event) { + NfcRfidDetectorApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNfcRfidDetectorAbout) { + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneAbout); + consumed = true; + } else if(event.event == SubmenuIndexNfcRfidDetectorFieldPresence) { + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneFieldPresence); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, NfcRfidDetectorSceneStart, event.event); + } + + return consumed; +} + +void nfc_rfid_detector_scene_start_on_exit(void* context) { + NfcRfidDetectorApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c new file mode 100644 index 000000000000..e65eb8362b48 --- /dev/null +++ b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c @@ -0,0 +1,164 @@ +#include "nfc_rfid_detector_view_field_presence.h" +#include "../nfc_rfid_detector_app_i.h" +#include + +#include +#include + +#define FIELD_FOUND_WEIGHT 5 + +typedef enum { + NfcRfidDetectorTypeFieldPresenceNfc, + NfcRfidDetectorTypeFieldPresenceRfid, +} NfcRfidDetectorTypeFieldPresence; + +static const Icon* NfcRfidDetectorFieldPresenceIcons[] = { + [NfcRfidDetectorTypeFieldPresenceNfc] = &I_NFC_detect_45x30, + [NfcRfidDetectorTypeFieldPresenceRfid] = &I_Rfid_detect_45x30, +}; + +struct NfcRfidDetectorFieldPresence { + View* view; +}; + +typedef struct { + uint8_t nfc_field; + uint8_t rfid_field; + uint32_t rfid_frequency; +} NfcRfidDetectorFieldPresenceModel; + +void nfc_rfid_detector_view_field_presence_update( + NfcRfidDetectorFieldPresence* instance, + bool nfc_field, + bool rfid_field, + uint32_t rfid_frequency) { + furi_assert(instance); + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + if(nfc_field) { + model->nfc_field = FIELD_FOUND_WEIGHT; + } else if(model->nfc_field) { + model->nfc_field--; + } + if(rfid_field) { + model->rfid_field = FIELD_FOUND_WEIGHT; + model->rfid_frequency = rfid_frequency; + } else if(model->rfid_field) { + model->rfid_field--; + } + }, + true); +} + +void nfc_rfid_detector_view_field_presence_draw( + Canvas* canvas, + NfcRfidDetectorFieldPresenceModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + if(!model->nfc_field && !model->rfid_field) { + canvas_draw_icon(canvas, 0, 16, &I_Modern_reader_18x34); + canvas_draw_icon(canvas, 22, 12, &I_Move_flipper_26x39); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 56, 36, "Touch the reader"); + } else { + if(model->nfc_field) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 21, 10, "NFC"); + canvas_draw_icon( + canvas, + 9, + 17, + NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceNfc]); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 9, 62, "13,56 MHz"); + } + + if(model->rfid_field) { + char str[16]; + snprintf(str, sizeof(str), "%.02f KHz", (double)model->rfid_frequency / 1000); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 76, 10, "LF RFID"); + canvas_draw_icon( + canvas, + 71, + 17, + NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceRfid]); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 69, 62, str); + } + } +} + +bool nfc_rfid_detector_view_field_presence_input(InputEvent* event, void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + UNUSED(instance); + + if(event->key == InputKeyBack) { + return false; + } + + return true; +} + +void nfc_rfid_detector_view_field_presence_enter(void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + model->nfc_field = 0; + model->rfid_field = 0; + model->rfid_frequency = 0; + }, + true); +} + +void nfc_rfid_detector_view_field_presence_exit(void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + UNUSED(instance); +} + +NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc() { + NfcRfidDetectorFieldPresence* instance = malloc(sizeof(NfcRfidDetectorFieldPresence)); + + // View allocation and configuration + instance->view = view_alloc(); + + view_allocate_model( + instance->view, ViewModelTypeLocking, sizeof(NfcRfidDetectorFieldPresenceModel)); + view_set_context(instance->view, instance); + view_set_draw_callback( + instance->view, (ViewDrawCallback)nfc_rfid_detector_view_field_presence_draw); + view_set_input_callback(instance->view, nfc_rfid_detector_view_field_presence_input); + view_set_enter_callback(instance->view, nfc_rfid_detector_view_field_presence_enter); + view_set_exit_callback(instance->view, nfc_rfid_detector_view_field_presence_exit); + + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + model->nfc_field = 0; + model->rfid_field = 0; + model->rfid_frequency = 0; + }, + true); + return instance; +} + +void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h new file mode 100644 index 000000000000..0ddb4e2cd589 --- /dev/null +++ b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/nfc_rfid_detector_types.h" +#include "../helpers/nfc_rfid_detector_event.h" + +typedef struct NfcRfidDetectorFieldPresence NfcRfidDetectorFieldPresence; + +void nfc_rfid_detector_view_field_presence_update( + NfcRfidDetectorFieldPresence* instance, + bool nfc_field, + bool rfid_field, + uint32_t rfid_frequency); + +NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc(); + +void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance); + +View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance); diff --git a/applications/external/picopass/picopass_device.c b/applications/external/picopass/picopass_device.c index 53778cfb322a..de43b0bb7c54 100644 --- a/applications/external/picopass/picopass_device.c +++ b/applications/external/picopass/picopass_device.c @@ -16,6 +16,7 @@ PicopassDevice* picopass_device_alloc() { PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice)); picopass_dev->dev_data.pacs.legacy = false; picopass_dev->dev_data.pacs.se_enabled = false; + picopass_dev->dev_data.pacs.elite_kdf = false; picopass_dev->dev_data.pacs.pin_length = 0; picopass_dev->storage = furi_record_open(RECORD_STORAGE); picopass_dev->dialogs = furi_record_open(RECORD_DIALOGS); @@ -77,6 +78,7 @@ static bool picopass_device_save_file( break; } } + // TODO: Add elite if(!flipper_format_write_comment_cstr(file, "Picopass blocks")) break; bool block_saved = true; @@ -256,6 +258,7 @@ void picopass_device_data_clear(PicopassDeviceData* dev_data) { } dev_data->pacs.legacy = false; dev_data->pacs.se_enabled = false; + dev_data->pacs.elite_kdf = false; dev_data->pacs.pin_length = 0; } diff --git a/applications/external/picopass/picopass_device.h b/applications/external/picopass/picopass_device.h index 7fc35ebda151..b45df346cf26 100644 --- a/applications/external/picopass/picopass_device.h +++ b/applications/external/picopass/picopass_device.h @@ -62,6 +62,7 @@ typedef struct { bool sio; bool biometrics; uint8_t key[8]; + bool elite_kdf; uint8_t pin_length; PicopassEncryption encryption; uint8_t credential[8]; diff --git a/applications/external/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c index e671552c5cb0..6301704ca8bd 100644 --- a/applications/external/picopass/picopass_worker.c +++ b/applications/external/picopass/picopass_worker.c @@ -550,6 +550,7 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { if(err == ERR_NONE) { FURI_LOG_I(TAG, "Found key"); memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); + pacs->elite_kdf = elite; err = picopass_read_card(AA1); if(err != ERR_NONE) { FURI_LOG_E(TAG, "picopass_read_card error %d", err); @@ -720,7 +721,7 @@ void picopass_worker_write_key(PicopassWorker* picopass_worker) { uint8_t* oldKey = AA1[PICOPASS_KD_BLOCK_INDEX].data; uint8_t newKey[PICOPASS_BLOCK_LEN] = {0}; - loclass_iclass_calc_div_key(csn, pacs->key, newKey, false); + loclass_iclass_calc_div_key(csn, pacs->key, newKey, pacs->elite_kdf); if((fuses & 0x80) == 0x80) { FURI_LOG_D(TAG, "Plain write for personalized mode key change"); diff --git a/applications/external/picopass/scenes/picopass_scene_device_info.c b/applications/external/picopass/scenes/picopass_scene_device_info.c index 41caeabf5adc..bb149aa6b378 100644 --- a/applications/external/picopass/scenes/picopass_scene_device_info.c +++ b/applications/external/picopass/scenes/picopass_scene_device_info.c @@ -19,7 +19,7 @@ void picopass_scene_device_info_on_enter(void* context) { FuriString* wiegand_str = furi_string_alloc(); FuriString* sio_str = furi_string_alloc(); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Setup view PicopassBlock* AA1 = picopass->dev->dev_data.AA1; diff --git a/applications/external/picopass/scenes/picopass_scene_key_menu.c b/applications/external/picopass/scenes/picopass_scene_key_menu.c index 8aac6cb2491a..15a32ff4418d 100644 --- a/applications/external/picopass/scenes/picopass_scene_key_menu.c +++ b/applications/external/picopass/scenes/picopass_scene_key_menu.c @@ -60,24 +60,28 @@ bool picopass_scene_key_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteStandard); memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN); + picopass->dev->dev_data.pacs.elite_kdf = false; scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } else if(event.event == SubmenuIndexWriteiCE) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); memcpy(picopass->dev->dev_data.pacs.key, picopass_xice_key, PICOPASS_BLOCK_LEN); + picopass->dev->dev_data.pacs.elite_kdf = true; scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } else if(event.event == SubmenuIndexWriteiCL) { scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); + picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCL); memcpy(picopass->dev->dev_data.pacs.key, picopass_xicl_key, PICOPASS_BLOCK_LEN); + picopass->dev->dev_data.pacs.elite_kdf = false; scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } else if(event.event == SubmenuIndexWriteiCS) { scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); + picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCS); memcpy(picopass->dev->dev_data.pacs.key, picopass_xics_key, PICOPASS_BLOCK_LEN); + picopass->dev->dev_data.pacs.elite_kdf = false; scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } diff --git a/applications/external/picopass/scenes/picopass_scene_read_card.c b/applications/external/picopass/scenes/picopass_scene_read_card.c index 96ec7c668b96..c1cc7249c428 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card.c @@ -10,7 +10,7 @@ void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context void picopass_scene_read_card_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); // Setup view Popup* popup = picopass->popup; diff --git a/applications/external/picopass/scenes/picopass_scene_read_card_success.c b/applications/external/picopass/scenes/picopass_scene_read_card_success.c index cc18ac066a0c..ffe7195b7928 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card_success.c @@ -21,7 +21,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { FuriString* wiegand_str = furi_string_alloc(); FuriString* sio_str = furi_string_alloc(); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Send notification notification_message(picopass->notifications, &sequence_success); diff --git a/applications/external/picopass/scenes/picopass_scene_read_factory_success.c b/applications/external/picopass/scenes/picopass_scene_read_factory_success.c index bc07bb95302e..f5fcd10fda1f 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_factory_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_factory_success.c @@ -19,7 +19,7 @@ void picopass_scene_read_factory_success_on_enter(void* context) { FuriString* title = furi_string_alloc_set("Factory Default"); FuriString* subtitle = furi_string_alloc_set(""); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Send notification notification_message(picopass->notifications, &sequence_success); diff --git a/applications/external/picopass/scenes/picopass_scene_save_success.c b/applications/external/picopass/scenes/picopass_scene_save_success.c index e92d91fb4400..3b0a1cadd257 100644 --- a/applications/external/picopass/scenes/picopass_scene_save_success.c +++ b/applications/external/picopass/scenes/picopass_scene_save_success.c @@ -8,7 +8,7 @@ void picopass_scene_save_success_popup_callback(void* context) { void picopass_scene_save_success_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); // Setup view Popup* popup = picopass->popup; diff --git a/applications/external/picopass/scenes/picopass_scene_write_card.c b/applications/external/picopass/scenes/picopass_scene_write_card.c index a905dca95566..ce396fc10e12 100644 --- a/applications/external/picopass/scenes/picopass_scene_write_card.c +++ b/applications/external/picopass/scenes/picopass_scene_write_card.c @@ -9,7 +9,7 @@ void picopass_write_card_worker_callback(PicopassWorkerEvent event, void* contex void picopass_scene_write_card_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); // Setup view Popup* popup = picopass->popup; diff --git a/applications/external/picopass/scenes/picopass_scene_write_card_success.c b/applications/external/picopass/scenes/picopass_scene_write_card_success.c index 4bbca816aa29..cd760272fe9a 100644 --- a/applications/external/picopass/scenes/picopass_scene_write_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_write_card_success.c @@ -18,7 +18,7 @@ void picopass_scene_write_card_success_on_enter(void* context) { Widget* widget = picopass->widget; FuriString* str = furi_string_alloc_set("Write Success!"); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Send notification notification_message(picopass->notifications, &sequence_success); diff --git a/applications/external/picopass/scenes/picopass_scene_write_key.c b/applications/external/picopass/scenes/picopass_scene_write_key.c index 0f417e1c3fe8..806a2b5a8561 100644 --- a/applications/external/picopass/scenes/picopass_scene_write_key.c +++ b/applications/external/picopass/scenes/picopass_scene_write_key.c @@ -9,7 +9,7 @@ void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context void picopass_scene_write_key_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); // Setup view Popup* popup = picopass->popup; diff --git a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c index 7ac3fadda4c1..1cadb3a1a47f 100644 --- a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c +++ b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c @@ -33,7 +33,13 @@ void signal_gen_scene_pwm_on_enter(void* context) { signal_gen_pwm_set_callback(app->pwm_view, signal_gen_pwm_callback, app); signal_gen_pwm_set_params(app->pwm_view, 0, DEFAULT_FREQ, DEFAULT_DUTY); - furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + + if(!furi_hal_pwm_is_running(pwm_ch_id[0])) { + furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + } else { + furi_hal_pwm_stop(pwm_ch_id[0]); + furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + } } bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { @@ -46,8 +52,18 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { furi_hal_pwm_set_params(app->pwm_ch, app->pwm_freq, app->pwm_duty); } else if(event.event == SignalGenPwmEventChannelChange) { consumed = true; - furi_hal_pwm_stop(app->pwm_ch_prev); - furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + // Stop previous channel PWM + if(furi_hal_pwm_is_running(app->pwm_ch_prev)) { + furi_hal_pwm_stop(app->pwm_ch_prev); + } + + // Start PWM and restart if it was starter already + if(furi_hal_pwm_is_running(app->pwm_ch)) { + furi_hal_pwm_stop(app->pwm_ch); + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + } else { + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + } } } return consumed; @@ -56,5 +72,8 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { void signal_gen_scene_pwm_on_exit(void* context) { SignalGenApp* app = context; variable_item_list_reset(app->var_item_list); - furi_hal_pwm_stop(app->pwm_ch); + + if(furi_hal_pwm_is_running(app->pwm_ch)) { + furi_hal_pwm_stop(app->pwm_ch); + } } diff --git a/applications/external/snake_game/snake_game.c b/applications/external/snake_game/snake_game.c index 3cf9b6d53d02..6852cb215b28 100644 --- a/applications/external/snake_game/snake_game.c +++ b/applications/external/snake_game/snake_game.c @@ -346,7 +346,7 @@ int32_t snake_game_app(void* p) { notification_message_block(notification, &sequence_display_backlight_enforce_on); - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); SnakeEvent event; for(bool processing = true; processing;) { diff --git a/applications/external/weather_station/protocols/oregon3.c b/applications/external/weather_station/protocols/oregon3.c new file mode 100644 index 000000000000..bd35c2fd576f --- /dev/null +++ b/applications/external/weather_station/protocols/oregon3.c @@ -0,0 +1,365 @@ +#include "oregon3.h" + +#include +#include +#include +#include +#include "ws_generic.h" + +#include +#include + +#define TAG "WSProtocolOregon3" + +static const SubGhzBlockConst ws_oregon3_const = { + .te_long = 1100, + .te_short = 500, + .te_delta = 300, + .min_count_bit_for_found = 32, +}; + +#define OREGON3_PREAMBLE_BITS 28 +#define OREGON3_PREAMBLE_MASK 0b1111111111111111111111111111 +// 24 ones + 0101 (inverted A) +#define OREGON3_PREAMBLE 0b1111111111111111111111110101 + +// Fixed part contains: +// - Sensor type: 16 bits +// - Channel: 4 bits +// - ID (changes when batteries are changed): 8 bits +// - Battery status: 4 bits +#define OREGON3_FIXED_PART_BITS (16 + 4 + 8 + 4) +#define OREGON3_SENSOR_ID(d) (((d) >> 16) & 0xFFFF) +#define OREGON3_CHECKSUM_BITS 8 + +// bit indicating the low battery +#define OREGON3_FLAG_BAT_LOW 0x4 + +/// Documentation for Oregon Scientific protocols can be found here: +/// https://www.osengr.org/Articles/OS-RF-Protocols-IV.pdf +// Sensors ID +#define ID_THGR221 0xf824 + +struct WSProtocolDecoderOregon3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + ManchesterState manchester_state; + bool prev_bit; + + uint8_t var_bits; + uint64_t var_data; +}; + +typedef struct WSProtocolDecoderOregon3 WSProtocolDecoderOregon3; + +typedef enum { + Oregon3DecoderStepReset = 0, + Oregon3DecoderStepFoundPreamble, + Oregon3DecoderStepVarData, +} Oregon3DecoderStep; + +void* ws_protocol_decoder_oregon3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderOregon3* instance = malloc(sizeof(WSProtocolDecoderOregon3)); + instance->base.protocol = &ws_protocol_oregon3; + instance->generic.protocol_name = instance->base.protocol->name; + instance->generic.humidity = WS_NO_HUMIDITY; + instance->generic.temp = WS_NO_TEMPERATURE; + instance->generic.btn = WS_NO_BTN; + instance->generic.channel = WS_NO_CHANNEL; + instance->generic.battery_low = WS_NO_BATT; + instance->generic.id = WS_NO_ID; + instance->prev_bit = false; + return instance; +} + +void ws_protocol_decoder_oregon3_free(void* context) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + free(instance); +} + +void ws_protocol_decoder_oregon3_reset(void* context) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + instance->decoder.parser_step = Oregon3DecoderStepReset; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL); + instance->prev_bit = false; + instance->var_data = 0; + instance->var_bits = 0; +} + +static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) { + bool is_long = false; + + if(DURATION_DIFF(duration, ws_oregon3_const.te_long) < ws_oregon3_const.te_delta) { + is_long = true; + } else if(DURATION_DIFF(duration, ws_oregon3_const.te_short) < ws_oregon3_const.te_delta) { + is_long = false; + } else { + return ManchesterEventReset; + } + + if(level) + return is_long ? ManchesterEventLongHigh : ManchesterEventShortHigh; + else + return is_long ? ManchesterEventLongLow : ManchesterEventShortLow; +} + +// From sensor id code return amount of bits in variable section +// https://temofeev.ru/info/articles/o-dekodirovanii-protokola-pogodnykh-datchikov-oregon-scientific +static uint8_t oregon3_sensor_id_var_bits(uint16_t sensor_id) { + switch(sensor_id) { + case ID_THGR221: + // nibbles: temp + hum + '0' + return (4 + 2 + 1) * 4; + default: + FURI_LOG_D(TAG, "Unsupported sensor id 0x%x", sensor_id); + return 0; + } +} + +static void ws_oregon3_decode_const_data(WSBlockGeneric* ws_block) { + ws_block->id = OREGON3_SENSOR_ID(ws_block->data); + ws_block->channel = (ws_block->data >> 12) & 0xF; + ws_block->battery_low = (ws_block->data & OREGON3_FLAG_BAT_LOW) ? 1 : 0; +} + +static uint16_t ws_oregon3_bcd_decode_short(uint32_t data) { + return (data & 0xF) * 10 + ((data >> 4) & 0xF); +} + +static float ws_oregon3_decode_temp(uint32_t data) { + int32_t temp_val; + temp_val = ws_oregon3_bcd_decode_short(data >> 4); + temp_val *= 10; + temp_val += (data >> 12) & 0xF; + if(data & 0xF) temp_val = -temp_val; + return (float)temp_val / 10.0; +} + +static void ws_oregon3_decode_var_data(WSBlockGeneric* ws_b, uint16_t sensor_id, uint32_t data) { + switch(sensor_id) { + case ID_THGR221: + default: + ws_b->humidity = ws_oregon3_bcd_decode_short(data >> 4); + ws_b->temp = ws_oregon3_decode_temp(data >> 12); + break; + } +} + +void ws_protocol_decoder_oregon3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + // Oregon v3.0 protocol is inverted + ManchesterEvent event = level_and_duration_to_event(!level, duration); + + // low-level bit sequence decoding + if(event == ManchesterEventReset) { + instance->decoder.parser_step = Oregon3DecoderStepReset; + instance->prev_bit = false; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + } + if(manchester_advance( + instance->manchester_state, event, &instance->manchester_state, &instance->prev_bit)) { + subghz_protocol_blocks_add_bit(&instance->decoder, instance->prev_bit); + } + + switch(instance->decoder.parser_step) { + case Oregon3DecoderStepReset: + // waiting for fixed oregon3 preamble + if(instance->decoder.decode_count_bit >= OREGON3_PREAMBLE_BITS && + ((instance->decoder.decode_data & OREGON3_PREAMBLE_MASK) == OREGON3_PREAMBLE)) { + instance->decoder.parser_step = Oregon3DecoderStepFoundPreamble; + instance->decoder.decode_count_bit = 0; + instance->decoder.decode_data = 0UL; + } + break; + case Oregon3DecoderStepFoundPreamble: + // waiting for fixed oregon3 data + if(instance->decoder.decode_count_bit == OREGON3_FIXED_PART_BITS) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + + // reverse nibbles in decoded data as oregon v3.0 is LSB first + instance->generic.data = (instance->generic.data & 0x55555555) << 1 | + (instance->generic.data & 0xAAAAAAAA) >> 1; + instance->generic.data = (instance->generic.data & 0x33333333) << 2 | + (instance->generic.data & 0xCCCCCCCC) >> 2; + + ws_oregon3_decode_const_data(&instance->generic); + instance->var_bits = + oregon3_sensor_id_var_bits(OREGON3_SENSOR_ID(instance->generic.data)); + + if(!instance->var_bits) { + // sensor is not supported, stop decoding + instance->decoder.parser_step = Oregon3DecoderStepReset; + } else { + instance->decoder.parser_step = Oregon3DecoderStepVarData; + } + } + break; + case Oregon3DecoderStepVarData: + // waiting for variable (sensor-specific data) + if(instance->decoder.decode_count_bit == instance->var_bits + OREGON3_CHECKSUM_BITS) { + instance->var_data = instance->decoder.decode_data & 0xFFFFFFFFFFFFFFFF; + + // reverse nibbles in var data + instance->var_data = (instance->var_data & 0x5555555555555555) << 1 | + (instance->var_data & 0xAAAAAAAAAAAAAAAA) >> 1; + instance->var_data = (instance->var_data & 0x3333333333333333) << 2 | + (instance->var_data & 0xCCCCCCCCCCCCCCCC) >> 2; + + ws_oregon3_decode_var_data( + &instance->generic, + OREGON3_SENSOR_ID(instance->generic.data), + instance->var_data >> OREGON3_CHECKSUM_BITS); + + instance->decoder.parser_step = Oregon3DecoderStepReset; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } +} + +uint8_t ws_protocol_decoder_oregon3_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_oregon3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + ret = ws_block_generic_serialize(&instance->generic, flipper_format, preset); + if(ret != SubGhzProtocolStatusOk) return ret; + uint32_t temp = instance->var_bits; + if(!flipper_format_write_uint32(flipper_format, "VarBits", &temp, 1)) { + FURI_LOG_E(TAG, "Error adding VarBits"); + return SubGhzProtocolStatusErrorParserOthers; + } + if(!flipper_format_write_hex( + flipper_format, + "VarData", + (const uint8_t*)&instance->var_data, + sizeof(instance->var_data))) { + FURI_LOG_E(TAG, "Error adding VarData"); + return SubGhzProtocolStatusErrorParserOthers; + } + return ret; +} + +SubGhzProtocolStatus + ws_protocol_decoder_oregon3_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + uint32_t temp_data; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = ws_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { + break; + } + if(!flipper_format_read_uint32(flipper_format, "VarBits", &temp_data, 1)) { + FURI_LOG_E(TAG, "Missing VarLen"); + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + instance->var_bits = (uint8_t)temp_data; + if(!flipper_format_read_hex( + flipper_format, + "VarData", + (uint8_t*)&instance->var_data, + sizeof(instance->var_data))) { //-V1051 + FURI_LOG_E(TAG, "Missing VarData"); + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + if(instance->generic.data_count_bit != ws_oregon3_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key: %d", instance->generic.data_count_bit); + ret = SubGhzProtocolStatusErrorValueBitCount; + break; + } + } while(false); + return ret; +} + +static void oregon3_append_check_sum(uint32_t fix_data, uint64_t var_data, FuriString* output) { + uint8_t sum = fix_data & 0xF; + uint8_t ref_sum = var_data & 0xFF; + var_data >>= 4; + + for(uint8_t i = 1; i < 8; i++) { + fix_data >>= 4; + var_data >>= 4; + sum += (fix_data & 0xF) + (var_data & 0xF); + } + + // swap calculated sum nibbles + sum = (((sum >> 4) & 0xF) | (sum << 4)) & 0xFF; + if(sum == ref_sum) + furi_string_cat_printf(output, "Sum ok: 0x%hhX", ref_sum); + else + furi_string_cat_printf(output, "Sum err: 0x%hhX vs 0x%hhX", ref_sum, sum); +} + +void ws_protocol_decoder_oregon3_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + furi_string_cat_printf( + output, + "%s\r\n" + "ID: 0x%04lX, ch: %d, bat: %d, rc: 0x%02lX\r\n", + instance->generic.protocol_name, + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (uint32_t)(instance->generic.data >> 4) & 0xFF); + + if(instance->var_bits > 0) { + furi_string_cat_printf( + output, + "Temp:%d.%d C Hum:%d%%", + (int16_t)instance->generic.temp, + abs( + ((int16_t)(instance->generic.temp * 10) - + (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); + oregon3_append_check_sum((uint32_t)instance->generic.data, instance->var_data, output); + } +} + +const SubGhzProtocolDecoder ws_protocol_oregon3_decoder = { + .alloc = ws_protocol_decoder_oregon3_alloc, + .free = ws_protocol_decoder_oregon3_free, + + .feed = ws_protocol_decoder_oregon3_feed, + .reset = ws_protocol_decoder_oregon3_reset, + + .get_hash_data = ws_protocol_decoder_oregon3_get_hash_data, + .serialize = ws_protocol_decoder_oregon3_serialize, + .deserialize = ws_protocol_decoder_oregon3_deserialize, + .get_string = ws_protocol_decoder_oregon3_get_string, +}; + +const SubGhzProtocol ws_protocol_oregon3 = { + .name = WS_PROTOCOL_OREGON3_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_oregon3_decoder, +}; diff --git a/applications/external/weather_station/protocols/oregon3.h b/applications/external/weather_station/protocols/oregon3.h new file mode 100644 index 000000000000..ec51ddb00014 --- /dev/null +++ b/applications/external/weather_station/protocols/oregon3.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +#define WS_PROTOCOL_OREGON3_NAME "Oregon3" +extern const SubGhzProtocol ws_protocol_oregon3; diff --git a/applications/external/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c index cd4bae76dc28..93dc25488ddb 100644 --- a/applications/external/weather_station/protocols/protocol_items.c +++ b/applications/external/weather_station/protocols/protocol_items.c @@ -11,6 +11,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_lacrosse_tx, &ws_protocol_lacrosse_tx141thbv2, &ws_protocol_oregon2, + &ws_protocol_oregon3, &ws_protocol_acurite_592txr, &ws_protocol_ambient_weather, &ws_protocol_auriol_th, @@ -21,4 +22,4 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { const SubGhzProtocolRegistry weather_station_protocol_registry = { .items = weather_station_protocol_registry_items, - .size = COUNT_OF(weather_station_protocol_registry_items)}; \ No newline at end of file + .size = COUNT_OF(weather_station_protocol_registry_items)}; diff --git a/applications/external/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h index 0398c11f256b..712eb07f2210 100644 --- a/applications/external/weather_station/protocols/protocol_items.h +++ b/applications/external/weather_station/protocols/protocol_items.h @@ -11,6 +11,7 @@ #include "lacrosse_tx.h" #include "lacrosse_tx141thbv2.h" #include "oregon2.h" +#include "oregon3.h" #include "acurite_592txr.h" #include "ambient_weather.h" #include "auriol_hg0601a.h" diff --git a/applications/main/application.fam b/applications/main/application.fam index 5c2c21d37535..a1c4a06804fd 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -3,16 +3,30 @@ App( name="Basic applications for main menu", apptype=FlipperAppType.METAPACKAGE, provides=[ - "gpio", - "onewire", - "ibutton", - "infrared", - "lfrfid", + # "gpio", + # "ibutton", + # "infrared", + # "lfrfid", "nfc", - "subghz", - "bad_usb", - "u2f", - "fap_loader", - "archive", + "nfc_rpc", + # "subghz", + # "bad_usb", + # "u2f", + # "archive", + # "main_apps_on_start", + ], +) + +App( + appid="main_apps_on_start", + name="On start hooks", + apptype=FlipperAppType.METAPACKAGE, + provides=[ + "ibutton_start", + "onewire_start", + "subghz_start", + "infrared_start", + "lfrfid_start", + "nfc_start", ], ) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 9a7973cb3884..51457fe81fe7 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include static void @@ -64,8 +64,20 @@ static void if(!is_last) { archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path)); } else { + bool load_again = false; with_view_model( - browser->view, ArchiveBrowserViewModel * model, { model->list_loading = false; }, true); + browser->view, + ArchiveBrowserViewModel * model, + { + model->list_loading = false; + if(archive_is_file_list_load_required(model)) { + load_again = true; + } + }, + true); + if(load_again) { + archive_file_array_load(browser, 0); + } } } @@ -111,6 +123,26 @@ bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx) { return true; } +bool archive_is_file_list_load_required(ArchiveBrowserViewModel* model) { + size_t array_size = files_array_size(model->files); + + if((model->list_loading) || (array_size >= model->item_cnt)) { + return false; + } + + if((model->array_offset > 0) && + (model->item_idx < (model->array_offset + FILE_LIST_BUF_LEN / 4))) { + return true; + } + + if(((model->array_offset + array_size) < model->item_cnt) && + (model->item_idx > (int32_t)(model->array_offset + array_size - FILE_LIST_BUF_LEN / 4))) { + return true; + } + + return false; +} + void archive_update_offset(ArchiveBrowserView* browser) { furi_assert(browser); @@ -367,7 +399,7 @@ void archive_add_app_item(ArchiveBrowserView* browser, const char* name) { static bool archive_get_fap_meta(FuriString* file_path, FuriString* fap_name, uint8_t** icon_ptr) { Storage* storage = furi_record_open(RECORD_STORAGE); bool success = false; - if(fap_loader_load_name_and_icon(file_path, storage, icon_ptr, fap_name)) { + if(flipper_application_load_name_and_icon(file_path, storage, icon_ptr, fap_name)) { success = true; } furi_record_close(RECORD_STORAGE); diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 09ffea1f9c88..5e66a3dbbcbb 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -64,6 +64,7 @@ inline bool archive_is_known_app(ArchiveFileTypeEnum type) { } bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx); +bool archive_is_file_list_load_required(ArchiveBrowserViewModel* model); void archive_update_offset(ArchiveBrowserView* browser); void archive_update_focus(ArchiveBrowserView* browser, const char* target); diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index c28f91f52448..370830a00185 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -5,23 +5,35 @@ #include "../helpers/archive_browser.h" #include "../views/archive_browser_view.h" #include "archive/scenes/archive_scene.h" +#include #define TAG "ArchiveSceneBrowser" #define SCENE_STATE_DEFAULT (0) #define SCENE_STATE_NEED_REFRESH (1) -static const char* flipper_app_name[] = { - [ArchiveFileTypeIButton] = "iButton", - [ArchiveFileTypeNFC] = "NFC", - [ArchiveFileTypeSubGhz] = "Sub-GHz", - [ArchiveFileTypeLFRFID] = "125 kHz RFID", - [ArchiveFileTypeInfrared] = "Infrared", - [ArchiveFileTypeBadUsb] = "Bad USB", - [ArchiveFileTypeU2f] = "U2F", - [ArchiveFileTypeUpdateManifest] = "UpdaterApp", - [ArchiveFileTypeApplication] = "Applications", -}; +static const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) { + switch(file_type) { + case ArchiveFileTypeIButton: + return "iButton"; + case ArchiveFileTypeNFC: + return "NFC"; + case ArchiveFileTypeSubGhz: + return "Sub-GHz"; + case ArchiveFileTypeLFRFID: + return "125 kHz RFID"; + case ArchiveFileTypeInfrared: + return "Infrared"; + case ArchiveFileTypeBadUsb: + return "Bad USB"; + case ArchiveFileTypeU2f: + return "U2F"; + case ArchiveFileTypeUpdateManifest: + return "UpdaterApp"; + default: + return NULL; + } +} static void archive_loader_callback(const void* message, void* context) { furi_assert(message); @@ -39,20 +51,20 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec UNUSED(browser); Loader* loader = furi_record_open(RECORD_LOADER); - LoaderStatus status; - if(selected->is_app) { - char* param = strrchr(furi_string_get_cstr(selected->path), '/'); - if(param != NULL) { - param++; + const char* app_name = archive_get_flipper_app_name(selected->type); + + if(app_name) { + if(selected->is_app) { + char* param = strrchr(furi_string_get_cstr(selected->path), '/'); + if(param != NULL) { + param++; + } + loader_start_with_gui_error(loader, app_name, param); + } else { + loader_start_with_gui_error(loader, app_name, furi_string_get_cstr(selected->path)); } - status = loader_start(loader, flipper_app_name[selected->type], param); } else { - status = loader_start( - loader, flipper_app_name[selected->type], furi_string_get_cstr(selected->path)); - } - - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); + loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL); } furi_record_close(RECORD_LOADER); diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 2aca3c02ba45..ba147f74c8cf 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -248,24 +248,10 @@ View* archive_browser_get_view(ArchiveBrowserView* browser) { return browser->view; } -static bool is_file_list_load_required(ArchiveBrowserViewModel* model) { - size_t array_size = files_array_size(model->files); - - if((model->list_loading) || (array_size >= model->item_cnt)) { - return false; +static void file_list_rollover(ArchiveBrowserViewModel* model) { + if(!model->list_loading && files_array_size(model->files) < model->item_cnt) { + files_array_reset(model->files); } - - if((model->array_offset > 0) && - (model->item_idx < (model->array_offset + FILE_LIST_BUF_LEN / 4))) { - return true; - } - - if(((model->array_offset + array_size) < model->item_cnt) && - (model->item_idx > (int32_t)(model->array_offset + array_size - FILE_LIST_BUF_LEN / 4))) { - return true; - } - - return false; } static bool archive_view_input(InputEvent* event, void* context) { @@ -334,10 +320,26 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->view, ArchiveBrowserViewModel * model, { + int32_t scroll_speed = 1; + if(model->button_held_for_ticks > 5) { + if(model->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = model->button_held_for_ticks > 9 ? 4 : 2; + } + } + if(event->key == InputKeyUp) { - model->item_idx = - ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; - if(is_file_list_load_required(model)) { + if(model->item_idx < scroll_speed) { + model->button_held_for_ticks = 0; + model->item_idx = model->item_cnt - 1; + file_list_rollover(model); + } else { + model->item_idx = + ((model->item_idx - scroll_speed) + model->item_cnt) % + model->item_cnt; + } + if(archive_is_file_list_load_required(model)) { model->list_loading = true; browser->callback(ArchiveBrowserEventLoadPrevItems, browser->context); } @@ -345,9 +347,17 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->callback(ArchiveBrowserEventFavMoveUp, browser->context); } model->scroll_counter = 0; + model->button_held_for_ticks += 1; } else if(event->key == InputKeyDown) { - model->item_idx = (model->item_idx + 1) % model->item_cnt; - if(is_file_list_load_required(model)) { + int32_t count = model->item_cnt; + if(model->item_idx + scroll_speed >= count) { + model->button_held_for_ticks = 0; + model->item_idx = 0; + file_list_rollover(model); + } else { + model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; + } + if(archive_is_file_list_load_required(model)) { model->list_loading = true; browser->callback(ArchiveBrowserEventLoadNextItems, browser->context); } @@ -355,6 +365,7 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->callback(ArchiveBrowserEventFavMoveDown, browser->context); } model->scroll_counter = 0; + model->button_held_for_ticks += 1; } }, true); @@ -391,6 +402,14 @@ static bool archive_view_input(InputEvent* event, void* context) { } } + if(event->type == InputTypeRelease) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { model->button_held_for_ticks = 0; }, + true); + } + return true; } diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 0a000e5ac348..25490aedd3e4 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -100,6 +100,8 @@ typedef struct { int32_t array_offset; int32_t list_offset; size_t scroll_counter; + + uint32_t button_held_for_ticks; } ArchiveBrowserViewModel; void archive_browser_set_callback( diff --git a/applications/main/bad_usb/application.fam b/applications/main/bad_usb/application.fam index 2442dd3aa0fb..5c42c9fa3f17 100644 --- a/applications/main/bad_usb/application.fam +++ b/applications/main/bad_usb/application.fam @@ -1,15 +1,12 @@ App( appid="bad_usb", name="Bad USB", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.MENUEXTERNAL, entry_point="bad_usb_app", - cdefines=["APP_BAD_USB"], - requires=[ - "gui", - "dialogs", - ], stack_size=2 * 1024, icon="A_BadUsb_14", order=70, fap_libs=["assets"], + fap_icon="icon.png", + fap_category="USB", ) diff --git a/applications/main/bad_usb/helpers/ducky_script.c b/applications/main/bad_usb/helpers/ducky_script.c index 47d8a7e0517f..f194178a067e 100644 --- a/applications/main/bad_usb/helpers/ducky_script.c +++ b/applications/main/bad_usb/helpers/ducky_script.c @@ -16,10 +16,11 @@ (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) typedef enum { - WorkerEvtToggle = (1 << 0), - WorkerEvtEnd = (1 << 1), - WorkerEvtConnect = (1 << 2), - WorkerEvtDisconnect = (1 << 3), + WorkerEvtStartStop = (1 << 0), + WorkerEvtPauseResume = (1 << 1), + WorkerEvtEnd = (1 << 2), + WorkerEvtConnect = (1 << 3), + WorkerEvtDisconnect = (1 << 4), } WorkerEvtFlags; static const char ducky_cmd_id[] = {"ID"}; @@ -372,6 +373,7 @@ static int32_t bad_usb_worker(void* context) { BadUsbScript* bad_usb = context; BadUsbWorkerState worker_state = BadUsbStateInit; + BadUsbWorkerState pause_state = BadUsbStateRunning; int32_t delay_val = 0; FURI_LOG_I(WORKER_TAG, "Init"); @@ -406,25 +408,25 @@ static int32_t bad_usb_worker(void* context) { } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected uint32_t flags = bad_usb_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { worker_state = BadUsbStateIdle; // Ready to run - } else if(flags & WorkerEvtToggle) { + } else if(flags & WorkerEvtStartStop) { worker_state = BadUsbStateWillRun; // Will run when USB is connected } bad_usb->st.state = worker_state; } else if(worker_state == BadUsbStateIdle) { // State: ready to start uint32_t flags = bad_usb_flags_get( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriWaitForever); + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever); if(flags & WorkerEvtEnd) { break; - } else if(flags & WorkerEvtToggle) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); + } else if(flags & WorkerEvtStartStop) { // Start executing script + dolphin_deed(DolphinDeedBadUsbPlayScript); delay_val = 0; bad_usb->buf_len = 0; bad_usb->st.line_cur = 0; @@ -442,12 +444,12 @@ static int32_t bad_usb_worker(void* context) { } else if(worker_state == BadUsbStateWillRun) { // State: start on connection uint32_t flags = bad_usb_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); + dolphin_deed(DolphinDeedBadUsbPlayScript); delay_val = 0; bad_usb->buf_len = 0; bad_usb->st.line_cur = 0; @@ -458,17 +460,17 @@ static int32_t bad_usb_worker(void* context) { storage_file_seek(script_file, 0, true); // extra time for PC to recognize Flipper as keyboard flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle, + WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop, FuriFlagWaitAny | FuriFlagNoClear, 1500); if(flags == (unsigned)FuriFlagErrorTimeout) { // If nothing happened - start script execution worker_state = BadUsbStateRunning; - } else if(flags & WorkerEvtToggle) { + } else if(flags & WorkerEvtStartStop) { worker_state = BadUsbStateIdle; - furi_thread_flags_clear(WorkerEvtToggle); + furi_thread_flags_clear(WorkerEvtStartStop); } - } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution + } else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution worker_state = BadUsbStateNotConnected; } bad_usb->st.state = worker_state; @@ -476,18 +478,23 @@ static int32_t bad_usb_worker(void* context) { } else if(worker_state == BadUsbStateRunning) { // State: running uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriFlagWaitAny, + delay_cur); delay_val -= delay_cur; if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { break; - } else if(flags & WorkerEvtToggle) { + } else if(flags & WorkerEvtStartStop) { worker_state = BadUsbStateIdle; // Stop executing script furi_hal_hid_kb_release_all(); } else if(flags & WorkerEvtDisconnect) { worker_state = BadUsbStateNotConnected; // USB disconnected furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtPauseResume) { + pause_state = BadUsbStateRunning; + worker_state = BadUsbStatePaused; // Pause } bad_usb->st.state = worker_state; continue; @@ -526,13 +533,13 @@ static int32_t bad_usb_worker(void* context) { furi_check((flags & FuriFlagError) == 0); } } else if(worker_state == BadUsbStateWaitForBtn) { // State: Wait for button Press - uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriWaitForever); if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { break; - } else if(flags & WorkerEvtToggle) { + } else if(flags & WorkerEvtStartStop) { delay_val = 0; worker_state = BadUsbStateRunning; } else if(flags & WorkerEvtDisconnect) { @@ -542,21 +549,55 @@ static int32_t bad_usb_worker(void* context) { bad_usb->st.state = worker_state; continue; } + } else if(worker_state == BadUsbStatePaused) { // State: Paused + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriWaitForever); + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateIdle; // Stop executing script + bad_usb->st.state = worker_state; + furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // USB disconnected + bad_usb->st.state = worker_state; + furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtPauseResume) { + if(pause_state == BadUsbStateRunning) { + if(delay_val > 0) { + bad_usb->st.state = BadUsbStateDelay; + bad_usb->st.delay_remain = delay_val / 1000; + } else { + bad_usb->st.state = BadUsbStateRunning; + delay_val = 0; + } + worker_state = BadUsbStateRunning; // Resume + } else if(pause_state == BadUsbStateStringDelay) { + bad_usb->st.state = BadUsbStateRunning; + worker_state = BadUsbStateStringDelay; // Resume + } + } + continue; + } } else if(worker_state == BadUsbStateStringDelay) { // State: print string with delays - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, - FuriFlagWaitAny, + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, bad_usb->stringdelay); if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { break; - } else if(flags & WorkerEvtToggle) { + } else if(flags & WorkerEvtStartStop) { worker_state = BadUsbStateIdle; // Stop executing script furi_hal_hid_kb_release_all(); } else if(flags & WorkerEvtDisconnect) { worker_state = BadUsbStateNotConnected; // USB disconnected furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtPauseResume) { + pause_state = BadUsbStateStringDelay; + worker_state = BadUsbStatePaused; // Pause } bad_usb->st.state = worker_state; continue; @@ -651,9 +692,14 @@ void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layou storage_file_free(layout_file); } -void bad_usb_script_toggle(BadUsbScript* bad_usb) { +void bad_usb_script_start_stop(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtStartStop); +} + +void bad_usb_script_pause_resume(BadUsbScript* bad_usb) { furi_assert(bad_usb); - furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle); + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtPauseResume); } BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) { diff --git a/applications/main/bad_usb/helpers/ducky_script.h b/applications/main/bad_usb/helpers/ducky_script.h index cff7239420e5..c8705dbdd10d 100644 --- a/applications/main/bad_usb/helpers/ducky_script.h +++ b/applications/main/bad_usb/helpers/ducky_script.h @@ -16,6 +16,7 @@ typedef enum { BadUsbStateDelay, BadUsbStateStringDelay, BadUsbStateWaitForBtn, + BadUsbStatePaused, BadUsbStateDone, BadUsbStateScriptError, BadUsbStateFileError, @@ -42,7 +43,9 @@ void bad_usb_script_start(BadUsbScript* bad_usb); void bad_usb_script_stop(BadUsbScript* bad_usb); -void bad_usb_script_toggle(BadUsbScript* bad_usb); +void bad_usb_script_start_stop(BadUsbScript* bad_usb); + +void bad_usb_script_pause_resume(BadUsbScript* bad_usb); BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb); diff --git a/applications/main/bad_usb/icon.png b/applications/main/bad_usb/icon.png new file mode 100644 index 000000000000..037474aa3bc9 Binary files /dev/null and b/applications/main/bad_usb/icon.png differ diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index afc2e6f6f136..ad33a124d2a7 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -21,7 +21,10 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { } consumed = true; } else if(event.event == InputKeyOk) { - bad_usb_script_toggle(app->bad_usb_script); + bad_usb_script_start_stop(app->bad_usb_script); + consumed = true; + } else if(event.event == InputKeyRight) { + bad_usb_script_pause_resume(app->bad_usb_script); consumed = true; } } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index 0ab4365b7b1f..fa75b50d038e 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -16,6 +16,7 @@ typedef struct { char file_name[MAX_NAME_LEN]; char layout[MAX_NAME_LEN]; BadUsbState state; + bool pause_wait; uint8_t anim_frame; } BadUsbModel; @@ -31,11 +32,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if(strlen(model->layout) == 0) { furi_string_set(disp_str, "(default)"); } else { - furi_string_reset(disp_str); - furi_string_push_back(disp_str, '('); - for(size_t i = 0; i < strlen(model->layout); i++) - furi_string_push_back(disp_str, model->layout[i]); - furi_string_push_back(disp_str, ')'); + furi_string_printf(disp_str, "(%s)", model->layout); } elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_draw_str( @@ -45,34 +42,42 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); - if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || - (model->state.state == BadUsbStateNotConnected)) { + BadUsbWorkerState state = model->state.state; + + if((state == BadUsbStateIdle) || (state == BadUsbStateDone) || + (state == BadUsbStateNotConnected)) { elements_button_center(canvas, "Run"); elements_button_left(canvas, "Config"); - } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) { + } else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) { elements_button_center(canvas, "Stop"); - } else if(model->state.state == BadUsbStateWaitForBtn) { + if(!model->pause_wait) { + elements_button_right(canvas, "Pause"); + } + } else if(state == BadUsbStatePaused) { + elements_button_center(canvas, "End"); + elements_button_right(canvas, "Resume"); + } else if(state == BadUsbStateWaitForBtn) { elements_button_center(canvas, "Press to continue"); - } else if(model->state.state == BadUsbStateWillRun) { + } else if(state == BadUsbStateWillRun) { elements_button_center(canvas, "Cancel"); } - if(model->state.state == BadUsbStateNotConnected) { + if(state == BadUsbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to USB"); - } else if(model->state.state == BadUsbStateWillRun) { + } else if(state == BadUsbStateWillRun) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); - } else if(model->state.state == BadUsbStateFileError) { + } else if(state == BadUsbStateFileError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); - } else if(model->state.state == BadUsbStateScriptError) { + } else if(state == BadUsbStateScriptError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); @@ -87,12 +92,12 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned( canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); - } else if(model->state.state == BadUsbStateIdle) { + } else if(state == BadUsbStateIdle) { canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); canvas_set_font(canvas, FontBigNumbers); canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateRunning) { + } else if(state == BadUsbStateRunning) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); } else { @@ -105,13 +110,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateDone) { + } else if(state == BadUsbStateDone) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); canvas_set_font(canvas, FontBigNumbers); canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); furi_string_reset(disp_str); canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateDelay) { + } else if(state == BadUsbStateDelay) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { @@ -129,6 +134,22 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned( canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); + } else if((state == BadUsbStatePaused) || (state == BadUsbStateWaitForBtn)) { + if(model->anim_frame == 0) { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); + } else { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); + } + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 127, 50, AlignRight, AlignBottom, "Paused"); + furi_string_reset(disp_str); } else { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); } @@ -142,7 +163,27 @@ static bool bad_usb_input_callback(InputEvent* event, void* context) { bool consumed = false; if(event->type == InputTypeShort) { - if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) { + if(event->key == InputKeyLeft) { + consumed = true; + furi_assert(bad_usb->callback); + bad_usb->callback(event->key, bad_usb->context); + } else if(event->key == InputKeyOk) { + with_view_model( + bad_usb->view, BadUsbModel * model, { model->pause_wait = false; }, true); + consumed = true; + furi_assert(bad_usb->callback); + bad_usb->callback(event->key, bad_usb->context); + } else if(event->key == InputKeyRight) { + with_view_model( + bad_usb->view, + BadUsbModel * model, + { + if((model->state.state == BadUsbStateRunning) || + (model->state.state == BadUsbStateDelay)) { + model->pause_wait = true; + } + }, + true); consumed = true; furi_assert(bad_usb->callback); bad_usb->callback(event->key, bad_usb->context); @@ -215,6 +256,9 @@ void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { { memcpy(&(model->state), st, sizeof(BadUsbState)); model->anim_frame ^= 1; + if(model->state.state == BadUsbStatePaused) { + model->pause_wait = false; + } }, true); } diff --git a/applications/main/fap_loader/application.fam b/applications/main/fap_loader/application.fam deleted file mode 100644 index b0e67cd42e3b..000000000000 --- a/applications/main/fap_loader/application.fam +++ /dev/null @@ -1,15 +0,0 @@ -App( - appid="fap_loader", - name="Applications", - apptype=FlipperAppType.APP, - entry_point="fap_loader_app", - cdefines=["APP_FAP_LOADER"], - requires=[ - "gui", - "storage", - "loader", - ], - stack_size=int(1.5 * 1024), - icon="A_Plugins_14", - order=90, -) diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c deleted file mode 100644 index 7af5244ae493..000000000000 --- a/applications/main/fap_loader/fap_loader_app.c +++ /dev/null @@ -1,216 +0,0 @@ -#include "fap_loader_app.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define TAG "FapLoader" - -struct FapLoader { - FlipperApplication* app; - Storage* storage; - DialogsApp* dialogs; - Gui* gui; - FuriString* fap_path; - ViewDispatcher* view_dispatcher; - Loading* loading; -}; - -bool fap_loader_load_name_and_icon( - FuriString* path, - Storage* storage, - uint8_t** icon_ptr, - FuriString* item_name) { - FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); - - FlipperApplicationPreloadStatus preload_res = - flipper_application_preload_manifest(app, furi_string_get_cstr(path)); - - bool load_success = false; - - if(preload_res == FlipperApplicationPreloadStatusSuccess) { - const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); - if(manifest->has_icon) { - memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); - } - furi_string_set(item_name, manifest->name); - load_success = true; - } else { - FURI_LOG_E(TAG, "FAP Loader failed to preload %s", furi_string_get_cstr(path)); - load_success = false; - } - - flipper_application_free(app); - return load_success; -} - -static bool fap_loader_item_callback( - FuriString* path, - void* context, - uint8_t** icon_ptr, - FuriString* item_name) { - FapLoader* fap_loader = context; - furi_assert(fap_loader); - return fap_loader_load_name_and_icon(path, fap_loader->storage, icon_ptr, item_name); -} - -static bool fap_loader_run_selected_app(FapLoader* loader) { - furi_assert(loader); - - FuriString* error_message; - - error_message = furi_string_alloc_set("unknown error"); - - bool file_selected = false; - bool show_error = true; - do { - file_selected = true; - loader->app = flipper_application_alloc(loader->storage, firmware_api_interface); - size_t start = furi_get_tick(); - - FURI_LOG_I(TAG, "FAP Loader is loading %s", furi_string_get_cstr(loader->fap_path)); - - FlipperApplicationPreloadStatus preload_res = - flipper_application_preload(loader->app, furi_string_get_cstr(loader->fap_path)); - if(preload_res != FlipperApplicationPreloadStatusSuccess) { - const char* err_msg = flipper_application_preload_status_to_string(preload_res); - furi_string_printf(error_message, "Preload failed: %s", err_msg); - FURI_LOG_E( - TAG, - "FAP Loader failed to preload %s: %s", - furi_string_get_cstr(loader->fap_path), - err_msg); - break; - } - - FURI_LOG_I(TAG, "FAP Loader is mapping"); - FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(loader->app); - if(load_status != FlipperApplicationLoadStatusSuccess) { - const char* err_msg = flipper_application_load_status_to_string(load_status); - furi_string_printf(error_message, "Load failed: %s", err_msg); - FURI_LOG_E( - TAG, - "FAP Loader failed to map to memory %s: %s", - furi_string_get_cstr(loader->fap_path), - err_msg); - break; - } - - FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); - FURI_LOG_I(TAG, "FAP Loader is starting app"); - - FuriThread* thread = flipper_application_spawn(loader->app, NULL); - - /* This flag is set by the debugger - to break on app start */ - if(furi_hal_debug_is_gdb_session_active()) { - FURI_LOG_W(TAG, "Triggering BP for debugger"); - /* After hitting this, you can set breakpoints in your .fap's code - * Note that you have to toggle breakpoints that were set before */ - __asm volatile("bkpt 0"); - } - - FuriString* app_name = furi_string_alloc(); - path_extract_filename_no_ext(furi_string_get_cstr(loader->fap_path), app_name); - furi_thread_set_appid(thread, furi_string_get_cstr(app_name)); - furi_string_free(app_name); - - furi_thread_start(thread); - furi_thread_join(thread); - - show_error = false; - int ret = furi_thread_get_return_code(thread); - - FURI_LOG_I(TAG, "FAP app returned: %i", ret); - } while(0); - - if(show_error) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); - dialog_message_set_buttons(message, NULL, NULL, NULL); - - FuriString* buffer; - buffer = furi_string_alloc(); - furi_string_printf(buffer, "%s", furi_string_get_cstr(error_message)); - furi_string_replace(buffer, ":", "\n"); - dialog_message_set_text( - message, furi_string_get_cstr(buffer), 64, 32, AlignCenter, AlignCenter); - - dialog_message_show(loader->dialogs, message); - dialog_message_free(message); - furi_string_free(buffer); - } - - furi_string_free(error_message); - - if(file_selected) { - flipper_application_free(loader->app); - } - - return file_selected; -} - -static bool fap_loader_select_app(FapLoader* loader) { - const DialogsFileBrowserOptions browser_options = { - .extension = ".fap", - .skip_assets = true, - .icon = &I_unknown_10px, - .hide_ext = true, - .item_loader_callback = fap_loader_item_callback, - .item_loader_context = loader, - .base_path = EXT_PATH("apps"), - }; - - return dialog_file_browser_show( - loader->dialogs, loader->fap_path, loader->fap_path, &browser_options); -} - -static FapLoader* fap_loader_alloc(const char* path) { - FapLoader* loader = malloc(sizeof(FapLoader)); //-V799 - loader->fap_path = furi_string_alloc_set(path); - loader->storage = furi_record_open(RECORD_STORAGE); - loader->dialogs = furi_record_open(RECORD_DIALOGS); - loader->gui = furi_record_open(RECORD_GUI); - loader->view_dispatcher = view_dispatcher_alloc(); - loader->loading = loading_alloc(); - view_dispatcher_attach_to_gui( - loader->view_dispatcher, loader->gui, ViewDispatcherTypeFullscreen); - view_dispatcher_add_view(loader->view_dispatcher, 0, loading_get_view(loader->loading)); - return loader; -} //-V773 - -static void fap_loader_free(FapLoader* loader) { - view_dispatcher_remove_view(loader->view_dispatcher, 0); - loading_free(loader->loading); - view_dispatcher_free(loader->view_dispatcher); - furi_string_free(loader->fap_path); - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_DIALOGS); - furi_record_close(RECORD_STORAGE); - free(loader); -} - -int32_t fap_loader_app(void* p) { - FapLoader* loader; - if(p) { - loader = fap_loader_alloc((const char*)p); - view_dispatcher_switch_to_view(loader->view_dispatcher, 0); - fap_loader_run_selected_app(loader); - } else { - loader = fap_loader_alloc(EXT_PATH("apps")); - while(fap_loader_select_app(loader)) { - view_dispatcher_switch_to_view(loader->view_dispatcher, 0); - fap_loader_run_selected_app(loader); - }; - } - - fap_loader_free(loader); - return 0; -} diff --git a/applications/main/fap_loader/fap_loader_app.h b/applications/main/fap_loader/fap_loader_app.h deleted file mode 100644 index 9ed725efe528..000000000000 --- a/applications/main/fap_loader/fap_loader_app.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct FapLoader FapLoader; - -/** - * @brief Load name and icon from FAP file. - * - * @param path Path to FAP file. - * @param storage Storage instance. - * @param icon_ptr Icon pointer. - * @param item_name Application name. - * @return true if icon and name were loaded successfully. - */ -bool fap_loader_load_name_and_icon( - FuriString* path, - Storage* storage, - uint8_t** icon_ptr, - FuriString* item_name); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index efeb8b6fe8bc..76391992178c 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -1,12 +1,12 @@ App( appid="gpio", name="GPIO", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.MENUEXTERNAL, entry_point="gpio_app", - cdefines=["APP_GPIO"], - requires=["gui"], stack_size=1 * 1024, icon="A_GPIO_14", order=50, fap_libs=["assets"], + fap_icon="icon.png", + fap_category="GPIO", ) diff --git a/applications/main/gpio/icon.png b/applications/main/gpio/icon.png new file mode 100644 index 000000000000..4a6eccf05843 Binary files /dev/null and b/applications/main/gpio/icon.png differ diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c index 0272677934c5..421936488007 100644 --- a/applications/main/gpio/scenes/gpio_scene_start.c +++ b/applications/main/gpio/scenes/gpio_scene_start.c @@ -89,7 +89,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == GpioStartEventUsbUart) { scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); if(!furi_hal_usb_is_locked()) { - DOLPHIN_DEED(DolphinDeedGpioUartBridge); + dolphin_deed(DolphinDeedGpioUartBridge); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); } else { scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 06968bba48f6..a8faa629cea2 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -1,25 +1,21 @@ App( appid="ibutton", name="iButton", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.MENUEXTERNAL, targets=["f7"], entry_point="ibutton_app", - cdefines=["APP_IBUTTON"], - requires=[ - "gui", - "dialogs", - ], - provides=["ibutton_start"], icon="A_iButton_14", stack_size=2 * 1024, order=60, fap_libs=["assets"], + fap_icon="icon.png", + fap_category="iButton", ) App( appid="ibutton_start", apptype=FlipperAppType.STARTUP, + targets=["f7"], entry_point="ibutton_on_system_start", - requires=["ibutton"], order=60, ) diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 79999adb28cd..ad5b233b56e6 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -282,14 +282,14 @@ int32_t ibutton_app(void* arg) { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); if(key_loaded) { //-V547 scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); } diff --git a/applications/main/ibutton/icon.png b/applications/main/ibutton/icon.png new file mode 100644 index 000000000000..2fdaf123a657 Binary files /dev/null and b/applications/main/ibutton/icon.png differ diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c b/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c index 587cb748cd4e..b293af952cef 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c @@ -12,15 +12,15 @@ void ibutton_scene_delete_confirm_on_enter(void* context) { widget_add_button_element( widget, GuiButtonTypeRight, "Delete", ibutton_widget_callback, context); - furi_string_printf(tmp, "Delete %s?", ibutton->key_name); - widget_add_string_element( - widget, 128 / 2, 0, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp)); + furi_string_printf(tmp, "\e#Delete %s?\e#", ibutton->key_name); + widget_add_text_box_element( + widget, 0, 0, 128, 23, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), false); furi_string_reset(tmp); ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp); widget_add_string_multiline_element( - widget, 128 / 2, 16, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp)); + widget, 128 / 2, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp)); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); furi_string_free(tmp); diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c index a840fb7b7ec3..f360c3ac43af 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read.c @@ -38,7 +38,7 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); - DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); + dolphin_deed(DolphinDeedIbuttonReadSuccess); } else { scene_manager_next_scene(scene_manager, iButtonSceneReadError); diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c index 716f72c7d3da..1555f2cc20c3 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c @@ -75,7 +75,7 @@ bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event scene_manager_next_scene(scene_manager, iButtonSceneSaveName); } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(scene_manager, iButtonSceneEmulate); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else if(event.event == SubmenuIndexViewData) { scene_manager_next_scene(scene_manager, iButtonSceneViewData); } else if(event.event == SubmenuIndexWriteBlank) { diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_name.c b/applications/main/ibutton/scenes/ibutton_scene_save_name.c index 4ad0315e54e5..7bd49df8337e 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_name.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_name.c @@ -58,9 +58,9 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { // Nothing, do not count editing as saving } else if(scene_manager_has_previous_scene( ibutton->scene_manager, iButtonSceneAddType)) { - DOLPHIN_DEED(DolphinDeedIbuttonAdd); + dolphin_deed(DolphinDeedIbuttonAdd); } else { - DOLPHIN_DEED(DolphinDeedIbuttonSave); + dolphin_deed(DolphinDeedIbuttonSave); } } else { diff --git a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c index 80fca28b5eea..fc0cf42e2ffe 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c @@ -48,7 +48,7 @@ bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent even consumed = true; if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(scene_manager, iButtonSceneEmulate); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else if(event.event == SubmenuIndexWriteBlank) { ibutton->write_mode = iButtonWriteModeBlank; scene_manager_next_scene(scene_manager, iButtonSceneWrite); diff --git a/applications/main/ibutton/scenes/ibutton_scene_start.c b/applications/main/ibutton/scenes/ibutton_scene_start.c index 37bf96f39f00..63a4cf869d32 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_start.c +++ b/applications/main/ibutton/scenes/ibutton_scene_start.c @@ -33,7 +33,7 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == SubmenuIndexRead) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); - DOLPHIN_DEED(DolphinDeedIbuttonRead); + dolphin_deed(DolphinDeedIbuttonRead); } else if(event.event == SubmenuIndexSaved) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); } else if(event.event == SubmenuIndexAdd) { diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index e5483e9ffa91..b78b088a7277 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -1,25 +1,21 @@ App( appid="infrared", name="Infrared", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.MENUEXTERNAL, entry_point="infrared_app", targets=["f7"], - cdefines=["APP_INFRARED"], - requires=[ - "gui", - "dialogs", - ], - provides=["infrared_start"], icon="A_Infrared_14", stack_size=3 * 1024, order=40, fap_libs=["assets"], + fap_icon="icon.png", + fap_category="Infrared", ) App( appid="infrared_start", apptype=FlipperAppType.STARTUP, + targets=["f7"], entry_point="infrared_on_system_start", - requires=["infrared"], order=20, ) diff --git a/applications/main/infrared/icon.png b/applications/main/infrared/icon.png new file mode 100644 index 000000000000..22c986180a2b Binary files /dev/null and b/applications/main/infrared/icon.png differ diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index 4f450496d4c1..5957cdb13361 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -312,13 +312,14 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { if(infrared_signal_is_raw(signal)) { InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); - infrared_worker_set_raw_signal(infrared->worker, raw->timings, raw->timings_size); + infrared_worker_set_raw_signal( + infrared->worker, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle); } else { InfraredMessage* message = infrared_signal_get_message(signal); infrared_worker_set_decoded_signal(infrared->worker, message); } - DOLPHIN_DEED(DolphinDeedIrSend); + dolphin_deed(DolphinDeedIrSend); infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); infrared_worker_tx_set_get_signal_callback( diff --git a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c index d55d8d0a6dba..96f28cc48917 100644 --- a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -70,7 +70,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e uint32_t record_count; if(infrared_brute_force_start( brute_force, infrared_custom_event_get_value(event.event), &record_count)) { - DOLPHIN_DEED(DolphinDeedIrSend); + dolphin_deed(DolphinDeedIrSend); infrared_scene_universal_common_show_popup(infrared, record_count); } else { scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); diff --git a/applications/main/infrared/scenes/infrared_scene_learn.c b/applications/main/infrared/scenes/infrared_scene_learn.c index 48699a71fc99..46646c6d69a0 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn.c +++ b/applications/main/infrared/scenes/infrared_scene_learn.c @@ -28,7 +28,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { if(event.event == InfraredCustomEventTypeSignalReceived) { infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess); - DOLPHIN_DEED(DolphinDeedIrLearnSuccess); + dolphin_deed(DolphinDeedIrLearnSuccess); consumed = true; } } diff --git a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c index a8772a985cc3..104a4cb7b65f 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c @@ -50,7 +50,7 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e if(success) { scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); - DOLPHIN_DEED(DolphinDeedIrSave); + dolphin_deed(DolphinDeedIrSave); } else { dialog_message_show_storage_error(infrared->dialogs, "Failed to save file"); const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index 8fe1bac4dc6b..cad07fbf777c 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -1,27 +1,21 @@ App( appid="lfrfid", name="125 kHz RFID", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.MENUEXTERNAL, targets=["f7"], entry_point="lfrfid_app", - cdefines=["APP_LF_RFID"], - requires=[ - "gui", - "dialogs", - ], - provides=[ - "lfrfid_start", - ], icon="A_125khz_14", stack_size=2 * 1024, order=20, fap_libs=["assets"], + fap_icon="icon.png", + fap_category="RFID", ) App( appid="lfrfid_start", + targets=["f7"], apptype=FlipperAppType.STARTUP, entry_point="lfrfid_on_system_start", - requires=["lfrfid"], order=50, ) diff --git a/applications/main/lfrfid/icon.png b/applications/main/lfrfid/icon.png new file mode 100644 index 000000000000..ce01284a2c1f Binary files /dev/null and b/applications/main/lfrfid/icon.png differ diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index 85a00eea00d0..edde23804c27 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -183,14 +183,14 @@ int32_t lfrfid_app(void* p) { view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); } else { furi_string_set(app->file_path, args); lfrfid_load_key_data(app, app->file_path, true); view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); } } else { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index fac2ebcec541..1aed9a03c379 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -58,12 +58,12 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) if(event.event == SubmenuIndexASK) { app->read_type = LFRFIDWorkerReadTypeASKOnly; scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); - DOLPHIN_DEED(DolphinDeedRfidRead); + dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexPSK) { app->read_type = LFRFIDWorkerReadTypePSKOnly; scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); - DOLPHIN_DEED(DolphinDeedRfidRead); + dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_read.c index 5f19597282a7..d04ce41d4a6c 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read.c @@ -81,7 +81,7 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { notification_message(app->notifications, &sequence_success); furi_string_reset(app->file_name); scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess); - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); + dolphin_deed(DolphinDeedRfidReadSuccess); consumed = true; } else if(event.event == LfRfidEventReadStartPSK) { if(app->read_type == LFRFIDWorkerReadTypeAuto) { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c index 081c479123fb..36f0d6d93a32 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c @@ -44,7 +44,7 @@ bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) consumed = true; } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); consumed = true; } scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c index 87e110f185bf..771f2f60387b 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c @@ -59,9 +59,9 @@ bool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) { // Nothing, do not count editing as saving } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) { - DOLPHIN_DEED(DolphinDeedRfidAdd); + dolphin_deed(DolphinDeedRfidAdd); } else { - DOLPHIN_DEED(DolphinDeedRfidSave); + dolphin_deed(DolphinDeedRfidSave); } } else { scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c index d3c3d389a8d6..206074e9b080 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -43,7 +43,7 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); consumed = true; } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c index 2d83ba53b3b7..8a01fc707b1e 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c @@ -49,7 +49,7 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexRead) { scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead); scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); - DOLPHIN_DEED(DolphinDeedRfidRead); + dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexSaved) { // Like in the other apps, explicitly save the scene state diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index f091270455fb..25a232056ce0 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -15,6 +15,43 @@ App( order=30, ) +# Parser plugins + +App( + appid="all_in_one_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="all_in_one_plugin_ep", + requires=["nfc"], +) + +App( + appid="opal_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="opal_plugin_ep", + requires=["nfc"], +) + +App( + appid="troika_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="troika_plugin_ep", + requires=["nfc"], +) + +App( + appid="plantain_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="plantain_plugin_ep", + requires=["nfc"], +) + +App( + appid="two_cities_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="two_cities_plugin_ep", + requires=["nfc"], +) + App( appid="nfc_start", apptype=FlipperAppType.STARTUP, diff --git a/applications/main/nfc/helpers/mf_classic_key_cache.c b/applications/main/nfc/helpers/mf_classic_key_cache.c new file mode 100644 index 000000000000..ef6a0f6bcaa6 --- /dev/null +++ b/applications/main/nfc/helpers/mf_classic_key_cache.c @@ -0,0 +1,211 @@ +#include "mf_classic_key_cache.h" + +#include +#include + +#define NFC_APP_KEYS_EXTENSION ".keys" +#define NFC_APP_KEY_CACHE_FOLDER "/ext/nfc/.cache" + +static const char* mf_classic_key_cache_file_header = "Flipper NFC keys"; +static const uint32_t mf_classic_key_cache_file_version = 1; + +struct MfClassicKeyCache { + MfClassicDeviceKeys keys; + MfClassicKeyType current_key_type; + uint8_t current_sector; +}; + +static void nfc_get_key_cache_file_path(const uint8_t* uid, size_t uid_len, FuriString* path) { + furi_string_printf(path, "%s/", NFC_APP_KEY_CACHE_FOLDER); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(path, "%02X", uid[i]); + } + furi_string_cat_printf(path, "%s", NFC_APP_KEYS_EXTENSION); +} + +MfClassicKeyCache* mf_classic_key_cache_alloc() { + MfClassicKeyCache* instance = malloc(sizeof(MfClassicKeyCache)); + + return instance; +} + +void mf_classic_key_cache_free(MfClassicKeyCache* instance) { + furi_assert(instance); + + free(instance); +} + +bool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* data) { + UNUSED(instance); + furi_assert(data); + + size_t uid_len = 0; + const uint8_t* uid = mf_classic_get_uid(data, &uid_len); + FuriString* file_path = furi_string_alloc(); + nfc_get_key_cache_file_path(uid, uid_len, file_path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + FuriString* temp_str = furi_string_alloc(); + bool save_success = false; + do { + if(!storage_simply_mkdir(storage, NFC_APP_KEY_CACHE_FOLDER)) break; + if(!storage_simply_remove(storage, furi_string_get_cstr(file_path))) break; + if(!flipper_format_buffered_file_open_always(ff, furi_string_get_cstr(file_path))) break; + + if(!flipper_format_write_header_cstr( + ff, mf_classic_key_cache_file_header, mf_classic_key_cache_file_version)) + break; + if(!flipper_format_write_string_cstr( + ff, "Mifare Classic type", mf_classic_get_device_name(data, NfcDeviceNameTypeShort))) + break; + if(!flipper_format_write_hex_uint64(ff, "Key A map", &data->key_a_mask, 1)) break; + if(!flipper_format_write_hex_uint64(ff, "Key B map", &data->key_b_mask, 1)) break; + + uint8_t sector_num = mf_classic_get_total_sectors_num(data->type); + bool key_save_success = true; + for(size_t i = 0; (i < sector_num) && (key_save_success); i++) { + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); + if(FURI_BIT(data->key_a_mask, i)) { + furi_string_printf(temp_str, "Key A sector %d", i); + key_save_success = flipper_format_write_hex( + ff, furi_string_get_cstr(temp_str), sec_tr->key_a.data, sizeof(MfClassicKey)); + } + if(!key_save_success) break; + if(FURI_BIT(data->key_b_mask, i)) { + furi_string_printf(temp_str, "Key B sector %d", i); + key_save_success = flipper_format_write_hex( + ff, furi_string_get_cstr(temp_str), sec_tr->key_b.data, sizeof(MfClassicKey)); + } + } + save_success = key_save_success; + } while(false); + + flipper_format_free(ff); + furi_string_free(temp_str); + furi_string_free(file_path); + furi_record_close(RECORD_STORAGE); + + return save_success; +} + +bool mf_classic_key_cache_load(MfClassicKeyCache* instance, const uint8_t* uid, size_t uid_len) { + furi_assert(instance); + furi_assert(uid); + + FuriString* file_path = furi_string_alloc(); + nfc_get_key_cache_file_path(uid, uid_len, file_path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + FuriString* temp_str = furi_string_alloc(); + bool load_success = false; + do { + if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(file_path))) break; + + uint32_t version = 0; + if(!flipper_format_read_header(ff, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, mf_classic_key_cache_file_header)) break; + if(version != mf_classic_key_cache_file_version) break; + + if(!flipper_format_read_hex_uint64(ff, "Key A map", &instance->keys.key_a_mask, 1)) break; + if(!flipper_format_read_hex_uint64(ff, "Key B map", &instance->keys.key_b_mask, 1)) break; + + bool key_read_success = true; + for(size_t i = 0; (i < MF_CLASSIC_TOTAL_SECTORS_MAX) && (key_read_success); i++) { + if(FURI_BIT(instance->keys.key_a_mask, i)) { + furi_string_printf(temp_str, "Key A sector %d", i); + key_read_success = flipper_format_read_hex( + ff, + furi_string_get_cstr(temp_str), + instance->keys.key_a[i].data, + sizeof(MfClassicKey)); + } + if(!key_read_success) break; + if(FURI_BIT(instance->keys.key_b_mask, i)) { + furi_string_printf(temp_str, "Key B sector %d", i); + key_read_success = flipper_format_read_hex( + ff, + furi_string_get_cstr(temp_str), + instance->keys.key_b[i].data, + sizeof(MfClassicKey)); + } + } + load_success = key_read_success; + load_success = true; + } while(false); + + flipper_format_buffered_file_close(ff); + flipper_format_free(ff); + furi_string_free(temp_str); + furi_string_free(file_path); + furi_record_close(RECORD_STORAGE); + + return load_success; +} + +void mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfClassicData* data) { + furi_assert(instance); + furi_assert(data); + + mf_classic_key_cache_reset(instance); + instance->keys.key_a_mask = data->key_a_mask; + instance->keys.key_b_mask = data->key_b_mask; + for(size_t i = 0; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) { + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); + + if(FURI_BIT(data->key_a_mask, i)) { + instance->keys.key_a[i] = sec_tr->key_a; + } + if(FURI_BIT(data->key_b_mask, i)) { + instance->keys.key_b[i] = sec_tr->key_b; + } + } +} + +bool mf_classic_key_cahce_get_next_key( + MfClassicKeyCache* instance, + uint8_t* sector_num, + MfClassicKey* key, + MfClassicKeyType* key_type) { + furi_assert(instance); + furi_assert(sector_num); + furi_assert(key); + furi_assert(key_type); + + bool next_key_found = false; + for(uint8_t i = instance->current_sector; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) { + if(FURI_BIT(instance->keys.key_a_mask, i)) { + FURI_BIT_CLEAR(instance->keys.key_a_mask, i); + *key = instance->keys.key_a[i]; + *key_type = MfClassicKeyTypeA; + *sector_num = i; + + next_key_found = true; + break; + } + if(FURI_BIT(instance->keys.key_b_mask, i)) { + FURI_BIT_CLEAR(instance->keys.key_b_mask, i); + *key = instance->keys.key_b[i]; + *key_type = MfClassicKeyTypeB; + *sector_num = i; + + next_key_found = true; + instance->current_sector = i; + break; + } + } + + return next_key_found; +} + +void mf_classic_key_cache_reset(MfClassicKeyCache* instance) { + furi_assert(instance); + + instance->current_key_type = MfClassicKeyTypeA; + instance->current_sector = 0; + instance->keys.key_a_mask = 0; + instance->keys.key_b_mask = 0; +} diff --git a/applications/main/nfc/helpers/mf_classic_key_cache.h b/applications/main/nfc/helpers/mf_classic_key_cache.h new file mode 100644 index 000000000000..407c6e28bd42 --- /dev/null +++ b/applications/main/nfc/helpers/mf_classic_key_cache.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfClassicKeyCache MfClassicKeyCache; + +MfClassicKeyCache* mf_classic_key_cache_alloc(); + +void mf_classic_key_cache_free(MfClassicKeyCache* instance); + +bool mf_classic_key_cache_load(MfClassicKeyCache* instance, const uint8_t* uid, size_t uid_len); + +void mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfClassicData* data); + +bool mf_classic_key_cahce_get_next_key( + MfClassicKeyCache* instance, + uint8_t* sector_num, + MfClassicKey* key, + MfClassicKeyType* key_type); + +bool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* data); + +void mf_classic_key_cache_reset(MfClassicKeyCache* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/mf_dict.c b/applications/main/nfc/helpers/mf_dict.c new file mode 100644 index 000000000000..094dc0e27614 --- /dev/null +++ b/applications/main/nfc/helpers/mf_dict.c @@ -0,0 +1,277 @@ +#include "mf_dict.h" + +#include +#include + +#include + +#define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc") +#define MF_CLASSIC_DICT_SYSTEM_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc") +#define MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_dict.nfc") + +#define TAG "MfDict" + +#define NFC_MF_CLASSIC_KEY_LEN (13) + +struct MfDict { + Stream* stream; + uint32_t total_keys; +}; + +typedef struct { + const char* path; + FS_OpenMode open_mode; +} MfDictFile; + +static const MfDictFile mf_dict_file[MfDictTypeNum] = { + [MfDictTypeUser] = + { + .path = MF_CLASSIC_DICT_USER_PATH, + .open_mode = FSOM_OPEN_ALWAYS, + }, + [MfDictTypeSystem] = + { + .path = MF_CLASSIC_DICT_SYSTEM_PATH, + .open_mode = FSOM_OPEN_EXISTING, + }, + [MfDictTypeUnitTest] = + { + .path = MF_CLASSIC_DICT_UNIT_TEST_PATH, + .open_mode = FSOM_OPEN_ALWAYS, + }, +}; + +bool mf_dict_check_presence(MfDictType dict_type) { + furi_assert(dict_type < MfDictTypeNum); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + const char* path = mf_dict_file[dict_type].path; + bool dict_present = storage_common_stat(storage, path, NULL) == FSE_OK; + + furi_record_close(RECORD_STORAGE); + + return dict_present; +} + +MfDict* mf_dict_alloc(MfDictType dict_type) { + furi_assert(dict_type < MfDictTypeNum); + + MfDict* dict = malloc(sizeof(MfDict)); + Storage* storage = furi_record_open(RECORD_STORAGE); + dict->stream = buffered_file_stream_alloc(storage); + furi_record_close(RECORD_STORAGE); + + bool dict_loaded = false; + do { + if(!buffered_file_stream_open( + dict->stream, + mf_dict_file[dict_type].path, + FSAM_READ_WRITE, + mf_dict_file[dict_type].open_mode)) { + buffered_file_stream_close(dict->stream); + break; + } + + // Check for new line ending + if(!stream_eof(dict->stream)) { + if(!stream_seek(dict->stream, -1, StreamOffsetFromEnd)) break; + uint8_t last_char = 0; + if(stream_read(dict->stream, &last_char, 1) != 1) break; + if(last_char != '\n') { + FURI_LOG_D(TAG, "Adding new line ending"); + if(stream_write_char(dict->stream, '\n') != 1) break; + } + if(!stream_rewind(dict->stream)) break; + } + + // Read total amount of keys + FuriString* next_line; + next_line = furi_string_alloc(); + while(true) { + if(!stream_read_line(dict->stream, next_line)) { + FURI_LOG_T(TAG, "No keys left in dict"); + break; + } + FURI_LOG_T( + TAG, + "Read line: %s, len: %zu", + furi_string_get_cstr(next_line), + furi_string_size(next_line)); + if(furi_string_get_char(next_line, 0) == '#') continue; + if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + dict->total_keys++; + } + furi_string_free(next_line); + stream_rewind(dict->stream); + + dict_loaded = true; + FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys); + } while(false); + + if(!dict_loaded) { + buffered_file_stream_close(dict->stream); + free(dict); + dict = NULL; + } + + return dict; +} + +void mf_dict_free(MfDict* dict) { + furi_assert(dict); + furi_assert(dict->stream); + + buffered_file_stream_close(dict->stream); + stream_free(dict->stream); + free(dict); +} + +static void mf_dict_int_to_str(const uint8_t* key_int, FuriString* key_str) { + furi_string_reset(key_str); + for(size_t i = 0; i < 6; i++) { + furi_string_cat_printf(key_str, "%02X", key_int[i]); + } +} + +static void mf_dict_str_to_int(FuriString* key_str, uint64_t* key_int) { + uint8_t key_byte_tmp; + + *key_int = 0ULL; + for(uint8_t i = 0; i < 12; i += 2) { + args_char_to_hex( + furi_string_get_char(key_str, i), furi_string_get_char(key_str, i + 1), &key_byte_tmp); + *key_int |= (uint64_t)key_byte_tmp << (8 * (5 - i / 2)); + } +} + +uint32_t mf_dict_get_total_keys(MfDict* dict) { + furi_assert(dict); + + return dict->total_keys; +} + +bool mf_dict_rewind(MfDict* dict) { + furi_assert(dict); + furi_assert(dict->stream); + + return stream_rewind(dict->stream); +} + +static bool mf_dict_get_next_key_str(MfDict* dict, FuriString* key) { + furi_assert(dict); + furi_assert(dict->stream); + + bool key_read = false; + furi_string_reset(key); + while(!key_read) { + if(!stream_read_line(dict->stream, key)) break; + if(furi_string_get_char(key, 0) == '#') continue; + if(furi_string_size(key) != NFC_MF_CLASSIC_KEY_LEN) continue; + furi_string_left(key, 12); + key_read = true; + } + + return key_read; +} + +bool mf_dict_get_next_key(MfDict* dict, MfClassicKey* key) { + furi_assert(dict); + furi_assert(dict->stream); + + FuriString* temp_key; + uint64_t key_int = 0; + temp_key = furi_string_alloc(); + bool key_read = mf_dict_get_next_key_str(dict, temp_key); + if(key_read) { + mf_dict_str_to_int(temp_key, &key_int); + nfc_util_num2bytes(key_int, 6, key->data); + } + furi_string_free(temp_key); + return key_read; +} + +static bool mf_dict_is_key_present_str(MfDict* dict, FuriString* key) { + furi_assert(dict); + furi_assert(dict->stream); + + FuriString* next_line; + next_line = furi_string_alloc(); + + bool key_found = false; + stream_rewind(dict->stream); + while(!key_found) { //-V654 + if(!stream_read_line(dict->stream, next_line)) break; + if(furi_string_get_char(next_line, 0) == '#') continue; + if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; + furi_string_left(next_line, 12); + if(!furi_string_equal(key, next_line)) continue; + key_found = true; + } + + furi_string_free(next_line); + return key_found; +} + +bool mf_dict_is_key_present(MfDict* dict, const MfClassicKey* key) { + FuriString* temp_key; + + temp_key = furi_string_alloc(); + mf_dict_int_to_str(key->data, temp_key); + bool key_found = mf_dict_is_key_present_str(dict, temp_key); + furi_string_free(temp_key); + return key_found; +} + +static bool mf_dict_add_key_str(MfDict* dict, FuriString* key) { + furi_assert(dict); + furi_assert(dict->stream); + + furi_string_cat_printf(key, "\n"); + + bool key_added = false; + do { + if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; + if(!stream_insert_string(dict->stream, key)) break; + dict->total_keys++; + key_added = true; + } while(false); + + furi_string_left(key, 12); + return key_added; +} + +bool mf_dict_add_key(MfDict* dict, const MfClassicKey* key) { + furi_assert(dict); + furi_assert(dict->stream); + + FuriString* temp_key; + temp_key = furi_string_alloc(); + mf_dict_int_to_str(key->data, temp_key); + bool key_added = mf_dict_add_key_str(dict, temp_key); + + furi_string_free(temp_key); + return key_added; +} + +bool mf_dict_delete_key(MfDict* dict, const MfClassicKey* key) { + furi_assert(dict); + furi_assert(dict->stream); + + bool key_removed = false; + MfClassicKey temp_key = {}; + + mf_dict_rewind(dict); + while(!key_removed) { + if(!mf_dict_get_next_key(dict, &temp_key)) break; + if(memcmp(temp_key.data, key->data, sizeof(MfClassicKey)) == 0) { + stream_seek(dict->stream, -NFC_MF_CLASSIC_KEY_LEN, StreamOffsetFromCurrent); + if(!stream_delete(dict->stream, NFC_MF_CLASSIC_KEY_LEN)) break; + dict->total_keys--; + key_removed = true; + } + } + mf_dict_rewind(dict); + + return key_removed; +} diff --git a/applications/main/nfc/helpers/mf_dict.h b/applications/main/nfc/helpers/mf_dict.h new file mode 100644 index 000000000000..33c6f32e7420 --- /dev/null +++ b/applications/main/nfc/helpers/mf_dict.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MfDictTypeUser, + MfDictTypeSystem, + MfDictTypeUnitTest, + + MfDictTypeNum, +} MfDictType; + +typedef struct MfDict MfDict; + +bool mf_dict_check_presence(MfDictType dict_type); + +/** Allocate MfDict instance + * + * @param[in] dict_type The dictionary type + * + * @return MfDict instance + */ +MfDict* mf_dict_alloc(MfDictType dict_type); + +/** Free MfDict instance + * + * @param dict MfDict instance + */ +void mf_dict_free(MfDict* dict); + +/** Get total keys count + * + * @param dict MfDict instance + * + * @return total keys count + */ +uint32_t mf_dict_get_total_keys(MfDict* dict); + +/** Rewind to the beginning + * + * @param dict MfDict instance + * + * @return true on success + */ +bool mf_dict_rewind(MfDict* dict); + +bool mf_dict_is_key_present(MfDict* dict, const MfClassicKey* key); + +bool mf_dict_get_next_key(MfDict* dict, MfClassicKey* key); + +bool mf_dict_add_key(MfDict* dict, const MfClassicKey* key); + +bool mf_dict_delete_key(MfDict* dict, const MfClassicKey* key); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/mf_ultralight_auth.c b/applications/main/nfc/helpers/mf_ultralight_auth.c new file mode 100644 index 000000000000..683ff47d4463 --- /dev/null +++ b/applications/main/nfc/helpers/mf_ultralight_auth.c @@ -0,0 +1,62 @@ +#include "mf_ultralight_auth.h" + +#include +#include + +MfUltralightAuth* mf_ultralight_auth_alloc() { + MfUltralightAuth* instance = malloc(sizeof(MfUltralightAuth)); + + return instance; +} + +void mf_ultralight_auth_free(MfUltralightAuth* instance) { + furi_assert(instance); + + free(instance); +} + +void mf_ultralight_auth_reset(MfUltralightAuth* instance) { + furi_assert(instance); + + instance->type = MfUltralightAuthTypeNone; + memset(&instance->password, 0, sizeof(MfUltralightAuthPassword)); + memset(&instance->pack, 0, sizeof(MfUltralightAuthPack)); +} + +bool mf_ultralight_generate_amiibo_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len) { + furi_assert(instance); + furi_assert(uid); + + uint32_t pwd = 0; + bool generated = false; + if(uid_len == 7) { + pwd |= (uid[1] ^ uid[3] ^ 0xAA) << 24; + pwd |= (uid[2] ^ uid[4] ^ 0x55) << 16; + pwd |= (uid[3] ^ uid[5] ^ 0xAA) << 8; + pwd |= uid[4] ^ uid[6] ^ 0x55; + instance->password.pass = pwd; + generated = true; + } + + return generated; +} + +bool mf_ultralight_generate_xiaomi_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len) { + furi_assert(instance); + furi_assert(uid); + + uint32_t pwd = 0; + uint8_t hash[20]; + bool generated = false; + if(uid_len == 7) { + mbedtls_sha1(uid, uid_len, hash); + pwd |= (hash[hash[0] % 20]) << 24; + pwd |= (hash[(hash[0] + 5) % 20]) << 16; + pwd |= (hash[(hash[0] + 13) % 20]) << 8; + pwd |= (hash[(hash[0] + 17) % 20]); + instance->password.pass = pwd; + generated = true; + } + + return generated; +} \ No newline at end of file diff --git a/applications/main/nfc/helpers/mf_ultralight_auth.h b/applications/main/nfc/helpers/mf_ultralight_auth.h new file mode 100644 index 000000000000..ea55fe0f77bd --- /dev/null +++ b/applications/main/nfc/helpers/mf_ultralight_auth.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MfUltralightAuthTypeNone, + MfUltralightAuthTypeReader, + MfUltralightAuthTypeManual, + MfUltralightAuthTypeXiaomii, + MfUltralightAuthTypeAmiibo, +} MfUltralightAuthType; + +typedef struct { + MfUltralightAuthType type; + MfUltralightAuthPassword password; + MfUltralightAuthPack pack; +} MfUltralightAuth; + +MfUltralightAuth* mf_ultralight_auth_alloc(); + +void mf_ultralight_auth_free(MfUltralightAuth* instance); + +void mf_ultralight_auth_reset(MfUltralightAuth* instance); + +bool mf_ultralight_generate_amiibo_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len); + +bool mf_ultralight_generate_xiaomi_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/mf_user_dict.c b/applications/main/nfc/helpers/mf_user_dict.c new file mode 100644 index 000000000000..3fefe9274624 --- /dev/null +++ b/applications/main/nfc/helpers/mf_user_dict.c @@ -0,0 +1,74 @@ +#include "mf_user_dict.h" + +#include + +struct MfUserDict { + size_t keys_num; + MfClassicKey* keys_arr; +}; + +MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) { + MfUserDict* instance = malloc(sizeof(MfUserDict)); + + MfDict* dict = mf_dict_alloc(MfDictTypeUser); + furi_assert(dict); + + size_t dict_keys_num = mf_dict_get_total_keys(dict); + instance->keys_num = MIN(max_keys_to_load, dict_keys_num); + + if(instance->keys_num > 0) { + instance->keys_arr = malloc(instance->keys_num * sizeof(MfClassicKey)); + for(size_t i = 0; i < instance->keys_num; i++) { + bool key_loaded = mf_dict_get_next_key(dict, &instance->keys_arr[i]); + furi_assert(key_loaded); + } + } + mf_dict_free(dict); + + return instance; +} + +void mf_user_dict_free(MfUserDict* instance) { + furi_assert(instance); + + if(instance->keys_num > 0) { + free(instance->keys_arr); + } + free(instance); +} + +size_t mf_user_dict_get_keys_cnt(MfUserDict* instance) { + furi_assert(instance); + + return instance->keys_num; +} + +void mf_user_dict_get_key_str(MfUserDict* instance, uint32_t index, FuriString* str) { + furi_assert(instance); + furi_assert(str); + furi_assert(index < instance->keys_num); + furi_assert(instance->keys_arr); + + furi_string_reset(str); + for(size_t i = 0; i < sizeof(MfClassicKey); i++) { + furi_string_cat_printf(str, "%02X", instance->keys_arr[index].data[i]); + } +} + +bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index) { + furi_assert(instance); + furi_assert(index < instance->keys_num); + furi_assert(instance->keys_arr); + + MfDict* dict = mf_dict_alloc(MfDictTypeUser); + furi_assert(dict); + + bool key_delete_success = mf_dict_delete_key(dict, &instance->keys_arr[index]); + mf_dict_free(dict); + + if(key_delete_success) { + instance->keys_num--; + } + + return key_delete_success; +} diff --git a/applications/main/nfc/helpers/mf_user_dict.h b/applications/main/nfc/helpers/mf_user_dict.h new file mode 100644 index 000000000000..74a9a40ff899 --- /dev/null +++ b/applications/main/nfc/helpers/mf_user_dict.h @@ -0,0 +1,23 @@ +#pragma once + +#include "mf_dict.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfUserDict MfUserDict; + +MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load); + +void mf_user_dict_free(MfUserDict* instance); + +size_t mf_user_dict_get_keys_cnt(MfUserDict* instance); + +void mf_user_dict_get_key_str(MfUserDict* instance, uint32_t index, FuriString* str); + +bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/mfkey32_logger.c b/applications/main/nfc/helpers/mfkey32_logger.c new file mode 100644 index 000000000000..8024179f809f --- /dev/null +++ b/applications/main/nfc/helpers/mfkey32_logger.c @@ -0,0 +1,173 @@ +#include "mfkey32_logger.h" + +#include + +#include +#include +#include + +#define MFKEY32_LOGGER_MAX_NONCES_SAVED (100) + +typedef struct { + bool is_filled; + uint32_t cuid; + uint8_t sector_num; + MfClassicKeyType key_type; + uint32_t nt0; + uint32_t nr0; + uint32_t ar0; + uint32_t nt1; + uint32_t nr1; + uint32_t ar1; +} Mfkey32LoggerParams; + +ARRAY_DEF(Mfkey32LoggerParams, Mfkey32LoggerParams, M_POD_OPLIST); + +struct Mfkey32Logger { + uint32_t cuid; + Mfkey32LoggerParams_t params_arr; + size_t nonces_saves; + size_t params_collected; +}; + +Mfkey32Logger* mfkey32_logger_alloc(uint32_t cuid) { + Mfkey32Logger* instance = malloc(sizeof(Mfkey32Logger)); + instance->cuid = cuid; + Mfkey32LoggerParams_init(instance->params_arr); + + return instance; +} + +void mfkey32_logger_free(Mfkey32Logger* instance) { + furi_assert(instance); + furi_assert(instance->params_arr); + + Mfkey32LoggerParams_clear(instance->params_arr); + free(instance); +} + +static bool mfkey32_logger_add_nonce_to_existing_params( + Mfkey32Logger* instance, + MfClassicAuthContext* auth_context) { + bool nonce_added = false; + do { + if(Mfkey32LoggerParams_size(instance->params_arr) == 0) break; + + Mfkey32LoggerParams_it_t it; + for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it); + Mfkey32LoggerParams_next(it)) { + Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it); + if(params->is_filled) continue; + + uint8_t sector_num = mf_classic_get_sector_by_block(auth_context->block_num); + if(params->sector_num != sector_num) continue; + if(params->key_type != auth_context->key_type) continue; + + params->nt1 = nfc_util_bytes2num(auth_context->nt.data, sizeof(MfClassicNt)); + params->nr1 = nfc_util_bytes2num(auth_context->nr.data, sizeof(MfClassicNr)); + params->ar1 = nfc_util_bytes2num(auth_context->ar.data, sizeof(MfClassicAr)); + params->is_filled = true; + + instance->params_collected++; + nonce_added = true; + break; + } + + } while(false); + + return nonce_added; +} + +void mfkey32_logger_add_nonce(Mfkey32Logger* instance, MfClassicAuthContext* auth_context) { + furi_assert(instance); + furi_assert(auth_context); + + bool nonce_added = mfkey32_logger_add_nonce_to_existing_params(instance, auth_context); + if(!nonce_added && (instance->nonces_saves < MFKEY32_LOGGER_MAX_NONCES_SAVED)) { + uint8_t sector_num = mf_classic_get_sector_by_block(auth_context->block_num); + Mfkey32LoggerParams params = { + .is_filled = false, + .cuid = instance->cuid, + .sector_num = sector_num, + .key_type = auth_context->key_type, + .nt0 = nfc_util_bytes2num(auth_context->nt.data, sizeof(MfClassicNt)), + .nr0 = nfc_util_bytes2num(auth_context->nr.data, sizeof(MfClassicNr)), + .ar0 = nfc_util_bytes2num(auth_context->ar.data, sizeof(MfClassicAr)), + }; + Mfkey32LoggerParams_push_back(instance->params_arr, params); + instance->nonces_saves++; + } +} + +size_t mfkey32_logger_get_params_num(Mfkey32Logger* instance) { + furi_assert(instance); + + return instance->params_collected; +} + +bool mfkey32_logger_save_params(Mfkey32Logger* instance, const char* path) { + furi_assert(instance); + furi_assert(path); + furi_assert(instance->params_collected > 0); + furi_assert(instance->params_arr); + + bool params_saved = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* stream = buffered_file_stream_alloc(storage); + FuriString* temp_str = furi_string_alloc(); + + do { + if(!buffered_file_stream_open(stream, path, FSAM_WRITE, FSOM_OPEN_APPEND)) break; + + bool params_write_success = true; + Mfkey32LoggerParams_it_t it; + for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it); + Mfkey32LoggerParams_next(it)) { + Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it); + if(!params->is_filled) continue; + furi_string_printf( + temp_str, + "Sec %d key %c cuid %08lx nt0 %08lx nr0 %08lx ar0 %08lx nt1 %08lx nr1 %08lx ar1 %08lx\n", + params->sector_num, + params->key_type == MfClassicKeyTypeA ? 'A' : 'B', + params->cuid, + params->nt0, + params->nr0, + params->ar0, + params->nt1, + params->nr1, + params->ar1); + if(!stream_write_string(stream, temp_str)) { + params_write_success = false; + break; + } + } + if(!params_write_success) break; + + params_saved = true; + } while(false); + + furi_string_free(temp_str); + buffered_file_stream_close(stream); + stream_free(stream); + furi_record_close(RECORD_STORAGE); + + return params_saved; +} + +void mfkey32_logger_get_params_data(Mfkey32Logger* instance, FuriString* str) { + furi_assert(instance); + furi_assert(str); + furi_assert(instance->params_collected > 0); + + furi_string_reset(str); + Mfkey32LoggerParams_it_t it; + for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it); + Mfkey32LoggerParams_next(it)) { + Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it); + if(!params->is_filled) continue; + + char key_char = params->key_type == MfClassicKeyTypeA ? 'A' : 'B'; + furi_string_cat_printf(str, "Sector %d, key %c\n", params->sector_num, key_char); + } +} diff --git a/applications/main/nfc/helpers/mfkey32_logger.h b/applications/main/nfc/helpers/mfkey32_logger.h new file mode 100644 index 000000000000..12b42e1fb516 --- /dev/null +++ b/applications/main/nfc/helpers/mfkey32_logger.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Mfkey32Logger Mfkey32Logger; + +Mfkey32Logger* mfkey32_logger_alloc(uint32_t cuid); + +void mfkey32_logger_free(Mfkey32Logger* instance); + +void mfkey32_logger_add_nonce(Mfkey32Logger* instance, MfClassicAuthContext* auth_context); + +size_t mfkey32_logger_get_params_num(Mfkey32Logger* instance); + +bool mfkey32_logger_save_params(Mfkey32Logger* instance, const char* path); + +void mfkey32_logger_get_params_data(Mfkey32Logger* instance, FuriString* str); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index 4227a5b14e33..93c39d4b51cd 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -1,15 +1,32 @@ #pragma once -enum NfcCustomEvent { +typedef enum { // Reserve first 100 events for button types and indexes, starting from 0 NfcCustomEventReserved = 100, + // Mf classic dict attack events + NfcCustomEventDictAttackComplete, + NfcCustomEventDictAttackSkip, + NfcCustomEventDictAttackDataUpdate, + + NfcCustomEventCardDetected, + NfcCustomEventCardLost, + NfcCustomEventViewExit, NfcCustomEventWorkerExit, + NfcCustomEventWorkerUpdate, + NfcCustomEventWrongCard, + NfcCustomEventTimerExpired, NfcCustomEventByteInputDone, NfcCustomEventTextInputDone, NfcCustomEventDictAttackDone, - NfcCustomEventDictAttackSkip, + NfcCustomEventRpcLoad, NfcCustomEventRpcSessionClose, -}; + + NfcCustomEventPollerSuccess, + NfcCustomEventPollerIncomplete, + NfcCustomEventPollerFailure, + + NfcCustomEventListenerUpdate, +} NfcCustomEvent; diff --git a/applications/main/nfc/helpers/nfc_supported_cards.c b/applications/main/nfc/helpers/nfc_supported_cards.c new file mode 100644 index 000000000000..303334e6bd28 --- /dev/null +++ b/applications/main/nfc/helpers/nfc_supported_cards.c @@ -0,0 +1,147 @@ +#include "nfc_supported_cards.h" +#include "../plugins/supported_cards/nfc_supported_card_plugin.h" + +#include +#include +#include + +#include +#include + +#define TAG "NfcSupportedCards" + +#define NFC_SUPPORTED_CARDS_PLUGINS_PATH APP_DATA_PATH("plugins") +#define NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX "_parser.fal" + +typedef struct { + Storage* storage; + File* directory; + FuriString* file_path; + char file_name[256]; + FlipperApplication* app; +} NfcSupportedCards; + +static NfcSupportedCards* nfc_supported_cards_alloc() { + NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards)); + + instance->storage = furi_record_open(RECORD_STORAGE); + instance->directory = storage_file_alloc(instance->storage); + instance->file_path = furi_string_alloc(); + + if(!storage_dir_open(instance->directory, NFC_SUPPORTED_CARDS_PLUGINS_PATH)) { + FURI_LOG_D(TAG, "Failed to open directory: %s", NFC_SUPPORTED_CARDS_PLUGINS_PATH); + } + + return instance; +} + +static void nfc_supported_cards_free(NfcSupportedCards* instance) { + if(instance->app) { + flipper_application_free(instance->app); + } + + furi_string_free(instance->file_path); + + storage_dir_close(instance->directory); + storage_file_free(instance->directory); + + furi_record_close(RECORD_STORAGE); + free(instance); +} + +static const NfcSupportedCardsPlugin* + nfc_supported_cards_get_next_plugin(NfcSupportedCards* instance) { + const NfcSupportedCardsPlugin* plugin = NULL; + + do { + if(!storage_file_is_open(instance->directory)) break; + if(!storage_dir_read( + instance->directory, NULL, instance->file_name, sizeof(instance->file_name))) + break; + + furi_string_set(instance->file_path, instance->file_name); + if(!furi_string_end_with_str(instance->file_path, NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX)) + continue; + + path_concat(NFC_SUPPORTED_CARDS_PLUGINS_PATH, instance->file_name, instance->file_path); + + if(instance->app) flipper_application_free(instance->app); + instance->app = flipper_application_alloc(instance->storage, firmware_api_interface); + + if(flipper_application_preload(instance->app, furi_string_get_cstr(instance->file_path)) != + FlipperApplicationPreloadStatusSuccess) + continue; + if(!flipper_application_is_plugin(instance->app)) continue; + + if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess) + continue; + + const FlipperAppPluginDescriptor* descriptor = + flipper_application_plugin_get_descriptor(instance->app); + + if(descriptor == NULL) continue; + + if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) continue; + if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) continue; + + plugin = descriptor->entry_point; + } while(plugin == NULL); + + return plugin; +} + +bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc) { + furi_assert(device); + furi_assert(nfc); + + bool card_read = false; + + NfcSupportedCards* supported_cards = nfc_supported_cards_alloc(); + + do { + const NfcSupportedCardsPlugin* plugin = + nfc_supported_cards_get_next_plugin(supported_cards); + if(plugin == NULL) break; + + const NfcProtocol protocol = nfc_device_get_protocol(device); + if(plugin->protocol != protocol) continue; + + if(plugin->verify) { + if(!plugin->verify(nfc)) continue; + } + + if(plugin->read) { + card_read = plugin->read(nfc, device); + } + + } while(!card_read); + + nfc_supported_cards_free(supported_cards); + return card_read; +} + +bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + bool parsed = false; + + NfcSupportedCards* supported_cards = nfc_supported_cards_alloc(); + + do { + const NfcSupportedCardsPlugin* plugin = + nfc_supported_cards_get_next_plugin(supported_cards); + if(plugin == NULL) break; + + const NfcProtocol protocol = nfc_device_get_protocol(device); + if(plugin->protocol != protocol) continue; + + if(plugin->parse) { + parsed = plugin->parse(device, parsed_data); + } + + } while(!parsed); + + nfc_supported_cards_free(supported_cards); + return parsed; +} diff --git a/applications/main/nfc/helpers/nfc_supported_cards.h b/applications/main/nfc/helpers/nfc_supported_cards.h new file mode 100644 index 000000000000..2bf57211c780 --- /dev/null +++ b/applications/main/nfc/helpers/nfc_supported_cards.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc); + +bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c new file mode 100644 index 000000000000..8c9aae3c73ac --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -0,0 +1,111 @@ +#include "felica.h" +#include "felica_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_felica(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolFelica); + + NfcApp* instance = context; + const FelicaPollerEvent* felica_event = event.data; + + if(felica_event->type == FelicaPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_felica(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_felica, instance); +} + +static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static bool nfc_scene_info_on_event_felica(NfcApp* instance, uint32_t event) { + if(event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_felica = { + .features = NfcProtocolFeatureNone, // TODO: Implement better UID editing, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_felica, + .on_event = nfc_scene_info_on_event_felica, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_felica, + .on_event = NULL, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_felica, + .on_event = NULL, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_saved_menu_on_event_felica, + }, + .scene_emulate = + { + .on_enter = NULL, + .on_event = NULL, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.h b/applications/main/nfc/helpers/protocol_support/felica/felica.h new file mode 100644 index 000000000000..e581b6709e7a --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_felica; diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica_render.c b/applications/main/nfc/helpers/protocol_support/felica/felica_render.c new file mode 100644 index 000000000000..3142b2c6dbf0 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica_render.c @@ -0,0 +1,19 @@ +#include "felica_render.h" + +void nfc_render_felica_info( + const FelicaData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + furi_string_cat_printf(str, "IDm:"); + + for(size_t i = 0; i < FELICA_IDM_SIZE; i++) { + furi_string_cat_printf(str, " %02X", data->idm.data[i]); + } + + if(format_type == NfcProtocolFormatTypeFull) { + furi_string_cat_printf(str, "\nPMm:"); + for(size_t i = 0; i < FELICA_PMM_SIZE; ++i) { + furi_string_cat_printf(str, " %02X", data->pmm.data[i]); + } + } +} diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica_render.h b/applications/main/nfc/helpers/protocol_support/felica/felica_render.h new file mode 100644 index 000000000000..6d9816fc6630 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica_render.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_felica_info( + const FelicaData* data, + NfcProtocolFormatType format_type, + FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c new file mode 100644 index 000000000000..1ac5870c7fd5 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c @@ -0,0 +1,158 @@ +#include "iso14443_3a_i.h" +#include "iso14443_3a_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_iso14443_3a(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_3aData* data = nfc_device_get_data(device, NfcProtocolIso14443_3a); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_3a_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand + nfc_scene_read_poller_callback_iso14443_3a(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + NfcApp* instance = context; + const Iso14443_3aPollerEvent* iso14443_3a_event = event.data; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolIso14443_3a, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_iso14443_3a(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_3a, instance); +} + +static void nfc_scene_read_success_on_enter_iso14443_3a(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_3aData* data = nfc_device_get_data(device, NfcProtocolIso14443_3a); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_3a_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand + nfc_scene_emulate_listener_callback_iso14443_3a(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(event.data); + + NfcApp* nfc = context; + Iso14443_3aListenerEvent* iso14443_3a_event = event.data; + + if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) { + furi_string_cat_printf(nfc->text_box_store, "R:"); + for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_3a_event->data->buffer); i++) { + furi_string_cat_printf( + nfc->text_box_store, + " %02X", + bit_buffer_get_byte(iso14443_3a_event->data->buffer, i)); + } + furi_string_push_back(nfc->text_box_store, '\n'); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + } + + return NfcCommandContinue; +} + +static void nfc_scene_emulate_on_enter_iso14443_3a(NfcApp* instance) { + const Iso14443_3aData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_3a); + + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_3a, data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance); +} + +static bool nfc_scene_info_on_event_iso14443_3a(NfcApp* instance, uint32_t event) { + if(event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + return true; + } + + return false; +} + +static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEmulate) { + scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); + return true; + } + + return false; +} + +bool nfc_scene_saved_menu_on_event_iso14443_3a_common(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_iso14443_3a(NfcApp* instance, uint32_t event) { + return nfc_scene_saved_menu_on_event_iso14443_3a_common(instance, event); +} + +const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = { + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_iso14443_3a, + .on_event = nfc_scene_info_on_event_iso14443_3a, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_iso14443_3a, + .on_event = NULL, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_read_menu_on_event_iso14443_3a, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_iso14443_3a, + .on_event = NULL, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_saved_menu_on_event_iso14443_3a, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_iso14443_3a, + .on_event = NULL, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.h b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.h new file mode 100644 index 000000000000..d085f25c77ba --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_i.h b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_i.h new file mode 100644 index 000000000000..e087d71d997b --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_i.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "iso14443_3a.h" + +bool nfc_scene_saved_menu_on_event_iso14443_3a_common(NfcApp* instance, uint32_t event); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c new file mode 100644 index 000000000000..805680856cf6 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c @@ -0,0 +1,22 @@ +#include "iso14443_3a_render.h" + +void nfc_render_iso14443_3a_info( + const Iso14443_3aData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + if(format_type == NfcProtocolFormatTypeFull) { + const char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3'; + furi_string_cat_printf(str, "ISO 14443-%c (NFC-A)\n", iso_type); + } + + furi_string_cat_printf(str, "UID:"); + + for(size_t i = 0; i < data->uid_len; i++) { + furi_string_cat_printf(str, " %02X", data->uid[i]); + } + + if(format_type == NfcProtocolFormatTypeFull) { + furi_string_cat_printf(str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]); + furi_string_cat_printf(str, " SAK: %02X", data->sak); + } +} diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h new file mode 100644 index 000000000000..ed5e3677c9d6 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_iso14443_3a_info( + const Iso14443_3aData* data, + NfcProtocolFormatType format_type, + FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c new file mode 100644 index 000000000000..b2b332aa450a --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c @@ -0,0 +1,112 @@ +#include "iso14443_3b.h" +#include "iso14443_3b_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_iso14443_3b(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_3bData* data = nfc_device_get_data(device, NfcProtocolIso14443_3b); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_3b_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand + nfc_scene_read_poller_callback_iso14443_3b(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_3b); + + NfcApp* instance = context; + const Iso14443_3bPollerEvent* iso14443_3b_event = event.data; + + if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolIso14443_3b, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_iso14443_3b(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_3b, instance); +} + +static void nfc_scene_read_success_on_enter_iso14443_3b(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_3bData* data = nfc_device_get_data(device, NfcProtocolIso14443_3b); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_3b_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static bool nfc_scene_info_on_event_iso14443_3b(NfcApp* instance, uint32_t event) { + if(event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { + .features = NfcProtocolFeatureNone, // TODO: Implement better UID editing, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_iso14443_3b, + .on_event = nfc_scene_info_on_event_iso14443_3b, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_iso14443_3b, + .on_event = NULL, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_iso14443_3b, + .on_event = NULL, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_saved_menu_on_event_iso14443_3b, + }, + .scene_emulate = + { + .on_enter = NULL, + .on_event = NULL, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.h b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.h new file mode 100644 index 000000000000..bf3caa0c0e31 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c new file mode 100644 index 000000000000..c4098d61c25a --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c @@ -0,0 +1,28 @@ +#include "iso14443_3b_render.h" + +void nfc_render_iso14443_3b_info( + const Iso14443_3bData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + if(format_type == NfcProtocolFormatTypeFull) { + const char iso_type = FURI_BIT(data->protocol_info[1], 0) ? '4' : '3'; + furi_string_cat_printf(str, "ISO 14443-%c (NFC-B)\n", iso_type); + } + + furi_string_cat_printf(str, "UID:"); + + for(size_t i = 0; i < ISO14443_3B_UID_SIZE; i++) { + furi_string_cat_printf(str, " %02X", data->uid[i]); + } + + if(format_type == NfcProtocolFormatTypeFull) { + furi_string_cat_printf(str, "\nApp. data:"); + for(size_t i = 0; i < ISO14443_3B_APP_DATA_SIZE; ++i) { + furi_string_cat_printf(str, " %02X", data->app_data[i]); + } + furi_string_cat_printf(str, "\nProtocol info:"); + for(size_t i = 0; i < ISO14443_3B_PROTOCOL_INFO_SIZE; ++i) { + furi_string_cat_printf(str, " %02X", data->protocol_info[i]); + } + } +} diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.h b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.h new file mode 100644 index 000000000000..ee50f6acf5a8 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_iso14443_3b_info( + const Iso14443_3bData* data, + NfcProtocolFormatType format_type, + FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c new file mode 100644 index 000000000000..8de3c8af83cd --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c @@ -0,0 +1,154 @@ +#include "iso14443_4a.h" +#include "iso14443_4a_render.h" + +#include +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_gui_common.h" +#include "../iso14443_3a/iso14443_3a_i.h" + +static void nfc_scene_info_on_enter_iso14443_4a(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_4aData* data = nfc_device_get_data(device, NfcProtocolIso14443_4a); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_4a_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand + nfc_scene_read_poller_callback_iso14443_4a(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + NfcApp* instance = context; + const Iso14443_4aPollerEvent* iso14443_4a_event = event.data; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolIso14443_4a, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_iso14443_4a(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_4a, instance); +} + +static void nfc_scene_read_success_on_enter_iso14443_4a(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_4aData* data = nfc_device_get_data(device, NfcProtocolIso14443_4a); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_4a_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_saved_menu_on_enter_iso14443_4a(NfcApp* instance) { + UNUSED(instance); +} + +NfcCommand nfc_scene_emulate_listener_callback_iso14443_4a(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso14443_4a); + furi_assert(event.data); + + NfcApp* nfc = context; + Iso14443_4aListenerEvent* iso14443_4a_event = event.data; + + if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) { + furi_string_cat_printf(nfc->text_box_store, "R:"); + for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_4a_event->data->buffer); i++) { + furi_string_cat_printf( + nfc->text_box_store, + " %02X", + bit_buffer_get_byte(iso14443_4a_event->data->buffer, i)); + } + furi_string_push_back(nfc->text_box_store, '\n'); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + } + + return NfcCommandContinue; +} + +static void nfc_scene_emulate_on_enter_iso14443_4a(NfcApp* instance) { + const Iso14443_4aData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a); + + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); +} + +static bool nfc_scene_info_on_event_iso14443_4a(NfcApp* instance, uint32_t event) { + if(event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + return true; + } + + return false; +} + +static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEmulate) { + scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_iso14443_4a(NfcApp* instance, uint32_t event) { + return nfc_scene_saved_menu_on_event_iso14443_3a_common(instance, event); +} + +const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = { + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_iso14443_4a, + .on_event = nfc_scene_info_on_event_iso14443_4a, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_iso14443_4a, + .on_event = NULL, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_read_menu_on_event_iso14443_4a, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_iso14443_4a, + .on_event = NULL, + }, + .scene_saved_menu = + { + .on_enter = nfc_scene_saved_menu_on_enter_iso14443_4a, + .on_event = nfc_scene_saved_menu_on_event_iso14443_4a, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_iso14443_4a, + .on_event = NULL, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.h b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.h new file mode 100644 index 000000000000..1b173d7b091c --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_i.h b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_i.h new file mode 100644 index 000000000000..7e13403da13a --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_i.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "iso14443_4a.h" + +NfcCommand nfc_scene_emulate_listener_callback_iso14443_4a(NfcGenericEvent event, void* context); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c new file mode 100644 index 000000000000..7d426c4b20b1 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c @@ -0,0 +1,11 @@ +#include "iso14443_4a_render.h" + +#include "../iso14443_3a/iso14443_3a_render.h" + +void nfc_render_iso14443_4a_info( + const Iso14443_4aData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_3a_info(data->iso14443_3a_data, format_type, str); + // TODO: Add RATS info? +} diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.h b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.h new file mode 100644 index 000000000000..c736707e00f5 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_iso14443_4a_info( + const Iso14443_4aData* data, + NfcProtocolFormatType format_type, + FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c new file mode 100644 index 000000000000..57a73cc0ca3f --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -0,0 +1,146 @@ +#include "iso15693_3.h" +#include "iso15693_3_render.h" + +#include +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_iso15693_3(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso15693_3_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_read_poller_callback_iso15693_3(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso15693_3); + + NfcApp* instance = context; + const Iso15693_3PollerEvent* iso15693_3_event = event.data; + + if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolIso15693_3, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_iso15693_3(NfcApp* instance) { + UNUSED(instance); + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso15693_3, instance); +} + +static void nfc_scene_read_success_on_enter_iso15693_3(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso15693_3_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand + nfc_scene_emulate_listener_callback_iso15693_3(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso15693_3); + furi_assert(event.data); + + NfcApp* nfc = context; + Iso15693_3ListenerEvent* iso15693_3_event = event.data; + + if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) { + furi_string_cat_printf(nfc->text_box_store, "R:"); + for(size_t i = 0; i < bit_buffer_get_size_bytes(iso15693_3_event->data->buffer); i++) { + furi_string_cat_printf( + nfc->text_box_store, + " %02X", + bit_buffer_get_byte(iso15693_3_event->data->buffer, i)); + } + furi_string_push_back(nfc->text_box_store, '\n'); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + } + + return NfcCommandContinue; +} + +static void nfc_scene_emulate_on_enter_iso15693_3(NfcApp* instance) { + const Iso15693_3Data* data = nfc_device_get_data(instance->nfc_device, NfcProtocolIso15693_3); + + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolIso15693_3, data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance); +} + +static bool nfc_scene_info_on_event_iso15693_3(NfcApp* instance, uint32_t event) { + if(event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { + .features = + NfcProtocolFeatureEmulateFull, // | NfcProtocolFeatureEditUid, // TODO: Implement better UID editing + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_iso15693_3, + .on_event = nfc_scene_info_on_event_iso15693_3, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_iso15693_3, + .on_event = NULL, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_iso15693_3, + .on_event = NULL, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_saved_menu_on_event_iso15693_3, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_iso15693_3, + .on_event = NULL, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.h b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.h new file mode 100644 index 000000000000..a26633ee6103 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_iso15693_3; diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c new file mode 100644 index 000000000000..8df5d644ca42 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c @@ -0,0 +1,90 @@ +#include "iso15693_3_render.h" + +#define NFC_RENDER_ISO15693_3_MAX_BYTES (128U) + +void nfc_render_iso15693_3_info( + const Iso15693_3Data* data, + NfcProtocolFormatType format_type, + FuriString* str) { + if(format_type == NfcProtocolFormatTypeFull) { + furi_string_cat(str, "ISO15693-3 (NFC-V)\n"); + } + + nfc_render_iso15693_3_header(data, str); + + if(format_type != NfcProtocolFormatTypeFull) return; + + furi_string_push_back(str, '\n'); + + nfc_render_iso15693_3_main_info(data, str); +} + +void nfc_render_iso15693_3_header(const Iso15693_3Data* data, FuriString* str) { + furi_string_cat_printf(str, "UID:"); + + for(size_t i = 0; i < ISO15693_3_UID_SIZE; i++) { + furi_string_cat_printf(str, " %02X", data->uid[i]); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + const uint16_t block_count = data->system_info.block_count; + const uint8_t block_size = data->system_info.block_size; + + furi_string_cat_printf(str, "Memory: %u bytes\n", block_count * block_size); + furi_string_cat_printf(str, "(%u blocks x %u bytes)", block_count, block_size); + } +} + +void nfc_render_iso15693_3_main_info(const Iso15693_3Data* data, FuriString* str) { + furi_string_cat(str, "\e#General info\n"); + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + furi_string_cat_printf(str, "DSFID: %02X\n", data->system_info.ic_ref); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { + furi_string_cat_printf(str, "AFI: %02X\n", data->system_info.afi); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { + furi_string_cat_printf(str, "IC Reference: %02X\n", data->system_info.ic_ref); + } + + furi_string_cat(str, "\e#Lock bits\n"); + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + furi_string_cat_printf( + str, "DSFID: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { + furi_string_cat_printf( + str, "AFI: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + furi_string_cat(str, "\e#Memory data\n\e*--------------------\n"); + + const uint32_t block_data_size = simple_array_get_count(data->block_data); + const uint32_t display_data_size = MIN(block_data_size, NFC_RENDER_ISO15693_3_MAX_BYTES); + const uint32_t block_count = display_data_size / data->system_info.block_size; + + // TODO: Improve hex data display + for(uint32_t i = 0; i < block_count; ++i) { + furi_string_cat(str, "\e*"); + + for(uint32_t j = 0; j < data->system_info.block_size; j++) { + const uint8_t byte = *(uint8_t*)simple_array_cget( + data->block_data, i * data->system_info.block_size + j); + furi_string_cat_printf(str, "%02X ", byte); + } + + const uint8_t security = *(uint8_t*)simple_array_cget(data->block_security, i + 1); + furi_string_cat_printf(str, "| %s\n", (security & 0x01) ? "[LOCK]" : ""); + } + + if(block_data_size != display_data_size) { + furi_string_cat_printf( + str, "(Data is too big. Showing only the first %lu bytes.)", display_data_size); + } + } +} diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h new file mode 100644 index 000000000000..d9d64f97103c --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_iso15693_3_info( + const Iso15693_3Data* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_iso15693_3_header(const Iso15693_3Data* data, FuriString* str); + +void nfc_render_iso15693_3_main_info(const Iso15693_3Data* data, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c new file mode 100644 index 000000000000..7665ec6b9876 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -0,0 +1,238 @@ +#include "mf_classic.h" +#include "mf_classic_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_gui_common.h" + +#define TAG "MfClassicApp" + +enum { + SubmenuIndexDetectReader = SubmenuIndexCommonMax, + SubmenuIndexWrite, + SubmenuIndexUpdate, +}; + +static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "More", + nfc_protocol_support_common_widget_callback, + instance); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_read_poller_callback_mf_classic(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcApp* instance = context; + const MfClassicPollerEvent* mfc_event = event.data; + NfcCommand command = NfcCommandContinue; + + if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller)); + size_t uid_len = 0; + const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len); + if(mf_classic_key_cache_load(instance->mfc_key_cache, uid, uid_len)) { + FURI_LOG_I(TAG, "Key cache found"); + mfc_event->data->poller_mode.mode = MfClassicPollerModeRead; + } else { + FURI_LOG_I(TAG, "Key cache not found"); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventPollerIncomplete); + command = NfcCommandStop; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) { + uint8_t sector_num = 0; + MfClassicKey key = {}; + MfClassicKeyType key_type = MfClassicKeyTypeA; + if(mf_classic_key_cahce_get_next_key( + instance->mfc_key_cache, §or_num, &key, &key_type)) { + mfc_event->data->read_sector_request_data.sector_num = sector_num; + mfc_event->data->read_sector_request_data.key = key; + mfc_event->data->read_sector_request_data.key_type = key_type; + mfc_event->data->read_sector_request_data.key_provided = true; + } else { + mfc_event->data->read_sector_request_data.key_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller)); + const MfClassicData* mfc_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + NfcCustomEvent custom_event = mf_classic_is_card_read(mfc_data) ? + NfcCustomEventPollerSuccess : + NfcCustomEventPollerIncomplete; + view_dispatcher_send_custom_event(instance->view_dispatcher, custom_event); + command = NfcCommandStop; + } + + return command; +} + +static void nfc_scene_read_on_enter_mf_classic(NfcApp* instance) { + mf_classic_key_cache_reset(instance->mfc_key_cache); + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_classic, instance); +} + +static bool nfc_scene_read_on_event_mf_classic(NfcApp* instance, uint32_t event) { + if(event == NfcCustomEventPollerIncomplete) { + const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller); + if(mf_classic_is_card_read(mfc_data)) { + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventPollerSuccess); + } else { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); + } + } + + return true; +} + +static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { + Submenu* submenu = instance->submenu; + const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + + if(!mf_classic_is_card_read(data)) { + submenu_add_item( + submenu, + "Detect Reader", + SubmenuIndexDetectReader, + nfc_protocol_support_common_submenu_callback, + instance); + } +} + +static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) { + Submenu* submenu = instance->submenu; + const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + + if(!mf_classic_is_card_read(data)) { + submenu_add_item( + submenu, + "Detect Reader", + SubmenuIndexDetectReader, + nfc_protocol_support_common_submenu_callback, + instance); + } + submenu_add_item( + submenu, + "Write to Initial Card", + SubmenuIndexWrite, + nfc_protocol_support_common_submenu_callback, + instance); + submenu_add_item( + submenu, + "Update from Initial Card", + SubmenuIndexUpdate, + nfc_protocol_support_common_submenu_callback, + instance); +} + +static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) { + const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolMfClassic, data); + nfc_listener_start(instance->listener, NULL, NULL); +} + +static bool nfc_scene_info_on_event_mf_classic(NfcApp* instance, uint32_t event) { + if(event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + return true; + } + + return false; +} + +static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexDetectReader) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); + dolphin_deed(DolphinDeedNfcDetectReader); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { + bool consumed = false; + + if(event == SubmenuIndexDetectReader) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; + } else if(event == SubmenuIndexWrite) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial); + consumed = true; + } else if(event == SubmenuIndexUpdate) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); + consumed = true; + } + + return consumed; +} + +const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { + .features = NfcProtocolFeatureEmulateFull, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_mf_classic, + .on_event = nfc_scene_info_on_event_mf_classic, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_mf_classic, + .on_event = nfc_scene_read_on_event_mf_classic, + }, + .scene_read_menu = + { + .on_enter = nfc_scene_read_menu_on_enter_mf_classic, + .on_event = nfc_scene_read_menu_on_event_mf_classic, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_mf_classic, + .on_event = NULL, + }, + .scene_saved_menu = + { + .on_enter = nfc_scene_saved_menu_on_enter_mf_classic, + .on_event = nfc_scene_saved_menu_on_event_mf_classic, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_mf_classic, + .on_event = NULL, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.h b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.h new file mode 100644 index 000000000000..d0f09f5d78ea --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_mf_classic; diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c new file mode 100644 index 000000000000..3559369373e2 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c @@ -0,0 +1,21 @@ +#include "mf_classic_render.h" + +#include "../iso14443_3a/iso14443_3a_render.h" + +void nfc_render_mf_classic_info( + const MfClassicData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_3a_info(data->iso14443_3a_data, format_type, str); + + uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + uint8_t keys_total = sectors_total * 2; + uint8_t keys_found = 0; + uint8_t sectors_read = 0; + mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); + + furi_string_cat_printf(str, "\nKeys Found: %u/%u", keys_found, keys_total); + furi_string_cat_printf(str, "\nSectors Read: %u/%u", sectors_read, sectors_total); + + // TODO: Something else? +} diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.h b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.h new file mode 100644 index 000000000000..07177bd59200 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_mf_classic_info( + const MfClassicData* data, + NfcProtocolFormatType format_type, + FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c new file mode 100644 index 000000000000..c96c40d6ac25 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c @@ -0,0 +1,120 @@ +#include "mf_desfire.h" +#include "mf_desfire_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_gui_common.h" +#include "../iso14443_4a/iso14443_4a_i.h" + +static void nfc_scene_info_on_enter_mf_desfire(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "More", + nfc_protocol_support_common_widget_callback, + instance); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolMfDesfire); + + NfcApp* instance = context; + const MfDesfirePollerEvent* mf_desfire_event = event.data; + + if(mf_desfire_event->type == MfDesfirePollerEventTypeReadSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfDesfire, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_mf_desfire(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_desfire, instance); +} + +static void nfc_scene_read_success_on_enter_mf_desfire(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_emulate_on_enter_mf_desfire(NfcApp* instance) { + const MfDesfireData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfDesfire); + const Iso14443_4aData* iso14443_4a_data = data->iso14443_4a_data; + + instance->listener = + nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); +} + +static bool nfc_scene_info_on_event_mf_desfire(NfcApp* instance, uint32_t event) { + if(event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfDesfireData); + return true; + } + + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_mf_desfire = { + .features = NfcProtocolFeatureEmulateUid, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_mf_desfire, + .on_event = nfc_scene_info_on_event_mf_desfire, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_mf_desfire, + .on_event = NULL, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_mf_desfire, + .on_event = NULL, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_mf_desfire, + .on_event = NULL, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.h b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.h new file mode 100644 index 000000000000..504860f16a45 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_mf_desfire; diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c new file mode 100644 index 000000000000..1af01c56e920 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c @@ -0,0 +1,245 @@ +#include "mf_desfire_render.h" + +#include "../iso14443_3a/iso14443_3a_render.h" + +void nfc_render_mf_desfire_info( + const MfDesfireData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_3a_info(data->iso14443_4a_data->iso14443_3a_data, format_type, str); + + const uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1); + const uint32_t bytes_free = data->free_memory.is_present ? data->free_memory.bytes_free : 0; + + furi_string_cat_printf(str, "\n%lu", bytes_total); + + if(data->version.sw_storage & 1) { + furi_string_push_back(str, '+'); + } + + furi_string_cat_printf(str, " bytes, %lu bytes free\n", bytes_free); + + const uint32_t app_count = simple_array_get_count(data->applications); + uint32_t file_count = 0; + + for(uint32_t i = 0; i < app_count; ++i) { + const MfDesfireApplication* app = simple_array_cget(data->applications, i); + file_count += simple_array_get_count(app->file_ids); + } + + furi_string_cat_printf(str, "%lu Application%s", app_count, app_count != 1 ? "s" : ""); + furi_string_cat_printf(str, ", %lu File%s", file_count, file_count != 1 ? "s" : ""); +} + +void nfc_render_mf_desfire_data(const MfDesfireData* data, FuriString* str) { + nfc_render_mf_desfire_version(&data->version, str); + nfc_render_mf_desfire_free_memory(&data->free_memory, str); + nfc_render_mf_desfire_key_settings(&data->master_key_settings, str); + + for(uint32_t i = 0; i < simple_array_get_count(data->master_key_versions); ++i) { + nfc_render_mf_desfire_key_version(simple_array_cget(data->master_key_versions, i), i, str); + } +} + +void nfc_render_mf_desfire_version(const MfDesfireVersion* data, FuriString* str) { + furi_string_cat_printf( + str, + "%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + data->uid[0], + data->uid[1], + data->uid[2], + data->uid[3], + data->uid[4], + data->uid[5], + data->uid[6]); + furi_string_cat_printf( + str, + "hw %02x type %02x sub %02x\n" + " maj %02x min %02x\n" + " size %02x proto %02x\n", + data->hw_vendor, + data->hw_type, + data->hw_subtype, + data->hw_major, + data->hw_minor, + data->hw_storage, + data->hw_proto); + furi_string_cat_printf( + str, + "sw %02x type %02x sub %02x\n" + " maj %02x min %02x\n" + " size %02x proto %02x\n", + data->sw_vendor, + data->sw_type, + data->sw_subtype, + data->sw_major, + data->sw_minor, + data->sw_storage, + data->sw_proto); + furi_string_cat_printf( + str, + "batch %02x:%02x:%02x:%02x:%02x\n" + "week %d year %d\n", + data->batch[0], + data->batch[1], + data->batch[2], + data->batch[3], + data->batch[4], + data->prod_week, + data->prod_year); +} + +void nfc_render_mf_desfire_free_memory(const MfDesfireFreeMemory* data, FuriString* str) { + if(data->is_present) { + furi_string_cat_printf(str, "freeMem %lu\n", data->bytes_free); + } +} + +void nfc_render_mf_desfire_key_settings(const MfDesfireKeySettings* data, FuriString* str) { + furi_string_cat_printf(str, "changeKeyID %d\n", data->change_key_id); + furi_string_cat_printf(str, "configChangeable %d\n", data->is_config_changeable); + furi_string_cat_printf(str, "freeCreateDelete %d\n", data->is_free_create_delete); + furi_string_cat_printf(str, "freeDirectoryList %d\n", data->is_free_directory_list); + furi_string_cat_printf(str, "masterChangeable %d\n", data->is_master_key_changeable); + + if(data->flags) { + furi_string_cat_printf(str, "flags %d\n", data->flags); + } + + furi_string_cat_printf(str, "maxKeys %d\n", data->max_keys); +} + +void nfc_render_mf_desfire_key_version( + const MfDesfireKeyVersion* data, + uint32_t index, + FuriString* str) { + furi_string_cat_printf(str, "key %lu version %u\n", index, *data); +} + +void nfc_render_mf_desfire_application_id(const MfDesfireApplicationId* data, FuriString* str) { + const uint8_t* app_id = data->data; + furi_string_cat_printf(str, "Application %02x%02x%02x\n", app_id[0], app_id[1], app_id[2]); +} + +void nfc_render_mf_desfire_application(const MfDesfireApplication* data, FuriString* str) { + nfc_render_mf_desfire_key_settings(&data->key_settings, str); + + for(uint32_t i = 0; i < simple_array_get_count(data->key_versions); ++i) { + nfc_render_mf_desfire_key_version(simple_array_cget(data->key_versions, i), i, str); + } +} + +void nfc_render_mf_desfire_file_id(const MfDesfireFileId* data, FuriString* str) { + furi_string_cat_printf(str, "File %d\n", *data); +} + +void nfc_render_mf_desfire_file_settings_data( + const MfDesfireFileSettings* settings, + const MfDesfireFileData* data, + FuriString* str) { + const char* type; + switch(settings->type) { + case MfDesfireFileTypeStandard: + type = "standard"; + break; + case MfDesfireFileTypeBackup: + type = "backup"; + break; + case MfDesfireFileTypeValue: + type = "value"; + break; + case MfDesfireFileTypeLinearRecord: + type = "linear"; + break; + case MfDesfireFileTypeCyclicRecord: + type = "cyclic"; + break; + default: + type = "unknown"; + } + + const char* comm; + switch(settings->comm) { + case MfDesfireFileCommunicationSettingsPlaintext: + comm = "plain"; + break; + case MfDesfireFileCommunicationSettingsAuthenticated: + comm = "auth"; + break; + case MfDesfireFileCommunicationSettingsEnciphered: + comm = "enciphered"; + break; + default: + comm = "unknown"; + } + + furi_string_cat_printf(str, "%s %s\n", type, comm); + furi_string_cat_printf( + str, + "r %d w %d rw %d c %d\n", + settings->access_rights >> 12 & 0xF, + settings->access_rights >> 8 & 0xF, + settings->access_rights >> 4 & 0xF, + settings->access_rights & 0xF); + + uint32_t record_count = 1; + uint32_t record_size = 0; + + switch(settings->type) { + case MfDesfireFileTypeStandard: + case MfDesfireFileTypeBackup: + record_size = settings->data.size; + furi_string_cat_printf(str, "size %lu\n", record_size); + break; + case MfDesfireFileTypeValue: + furi_string_cat_printf( + str, "lo %lu hi %lu\n", settings->value.lo_limit, settings->value.hi_limit); + furi_string_cat_printf( + str, + "limit %lu enabled %d\n", + settings->value.limited_credit_value, + settings->value.limited_credit_enabled); + break; + case MfDesfireFileTypeLinearRecord: + case MfDesfireFileTypeCyclicRecord: + record_count = settings->record.cur; + record_size = settings->record.size; + furi_string_cat_printf(str, "size %lu\n", record_size); + furi_string_cat_printf(str, "num %lu max %lu\n", record_count, settings->record.max); + break; + } + + if(simple_array_get_count(data->data) == 0) { + return; + } + + // TODO: Replace with pretty_format + for(uint32_t rec = 0; rec < record_count; rec++) { + furi_string_cat_printf(str, "record %lu\n", rec); + for(uint32_t ch = 0; ch < record_size; ch += 4) { + furi_string_cat_printf(str, "%03lx|", ch); + for(uint32_t i = 0; i < 4; i++) { + if(ch + i < record_size) { + const uint32_t data_index = rec * record_size + ch + i; + const uint8_t data_byte = + *(const uint8_t*)simple_array_cget(data->data, data_index); + furi_string_cat_printf(str, "%02x ", data_byte); + } else { + furi_string_cat_printf(str, " "); + } + } + for(uint32_t i = 0; i < 4 && ch + i < record_size; i++) { + const uint32_t data_index = rec * record_size + ch + i; + const uint8_t data_byte = + *(const uint8_t*)simple_array_cget(data->data, data_index); + if(isprint(data_byte)) { + furi_string_cat_printf(str, "%c", data_byte); + } else { + furi_string_cat_printf(str, "."); + } + } + furi_string_push_back(str, '\n'); + } + furi_string_push_back(str, '\n'); + } +} diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.h b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.h new file mode 100644 index 000000000000..ff5e815bff05 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_mf_desfire_info( + const MfDesfireData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_mf_desfire_data(const MfDesfireData* data, FuriString* str); + +void nfc_render_mf_desfire_version(const MfDesfireVersion* data, FuriString* str); + +void nfc_render_mf_desfire_free_memory(const MfDesfireFreeMemory* data, FuriString* str); + +void nfc_render_mf_desfire_key_settings(const MfDesfireKeySettings* data, FuriString* str); + +void nfc_render_mf_desfire_key_version( + const MfDesfireKeyVersion* data, + uint32_t index, + FuriString* str); + +void nfc_render_mf_desfire_application_id(const MfDesfireApplicationId* data, FuriString* str); + +void nfc_render_mf_desfire_application(const MfDesfireApplication* data, FuriString* str); + +void nfc_render_mf_desfire_file_id(const MfDesfireFileId* data, FuriString* str); + +void nfc_render_mf_desfire_file_settings_data( + const MfDesfireFileSettings* settings, + const MfDesfireFileData* data, + FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c new file mode 100644 index 000000000000..00ead03e9fa3 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -0,0 +1,230 @@ +#include "mf_ultralight.h" +#include "mf_ultralight_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_gui_common.h" + +enum { + SubmenuIndexUnlock = SubmenuIndexCommonMax, + SubmenuIndexUnlockByReader, + SubmenuIndexUnlockByPassword, +}; + +static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "More", + nfc_protocol_support_common_widget_callback, + instance); + + furi_string_free(temp_str); +} + +static void nfc_scene_card_dump_on_enter_mf_ultralight(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfUltralightData* mfu = nfc_device_get_data(device, NfcProtocolMfUltralight); + + furi_string_reset(instance->text_box_store); + nfc_render_mf_ultralight_dump(mfu, instance->text_box_store); + + text_box_set_font(instance->text_box, TextBoxFontHex); + text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store)); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); +} + +static NfcCommand + nfc_scene_read_poller_callback_mf_ultralight(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolMfUltralight); + + NfcApp* instance = context; + const MfUltralightPollerEvent* mf_ultralight_event = event.data; + + if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller)); + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + if(instance->mf_ul_auth->type == MfUltralightAuthTypeXiaomii) { + if(mf_ultralight_generate_xiaomi_pass( + instance->mf_ul_auth, + data->iso14443_3a_data->uid, + data->iso14443_3a_data->uid_len)) { + mf_ultralight_event->data->auth_context.skip_auth = false; + } + } else if(instance->mf_ul_auth->type == MfUltralightAuthTypeAmiibo) { + if(mf_ultralight_generate_amiibo_pass( + instance->mf_ul_auth, + data->iso14443_3a_data->uid, + data->iso14443_3a_data->uid_len)) { + mf_ultralight_event->data->auth_context.skip_auth = false; + } + } else if(instance->mf_ul_auth->type == MfUltralightAuthTypeManual) { + mf_ultralight_event->data->auth_context.skip_auth = false; + } else { + mf_ultralight_event->data->auth_context.skip_auth = true; + } + if(!mf_ultralight_event->data->auth_context.skip_auth) { + mf_ultralight_event->data->auth_context.password = instance->mf_ul_auth->password; + } + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthSuccess) { + instance->mf_ul_auth->pack = mf_ultralight_event->data->auth_context.pack; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance); +} + +static void nfc_scene_read_menu_on_enter_mf_ultralight(NfcApp* instance) { + Submenu* submenu = instance->submenu; + + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + + if(!mf_ultralight_is_all_data_read(data)) { + submenu_add_item( + submenu, + "Unlock", + SubmenuIndexUnlock, + nfc_protocol_support_common_submenu_callback, + instance); + } +} + +static void nfc_scene_read_success_on_enter_mf_ultralight(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_saved_menu_on_enter_mf_ultralight(NfcApp* instance) { + Submenu* submenu = instance->submenu; + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + + if(!mf_ultralight_is_all_data_read(data)) { + submenu_add_item( + submenu, + "Unlock with Reader", + SubmenuIndexUnlockByReader, + nfc_protocol_support_common_submenu_callback, + instance); + + submenu_add_item( + submenu, + "Unlock with Password", + SubmenuIndexUnlockByPassword, + nfc_protocol_support_common_submenu_callback, + instance); + } +} + +static void nfc_scene_emulate_on_enter_mf_ultralight(NfcApp* instance) { + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolMfUltralight, data); + nfc_listener_start(instance->listener, NULL, NULL); +} + +static bool nfc_scene_info_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { + if(event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneCardDump); + return true; + } + + return false; +} + +static bool nfc_scene_read_menu_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexUnlock) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { + bool consumed = false; + + if(event == SubmenuIndexUnlockByReader) { + scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + consumed = true; + } else if(event == SubmenuIndexUnlockByPassword) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; + } + + return consumed; +} + +const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { + .features = NfcProtocolFeatureEmulateFull, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_mf_ultralight, + .on_event = nfc_scene_info_on_event_mf_ultralight, + }, + .scene_card_dump = + { + .on_enter = nfc_scene_card_dump_on_enter_mf_ultralight, + .on_event = NULL, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_mf_ultralight, + .on_event = NULL, + }, + .scene_read_menu = + { + .on_enter = nfc_scene_read_menu_on_enter_mf_ultralight, + .on_event = nfc_scene_read_menu_on_event_mf_ultralight, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_mf_ultralight, + .on_event = NULL, + }, + .scene_saved_menu = + { + .on_enter = nfc_scene_saved_menu_on_enter_mf_ultralight, + .on_event = nfc_scene_saved_menu_on_event_mf_ultralight, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_mf_ultralight, + .on_event = NULL, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.h b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.h new file mode 100644 index 000000000000..83e58732e41d --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight; diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c new file mode 100644 index 000000000000..714db3d532cf --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c @@ -0,0 +1,26 @@ +#include "mf_ultralight_render.h" + +#include "../iso14443_3a/iso14443_3a_render.h" + +void nfc_render_mf_ultralight_info( + const MfUltralightData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_3a_info(data->iso14443_3a_data, format_type, str); + + furi_string_cat_printf(str, "\nPages Read: %u/%u", data->pages_read, data->pages_total); + if(data->pages_read != data->pages_total) { + furi_string_cat_printf(str, "\nPassword-protected pages!"); + } + + //TODO: Something else? +} + +void nfc_render_mf_ultralight_dump(const MfUltralightData* data, FuriString* str) { + for(size_t i = 0; i < data->pages_read; i++) { + const uint8_t* page_data = data->page[i].data; + for(size_t j = 0; j < MF_ULTRALIGHT_PAGE_SIZE; j += 2) { + furi_string_cat_printf(str, "%02X%02X ", page_data[j], page_data[j + 1]); + } + } +} diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.h b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.h new file mode 100644 index 000000000000..e3924600dd99 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_mf_ultralight_info( + const MfUltralightData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_mf_ultralight_dump(const MfUltralightData* data, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c new file mode 100644 index 000000000000..866e7d2ef586 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -0,0 +1,576 @@ +#include "nfc_protocol_support.h" + +#include "nfc/nfc_app_i.h" +#include "nfc/helpers/nfc_supported_cards.h" + +#include "nfc_protocol_support_defs.h" +#include "nfc_protocol_support_gui_common.h" + +typedef void (*NfcProtocolSupportCommonOnEnter)(NfcApp* instance); +typedef bool (*NfcProtocolSupportCommonOnEvent)(NfcApp* instance, SceneManagerEvent event); +typedef void (*NfcProtocolSupportCommonOnExit)(NfcApp* instance); + +typedef struct { + NfcProtocolSupportCommonOnEnter on_enter; + NfcProtocolSupportCommonOnEvent on_event; + NfcProtocolSupportCommonOnExit on_exit; +} NfcProtocolSupportCommonSceneBase; + +static const NfcProtocolSupportCommonSceneBase nfc_protocol_support_scenes[]; + +// Interface functions +void nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context) { + furi_assert(scene < NfcProtocolSupportSceneCount); + furi_assert(context); + + NfcApp* instance = context; + nfc_protocol_support_scenes[scene].on_enter(instance); +} + +bool nfc_protocol_support_on_event( + NfcProtocolSupportScene scene, + void* context, + SceneManagerEvent event) { + furi_assert(scene < NfcProtocolSupportSceneCount); + furi_assert(context); + + NfcApp* instance = context; + return nfc_protocol_support_scenes[scene].on_event(instance, event); +} + +void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context) { + furi_assert(scene < NfcProtocolSupportSceneCount); + furi_assert(context); + + NfcApp* instance = context; + nfc_protocol_support_scenes[scene].on_exit(instance); +} + +static bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature) { + return nfc_protocol_support[protocol]->features & feature; +} + +// Common scene handlers +// SceneInfo +static void nfc_protocol_support_scene_info_on_enter(NfcApp* instance) { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + nfc_protocol_support[protocol]->scene_info.on_enter(instance); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +static bool nfc_protocol_support_scene_info_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + consumed = nfc_protocol_support[protocol]->scene_info.on_event(instance, event.event); + } else if(event.type == SceneManagerEventTypeBack) { + // If the card could not be parsed, return to the respective menu + if(!scene_manager_get_scene_state(instance->scene_manager, NfcSceneSupportedCard)) { + const uint32_t scenes[] = {NfcSceneSavedMenu, NfcSceneReadMenu}; + scene_manager_search_and_switch_to_previous_scene_one_of( + instance->scene_manager, scenes, COUNT_OF(scenes)); + consumed = true; + } + } + + return consumed; +} + +static void nfc_protocol_support_scene_info_on_exit(NfcApp* instance) { + widget_reset(instance->widget); +} + +static void nfc_protocol_support_scene_card_dump_on_enter(NfcApp* instance) { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + nfc_protocol_support[protocol]->scene_card_dump.on_enter(instance); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); +} + +static bool + nfc_protocol_support_scene_card_dump_on_event(NfcApp* instance, SceneManagerEvent event) { + UNUSED(instance); + UNUSED(event); + return false; +} + +static void nfc_protocol_support_scene_card_dump_on_exit(NfcApp* instance) { + text_box_reset(instance->text_box); + furi_string_reset(instance->text_box_store); +} + +// SceneRead +static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) { + popup_set_header( + instance->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); + popup_set_icon(instance->popup, 12, 23, &A_Loading_24); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); + + const NfcProtocol protocol = instance->protocols_detected[instance->protocols_detected_idx]; + instance->poller = nfc_poller_alloc(instance->nfc, protocol); + + // Start poller with the appropriate callback + nfc_protocol_support[protocol]->scene_read.on_enter(instance); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); + + nfc_blink_detect_start(instance); +} + +static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventPollerSuccess) { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else if(event.event == NfcCustomEventPollerIncomplete) { + bool card_read = nfc_supported_cards_read(instance->nfc_device, instance->nfc); + if(card_read) { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else { + const NfcProtocol protocol = + instance->protocols_detected[instance->protocols_detected_idx]; + if(nfc_protocol_support[protocol]->scene_read.on_event) { + consumed = + nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event); + } + } + } else if(event.event == NfcCustomEventPollerFailure) { + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDetect)) { + scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneDetect); + } + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + static const uint32_t possible_scenes[] = {NfcSceneSelectProtocol, NfcSceneStart}; + scene_manager_search_and_switch_to_previous_scene_one_of( + instance->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); + consumed = true; + } + + return consumed; +} + +static void nfc_protocol_support_scene_read_on_exit(NfcApp* instance) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} + +// SceneReadMenu +static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + + Submenu* submenu = instance->submenu; + + submenu_add_item( + submenu, + "Save", + SubmenuIndexCommonSave, + nfc_protocol_support_common_submenu_callback, + instance); + + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { + submenu_add_item( + submenu, + "Emulate UID", + SubmenuIndexCommonEmulate, + nfc_protocol_support_common_submenu_callback, + instance); + + } else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) { + submenu_add_item( + submenu, + "Emulate", + SubmenuIndexCommonEmulate, + nfc_protocol_support_common_submenu_callback, + instance); + } + + nfc_protocol_support[protocol]->scene_read_menu.on_enter(instance); + + submenu_add_item( + submenu, + "Info", + SubmenuIndexCommonInfo, + nfc_protocol_support_common_submenu_callback, + instance); + + submenu_set_selected_item( + instance->submenu, + scene_manager_get_scene_state(instance->scene_manager, NfcSceneReadMenu)); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +static bool + nfc_protocol_support_scene_read_menu_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneReadMenu, event.event); + + if(event.event == SubmenuIndexCommonSave) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexCommonInfo) { + scene_manager_next_scene(instance->scene_manager, NfcSceneInfo); + consumed = true; + } else if(event.event == SubmenuIndexCommonEmulate) { + dolphin_deed(DolphinDeedNfcEmulate); + scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); + consumed = true; + } else { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + consumed = + nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event.event); + } + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneSavedMenu, 0); + } + + return consumed; +} + +static void nfc_protocol_support_scene_read_menu_on_exit(NfcApp* instance) { + submenu_reset(instance->submenu); +} + +// SceneReadSuccess +static void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) { + Widget* widget = instance->widget; + + FuriString* temp_str = furi_string_alloc(); + if(nfc_supported_cards_parse(instance->nfc_device, temp_str)) { + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + } else { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + nfc_protocol_support[protocol]->scene_read_success.on_enter(instance); + } + + furi_string_free(temp_str); + + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_protocol_support_common_widget_callback, instance); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_protocol_support_common_widget_callback, instance); + + notification_message_block(instance->notifications, &sequence_set_green_255); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +static bool + nfc_protocol_support_scene_read_success_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(instance->scene_manager, NfcSceneRetryConfirm); + consumed = true; + + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneReadMenu); + consumed = true; + } + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + + return consumed; +} + +static void nfc_protocol_support_scene_read_success_on_exit(NfcApp* instance) { + notification_message_block(instance->notifications, &sequence_reset_green); + widget_reset(instance->widget); +} + +// SceneSavedMenu +static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + + Submenu* submenu = instance->submenu; + + // Header submenu items + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { + submenu_add_item( + submenu, + "Emulate UID", + SubmenuIndexCommonEmulate, + nfc_protocol_support_common_submenu_callback, + instance); + + } else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) { + submenu_add_item( + submenu, + "Emulate", + SubmenuIndexCommonEmulate, + nfc_protocol_support_common_submenu_callback, + instance); + } + + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEditUid)) { + submenu_add_item( + submenu, + "Edit UID", + SubmenuIndexCommonEdit, + nfc_protocol_support_common_submenu_callback, + instance); + } + + // Protocol-dependent menu items + nfc_protocol_support[protocol]->scene_saved_menu.on_enter(instance); + + // Trailer submenu items + submenu_add_item( + submenu, + "Info", + SubmenuIndexCommonInfo, + nfc_protocol_support_common_submenu_callback, + instance); + submenu_add_item( + submenu, + "Rename", + SubmenuIndexCommonRename, + nfc_protocol_support_common_submenu_callback, + instance); + submenu_add_item( + submenu, + "Delete", + SubmenuIndexCommonDelete, + nfc_protocol_support_common_submenu_callback, + instance); + + if(nfc_has_shadow_file(instance)) { + submenu_add_item( + submenu, + "Restore Data Changes", + SubmenuIndexCommonRestore, + nfc_protocol_support_common_submenu_callback, + instance); + } + + submenu_set_selected_item( + instance->submenu, + scene_manager_get_scene_state(instance->scene_manager, NfcSceneSavedMenu)); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +static bool + nfc_protocol_support_scene_saved_menu_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneSavedMenu, event.event); + + if(event.event == SubmenuIndexCommonRestore) { + scene_manager_next_scene(instance->scene_manager, NfcSceneRestoreOriginalConfirm); + consumed = true; + } else if(event.event == SubmenuIndexCommonInfo) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSupportedCard); + consumed = true; + } else if(event.event == SubmenuIndexCommonRename) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexCommonDelete) { + scene_manager_next_scene(instance->scene_manager, NfcSceneDelete); + consumed = true; + } else if(event.event == SubmenuIndexCommonEmulate) { + const bool is_added = + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType); + dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate); + scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); + consumed = true; + } else { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + consumed = + nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event.event); + } + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneSavedMenu, 0); + } + + return consumed; +} + +static void nfc_protocol_support_scene_saved_menu_on_exit(NfcApp* instance) { + submenu_reset(instance->submenu); +} + +// SceneEmulate +enum { + NfcSceneEmulateStateWidget, + NfcSceneEmulateStateTextBox, +}; + +static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { + Widget* widget = instance->widget; + TextBox* text_box = instance->text_box; + + FuriString* temp_str = furi_string_alloc(); + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + + widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); + + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { + widget_add_string_element( + widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating UID"); + + size_t uid_len; + const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len); + + for(size_t i = 0; i < uid_len; ++i) { + furi_string_cat_printf(temp_str, "%02X ", uid[i]); + } + + furi_string_trim(temp_str); + + } else { + widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating"); + furi_string_set( + temp_str, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + } + + widget_add_text_box_element( + widget, 56, 28, 68, 25, AlignCenter, AlignTop, furi_string_get_cstr(temp_str), false); + + furi_string_free(temp_str); + + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + furi_string_reset(instance->text_box_store); + + // instance->listener is allocated in the respective on_enter() handler + nfc_protocol_support[protocol]->scene_emulate.on_enter(instance); + + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidget); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); + nfc_blink_emulate_start(instance); +} + +static bool + nfc_protocol_support_scene_emulate_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + const uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneEmulate); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventListenerUpdate) { + // Add data button to widget if data is received for the first time + if(furi_string_size(instance->text_box_store)) { + widget_add_button_element( + instance->widget, + GuiButtonTypeCenter, + "Log", + nfc_protocol_support_common_widget_callback, + instance); + } + // Update TextBox data + text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store)); + consumed = true; + } else if(event.event == GuiButtonTypeCenter) { + if(state == NfcSceneEmulateStateWidget) { + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateTextBox); + consumed = true; + } + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneEmulateStateTextBox) { + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidget); + consumed = true; + } + } + + return consumed; +} + +static void nfc_protocol_support_scene_emulate_on_exit(NfcApp* instance) { + nfc_listener_stop(instance->listener); + + NfcDevice* nfc_stub = nfc_device_alloc(); + NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + const NfcDeviceData* data = nfc_listener_get_data(instance->listener, protocol); + nfc_device_set_data(nfc_stub, protocol, data); + + //TODO: think about nfc_device_is_equal(NfcDevice*,NfcDeviceData*); + if(!nfc_device_is_equal(nfc_stub, instance->nfc_device)) { + nfc_device_set_data(instance->nfc_device, protocol, data); + nfc_save_shadow_file(instance); + } + nfc_device_free(nfc_stub); + + nfc_listener_free(instance->listener); + + // Clear view + widget_reset(instance->widget); + text_box_reset(instance->text_box); + furi_string_reset(instance->text_box_store); + + nfc_blink_stop(instance); +} + +static const NfcProtocolSupportCommonSceneBase + nfc_protocol_support_scenes[NfcProtocolSupportSceneCount] = { + [NfcProtocolSupportSceneInfo] = + { + .on_enter = nfc_protocol_support_scene_info_on_enter, + .on_event = nfc_protocol_support_scene_info_on_event, + .on_exit = nfc_protocol_support_scene_info_on_exit, + }, + [NfcProtocolSupportSceneCardDump] = + { + .on_enter = nfc_protocol_support_scene_card_dump_on_enter, + .on_event = nfc_protocol_support_scene_card_dump_on_event, + .on_exit = nfc_protocol_support_scene_card_dump_on_exit, + }, + [NfcProtocolSupportSceneRead] = + { + .on_enter = nfc_protocol_support_scene_read_on_enter, + .on_event = nfc_protocol_support_scene_read_on_event, + .on_exit = nfc_protocol_support_scene_read_on_exit, + }, + [NfcProtocolSupportSceneReadMenu] = + { + .on_enter = nfc_protocol_support_scene_read_menu_on_enter, + .on_event = nfc_protocol_support_scene_read_menu_on_event, + .on_exit = nfc_protocol_support_scene_read_menu_on_exit, + }, + [NfcProtocolSupportSceneReadSuccess] = + { + .on_enter = nfc_protocol_support_scene_read_success_on_enter, + .on_event = nfc_protocol_support_scene_read_success_on_event, + .on_exit = nfc_protocol_support_scene_read_success_on_exit, + }, + [NfcProtocolSupportSceneSavedMenu] = + { + .on_enter = nfc_protocol_support_scene_saved_menu_on_enter, + .on_event = nfc_protocol_support_scene_saved_menu_on_event, + .on_exit = nfc_protocol_support_scene_saved_menu_on_exit, + }, + [NfcProtocolSupportSceneEmulate] = + { + .on_enter = nfc_protocol_support_scene_emulate_on_enter, + .on_event = nfc_protocol_support_scene_emulate_on_event, + .on_exit = nfc_protocol_support_scene_emulate_on_exit, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h new file mode 100644 index 000000000000..c5b295d5759e --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h @@ -0,0 +1,12 @@ +#pragma once + +#include "nfc_protocol_support_common.h" + +void nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context); + +bool nfc_protocol_support_on_event( + NfcProtocolSupportScene scene, + void* context, + SceneManagerEvent event); + +void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h new file mode 100644 index 000000000000..f152b99f49b0 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "nfc_protocol_support_common.h" + +typedef void (*NfcProtocolSupportOnEnter)(NfcApp* instance); + +typedef bool (*NfcProtocolSupportOnEvent)(NfcApp* instance, uint32_t event); + +typedef void (*NfcProtocolSupportOnExit)(NfcApp* instance); + +typedef struct { + NfcProtocolSupportOnEnter on_enter; + NfcProtocolSupportOnEvent on_event; + /*NfcProtocolSupportOnExit on_exit; is not necessary */ +} NfcProtocolSupportSceneBase; + +typedef struct { + const uint32_t features; + + NfcProtocolSupportSceneBase scene_info; + NfcProtocolSupportSceneBase scene_card_dump; + NfcProtocolSupportSceneBase scene_read; + NfcProtocolSupportSceneBase scene_read_menu; + NfcProtocolSupportSceneBase scene_read_success; + NfcProtocolSupportSceneBase scene_saved_menu; + NfcProtocolSupportSceneBase scene_emulate; + // TODO Add scene save to save mf classic key cache +} NfcProtocolSupportBase; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h new file mode 100644 index 000000000000..50fb3cbde763 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include "nfc_protocol_support_render_common.h" + +#include "../nfc_custom_event.h" +#include "../../scenes/nfc_scene.h" +#include "../../nfc_app.h" + +typedef enum { + NfcProtocolFeatureNone = 0, + NfcProtocolFeatureEmulateUid = 1UL << 0, + NfcProtocolFeatureEmulateFull = 1UL << 1, + NfcProtocolFeatureEditUid = 1UL << 2, +} NfcProtocolFeature; + +typedef enum { + NfcProtocolSupportSceneInfo = 0, + NfcProtocolSupportSceneRead, + NfcProtocolSupportSceneReadMenu, + NfcProtocolSupportSceneReadSuccess, + NfcProtocolSupportSceneSavedMenu, + NfcProtocolSupportSceneEmulate, + NfcProtocolSupportSceneCardDump, + + NfcProtocolSupportSceneCount, +} NfcProtocolSupportScene; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c new file mode 100644 index 000000000000..549b06b2b300 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c @@ -0,0 +1,24 @@ +#include "nfc_protocol_support_defs.h" + +#include "iso14443_3a/iso14443_3a.h" +#include "iso14443_3b/iso14443_3b.h" +#include "iso14443_4a/iso14443_4a.h" +#include "iso15693_3/iso15693_3.h" +#include "felica/felica.h" +#include "mf_ultralight/mf_ultralight.h" +#include "mf_classic/mf_classic.h" +#include "mf_desfire/mf_desfire.h" +#include "slix/slix.h" + +const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { + [NfcProtocolIso14443_3a] = &nfc_protocol_support_iso14443_3a, + [NfcProtocolIso14443_3b] = &nfc_protocol_support_iso14443_3b, + [NfcProtocolIso14443_4a] = &nfc_protocol_support_iso14443_4a, + [NfcProtocolIso15693_3] = &nfc_protocol_support_iso15693_3, + [NfcProtocolFelica] = &nfc_protocol_support_felica, + [NfcProtocolMfUltralight] = &nfc_protocol_support_mf_ultralight, + [NfcProtocolMfClassic] = &nfc_protocol_support_mf_classic, + [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, + [NfcProtocolSlix] = &nfc_protocol_support_slix, + /* Add new protocols here */ +}; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h new file mode 100644 index 000000000000..8a15a56ec57a --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include "nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase* nfc_protocol_support[]; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c new file mode 100644 index 000000000000..3330f48c52bc --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c @@ -0,0 +1,30 @@ +#include "nfc_protocol_support_gui_common.h" + +#include "nfc/nfc_app_i.h" + +void nfc_protocol_support_common_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +void nfc_protocol_support_common_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) { + UNUSED(instance); +} + +bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event) { + UNUSED(instance); + UNUSED(event); + return false; +} diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h new file mode 100644 index 000000000000..db14fed5ea04 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "nfc/nfc_app.h" + +enum { + SubmenuIndexCommonSave, + SubmenuIndexCommonEmulate, + SubmenuIndexCommonEdit, + SubmenuIndexCommonInfo, + SubmenuIndexCommonRename, + SubmenuIndexCommonDelete, + SubmenuIndexCommonRestore, + SubmenuIndexCommonMax, +}; + +void nfc_protocol_support_common_submenu_callback(void* context, uint32_t index); + +void nfc_protocol_support_common_widget_callback( + GuiButtonType result, + InputType type, + void* context); + +void nfc_protocol_support_common_on_enter_empty(NfcApp* instance); + +bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_render_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_render_common.h new file mode 100644 index 000000000000..102934668191 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_render_common.h @@ -0,0 +1,6 @@ +#pragma once + +typedef enum { + NfcProtocolFormatTypeShort, + NfcProtocolFormatTypeFull, +} NfcProtocolFormatType; diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c new file mode 100644 index 000000000000..e027e53b0092 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -0,0 +1,141 @@ +#include "slix.h" +#include "slix_render.h" + +#include +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_slix(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_slix_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_read_poller_callback_slix(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolSlix); + + NfcApp* instance = context; + const SlixPollerEvent* slix_event = event.data; + + if(slix_event->type == SlixPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolSlix, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_slix(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_slix, instance); +} + +static void nfc_scene_read_success_on_enter_slix(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_slix_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_emulate_listener_callback_slix(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolSlix); + furi_assert(event.data); + + NfcApp* nfc = context; + SlixListenerEvent* slix_event = event.data; + + if(slix_event->type == SlixListenerEventTypeCustomCommand) { + furi_string_cat_printf(nfc->text_box_store, "R:"); + for(size_t i = 0; i < bit_buffer_get_size_bytes(slix_event->data->buffer); i++) { + furi_string_cat_printf( + nfc->text_box_store, " %02X", bit_buffer_get_byte(slix_event->data->buffer, i)); + } + furi_string_push_back(nfc->text_box_store, '\n'); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + } + + return NfcCommandContinue; +} + +static void nfc_scene_emulate_on_enter_slix(NfcApp* instance) { + const SlixData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolSlix); + + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolSlix, data); + nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance); +} + +static bool nfc_scene_info_on_event_slix(NfcApp* instance, uint32_t event) { + if(event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneNotImplemented); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_slix = { + .features = + NfcProtocolFeatureEmulateFull, // | NfcProtocolFeatureEditUid, // TODO: Implement better UID editing + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_slix, + .on_event = nfc_scene_info_on_event_slix, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_slix, + .on_event = NULL, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_slix, + .on_event = NULL, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_saved_menu_on_event_slix, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_slix, + .on_event = NULL, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.h b/applications/main/nfc/helpers/protocol_support/slix/slix.h new file mode 100644 index 000000000000..9c7504ebafa2 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_slix; diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix_render.c b/applications/main/nfc/helpers/protocol_support/slix/slix_render.c new file mode 100644 index 000000000000..0cbaec05615a --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/slix/slix_render.c @@ -0,0 +1,81 @@ +#include "slix_render.h" + +#include "../iso15693_3/iso15693_3_render.h" + +void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str) { + if(format_type == NfcProtocolFormatTypeFull) { + furi_string_cat_printf(str, "%s\n", slix_get_device_name(data, NfcDeviceNameTypeFull)); + } + + const Iso15693_3Data* iso15693_3_data = slix_get_base_data(data); + + nfc_render_iso15693_3_header(iso15693_3_data, str); + + if(format_type != NfcProtocolFormatTypeFull) return; + const SlixType slix_type = slix_get_type(data); + + furi_string_cat(str, "\n\e#Passwords\n"); + + static const char* slix_password_names[] = { + "Read", + "Write", + "Privacy", + "Destroy", + "EAS/AFI", + }; + + for(uint32_t i = 0; i < SlixPasswordTypeCount; ++i) { + if(slix_type_supports_password(slix_type, i)) { + furi_string_cat_printf( + str, "%s : %08lX\n", slix_password_names[i], data->passwords[i]); + } + } + + furi_string_cat(str, "\e#Lock bits\n"); + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) { + furi_string_cat_printf( + str, "EAS: %s locked\n", data->system_info.lock_bits.eas ? "" : "not"); + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + furi_string_cat_printf( + str, "PPL: %s locked\n", data->system_info.lock_bits.ppl ? "" : "not"); + + const SlixProtection protection = data->system_info.protection; + + furi_string_cat(str, "\e#Page protection\n"); + furi_string_cat_printf(str, "Pointer: H >= %02X\n", protection.pointer); + + const char* rh = (protection.condition & SLIX_PP_CONDITION_RH) ? "" : "un"; + const char* rl = (protection.condition & SLIX_PP_CONDITION_RL) ? "" : "un"; + + const char* wh = (protection.condition & SLIX_PP_CONDITION_WH) ? "" : "un"; + const char* wl = (protection.condition & SLIX_PP_CONDITION_WL) ? "" : "un"; + + furi_string_cat_printf(str, "R: H %sprotec. L %sprotec.\n", rh, rl); + furi_string_cat_printf(str, "W: H %sprotec. L %sprotec.\n", wh, wl); + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) { + furi_string_cat(str, "\e#Privacy\n"); + furi_string_cat_printf(str, "Privacy mode: %sabled\n", data->privacy ? "en" : "dis"); + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) { + furi_string_cat(str, "\e#Signature\n"); + for(uint32_t i = 0; i < 4; ++i) { + furi_string_cat_printf(str, "%02X ", data->signature[i]); + } + + furi_string_cat(str, "[ ... ]"); + + for(uint32_t i = 0; i < 3; ++i) { + furi_string_cat_printf(str, " %02X", data->signature[sizeof(SlixSignature) - i - 1]); + } + } + + furi_string_cat(str, "\n\e#ISO15693-3 data\n"); + + nfc_render_iso15693_3_main_info(iso15693_3_data, str); +} diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix_render.h b/applications/main/nfc/helpers/protocol_support/slix/slix_render.h new file mode 100644 index 000000000000..98ae6dc97fd8 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/slix/slix_render.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str); diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c new file mode 100644 index 000000000000..f37d42a64820 --- /dev/null +++ b/applications/main/nfc/nfc_app.c @@ -0,0 +1,509 @@ +#include "nfc_app_i.h" + +#include + +bool nfc_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NfcApp* nfc = context; + return scene_manager_handle_custom_event(nfc->scene_manager, event); +} + +bool nfc_back_event_callback(void* context) { + furi_assert(context); + NfcApp* nfc = context; + return scene_manager_handle_back_event(nfc->scene_manager); +} + +// static void nfc_rpc_command_callback(RpcAppSystemEvent event, void* context) { +// furi_assert(context); +// NfcApp* nfc = context; + +// furi_assert(nfc->rpc_ctx); + +// if(event == RpcAppEventSessionClose) { +// view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose); +// rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); +// nfc->rpc_ctx = NULL; +// } else if(event == RpcAppEventAppExit) { +// view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +// } else if(event == RpcAppEventLoadFile) { +// view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad); +// } else { +// rpc_system_app_confirm(nfc->rpc_ctx, event, false); +// } +// } + +NfcApp* nfc_app_alloc() { + NfcApp* instance = malloc(sizeof(NfcApp)); + + instance->view_dispatcher = view_dispatcher_alloc(); + instance->scene_manager = scene_manager_alloc(&nfc_scene_handlers, instance); + view_dispatcher_enable_queue(instance->view_dispatcher); + view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance); + view_dispatcher_set_custom_event_callback( + instance->view_dispatcher, nfc_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + instance->view_dispatcher, nfc_back_event_callback); + + instance->nfc = nfc_alloc(); + + instance->mf_ul_auth = mf_ultralight_auth_alloc(); + instance->mfc_key_cache = mf_classic_key_cache_alloc(); + + // Nfc device + instance->nfc_device = nfc_device_alloc(); + nfc_device_set_loading_callback(instance->nfc_device, nfc_show_loading_popup, instance); + + // Open GUI record + instance->gui = furi_record_open(RECORD_GUI); + + // Open Notification record + instance->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Open Storage record + instance->storage = furi_record_open(RECORD_STORAGE); + + // Open Dialogs record + instance->dialogs = furi_record_open(RECORD_DIALOGS); + + // Submenu + instance->submenu = submenu_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewMenu, submenu_get_view(instance->submenu)); + + // Dialog + instance->dialog_ex = dialog_ex_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewDialogEx, dialog_ex_get_view(instance->dialog_ex)); + + // Popup + instance->popup = popup_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewPopup, popup_get_view(instance->popup)); + + // Loading + instance->loading = loading_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewLoading, loading_get_view(instance->loading)); + + // Text Input + instance->text_input = text_input_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewTextInput, text_input_get_view(instance->text_input)); + + // Byte Input + instance->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewByteInput, byte_input_get_view(instance->byte_input)); + + // TextBox + instance->text_box = text_box_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewTextBox, text_box_get_view(instance->text_box)); + instance->text_box_store = furi_string_alloc(); + + // Custom Widget + instance->widget = widget_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewWidget, widget_get_view(instance->widget)); + + // Dict attack + + instance->dict_attack = dict_attack_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(instance->dict_attack)); + + // Detect Reader + instance->detect_reader = detect_reader_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, + NfcViewDetectReader, + detect_reader_get_view(instance->detect_reader)); + + instance->iso14443_3a_edit_data = iso14443_3a_alloc(); + instance->file_path = furi_string_alloc_set(NFC_APP_FOLDER); + instance->file_name = furi_string_alloc(); + + return instance; +} + +void nfc_app_free(NfcApp* instance) { + furi_assert(instance); + + // if(instance->rpc_state == NfcRpcStateEmulating) { + // // Stop worker + // nfc_worker_stop(instance->worker); + // } else if(instance->rpc_state == NfcRpcStateEmulated) { + // // Stop worker + // nfc_worker_stop(instance->worker); + // // Save data in shadow file + // if(furi_string_size(instance->dev->load_path)) { + // nfc_device_save_shadow(instance->dev, furi_string_get_cstr(instance->dev->load_path)); + // } + // } + if(instance->rpc_ctx) { + rpc_system_app_send_exited(instance->rpc_ctx); + rpc_system_app_set_callback(instance->rpc_ctx, NULL, NULL); + instance->rpc_ctx = NULL; + } + + nfc_free(instance->nfc); + + mf_ultralight_auth_free(instance->mf_ul_auth); + mf_classic_key_cache_free(instance->mfc_key_cache); + + // Nfc device + nfc_device_free(instance->nfc_device); + + // Submenu + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewMenu); + submenu_free(instance->submenu); + + // DialogEx + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewDialogEx); + dialog_ex_free(instance->dialog_ex); + + // Popup + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewPopup); + popup_free(instance->popup); + + // Loading + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewLoading); + loading_free(instance->loading); + + // TextInput + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewTextInput); + text_input_free(instance->text_input); + + // ByteInput + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewByteInput); + byte_input_free(instance->byte_input); + + // TextBox + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewTextBox); + text_box_free(instance->text_box); + furi_string_free(instance->text_box_store); + + // Custom Widget + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewWidget); + widget_free(instance->widget); + + // Dict attack + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewDictAttack); + dict_attack_free(instance->dict_attack); + + // Detect reader + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewDetectReader); + detect_reader_free(instance->detect_reader); + + // View Dispatcher + view_dispatcher_free(instance->view_dispatcher); + + // Scene Manager + scene_manager_free(instance->scene_manager); + + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_NOTIFICATION); + // GUI + furi_record_close(RECORD_GUI); + instance->gui = NULL; + + instance->notifications = NULL; + + iso14443_3a_free(instance->iso14443_3a_edit_data); + furi_string_free(instance->file_path); + furi_string_free(instance->file_name); + + free(instance); +} + +void nfc_text_store_set(NfcApp* nfc, const char* text, ...) { + va_list args; + va_start(args, text); + + vsnprintf(nfc->text_store, sizeof(nfc->text_store), text, args); + + va_end(args); +} + +void nfc_text_store_clear(NfcApp* nfc) { + memset(nfc->text_store, 0, sizeof(nfc->text_store)); +} + +void nfc_blink_read_start(NfcApp* nfc) { + notification_message(nfc->notifications, &sequence_blink_start_cyan); +} + +void nfc_blink_emulate_start(NfcApp* nfc) { + notification_message(nfc->notifications, &sequence_blink_start_magenta); +} + +void nfc_blink_detect_start(NfcApp* nfc) { + notification_message(nfc->notifications, &sequence_blink_start_yellow); +} + +void nfc_blink_stop(NfcApp* nfc) { + notification_message(nfc->notifications, &sequence_blink_stop); +} + +void nfc_make_app_folders(NfcApp* instance) { + furi_assert(instance); + + if(!storage_simply_mkdir(instance->storage, NFC_APP_FOLDER)) { + dialog_message_show_storage_error(instance->dialogs, "Cannot create\napp folder"); + } +} + +bool nfc_save_file(NfcApp* instance, FuriString* path) { + furi_assert(instance); + furi_assert(path); + + bool result = nfc_device_save(instance->nfc_device, furi_string_get_cstr(instance->file_path)); + + if(!result) { + dialog_message_show_storage_error(instance->dialogs, "Cannot save\nkey file"); + } + + // TODO move this to protocol support save scene + if(nfc_device_get_protocol(instance->nfc_device) == NfcProtocolMfClassic) { + mf_classic_key_cache_save( + instance->mfc_key_cache, + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic)); + } + + return result; +} + +static bool nfc_set_shadow_file_path(FuriString* file_path, FuriString* shadow_file_path) { + furi_assert(file_path); + furi_assert(shadow_file_path); + + bool shadow_file_path_set = false; + if(furi_string_end_with(file_path, NFC_APP_SHADOW_EXTENSION)) { + furi_string_set(shadow_file_path, file_path); + shadow_file_path_set = true; + } else if(furi_string_end_with(file_path, NFC_APP_EXTENSION)) { + size_t path_len = furi_string_size(file_path); + // Cut .nfc + furi_string_set_n(shadow_file_path, file_path, 0, path_len - 4); + furi_string_cat_printf(shadow_file_path, "%s", NFC_APP_SHADOW_EXTENSION); + shadow_file_path_set = true; + } + + return shadow_file_path_set; +} + +static bool nfc_has_shadow_file_internal(NfcApp* instance, FuriString* path) { + furi_assert(path); + + bool has_shadow_file = false; + FuriString* shadow_file_path = furi_string_alloc(); + do { + if(furi_string_empty(path)) break; + if(!nfc_set_shadow_file_path(path, shadow_file_path)) break; + has_shadow_file = + storage_common_exists(instance->storage, furi_string_get_cstr(shadow_file_path)); + } while(false); + + furi_string_free(shadow_file_path); + + return has_shadow_file; +} + +bool nfc_has_shadow_file(NfcApp* instance) { + furi_assert(instance); + + return nfc_has_shadow_file_internal(instance, instance->file_path); +} + +static bool nfc_save_internal(NfcApp* instance, const char* extension) { + furi_assert(instance); + furi_assert(extension); + + bool result = false; + + nfc_make_app_folders(instance); + + if(furi_string_end_with(instance->file_path, NFC_APP_EXTENSION) || + (furi_string_end_with(instance->file_path, NFC_APP_SHADOW_EXTENSION))) { + size_t filename_start = furi_string_search_rchar(instance->file_path, '/'); + furi_string_left(instance->file_path, filename_start); + } + + furi_string_cat_printf( + instance->file_path, "/%s%s", furi_string_get_cstr(instance->file_name), extension); + + result = nfc_save_file(instance, instance->file_path); + + return result; +} + +bool nfc_save_shadow_file(NfcApp* instance) { + furi_assert(instance); + + return nfc_save_internal(instance, NFC_APP_SHADOW_EXTENSION); +} + +bool nfc_save(NfcApp* instance) { + furi_assert(instance); + + return nfc_save_internal(instance, NFC_APP_EXTENSION); +} + +bool nfc_load_file(NfcApp* instance, FuriString* path, bool show_dialog) { + furi_assert(instance); + furi_assert(path); + bool result = false; + + FuriString* load_path = furi_string_alloc(); + if(nfc_has_shadow_file_internal(instance, path)) { + nfc_set_shadow_file_path(path, load_path); + } else if(furi_string_end_with(path, NFC_APP_SHADOW_EXTENSION)) { + size_t path_len = furi_string_size(path); + furi_string_set_n(load_path, path, 0, path_len - 4); + furi_string_cat_printf(load_path, "%s", NFC_APP_EXTENSION); + } else { + furi_string_set(load_path, path); + } + + result = nfc_device_load(instance->nfc_device, furi_string_get_cstr(load_path)); + + if(result) { + path_extract_filename(load_path, instance->file_name, true); + } + + if((!result) && (show_dialog)) { + dialog_message_show_storage_error(instance->dialogs, "Cannot load\nkey file"); + } + + furi_string_free(load_path); + + return result; +} + +bool nfc_delete(NfcApp* instance) { + furi_assert(instance); + + if(nfc_has_shadow_file(instance)) { + nfc_delete_shadow_file(instance); + } + + return storage_simply_remove(instance->storage, furi_string_get_cstr(instance->file_path)); +} + +bool nfc_delete_shadow_file(NfcApp* instance) { + furi_assert(instance); + + FuriString* shadow_file_path = furi_string_alloc(); + + bool result = nfc_set_shadow_file_path(instance->file_path, shadow_file_path) && + storage_simply_remove(instance->storage, furi_string_get_cstr(shadow_file_path)); + + furi_string_free(shadow_file_path); + return result; +} + +bool nfc_load_from_file_select(NfcApp* instance) { + furi_assert(instance); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, NFC_APP_EXTENSION, &I_Nfc_10px); + browser_options.base_path = NFC_APP_FOLDER; + browser_options.hide_dot_files = true; + + // Input events and views are managed by file_browser + bool result = dialog_file_browser_show( + instance->dialogs, instance->file_path, instance->file_path, &browser_options); + + if(result) { + result = nfc_load_file(instance, instance->file_path, true); + } + + return result; +} + +void nfc_show_loading_popup(void* context, bool show) { + NfcApp* nfc = context; + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewLoading); + } else { + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + +void nfc_app_set_detected_protocols(NfcApp* instance, const NfcProtocol* types, uint32_t count) { + furi_assert(instance); + furi_assert(types); + furi_assert(count < NfcProtocolNum); + + memcpy(instance->protocols_detected, types, count); + instance->protocols_detected_num = count; + instance->protocols_detected_idx = 0; +} + +void nfc_app_reset_detected_protocols(NfcApp* instance) { + furi_assert(instance); + + instance->protocols_detected_idx = 0; + instance->protocols_detected_num = 0; +} + +static bool nfc_is_hal_ready() { + if(f_hal_nfc_is_hal_ready() != FHalNfcErrorNone) { + // No connection to the chip, show an error screen + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_text( + message, + "Error!\nNFC chip failed to start\n\n\nSend a photo of this to:\nsupport@flipperzero.one", + 0, + 0, + AlignLeft, + AlignTop); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + return false; + } else { + return true; + } +} + +int32_t nfc_app(void* p) { + UNUSED(p); + if(!nfc_is_hal_ready()) return 0; + + NfcApp* nfc = nfc_app_alloc(); + char* args = p; + // const char* args = "/ext/nfc/4.nfc"; + + // Check argument and run corresponding scene + if(args && strlen(args)) { + nfc_device_set_loading_callback(nfc->nfc_device, nfc_show_loading_popup, nfc); + view_dispatcher_attach_to_gui( + nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); + if(nfc_device_load(nfc->nfc_device, args)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulate); + + } else { + // Exit app + view_dispatcher_stop(nfc->view_dispatcher); + } + } else { + view_dispatcher_attach_to_gui( + nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(nfc->scene_manager, NfcSceneStart); + } + + view_dispatcher_run(nfc->view_dispatcher); + + nfc_app_free(nfc); + + return 0; +} diff --git a/applications/main/nfc/nfc_app.h b/applications/main/nfc/nfc_app.h new file mode 100644 index 000000000000..0e456fd48b27 --- /dev/null +++ b/applications/main/nfc/nfc_app.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct NfcApp NfcApp; diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h new file mode 100644 index 000000000000..90666f317161 --- /dev/null +++ b/applications/main/nfc/nfc_app_i.h @@ -0,0 +1,185 @@ +#pragma once + +#include "nfc_app.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "views/dict_attack.h" +#include "views/detect_reader.h" +#include "views/dict_attack.h" + +#include +#include "helpers/nfc_custom_event.h" +#include "helpers/mf_ultralight_auth.h" +#include "helpers/mf_dict.h" +#include "helpers/mfkey32_logger.h" +#include "helpers/mf_user_dict.h" +#include "helpers/mf_classic_key_cache.h" + +#include +#include +#include + +#include "rpc/rpc_app.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define NFC_NAME_SIZE 22 +#define NFC_TEXT_STORE_SIZE 128 +#define NFC_APP_FOLDER ANY_PATH("nfc") +#define NFC_APP_EXTENSION ".nfc" +#define NFC_APP_SHADOW_EXTENSION ".shd" + +#define NFC_APP_MFKEY32_LOGS_FILE_NAME ".mfkey32.log" +#define NFC_APP_MFKEY32_LOGS_FILE_PATH (NFC_APP_FOLDER "/" NFC_APP_MFKEY32_LOGS_FILE_NAME) + +typedef enum { + NfcRpcStateIdle, + NfcRpcStateEmulating, + NfcRpcStateEmulated, +} NfcRpcState; + +typedef struct { + MfDict* dict; + uint8_t sectors_total; + uint8_t sectors_read; + uint8_t current_sector; + uint8_t keys_found; + size_t dict_keys_total; + size_t dict_keys_current; + bool is_key_attack; + uint8_t key_attack_current_sector; +} NfcMfClassicDictAttackContext; + +struct NfcApp { + DialogsApp* dialogs; + Storage* storage; + Gui* gui; + ViewDispatcher* view_dispatcher; + NotificationApp* notifications; + SceneManager* scene_manager; + + char text_store[NFC_TEXT_STORE_SIZE + 1]; + FuriString* text_box_store; + uint8_t byte_input_store[6]; + + size_t protocols_detected_num; + size_t protocols_detected_idx; + NfcProtocol protocols_detected[NfcProtocolNum]; + + void* rpc_ctx; + NfcRpcState rpc_state; + + // Common Views + Submenu* submenu; + DialogEx* dialog_ex; + Popup* popup; + Loading* loading; + TextInput* text_input; + ByteInput* byte_input; + TextBox* text_box; + Widget* widget; + DetectReader* detect_reader; + DictAttack* dict_attack; + + Nfc* nfc; + NfcPoller* poller; + NfcScanner* scanner; + NfcListener* listener; + + MfUltralightAuth* mf_ul_auth; + NfcMfClassicDictAttackContext mf_dict_context; + Mfkey32Logger* mfkey32_logger; + MfUserDict* mf_user_dict; + MfClassicKeyCache* mfc_key_cache; + + NfcDevice* nfc_device; + Iso14443_3aData* iso14443_3a_edit_data; + FuriString* file_path; + FuriString* file_name; + FuriTimer* timer; +}; + +typedef enum { + NfcViewMenu, + NfcViewDialogEx, + NfcViewPopup, + NfcViewLoading, + NfcViewTextInput, + NfcViewByteInput, + NfcViewTextBox, + NfcViewWidget, + NfcViewDictAttack, + NfcViewDetectReader, +} NfcView; + +int32_t nfc_task(void* p); + +void nfc_text_store_set(NfcApp* nfc, const char* text, ...); + +void nfc_text_store_clear(NfcApp* nfc); + +void nfc_blink_read_start(NfcApp* nfc); + +void nfc_blink_emulate_start(NfcApp* nfc); + +void nfc_blink_detect_start(NfcApp* nfc); + +void nfc_blink_stop(NfcApp* nfc); + +void nfc_show_loading_popup(void* context, bool show); + +bool nfc_has_shadow_file(NfcApp* instance); + +bool nfc_save_shadow_file(NfcApp* instance); + +bool nfc_delete_shadow_file(NfcApp* instance); + +bool nfc_save(NfcApp* instance); + +bool nfc_delete(NfcApp* instance); + +bool nfc_load_from_file_select(NfcApp* instance); + +bool nfc_load_file(NfcApp* instance, FuriString* path, bool show_dialog); + +bool nfc_save_file(NfcApp* instance, FuriString* path); + +void nfc_make_app_folder(NfcApp* instance); + +void nfc_app_set_detected_protocols(NfcApp* instance, const NfcProtocol* types, uint32_t count); + +void nfc_app_reset_detected_protocols(NfcApp* instance); diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 6e6e04ca928b..818a27ec18de 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -4,8 +4,15 @@ #include #include -#include -#include +#include +#include +#include +#include + +#include +#include + +#define FLAG_EVENT (1 << 10) static void nfc_cli_print_usage() { printf("Usage:\r\n"); @@ -19,146 +26,72 @@ static void nfc_cli_print_usage() { } } -static void nfc_cli_detect(Cli* cli, FuriString* args) { - UNUSED(args); - // Check if nfc worker is not busy - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; - } +static void f_hal_nfc_iso15693_listener_transparent_mode_enter(FuriHalSpiBusHandle* handle) { + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSPARENT_MODE); - FuriHalNfcDevData dev_data = {}; - bool cmd_exit = false; - furi_hal_nfc_exit_sleep(); - printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n"); - while(!cmd_exit) { - cmd_exit |= cli_cmd_interrupt_received(cli); - if(furi_hal_nfc_detect(&dev_data, 400)) { - printf("Found: %s ", nfc_get_dev_type(dev_data.type)); - printf("UID length: %d, UID:", dev_data.uid_len); - for(size_t i = 0; i < dev_data.uid_len; i++) { - printf("%02X", dev_data.uid[i]); - } - printf("\r\n"); - break; - } - furi_hal_nfc_sleep(); - furi_delay_ms(50); - } - furi_hal_nfc_sleep(); + furi_hal_spi_bus_handle_deinit(handle); + f_hal_nfc_deinit_gpio_isr(); } -static void nfc_cli_emulate(Cli* cli, FuriString* args) { - UNUSED(args); - // Check if nfc worker is not busy - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; - } +static void f_hal_nfc_iso15693_listener_transparent_mode_exit(FuriHalSpiBusHandle* handle) { + // Configure gpio back to SPI and exit transparent mode + f_hal_nfc_init_gpio_isr(); + furi_hal_spi_bus_handle_init(handle); - furi_hal_nfc_exit_sleep(); - printf("Emulating NFC-A Type: T2T UID: 36 9C E7 B1 0A C1 34 SAK: 00 ATQA: 00/44\r\n"); - printf("Press Ctrl+C to abort\r\n"); - - FuriHalNfcDevData params = { - .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, - .uid_len = 7, - .atqa = {0x44, 0x00}, - .sak = 0x00, - .type = FuriHalNfcTypeA, - }; - - while(!cli_cmd_interrupt_received(cli)) { - if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 100)) { - printf("Reader detected\r\n"); - furi_hal_nfc_sleep(); - } - furi_delay_ms(50); - } - furi_hal_nfc_sleep(); + st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA); } -static void nfc_cli_field(Cli* cli, FuriString* args) { - UNUSED(args); - // Check if nfc worker is not busy - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; - } - - furi_hal_nfc_exit_sleep(); - furi_hal_nfc_field_on(); +static void f_hal_nfc_iso15693_parser_callback(Iso15693ParserEvent event, void* context) { + furi_assert(context); - printf("Field is on. Don't leave device in this mode for too long.\r\n"); - printf("Press Ctrl+C to abort\r\n"); - - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(50); + if(event == Iso15693ParserEventDataReceived) { + FuriThreadId thread_id = context; + furi_thread_flags_set(thread_id, FLAG_EVENT); } - - furi_hal_nfc_field_off(); - furi_hal_nfc_sleep(); } -static void nfc_cli_apdu(Cli* cli, FuriString* args) { +// TODO remove this test command +static void nfc_cli_check(Cli* cli, FuriString* args) { + UNUSED(args); UNUSED(cli); - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + uint8_t data[100] = {}; + size_t bits = 0; + + Nfc* nfc = nfc_alloc(); + f_hal_nfc_low_power_mode_stop(); + f_hal_nfc_set_mode(FHalNfcModeListener, FHalNfcTechIso15693); + f_hal_nfc_iso15693_listener_transparent_mode_enter(handle); + Iso15693Parser* instance = iso15693_parser_alloc(&gpio_spi_r_miso, 1024); + + FuriThreadId thread_id = furi_thread_get_current_id(); + furi_thread_set_current_priority(FuriThreadPriorityHighest); + iso15693_parser_start(instance, f_hal_nfc_iso15693_parser_callback, thread_id); + + while(true) { + uint32_t flag = furi_thread_flags_wait(FLAG_EVENT, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(flag); + + if(flag & FLAG_EVENT) { + if(iso15693_parser_run(instance)) { + iso15693_parser_get_data(instance, data, sizeof(data), &bits); + break; + } + } } - furi_hal_nfc_exit_sleep(); - FuriString* data = NULL; - data = furi_string_alloc(); - FuriHalNfcTxRxContext tx_rx = {}; - FuriHalNfcDevData dev_data = {}; - uint8_t* req_buffer = NULL; - uint8_t* resp_buffer = NULL; - size_t apdu_size = 0; - size_t resp_size = 0; - - do { - if(!args_read_string_and_trim(args, data)) { - printf( - "Use like `nfc apdu 00a404000e325041592e5359532e444446303100 00a4040008a0000003010102` \r\n"); - break; - } + for(size_t i = 0; i < bits / 8; i++) { + printf("%02X ", data[i]); + } + printf("\r\n"); - printf("detecting tag\r\n"); - if(!furi_hal_nfc_detect(&dev_data, 300)) { - printf("Failed to detect tag\r\n"); - break; - } - do { - apdu_size = furi_string_size(data) / 2; - req_buffer = malloc(apdu_size); - hex_chars_to_uint8(furi_string_get_cstr(data), req_buffer); - - memcpy(tx_rx.tx_data, req_buffer, apdu_size); - tx_rx.tx_bits = apdu_size * 8; - tx_rx.tx_rx_type = FuriHalNfcTxRxTypeDefault; - - printf("Sending APDU:%s to Tag\r\n", furi_string_get_cstr(data)); - if(!furi_hal_nfc_tx_rx(&tx_rx, 300)) { - printf("Failed to tx_rx\r\n"); - break; - } - resp_size = (tx_rx.rx_bits / 8) * 2; - resp_buffer = malloc(resp_size); - uint8_to_hex_chars(tx_rx.rx_data, resp_buffer, resp_size); - resp_buffer[resp_size] = 0; - printf("Response: %s\r\n", resp_buffer); - free(req_buffer); - free(resp_buffer); - req_buffer = NULL; - resp_buffer = NULL; - } while(args_read_string_and_trim(args, data)); - } while(false); + iso15693_parser_stop(instance); + f_hal_nfc_iso15693_listener_transparent_mode_exit(handle); - free(req_buffer); - free(resp_buffer); - furi_string_free(data); - furi_hal_nfc_sleep(); + iso15693_parser_free(instance); + f_hal_nfc_reset_mode(); + f_hal_nfc_low_power_mode_start(); + nfc_free(nfc); } static void nfc_cli(Cli* cli, FuriString* args, void* context) { @@ -171,27 +104,11 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) { nfc_cli_print_usage(); break; } - if(furi_string_cmp_str(cmd, "detect") == 0) { - nfc_cli_detect(cli, args); - break; - } - if(furi_string_cmp_str(cmd, "emulate") == 0) { - nfc_cli_emulate(cli, args); - break; - } - - if(furi_string_cmp_str(cmd, "apdu") == 0) { - nfc_cli_apdu(cli, args); + if(furi_string_cmp_str(cmd, "c") == 0) { + nfc_cli_check(cli, args); break; } - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - if(furi_string_cmp_str(cmd, "field") == 0) { - nfc_cli_field(cli, args); - break; - } - } - nfc_cli_print_usage(); } while(false); diff --git a/applications/main/nfc/plugins/supported_cards/all_in_one.c b/applications/main/nfc/plugins/supported_cards/all_in_one.c new file mode 100644 index 000000000000..1be23d1f3329 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/all_in_one.c @@ -0,0 +1,107 @@ +#include "nfc_supported_card_plugin.h" + +#include +#include + +#define TAG "AllInOne" + +typedef enum { + AllInOneLayoutTypeA, + AllInOneLayoutTypeD, + AllInOneLayoutTypeE2, + AllInOneLayoutTypeE3, + AllInOneLayoutTypeE5, + AllInOneLayoutType2, + AllInOneLayoutTypeUnknown, +} AllInOneLayoutType; + +static AllInOneLayoutType all_in_one_get_layout(const MfUltralightData* data) { + // Switch on the second half of the third byte of page 5 + const uint8_t layout_byte = data->page[5].data[2]; + const uint8_t layout_half_byte = data->page[5].data[2] & 0x0F; + + FURI_LOG_I(TAG, "Layout byte: %02x", layout_byte); + FURI_LOG_I(TAG, "Layout half-byte: %02x", layout_half_byte); + + switch(layout_half_byte) { + // If it is A, the layout type is a type A layout + case 0x0A: + return AllInOneLayoutTypeA; + case 0x0D: + return AllInOneLayoutTypeD; + case 0x02: + return AllInOneLayoutType2; + default: + FURI_LOG_I(TAG, "Unknown layout type: %d", layout_half_byte); + return AllInOneLayoutTypeUnknown; + } +} + +static bool all_in_one_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); + + bool parsed = false; + + do { + if(data->page[4].data[0] != 0x45 || data->page[4].data[1] != 0xD9) { + FURI_LOG_I(TAG, "Pass not verified"); + break; + } + + uint8_t ride_count = 0; + uint32_t serial = 0; + + const AllInOneLayoutType layout_type = all_in_one_get_layout(data); + + if(layout_type == AllInOneLayoutTypeA) { + // If the layout is A then the ride count is stored in the first byte of page 8 + ride_count = data->page[8].data[0]; + } else if(layout_type == AllInOneLayoutTypeD) { + // If the layout is D, the ride count is stored in the second byte of page 9 + ride_count = data->page[9].data[1]; + } else { + FURI_LOG_I(TAG, "Unknown layout: %d", layout_type); + ride_count = 137; + } + + // // The number starts at the second half of the third byte on page 4, and is 32 bits long + // // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte + // // B8 17 A2 A4 BD becomes 81 7A 2A 4B + const uint8_t* serial_data_lo = data->page[4].data; + const uint8_t* serial_data_hi = data->page[5].data; + + serial = (serial_data_lo[2] & 0x0F) << 28 | serial_data_lo[3] << 20 | + serial_data_hi[0] << 12 | serial_data_hi[1] << 4 | serial_data_hi[2] >> 4; + + // Format string for rides count + furi_string_printf( + parsed_data, "\e#All-In-One\nNumber: %lu\nRides left: %u", serial, ride_count); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin all_in_one_plugin = { + .protocol = NfcProtocolMfUltralight, + .verify = NULL, + .read = NULL, + .parse = all_in_one_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor all_in_one_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &all_in_one_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* all_in_one_plugin_ep() { + return &all_in_one_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/nfc_supported_card_plugin.h b/applications/main/nfc/plugins/supported_cards/nfc_supported_card_plugin.h new file mode 100644 index 000000000000..f87e21083180 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/nfc_supported_card_plugin.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include +#include + +#define NFC_SUPPORTED_CARD_PLUGIN_APP_ID "NfcSupportedCardPlugin" +#define NFC_SUPPORTED_CARD_PLUGIN_API_VERSION 1 + +typedef bool (*NfcSupportedCardPluginVerify)(Nfc* nfc); + +typedef bool (*NfcSupportedCardPluginRead)(Nfc* nfc, NfcDevice* device); + +typedef bool (*NfcSupportedCardPluginParse)(const NfcDevice* device, FuriString* parsed_data); + +typedef struct { + NfcProtocol protocol; + NfcSupportedCardPluginVerify verify; + NfcSupportedCardPluginRead read; + NfcSupportedCardPluginParse parse; +} NfcSupportedCardsPlugin; diff --git a/applications/main/nfc/plugins/supported_cards/opal.c b/applications/main/nfc/plugins/supported_cards/opal.c new file mode 100644 index 000000000000..9153c9ccd534 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/opal.c @@ -0,0 +1,233 @@ +/* + * opal.c - Parser for Opal card (Sydney, Australia). + * + * Copyright 2023 Michael Farrell + * + * This will only read "standard" MIFARE DESFire-based Opal cards. Free travel + * cards (including School Opal cards, veteran, vision-impaired persons and + * TfNSW employees' cards) and single-trip tickets are MIFARE Ultralight C + * cards and not supported. + * + * Reference: https://github.com/metrodroid/metrodroid/wiki/Opal + * + * Note: The card values are all little-endian (like Flipper), but the above + * reference was originally written based on Java APIs, which are big-endian. + * This implementation presumes a little-endian system. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "nfc_supported_card_plugin.h" + +#include +#include +#include + +#include + +static const MfDesfireApplicationId opal_app_id = {.data = {0x31, 0x45, 0x53}}; + +static const MfDesfireFileId opal_file_id = 0x07; + +static const char* opal_modes[5] = + {"Rail / Metro", "Ferry / Light Rail", "Bus", "Unknown mode", "Manly Ferry"}; + +static const char* opal_usages[14] = { + "New / Unused", + "Tap on: new journey", + "Tap on: transfer from same mode", + "Tap on: transfer from other mode", + NULL, // Manly Ferry: new journey + NULL, // Manly Ferry: transfer from ferry + NULL, // Manly Ferry: transfer from other + "Tap off: distance fare", + "Tap off: flat fare", + "Automated tap off: failed to tap off", + "Tap off: end of trip without start", + "Tap off: reversal", + "Tap on: rejected", + "Unknown usage", +}; + +// Opal file 0x7 structure. Assumes a little-endian CPU. +typedef struct __attribute__((__packed__)) { + uint32_t serial : 32; + uint8_t check_digit : 4; + bool blocked : 1; + uint16_t txn_number : 16; + int32_t balance : 21; + uint16_t days : 15; + uint16_t minutes : 11; + uint8_t mode : 3; + uint16_t usage : 4; + bool auto_topup : 1; + uint8_t weekly_journeys : 4; + uint16_t checksum : 16; +} OpalFile; + +static_assert(sizeof(OpalFile) == 16, "OpalFile"); + +// Converts an Opal timestamp to FuriHalRtcDateTime. +// +// Opal measures days since 1980-01-01 and minutes since midnight, and presumes +// all days are 1440 minutes. +static void opal_date_time_to_furi(uint16_t days, uint16_t minutes, FuriHalRtcDateTime* out) { + out->year = 1980; + out->month = 1; + // 1980-01-01 is a Tuesday + out->weekday = ((days + 1) % 7) + 1; + out->hour = minutes / 60; + out->minute = minutes % 60; + out->second = 0; + + // What year is it? + for(;;) { + const uint16_t num_days_in_year = furi_hal_rtc_get_days_per_year(out->year); + if(days < num_days_in_year) break; + days -= num_days_in_year; + out->year++; + } + + // 1-index the day of the year + days++; + + for(;;) { + // What month is it? + const bool is_leap = furi_hal_rtc_is_leap_year(out->year); + const uint8_t num_days_in_month = furi_hal_rtc_get_days_per_month(is_leap, out->month); + if(days <= num_days_in_month) break; + days -= num_days_in_month; + out->month++; + } + + out->day = days; +} + +static bool opal_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire); + + bool parsed = false; + + do { + const MfDesfireApplication* app = mf_desfire_get_application(data, &opal_app_id); + if(app == NULL) break; + + const MfDesfireFileSettings* file_settings = + mf_desfire_get_file_settings(app, &opal_file_id); + if(file_settings == NULL || file_settings->type != MfDesfireFileTypeStandard || + file_settings->data.size != sizeof(OpalFile)) + break; + + const MfDesfireFileData* file_data = mf_desfire_get_file_data(app, &opal_file_id); + if(file_data == NULL) break; + + const OpalFile* opal_file = simple_array_cget_data(file_data->data); + + const uint8_t serial2 = opal_file->serial / 10000000; + const uint16_t serial3 = (opal_file->serial / 1000) % 10000; + const uint16_t serial4 = (opal_file->serial % 1000); + + if(opal_file->check_digit > 9) break; + + // Negative balance. Make this a positive value again and record the + // sign separately, because then we can handle balances of -99..-1 + // cents, as the "dollars" division below would result in a positive + // zero value. + const bool is_negative_balance = (opal_file->balance < 0); + const char* sign = is_negative_balance ? "-" : ""; + const int32_t balance = is_negative_balance ? labs(opal_file->balance) : + opal_file->balance; + const uint8_t balance_cents = balance % 100; + const int32_t balance_dollars = balance / 100; + + FuriHalRtcDateTime timestamp; + opal_date_time_to_furi(opal_file->days, opal_file->minutes, ×tamp); + + // Usages 4..6 associated with the Manly Ferry, which correspond to + // usages 1..3 for other modes. + const bool is_manly_ferry = (opal_file->usage >= 4) && (opal_file->usage <= 6); + + // 3..7 are "reserved", but we use 4 to indicate the Manly Ferry. + const uint8_t mode = is_manly_ferry ? 4 : (opal_file->mode > 3 ? 3 : opal_file->mode); + const uint8_t usage = is_manly_ferry ? opal_file->usage - 3 : opal_file->usage; + + const char* mode_str = opal_modes[mode > 4 ? 3 : mode]; + const char* usage_str = opal_usages[usage > 12 ? 13 : usage]; + + furi_string_printf( + parsed_data, + "\e#Opal: $%s%ld.%02hu\n3085 22%02hhu %04hu %03hu%01hhu\n%s, %s\n", + sign, + balance_dollars, + balance_cents, + serial2, + serial3, + serial4, + opal_file->check_digit, + mode_str, + usage_str); + + FuriString* timestamp_str = furi_string_alloc(); + + locale_format_date(timestamp_str, ×tamp, locale_get_date_format(), "-"); + furi_string_cat(parsed_data, timestamp_str); + furi_string_cat(parsed_data, " at "); + + locale_format_time(timestamp_str, ×tamp, locale_get_time_format(), false); + furi_string_cat(parsed_data, timestamp_str); + + furi_string_free(timestamp_str); + + furi_string_cat_printf( + parsed_data, + "\nWeekly journeys: %hhu, Txn #%hu\n", + opal_file->weekly_journeys, + opal_file->txn_number); + + if(opal_file->auto_topup) { + furi_string_cat_str(parsed_data, "Auto-topup enabled\n"); + } + + if(opal_file->blocked) { + furi_string_cat_str(parsed_data, "Card blocked\n"); + } + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin opal_plugin = { + .protocol = NfcProtocolMfDesfire, + .verify = NULL, + .read = NULL, + .parse = opal_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor opal_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &opal_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* opal_plugin_ep() { + return &opal_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c new file mode 100644 index 000000000000..25f53e2ece23 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -0,0 +1,216 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "Plantain" + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +typedef struct { + const MfClassicKeyPair* keys; + uint32_t data_sector; +} PlantainCardConfig; + +static const MfClassicKeyPair plantain_1k_keys[] = { + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, + {.a = 0x77dabc9825e1, .b = 0x9764fec3154a}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, + {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3}, + {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, + {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56}, + {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, +}; + +static const MfClassicKeyPair plantain_4k_keys[] = { + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, {.a = 0x77dabc9825e1, .b = 0x9764fec3154a}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3}, + {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56}, + {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x72f96bdd3714, .b = 0x462225cd34cf}, {.a = 0x044ce1872bc3, .b = 0x8c90c70cff4a}, + {.a = 0xbc2d1791dec1, .b = 0xca96a487de0b}, {.a = 0x8791b2ccb5c4, .b = 0xc956c3b80da3}, + {.a = 0x8e26e45e7d65, .b = 0x8e65b3af7d22}, {.a = 0x0f318130ed18, .b = 0x0c420a20e056}, + {.a = 0x045ceca15535, .b = 0x31bec3d9e510}, {.a = 0x9d993c5d4ef4, .b = 0x86120e488abf}, + {.a = 0xc65d4eaa645b, .b = 0xb69d40d1a439}, {.a = 0x3a8a139c20b4, .b = 0x8818a9c5d406}, + {.a = 0xbaff3053b496, .b = 0x4b7cb25354d3}, {.a = 0x7413b599c4ea, .b = 0xb0a2AAF3A1BA}, + {.a = 0x0ce7cd2cc72b, .b = 0xfa1fbb3f0f1f}, {.a = 0x0be5fac8b06a, .b = 0x6f95887a4fd3}, + {.a = 0x0eb23cc8110b, .b = 0x04dc35277635}, {.a = 0xbc4580b7f20b, .b = 0xd0a4131fb290}, + {.a = 0x7a396f0d633d, .b = 0xad2bdc097023}, {.a = 0xa3faa6daff67, .b = 0x7600e889adf9}, + {.a = 0xfd8705e721b0, .b = 0x296fc317a513}, {.a = 0x22052b480d11, .b = 0xe19504c39461}, + {.a = 0xa7141147d430, .b = 0xff16014fefc7}, {.a = 0x8a8d88151a00, .b = 0x038b5f9b5a2a}, + {.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085}, +}; + +static bool plantain_get_card_config(PlantainCardConfig* config, MfClassicType type) { + bool success = true; + + if(type == MfClassicType1k) { + config->data_sector = 8; + config->keys = plantain_1k_keys; + } else if(type == MfClassicType4k) { + config->data_sector = 8; + config->keys = plantain_4k_keys; + } else { + success = false; + } + + return success; +} + +static bool plantain_verify_type(Nfc* nfc, MfClassicType type) { + bool verified = false; + + do { + PlantainCardConfig cfg = {}; + if(!plantain_get_card_config(&cfg, type)) break; + + const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector); + FURI_LOG_D(TAG, "Verifying sector %lu", cfg.data_sector); + + MfClassicKey key = {0}; + nfc_util_num2bytes(cfg.keys[cfg.data_sector].a, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_context; + MfClassicError error = + mf_classic_poller_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool plantain_verify(Nfc* nfc) { + return plantain_verify_type(nfc, MfClassicType1k) || + plantain_verify_type(nfc, MfClassicType4k); +} + +static bool plantain_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + if(!mf_classic_detect_protocol(data->iso14443_3a_data, &data->type)) break; + + PlantainCardConfig cfg = {}; + if(!plantain_get_card_config(&cfg, data->type)) break; + + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + nfc_util_num2bytes(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + } + keys.key_a_mask = 0xFFFFFFFFFFFFFFFFU; + keys.key_b_mask = 0xFFFFFFFFFFFFFFFFU; + + MfClassicError error = mf_classic_poller_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify card type + PlantainCardConfig cfg = {}; + if(!plantain_get_card_config(&cfg, data->type)) break; + + // Verify key + const MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, cfg.data_sector); + + const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data)); + if(key != cfg.keys[cfg.data_sector].a) break; + + // Point to block 0 of sector 4, value 0 + const uint8_t* temp_ptr = data->block[16].data; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = data->block[0].data; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + + furi_string_printf( + parsed_data, "\e#Plantain\nN:%llu-\nBalance:%lu\n", card_number, balance); + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin plantain_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = plantain_verify, + .read = plantain_read, + .parse = plantain_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor plantain_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &plantain_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* plantain_plugin_ep() { + return &plantain_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c new file mode 100644 index 000000000000..a1134ff6225c --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -0,0 +1,208 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "Troika" + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +typedef struct { + const MfClassicKeyPair* keys; + uint32_t data_sector; +} TroikaCardConfig; + +static const MfClassicKeyPair troika_1k_keys[] = { + {.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58}, + {.a = 0xa82607b01c0d, .b = 0x2910989b6880}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0x73068f118c13, .b = 0x2b7f3253fac5}, + {.a = 0xfbc2793d540b, .b = 0xd3a297dc2698}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0xae3d65a3dad4, .b = 0x0f1c63013dba}, + {.a = 0xa73f5dc1d333, .b = 0xe35173494a81}, + {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763}, + {.a = 0x9becdf3d9273, .b = 0xf8493407799d}, + {.a = 0x08b386463229, .b = 0x5efbaecef46b}, + {.a = 0xcd4c61c26e3d, .b = 0x31c7610de3b0}, + {.a = 0xa82607b01c0d, .b = 0x2910989b6880}, + {.a = 0x0e8f64340ba4, .b = 0x4acec1205d75}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, +}; + +static const MfClassicKeyPair troika_4k_keys[] = { + {.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58}, {.a = 0xa82607b01c0d, .b = 0x2910989b6880}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0x73068f118c13, .b = 0x2b7f3253fac5}, {.a = 0xfbc2793d540b, .b = 0xd3a297dc2698}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0xae3d65a3dad4, .b = 0x0f1c63013dbb}, + {.a = 0xa73f5dc1d333, .b = 0xe35173494a81}, {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763}, + {.a = 0x9becdf3d9273, .b = 0xf8493407799d}, {.a = 0x08b386463229, .b = 0x5efbaecef46b}, + {.a = 0xcd4c61c26e3d, .b = 0x31c7610de3b0}, {.a = 0xa82607b01c0d, .b = 0x2910989b6880}, + {.a = 0x0e8f64340ba4, .b = 0x4acec1205d75}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0x6b02733bb6ec, .b = 0x7038cd25c408}, {.a = 0x403d706ba880, .b = 0xb39d19a280df}, + {.a = 0xc11f4597efb5, .b = 0x70d901648cb9}, {.a = 0x0db520c78c1c, .b = 0x73e5b9d9d3a4}, + {.a = 0x3ebce0925b2f, .b = 0x372cc880f216}, {.a = 0x16a27af45407, .b = 0x9868925175ba}, + {.a = 0xaba208516740, .b = 0xce26ecb95252}, {.a = 0xcd64e567abcd, .b = 0x8f79c4fd8a01}, + {.a = 0x764cd061f1e6, .b = 0xa74332f74994}, {.a = 0x1cc219e9fec1, .b = 0xb90de525ceb6}, + {.a = 0x2fe3cb83ea43, .b = 0xfba88f109b32}, {.a = 0x07894ffec1d6, .b = 0xefcb0e689db3}, + {.a = 0x04c297b91308, .b = 0xc8454c154cb5}, {.a = 0x7a38e3511a38, .b = 0xab16584c972a}, + {.a = 0x7545df809202, .b = 0xecf751084a80}, {.a = 0x5125974cd391, .b = 0xd3eafb5df46d}, + {.a = 0x7a86aa203788, .b = 0xe41242278ca2}, {.a = 0xafcef64c9913, .b = 0x9db96dca4324}, + {.a = 0x04eaa462f70b, .b = 0xac17b93e2fae}, {.a = 0xe734c210f27e, .b = 0x29ba8c3e9fda}, + {.a = 0xd5524f591eed, .b = 0x5daf42861b4d}, {.a = 0xe4821a377b75, .b = 0xe8709e486465}, + {.a = 0x518dc6eea089, .b = 0x97c64ac98ca4}, {.a = 0xbb52f8cce07f, .b = 0x6b6119752c70}, +}; + +static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) { + bool success = true; + + if(type == MfClassicType1k) { + config->data_sector = 8; + config->keys = troika_1k_keys; + } else if(type == MfClassicType4k) { + config->data_sector = 4; + config->keys = troika_4k_keys; + } else { + success = false; + } + + return success; +} + +static bool troika_verify_type(Nfc* nfc, MfClassicType type) { + bool verified = false; + + do { + TroikaCardConfig cfg = {}; + if(!troika_get_card_config(&cfg, type)) break; + + const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector); + FURI_LOG_D(TAG, "Verifying sector %lu", cfg.data_sector); + + MfClassicKey key = {0}; + nfc_util_num2bytes(cfg.keys[cfg.data_sector].a, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_context; + MfClassicError error = + mf_classic_poller_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool troika_verify(Nfc* nfc) { + return troika_verify_type(nfc, MfClassicType1k) || troika_verify_type(nfc, MfClassicType4k); +} + +static bool troika_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + if(!mf_classic_detect_protocol(data->iso14443_3a_data, &data->type)) break; + + TroikaCardConfig cfg = {}; + if(!troika_get_card_config(&cfg, data->type)) break; + + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + nfc_util_num2bytes(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + } + keys.key_a_mask = 0xFFFFFFFFFFFFFFFFU; + keys.key_b_mask = 0xFFFFFFFFFFFFFFFFU; + + MfClassicError error = mf_classic_poller_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify card type + TroikaCardConfig cfg = {}; + if(!troika_get_card_config(&cfg, data->type)) break; + + // Verify key + const MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, cfg.data_sector); + + const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data)); + if(key != cfg.keys[cfg.data_sector].a) break; + + // Parse data + const uint8_t start_block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector); + + const uint8_t* temp_ptr = &data->block[start_block_num + 1].data[5]; + uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; + temp_ptr = &data->block[start_block_num].data[2]; + + uint32_t number = 0; + for(size_t i = 1; i < 5; i++) { + number <<= 8; + number |= temp_ptr[i]; + } + number >>= 4; + number |= (temp_ptr[0] & 0xf) << 28; + + furi_string_printf(parsed_data, "\e#Troika\nNum: %lu\nBalance: %u RUR", number, balance); + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin troika_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = troika_verify, + .read = troika_read, + .parse = troika_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor troika_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &troika_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* troika_plugin_ep() { + return &troika_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/two_cities.c b/applications/main/nfc/plugins/supported_cards/two_cities.c new file mode 100644 index 000000000000..530d62ad9570 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/two_cities.c @@ -0,0 +1,185 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "TwoCities" + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +static const MfClassicKeyPair two_cities_4k_keys[] = { + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, {.a = 0x77dabc9825e1, .b = 0x9764fec3154a}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xa73f5dc1d333, .b = 0xe35173494a81}, {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763}, + {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56}, + {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0x72f96bdd3714, .b = 0x462225cd34cf}, {.a = 0x044ce1872bc3, .b = 0x8c90c70cff4a}, + {.a = 0xbc2d1791dec1, .b = 0xca96a487de0b}, {.a = 0x8791b2ccb5c4, .b = 0xc956c3b80da3}, + {.a = 0x8e26e45e7d65, .b = 0x8e65b3af7d22}, {.a = 0x0f318130ed18, .b = 0x0c420a20e056}, + {.a = 0x045ceca15535, .b = 0x31bec3d9e510}, {.a = 0x9d993c5d4ef4, .b = 0x86120e488abf}, + {.a = 0xc65d4eaa645b, .b = 0xb69d40d1a439}, {.a = 0x3a8a139c20b4, .b = 0x8818a9c5d406}, + {.a = 0xbaff3053b496, .b = 0x4b7cb25354d3}, {.a = 0x7413b599c4ea, .b = 0xb0a2AAF3A1BA}, + {.a = 0x0ce7cd2cc72b, .b = 0xfa1fbb3f0f1f}, {.a = 0x0be5fac8b06a, .b = 0x6f95887a4fd3}, + {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3}, + {.a = 0x7a396f0d633d, .b = 0xad2bdc097023}, {.a = 0xa3faa6daff67, .b = 0x7600e889adf9}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0xa7141147d430, .b = 0xff16014fefc7}, {.a = 0x8a8d88151a00, .b = 0x038b5f9b5a2a}, + {.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085}, +}; + +bool two_cities_verify(Nfc* nfc) { + bool verified = false; + + do { + const uint8_t verify_sector = 4; + uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); + FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); + + MfClassicKey key = {}; + nfc_util_num2bytes(two_cities_4k_keys[verify_sector].a, COUNT_OF(key.data), key.data); + + MfClassicError error = + mf_classic_poller_auth(nfc, block_num, &key, MfClassicKeyTypeA, NULL); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool two_cities_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + if(!mf_classic_detect_protocol(data->iso14443_3a_data, &data->type)) break; + + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(two_cities_4k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + nfc_util_num2bytes(two_cities_4k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + } + keys.key_a_mask = 0xFFFFFFFFFFFFFFFFU; + keys.key_b_mask = 0xFFFFFFFFFFFFFFFFU; + + MfClassicError error = mf_classic_poller_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool two_cities_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6); + if(key != two_cities_4k_keys[4].a) return false; + + // ===== + // PLANTAIN + // ===== + + // Point to block 0 of sector 4, value 0 + const uint8_t* temp_ptr = data->block[16].data; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = data->block[0].data; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + + // ===== + // --PLANTAIN-- + // ===== + // TROIKA + // ===== + + const uint8_t* troika_temp_ptr = &data->block[33].data[5]; + uint16_t troika_balance = ((troika_temp_ptr[0] << 8) | troika_temp_ptr[1]) / 25; + troika_temp_ptr = &data->block[32].data[2]; + uint32_t troika_number = 0; + for(size_t i = 0; i < 4; i++) { + troika_number <<= 8; + troika_number |= troika_temp_ptr[i]; + } + troika_number >>= 4; + + furi_string_printf( + parsed_data, + "\e#Troika+Plantain\nPN: %llu-\nPB: %lu rur.\nTN: %lu\nTB: %u rur.\n", + card_number, + balance, + troika_number, + troika_balance); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin two_cities_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = two_cities_verify, + .read = two_cities_read, + .parse = two_cities_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor two_cities_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &two_cities_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* two_cities_plugin_ep() { + return &two_cities_plugin_descriptor; +} diff --git a/applications/main/nfc/scenes/nfc_scene_card_dump.c b/applications/main/nfc/scenes/nfc_scene_card_dump.c new file mode 100644 index 000000000000..490bb5b4f8e4 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_card_dump.c @@ -0,0 +1,16 @@ +#include + +#include "../nfc_app_i.h" +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_card_dump_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneCardDump, context); +} + +bool nfc_scene_card_dump_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneCardDump, context, event); +} + +void nfc_scene_card_dump_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneCardDump, context); +} \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a9da07dfda00..2d6b4975b53d 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -1,65 +1,58 @@ ADD_SCENE(nfc, start, Start) -ADD_SCENE(nfc, read, Read) +ADD_SCENE(nfc, file_select, FileSelect) ADD_SCENE(nfc, saved_menu, SavedMenu) -ADD_SCENE(nfc, extra_actions, ExtraActions) -ADD_SCENE(nfc, set_type, SetType) -ADD_SCENE(nfc, set_sak, SetSak) -ADD_SCENE(nfc, set_atqa, SetAtqa) -ADD_SCENE(nfc, set_uid, SetUid) -ADD_SCENE(nfc, generate_info, GenerateInfo) -ADD_SCENE(nfc, read_card_success, ReadCardSuccess) ADD_SCENE(nfc, save_name, SaveName) ADD_SCENE(nfc, save_success, SaveSuccess) -ADD_SCENE(nfc, file_select, FileSelect) -ADD_SCENE(nfc, emulate_uid, EmulateUid) -ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) -ADD_SCENE(nfc, nfca_menu, NfcaMenu) -ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) -ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) -ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) -ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate) -ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth) -ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult) -ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) -ADD_SCENE(nfc, mf_ultralight_unlock_auto, MfUltralightUnlockAuto) +ADD_SCENE(nfc, delete, Delete) +ADD_SCENE(nfc, delete_success, DeleteSuccess) +ADD_SCENE(nfc, restore_original_confirm, RestoreOriginalConfirm) +ADD_SCENE(nfc, restore_original, RestoreOriginal) + +ADD_SCENE(nfc, detect, Detect) +ADD_SCENE(nfc, read, Read) +ADD_SCENE(nfc, info, Info) +ADD_SCENE(nfc, supported_card, SupportedCard) +ADD_SCENE(nfc, select_protocol, SelectProtocol) +ADD_SCENE(nfc, extra_actions, ExtraActions) +ADD_SCENE(nfc, read_success, ReadSuccess) +ADD_SCENE(nfc, read_menu, ReadMenu) +ADD_SCENE(nfc, emulate, Emulate) +ADD_SCENE(nfc, debug, Debug) +ADD_SCENE(nfc, field, Field) +ADD_SCENE(nfc, retry_confirm, RetryConfirm) +ADD_SCENE(nfc, exit_confirm, ExitConfirm) +ADD_SCENE(nfc, card_dump, CardDump) + ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) -ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) -ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu) +ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) +ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass) + ADD_SCENE(nfc, mf_desfire_data, MfDesfireData) ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) -ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess) -ADD_SCENE(nfc, mf_classic_data, MfClassicData) -ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu) -ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) + +ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) +ADD_SCENE(nfc, mf_classic_detect_reader, MfClassicDetectReader) +ADD_SCENE(nfc, mf_classic_mfkey_nonces_info, MfClassicMfkeyNoncesInfo) +ADD_SCENE(nfc, mf_classic_mfkey_complete, MfClassicMfkeyComplete) +ADD_SCENE(nfc, mf_classic_update_initial, MfClassicUpdateInitial) +ADD_SCENE(nfc, mf_classic_update_initial_success, MfClassicUpdateInitialSuccess) +ADD_SCENE(nfc, mf_classic_write_initial, MfClassicWriteInitial) +ADD_SCENE(nfc, mf_classic_write_initial_success, MfClassicWriteInitialSuccess) +ADD_SCENE(nfc, mf_classic_write_initial_fail, MfClassicWriteInitialFail) +ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) + ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) -ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete) +ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate) -ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) -ADD_SCENE(nfc, mf_classic_write, MfClassicWrite) -ADD_SCENE(nfc, mf_classic_write_success, MfClassicWriteSuccess) -ADD_SCENE(nfc, mf_classic_write_fail, MfClassicWriteFail) -ADD_SCENE(nfc, mf_classic_update, MfClassicUpdate) -ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess) -ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) -ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) -ADD_SCENE(nfc, emv_menu, EmvMenu) -ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) -ADD_SCENE(nfc, device_info, DeviceInfo) -ADD_SCENE(nfc, delete, Delete) -ADD_SCENE(nfc, delete_success, DeleteSuccess) -ADD_SCENE(nfc, restore_original_confirm, RestoreOriginalConfirm) -ADD_SCENE(nfc, restore_original, RestoreOriginal) -ADD_SCENE(nfc, debug, Debug) -ADD_SCENE(nfc, field, Field) -ADD_SCENE(nfc, dict_not_found, DictNotFound) -ADD_SCENE(nfc, rpc, Rpc) -ADD_SCENE(nfc, exit_confirm, ExitConfirm) -ADD_SCENE(nfc, retry_confirm, RetryConfirm) -ADD_SCENE(nfc, detect_reader, DetectReader) -ADD_SCENE(nfc, mfkey_nonces_info, MfkeyNoncesInfo) -ADD_SCENE(nfc, mfkey_complete, MfkeyComplete) -ADD_SCENE(nfc, nfc_data_info, NfcDataInfo) -ADD_SCENE(nfc, read_card_type, ReadCardType) + +ADD_SCENE(nfc, set_type, SetType) +ADD_SCENE(nfc, set_sak, SetSak) +ADD_SCENE(nfc, set_atqa, SetAtqa) +ADD_SCENE(nfc, set_uid, SetUid) + +ADD_SCENE(nfc, generate_info, GenerateInfo) + +ADD_SCENE(nfc, not_implemented, NotImplemented) diff --git a/applications/main/nfc/scenes/nfc_scene_debug.c b/applications/main/nfc/scenes/nfc_scene_debug.c index ed079c2ed9aa..97592f2e270e 100644 --- a/applications/main/nfc/scenes/nfc_scene_debug.c +++ b/applications/main/nfc/scenes/nfc_scene_debug.c @@ -1,4 +1,4 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" enum SubmenuDebugIndex { SubmenuDebugIndexField, @@ -6,29 +6,26 @@ enum SubmenuDebugIndex { }; void nfc_scene_debug_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } void nfc_scene_debug_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; Submenu* submenu = nfc->submenu; submenu_add_item( submenu, "Field", SubmenuDebugIndexField, nfc_scene_debug_submenu_callback, nfc); - submenu_add_item( - submenu, "Apdu", SubmenuDebugIndexApdu, nfc_scene_debug_submenu_callback, nfc); submenu_set_selected_item( submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDebug)); - nfc_device_clear(nfc->dev); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } bool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { @@ -37,18 +34,13 @@ bool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) { nfc->scene_manager, NfcSceneDebug, SubmenuDebugIndexField); scene_manager_next_scene(nfc->scene_manager, NfcSceneField); consumed = true; - } else if(event.event == SubmenuDebugIndexApdu) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDebug, SubmenuDebugIndexApdu); - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateApduSequence); - consumed = true; } } return consumed; } void nfc_scene_debug_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; submenu_reset(nfc->submenu); } diff --git a/applications/main/nfc/scenes/nfc_scene_delete.c b/applications/main/nfc/scenes/nfc_scene_delete.c index cbb52bfd0f2b..c1a676168ad5 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete.c +++ b/applications/main/nfc/scenes/nfc_scene_delete.c @@ -1,21 +1,20 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; if(type == InputTypeShort) { view_dispatcher_send_custom_event(nfc->view_dispatcher, result); } } void nfc_scene_delete_on_enter(void* context) { - Nfc* nfc = context; - FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; + NfcApp* nfc = context; // Setup Custom Widget view FuriString* temp_str; temp_str = furi_string_alloc(); - furi_string_printf(temp_str, "\e#Delete %s?\e#", nfc->dev->dev_name); + furi_string_printf(temp_str, "\e#Delete %s?\e#", furi_string_get_cstr(nfc->file_name)); widget_add_text_box_element( nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, furi_string_get_cstr(temp_str), false); widget_add_button_element( @@ -23,42 +22,33 @@ void nfc_scene_delete_on_enter(void* context) { widget_add_button_element( nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc); + size_t uid_len; + const uint8_t* uid = nfc_device_get_uid(nfc->nfc_device, &uid_len); + furi_string_set(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", uid[i]); } widget_add_string_element( nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); - NfcProtocol protocol = nfc->dev->dev_data.protocol; - if(protocol == NfcDeviceProtocolEMV) { - furi_string_set(temp_str, "EMV bank card"); - } else if(protocol == NfcDeviceProtocolMifareUl) { - furi_string_set(temp_str, nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, true)); - } else if(protocol == NfcDeviceProtocolMifareClassic) { - furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); - } else if(protocol == NfcDeviceProtocolMifareDesfire) { - furi_string_set(temp_str, "MIFARE DESFire"); - } else { - furi_string_set(temp_str, "Unknown ISO tag"); - } + furi_string_set_str(temp_str, nfc_device_get_name(nfc->nfc_device, NfcDeviceNameTypeFull)); widget_add_string_element( nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); - widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A"); furi_string_free(temp_str); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeLeft) { consumed = scene_manager_previous_scene(nfc->scene_manager); } else if(event.event == GuiButtonTypeRight) { - if(nfc_device_delete(nfc->dev, true)) { + if(nfc_delete(nfc)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess); } else { scene_manager_search_and_switch_to_previous_scene( @@ -71,7 +61,7 @@ bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_delete_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; widget_reset(nfc->widget); } diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index 795363527fcf..f0c22eec4d58 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -1,12 +1,12 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_delete_success_popup_callback(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); } void nfc_scene_delete_success_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Setup view Popup* popup = nfc->popup; @@ -20,7 +20,7 @@ void nfc_scene_delete_success_on_enter(void* context) { } bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { @@ -38,7 +38,7 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_delete_success_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clear view popup_reset(nfc->popup); diff --git a/applications/main/nfc/scenes/nfc_scene_detect.c b/applications/main/nfc/scenes/nfc_scene_detect.c new file mode 100644 index 000000000000..5e7fc6528213 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_detect.c @@ -0,0 +1,61 @@ +#include "../nfc_app_i.h" +#include + +void nfc_scene_detect_scan_callback(NfcScannerEvent event, void* context) { + furi_assert(context); + + NfcApp* instance = context; + + if(event.type == NfcScannerEventTypeDetected) { + instance->protocols_detected_num = event.data.protocol_num; + memcpy(instance->protocols_detected, event.data.protocols, event.data.protocol_num); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWorkerExit); + } +} + +void nfc_scene_detect_on_enter(void* context) { + NfcApp* instance = context; + + // Setup view + popup_reset(instance->popup); + popup_set_text( + instance->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); + + nfc_app_reset_detected_protocols(instance); + + instance->scanner = nfc_scanner_alloc(instance->nfc); + nfc_scanner_start(instance->scanner, nfc_scene_detect_scan_callback, instance); + + nfc_blink_detect_start(instance); +} + +bool nfc_scene_detect_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventWorkerExit) { + if(instance->protocols_detected_num > 1) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol); + } else { + instance->protocols_detected_idx = 0; + scene_manager_next_scene(instance->scene_manager, NfcSceneRead); + } + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_detect_on_exit(void* context) { + NfcApp* instance = context; + + nfc_scanner_stop(instance->scanner); + nfc_scanner_free(instance->scanner); + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_emulate.c b/applications/main/nfc/scenes/nfc_scene_emulate.c new file mode 100644 index 000000000000..6f217f315487 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_emulate.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_emulate_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneEmulate, context); +} + +bool nfc_scene_emulate_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneEmulate, context, event); +} + +void nfc_scene_emulate_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneEmulate, context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c index 3ce4f6de839b..c024d31295a2 100644 --- a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c @@ -1,13 +1,13 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, result); } void nfc_scene_exit_confirm_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_set_left_button_text(dialog_ex, "Exit"); @@ -22,16 +22,16 @@ void nfc_scene_exit_confirm_on_enter(void* context) { } bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultRight) { consumed = scene_manager_previous_scene(nfc->scene_manager); } else if(event.event == DialogExResultLeft) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadCardType)) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSelectProtocol)) { consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneReadCardType); + nfc->scene_manager, NfcSceneSelectProtocol); } else { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneStart); @@ -45,7 +45,7 @@ bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_exit_confirm_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clean view dialog_ex_reset(nfc->dialog_ex); diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 66aaf5a26dc5..d9b49749c804 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -1,4 +1,4 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" enum SubmenuIndex { SubmenuIndexReadCardType, @@ -7,66 +7,66 @@ enum SubmenuIndex { }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; + NfcApp* instance = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); + view_dispatcher_send_custom_event(instance->view_dispatcher, index); } void nfc_scene_extra_actions_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; + NfcApp* instance = context; + Submenu* submenu = instance->submenu; submenu_add_item( submenu, "Read Specific Card Type", SubmenuIndexReadCardType, nfc_scene_extra_actions_submenu_callback, - nfc); + instance); submenu_add_item( submenu, "Mifare Classic Keys", SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, - nfc); + instance); submenu_add_item( submenu, "Unlock NTAG/Ultralight", SubmenuIndexMfUltralightUnlock, nfc_scene_extra_actions_submenu_callback, - nfc); + instance); submenu_set_selected_item( - submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + submenu, scene_manager_get_scene_state(instance->scene_manager, NfcSceneExtraActions)); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); } bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* instance = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexMfClassicKeys) { - if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeys); + if(mf_dict_check_presence(MfDictTypeSystem)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys); } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + scene_manager_previous_scene(instance->scene_manager); } consumed = true; } else if(event.event == SubmenuIndexMfUltralightUnlock) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + mf_ultralight_auth_reset(instance->mf_ul_auth); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); consumed = true; } else if(event.event == SubmenuIndexReadCardType) { - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); + scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol); consumed = true; } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + scene_manager_set_scene_state(instance->scene_manager, NfcSceneExtraActions, event.event); } return consumed; } void nfc_scene_extra_actions_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; - submenu_reset(nfc->submenu); + submenu_reset(instance->submenu); } diff --git a/applications/main/nfc/scenes/nfc_scene_field.c b/applications/main/nfc/scenes/nfc_scene_field.c index e3eb6a7088b8..bd7f3a75cde1 100644 --- a/applications/main/nfc/scenes/nfc_scene_field.c +++ b/applications/main/nfc/scenes/nfc_scene_field.c @@ -1,10 +1,10 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_field_on_enter(void* context) { - Nfc* nfc = context; - - furi_hal_nfc_field_on(); + NfcApp* nfc = context; + f_hal_nfc_low_power_mode_stop(); + f_hal_nfc_poller_field_on(); Popup* popup = nfc->popup; popup_set_header( popup, @@ -25,9 +25,9 @@ bool nfc_scene_field_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_field_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; - furi_hal_nfc_field_off(); + f_hal_nfc_low_power_mode_start(); notification_internal_message(nfc->notifications, &sequence_reset_blue); popup_reset(nfc->popup); } diff --git a/applications/main/nfc/scenes/nfc_scene_file_select.c b/applications/main/nfc/scenes/nfc_scene_file_select.c index 374a933d1c70..e1edcadb1be0 100644 --- a/applications/main/nfc/scenes/nfc_scene_file_select.c +++ b/applications/main/nfc/scenes/nfc_scene_file_select.c @@ -1,26 +1,20 @@ -#include "../nfc_i.h" -#include "nfc/nfc_device.h" +#include "../nfc_app_i.h" void nfc_scene_file_select_on_enter(void* context) { - Nfc* nfc = context; - // Process file_select return - nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc); - if(!furi_string_size(nfc->dev->load_path)) { - furi_string_set_str(nfc->dev->load_path, NFC_APP_FOLDER); - } - if(nfc_file_select(nfc->dev)) { - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, 0); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu); + NfcApp* instance = context; + + if(nfc_load_from_file_select(instance)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSavedMenu); } else { - scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); + scene_manager_previous_scene(instance->scene_manager); } - nfc_device_set_loading_callback(nfc->dev, NULL, nfc); } bool nfc_scene_file_select_on_event(void* context, SceneManagerEvent event) { UNUSED(context); UNUSED(event); - return false; + bool consumed = false; + return consumed; } void nfc_scene_file_select_on_exit(void* context) { diff --git a/applications/main/nfc/scenes/nfc_scene_generate_info.c b/applications/main/nfc/scenes/nfc_scene_generate_info.c index 7b84ae43b11f..c4f86ff4a6db 100644 --- a/applications/main/nfc/scenes/nfc_scene_generate_info.c +++ b/applications/main/nfc/scenes/nfc_scene_generate_info.c @@ -1,50 +1,55 @@ -#include "../nfc_i.h" -#include "lib/nfc/helpers/nfc_generators.h" +#include "../nfc_app_i.h" -void nfc_scene_generate_info_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; +void nfc_scene_generate_info_widget_callback(GuiButtonType result, InputType type, void* context) { + NfcApp* instance = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + if(type == InputTypeShort) { + if(result == GuiButtonTypeRight) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } + } } void nfc_scene_generate_info_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; + + NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + furi_assert((protocol == NfcProtocolMfUltralight) || (protocol == NfcProtocolMfClassic)); + + const Iso14443_3aData* iso14443_3a_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_3a); // Setup dialog view - FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; - DialogEx* dialog_ex = nfc->dialog_ex; - dialog_ex_set_right_button_text(dialog_ex, "More"); + Widget* widget = instance->widget; + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_scene_generate_info_widget_callback, instance); // Create info text - FuriString* info_str = furi_string_alloc_printf( - "%s\n%s\nUID:", nfc->generator->name, nfc_get_dev_type(data->type)); + NfcDataGeneratorType type = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneGenerateInfo); + const char* name = nfc_data_generator_get_name(type); + widget_add_string_element(widget, 0, 0, AlignLeft, AlignTop, FontPrimary, name); + widget_add_string_element(widget, 0, 13, AlignLeft, AlignTop, FontSecondary, "NFC-A"); + FuriString* temp_str = furi_string_alloc_printf("UID:"); // Append UID - for(int i = 0; i < data->uid_len; ++i) { - furi_string_cat_printf(info_str, " %02X", data->uid[i]); + for(int i = 0; i < iso14443_3a_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", iso14443_3a_data->uid[i]); } - nfc_text_store_set(nfc, furi_string_get_cstr(info_str)); - furi_string_free(info_str); - - dialog_ex_set_text(dialog_ex, nfc->text_store, 0, 0, AlignLeft, AlignTop); - dialog_ex_set_context(dialog_ex, nfc); - dialog_ex_set_result_callback(dialog_ex, nfc_scene_generate_info_dialog_callback); + widget_add_string_element( + widget, 0, 25, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); } bool nfc_scene_generate_info_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* instance = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultRight) { - // Switch either to NfcSceneMfClassicMenu or NfcSceneMfUltralightMenu - if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareClassic) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicMenu); - } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightMenu); - } + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneReadMenu); consumed = true; } } @@ -53,8 +58,8 @@ bool nfc_scene_generate_info_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_generate_info_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; // Clean views - dialog_ex_reset(nfc->dialog_ex); + widget_reset(instance->widget); } diff --git a/applications/main/nfc/scenes/nfc_scene_info.c b/applications/main/nfc/scenes/nfc_scene_info.c new file mode 100644 index 000000000000..6e9d504975f0 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_info.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_info_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneInfo, context); +} + +bool nfc_scene_info_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneInfo, context, event); +} + +void nfc_scene_info_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneInfo, context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c new file mode 100644 index 000000000000..ecb81094d7c4 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c @@ -0,0 +1,142 @@ +#include "../nfc_app_i.h" + +#include + +#define NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX (10U) +#define NFC_SCENE_DETECT_READER_WAIT_NONCES_TIMEOUT_MS (1000) + +static const NotificationSequence sequence_detect_reader = { + &message_green_255, + &message_blue_255, + &message_do_not_reset, + NULL, +}; + +void nfc_scene_mf_classic_detect_reader_view_callback(void* context) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +NfcCommand nfc_scene_mf_classic_detect_listener_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcApp* instance = context; + MfClassicListenerEvent* mfc_event = event.data; + + if(mfc_event->type == MfClassicListenerEventTypeAuthContextPartCollected) { + MfClassicAuthContext* auth_ctx = &mfc_event->data->auth_context; + mfkey32_logger_add_nonce(instance->mfkey32_logger, auth_ctx); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWorkerUpdate); + } + + return NfcCommandContinue; +} + +void nfc_scene_mf_classic_timer_callback(void* context) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventTimerExpired); +} + +void nfc_scene_mf_classic_detect_reader_on_enter(void* context) { + NfcApp* instance = context; + + if(nfc_device_get_protocol(instance->nfc_device) == NfcProtocolInvalid) { + const Iso14443_3aData iso3_data = { + .uid_len = 7, + .uid = {0x04, 0x77, 0x70, 0x2A, 0x23, 0x4F, 0x80}, + .atqa = {0x44, 0x00}, + .sak = 0x08, + }; + MfClassicData* mfc_data = mf_classic_alloc(); + + mfc_data->type = MfClassicType4k; + iso14443_3a_copy(mfc_data->iso14443_3a_data, &iso3_data); + nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data); + + mf_classic_free(mfc_data); + } + + const Iso14443_3aData* iso3_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_3a); + uint32_t cuid = iso14443_3a_get_cuid(iso3_data); + + instance->mfkey32_logger = mfkey32_logger_alloc(cuid); + instance->timer = + furi_timer_alloc(nfc_scene_mf_classic_timer_callback, FuriTimerTypeOnce, instance); + + detect_reader_set_nonces_max(instance->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX); + detect_reader_set_callback( + instance->detect_reader, nfc_scene_mf_classic_detect_reader_view_callback, instance); + + notification_message(instance->notifications, &sequence_detect_reader); + + instance->listener = nfc_listener_alloc( + instance->nfc, + NfcProtocolMfClassic, + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic)); + nfc_listener_start( + instance->listener, nfc_scene_mf_classic_detect_listener_callback, instance); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDetectReader); +} + +bool nfc_scene_mf_classic_detect_reader_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventWorkerUpdate) { + furi_timer_stop(instance->timer); + notification_message(instance->notifications, &sequence_blink_start_cyan); + + size_t nonces_pairs = 2 * mfkey32_logger_get_params_num(instance->mfkey32_logger); + detect_reader_set_state(instance->detect_reader, DetectReaderStateReaderDetected); + detect_reader_set_nonces_collected(instance->detect_reader, nonces_pairs); + if(nonces_pairs >= NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX) { + nfc_listener_stop(instance->listener); + nfc_listener_free(instance->listener); + detect_reader_set_state(instance->detect_reader, DetectReaderStateDone); + nfc_blink_stop(instance); + notification_message(instance->notifications, &sequence_single_vibro); + notification_message(instance->notifications, &sequence_set_green_255); + } else { + furi_timer_start(instance->timer, NFC_SCENE_DETECT_READER_WAIT_NONCES_TIMEOUT_MS); + } + consumed = true; + } else if(event.event == NfcCustomEventTimerExpired) { + detect_reader_set_state(instance->detect_reader, DetectReaderStateReaderLost); + nfc_blink_stop(instance); + notification_message(instance->notifications, &sequence_detect_reader); + } else if(event.event == NfcCustomEventViewExit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicMfkeyNoncesInfo); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + size_t nonces_pairs = 2 * mfkey32_logger_get_params_num(instance->mfkey32_logger); + if(nonces_pairs < NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX) { + nfc_listener_stop(instance->listener); + nfc_listener_free(instance->listener); + } + mfkey32_logger_free(instance->mfkey32_logger); + } + + return consumed; +} + +void nfc_scene_mf_classic_detect_reader_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + detect_reader_reset(instance->detect_reader); + + furi_timer_stop(instance->timer); + furi_timer_free(instance->timer); + + // Stop notifications + nfc_blink_stop(instance); + notification_message(instance->notifications, &sequence_reset_green); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index cb2f3a82d9e7..839b674c03a3 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -1,186 +1,259 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" + #include +#include #define TAG "NfcMfClassicDictAttack" typedef enum { - DictAttackStateIdle, DictAttackStateUserDictInProgress, - DictAttackStateFlipperDictInProgress, + DictAttackStateSystemDictInProgress, } DictAttackState; -bool nfc_dict_attack_worker_callback(NfcWorkerEvent event, void* context) { +NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context) { furi_assert(context); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, event); - return true; -} + furi_assert(event.data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolMfClassic); -void nfc_dict_attack_dict_attack_result_callback(void* context) { - furi_assert(context); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventDictAttackSkip); + NfcCommand command = NfcCommandContinue; + MfClassicPollerEvent* mfc_event = event.data; + + NfcApp* instance = context; + if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + const MfClassicData* mfc_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + mfc_event->data->poller_mode.mode = MfClassicPollerModeDictAttack; + mfc_event->data->poller_mode.data = mfc_data; + instance->mf_dict_context.sectors_total = mf_classic_get_total_sectors_num(mfc_data->type); + mf_classic_get_read_sectors_and_keys( + mfc_data, + &instance->mf_dict_context.sectors_read, + &instance->mf_dict_context.keys_found); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestKey) { + MfClassicKey key = {}; + if(mf_dict_get_next_key(instance->mf_dict_context.dict, &key)) { + mfc_event->data->key_request_data.key = key; + mfc_event->data->key_request_data.key_provided = true; + instance->mf_dict_context.dict_keys_current++; + if(instance->mf_dict_context.dict_keys_current % 10 == 0) { + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } + } else { + mfc_event->data->key_request_data.key_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeDataUpdate) { + MfClassicPollerEventDataUpdate* data_update = &mfc_event->data->data_update; + instance->mf_dict_context.sectors_read = data_update->sectors_read; + instance->mf_dict_context.keys_found = data_update->keys_found; + instance->mf_dict_context.current_sector = data_update->current_sector; + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeNextSector) { + mf_dict_rewind(instance->mf_dict_context.dict); + instance->mf_dict_context.dict_keys_current = 0; + instance->mf_dict_context.current_sector = + mfc_event->data->next_sector_data.current_sector; + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeFoundKeyA) { + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeFoundKeyB) { + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStart) { + instance->mf_dict_context.key_attack_current_sector = + mfc_event->data->key_attack_data.current_sector; + instance->mf_dict_context.is_key_attack = true; + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStop) { + mf_dict_rewind(instance->mf_dict_context.dict); + instance->mf_dict_context.is_key_attack = false; + instance->mf_dict_context.dict_keys_current = 0; + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller); + nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackComplete); + command = NfcCommandStop; + } + + return command; } -static void nfc_scene_mf_classic_dict_attack_update_view(Nfc* nfc) { - MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; - uint8_t sectors_read = 0; - uint8_t keys_found = 0; +void nfc_dict_attack_dict_attack_result_callback(DictAttackEvent event, void* context) { + furi_assert(context); + NfcApp* instance = context; - // Calculate found keys and read sectors - mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); - dict_attack_set_keys_found(nfc->dict_attack, keys_found); - dict_attack_set_sector_read(nfc->dict_attack, sectors_read); + if(event == DictAttackEventSkipPressed) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventDictAttackSkip); + } } -static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackState state) { - MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; - NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; - NfcWorkerState worker_state = NfcWorkerStateReady; - MfClassicDict* dict = NULL; +static void nfc_scene_mf_classic_dict_attack_update_view(NfcApp* instance) { + NfcMfClassicDictAttackContext* mfc_dict = &instance->mf_dict_context; - // Identify scene state - if(state == DictAttackStateIdle) { - if(mf_classic_dict_check_presence(MfClassicDictTypeUser)) { - state = DictAttackStateUserDictInProgress; - } else { - state = DictAttackStateFlipperDictInProgress; - } - } else if(state == DictAttackStateUserDictInProgress) { - state = DictAttackStateFlipperDictInProgress; + if(mfc_dict->is_key_attack) { + dict_attack_set_key_attack(instance->dict_attack, mfc_dict->key_attack_current_sector); + } else { + dict_attack_reset_key_attack(instance->dict_attack); + dict_attack_set_sectors_total(instance->dict_attack, mfc_dict->sectors_total); + dict_attack_set_sectors_read(instance->dict_attack, mfc_dict->sectors_read); + dict_attack_set_keys_found(instance->dict_attack, mfc_dict->keys_found); + dict_attack_set_current_dict_key(instance->dict_attack, mfc_dict->dict_keys_current); + dict_attack_set_current_sector(instance->dict_attack, mfc_dict->current_sector); } +} - // Setup view +static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) { + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack); if(state == DictAttackStateUserDictInProgress) { - worker_state = NfcWorkerStateMfClassicDictAttack; - dict_attack_set_header(nfc->dict_attack, "MF Classic User Dictionary"); - dict = mf_classic_dict_alloc(MfClassicDictTypeUser); - - // If failed to load user dictionary - try the system dictionary - if(!dict) { - FURI_LOG_E(TAG, "User dictionary not found"); - state = DictAttackStateFlipperDictInProgress; - } - } - if(state == DictAttackStateFlipperDictInProgress) { - worker_state = NfcWorkerStateMfClassicDictAttack; - dict_attack_set_header(nfc->dict_attack, "MF Classic System Dictionary"); - dict = mf_classic_dict_alloc(MfClassicDictTypeSystem); - if(!dict) { - FURI_LOG_E(TAG, "Flipper dictionary not found"); - // Pass through to let the worker handle the failure - } + do { + if(!mf_dict_check_presence(MfDictTypeUser)) { + state = DictAttackStateSystemDictInProgress; + break; + } + + instance->mf_dict_context.dict = mf_dict_alloc(MfDictTypeUser); + if(mf_dict_get_total_keys(instance->mf_dict_context.dict) == 0) { + mf_dict_free(instance->mf_dict_context.dict); + state = DictAttackStateSystemDictInProgress; + break; + } + + dict_attack_set_header(instance->dict_attack, "MF Classic User Dictionary"); + } while(false); } - // Free previous dictionary - if(dict_attack_data->dict) { - mf_classic_dict_free(dict_attack_data->dict); + if(state == DictAttackStateSystemDictInProgress) { + instance->mf_dict_context.dict = mf_dict_alloc(MfDictTypeSystem); + dict_attack_set_header(instance->dict_attack, "MF Classic System Dictionary"); } - dict_attack_data->dict = dict; - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack, state); - dict_attack_set_callback(nfc->dict_attack, nfc_dict_attack_dict_attack_result_callback, nfc); - dict_attack_set_current_sector(nfc->dict_attack, 0); - dict_attack_set_card_detected(nfc->dict_attack, data->type); + + instance->mf_dict_context.dict_keys_total = + mf_dict_get_total_keys(instance->mf_dict_context.dict); dict_attack_set_total_dict_keys( - nfc->dict_attack, dict ? mf_classic_dict_get_total_keys(dict) : 0); - nfc_scene_mf_classic_dict_attack_update_view(nfc); - nfc_worker_start( - nfc->worker, worker_state, &nfc->dev->dev_data, nfc_dict_attack_worker_callback, nfc); + instance->dict_attack, instance->mf_dict_context.dict_keys_total); + instance->mf_dict_context.dict_keys_current = 0; + + dict_attack_set_callback( + instance->dict_attack, nfc_dict_attack_dict_attack_result_callback, instance); + nfc_scene_mf_classic_dict_attack_update_view(instance); + + scene_manager_set_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack, state); } void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { - Nfc* nfc = context; - nfc_scene_mf_classic_dict_attack_prepare_view(nfc, DictAttackStateIdle); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDictAttack); - nfc_blink_read_start(nfc); - notification_message(nfc->notifications, &sequence_display_backlight_enforce_on); + NfcApp* instance = context; + + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress); + nfc_scene_mf_classic_dict_attack_prepare_view(instance); + dict_attack_set_card_state(instance->dict_attack, true); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack); + nfc_blink_read_start(instance); + notification_message(instance->notifications, &sequence_display_backlight_enforce_on); + + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); } bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + NfcApp* instance = context; bool consumed = false; uint32_t state = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack); + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack); if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcWorkerEventSuccess) { + if(event.event == NfcCustomEventDictAttackComplete) { if(state == DictAttackStateUserDictInProgress) { - nfc_worker_stop(nfc->worker); - nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); - consumed = true; - } else { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - consumed = true; - } - } else if(event.event == NfcWorkerEventAborted) { - if(state == DictAttackStateUserDictInProgress && - dict_attack_get_card_state(nfc->dict_attack)) { - nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + mf_dict_free(instance->mf_dict_context.dict); + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicDictAttack, + DictAttackStateSystemDictInProgress); + nfc_scene_mf_classic_dict_attack_prepare_view(instance); + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); consumed = true; } else { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); - // Counting failed attempts too - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } - } else if(event.event == NfcWorkerEventCardDetected) { - dict_attack_set_card_detected(nfc->dict_attack, data->type); + } else if(event.event == NfcCustomEventCardDetected) { + dict_attack_set_card_state(instance->dict_attack, true); consumed = true; - } else if(event.event == NfcWorkerEventNoCardDetected) { - dict_attack_set_card_removed(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcWorkerEventFoundKeyA) { - dict_attack_inc_keys_found(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcWorkerEventFoundKeyB) { - dict_attack_inc_keys_found(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcWorkerEventNewSector) { - nfc_scene_mf_classic_dict_attack_update_view(nfc); - dict_attack_inc_current_sector(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcWorkerEventNewDictKeyBatch) { - nfc_scene_mf_classic_dict_attack_update_view(nfc); - dict_attack_inc_current_dict_key(nfc->dict_attack, NFC_DICT_KEY_BATCH_SIZE); + } else if(event.event == NfcCustomEventCardLost) { + dict_attack_set_card_state(instance->dict_attack, false); consumed = true; + } else if(event.event == NfcCustomEventDictAttackDataUpdate) { + nfc_scene_mf_classic_dict_attack_update_view(instance); } else if(event.event == NfcCustomEventDictAttackSkip) { + const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller); + nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data); if(state == DictAttackStateUserDictInProgress) { - nfc_worker_stop(nfc->worker); + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + mf_dict_free(instance->mf_dict_context.dict); + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicDictAttack, + DictAttackStateSystemDictInProgress); + nfc_scene_mf_classic_dict_attack_prepare_view(instance); + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); consumed = true; - } else if(state == DictAttackStateFlipperDictInProgress) { - nfc_worker_stop(nfc->worker); + } else if(state == DictAttackStateSystemDictInProgress) { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } - } else if(event.event == NfcWorkerEventKeyAttackStart) { - dict_attack_set_key_attack( - nfc->dict_attack, - true, - nfc->dev->dev_data.mf_classic_dict_attack_data.current_sector); - } else if(event.event == NfcWorkerEventKeyAttackStop) { - dict_attack_set_key_attack(nfc->dict_attack, false, 0); - } else if(event.event == NfcWorkerEventKeyAttackNextSector) { - dict_attack_inc_key_attack_current_sector(nfc->dict_attack); } } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm); consumed = true; } return consumed; } void nfc_scene_mf_classic_dict_attack_on_exit(void* context) { - Nfc* nfc = context; - NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; - // Stop worker - nfc_worker_stop(nfc->worker); - if(dict_attack_data->dict) { - mf_classic_dict_free(dict_attack_data->dict); - dict_attack_data->dict = NULL; - } - dict_attack_reset(nfc->dict_attack); - nfc_blink_stop(nfc); - notification_message(nfc->notifications, &sequence_display_backlight_enforce_auto); + NfcApp* instance = context; + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + dict_attack_reset(instance->dict_attack); + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress); + + mf_dict_free(instance->mf_dict_context.dict); + + instance->mf_dict_context.current_sector = 0; + instance->mf_dict_context.sectors_total = 0; + instance->mf_dict_context.sectors_read = 0; + instance->mf_dict_context.current_sector = 0; + instance->mf_dict_context.keys_found = 0; + instance->mf_dict_context.dict_keys_total = 0; + instance->mf_dict_context.dict_keys_current = 0; + instance->mf_dict_context.is_key_attack = false; + instance->mf_dict_context.key_attack_current_sector = 0; + + nfc_blink_stop(instance); + notification_message(instance->notifications, &sequence_display_backlight_enforce_auto); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c index 8a7dc2c1839e..08491e0bb252 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -1,62 +1,81 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" + +#define NFC_SCENE_MF_CLASSIC_KEYS_MAX (100) void nfc_scene_mf_classic_keys_widget_callback(GuiButtonType result, InputType type, void* context) { - Nfc* nfc = context; + NfcApp* instance = context; if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + view_dispatcher_send_custom_event(instance->view_dispatcher, result); } } void nfc_scene_mf_classic_keys_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; // Load flipper dict keys total - uint32_t flipper_dict_keys_total = 0; - MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeSystem); - if(dict) { - flipper_dict_keys_total = mf_classic_dict_get_total_keys(dict); - mf_classic_dict_free(dict); - } + MfDict* dict = mf_dict_alloc(MfDictTypeSystem); + furi_assert(dict); + uint32_t flipper_dict_keys_total = mf_dict_get_total_keys(dict); + mf_dict_free(dict); + // Load user dict keys total uint32_t user_dict_keys_total = 0; - dict = mf_classic_dict_alloc(MfClassicDictTypeUser); - if(dict) { - user_dict_keys_total = mf_classic_dict_get_total_keys(dict); - mf_classic_dict_free(dict); - } + dict = mf_dict_alloc(MfDictTypeUser); + furi_assert(dict); + user_dict_keys_total = mf_dict_get_total_keys(dict); + mf_dict_free(dict); + FuriString* temp_str = furi_string_alloc(); + widget_add_string_element( + instance->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MIFARE Classic Keys"); + furi_string_printf(temp_str, "System dict: %lu", flipper_dict_keys_total); + widget_add_string_element( + instance->widget, + 0, + 20, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(temp_str)); + furi_string_printf(temp_str, "User dict: %lu", user_dict_keys_total); widget_add_string_element( - nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MIFARE Classic Keys"); - char temp_str[32]; - snprintf(temp_str, sizeof(temp_str), "System dict: %lu", flipper_dict_keys_total); - widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str); - snprintf(temp_str, sizeof(temp_str), "User dict: %lu", user_dict_keys_total); - widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); + instance->widget, + 0, + 32, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(temp_str)); + widget_add_icon_element(instance->widget, 87, 13, &I_Keychain_39x36); widget_add_button_element( - nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); - widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36); + instance->widget, + GuiButtonTypeCenter, + "Add", + nfc_scene_mf_classic_keys_widget_callback, + instance); if(user_dict_keys_total > 0) { widget_add_button_element( - nfc->widget, + instance->widget, GuiButtonTypeRight, "List", nfc_scene_mf_classic_keys_widget_callback, - nfc); + instance); } + furi_string_free(temp_str); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); } bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* instance = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeCenter) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeysAdd); consumed = true; } else if(event.event == GuiButtonTypeRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeysList); consumed = true; } } @@ -65,7 +84,7 @@ bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) } void nfc_scene_mf_classic_keys_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; - widget_reset(nfc->widget); + widget_reset(instance->widget); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c index b122aa225b92..77c9f88097a8 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -1,60 +1,61 @@ -#include "../nfc_i.h" -#include +#include "../nfc_app_i.h" void nfc_scene_mf_classic_keys_add_byte_input_callback(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone); } void nfc_scene_mf_classic_keys_add_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; // Setup view - ByteInput* byte_input = nfc->byte_input; + ByteInput* byte_input = instance->byte_input; byte_input_set_header_text(byte_input, "Enter the key in hex"); byte_input_set_result_callback( byte_input, nfc_scene_mf_classic_keys_add_byte_input_callback, NULL, - nfc, - nfc->byte_input_store, - 6); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); + instance, + instance->byte_input_store, + sizeof(MfClassicKey)); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput); } bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* instance = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { // Add key to dict - MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); - if(dict) { - if(mf_classic_dict_is_key_present(dict, nfc->byte_input_store)) { - scene_manager_next_scene( - nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); - } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); - DOLPHIN_DEED(DolphinDeedNfcMfcAdd); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); - } + MfDict* dict = mf_dict_alloc(MfDictTypeUser); + furi_assert(dict); + + MfClassicKey key = {}; + memcpy(key.data, instance->byte_input_store, sizeof(MfClassicKey)); + if(mf_dict_is_key_present(dict, &key)) { + scene_manager_next_scene( + instance->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); + } else if(mf_dict_add_key(dict, &key)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess); + dolphin_deed(DolphinDeedNfcMfcAdd); } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + scene_manager_previous_scene(instance->scene_manager); } - mf_classic_dict_free(dict); + + mf_dict_free(dict); consumed = true; } } + return consumed; } void nfc_scene_mf_classic_keys_add_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; // Clear view - byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(nfc->byte_input, ""); + byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(instance->byte_input, ""); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c index 0ea3f59a45ef..b245a2a552d6 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c @@ -1,42 +1,40 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_mf_classic_keys_delete_widget_callback( GuiButtonType result, InputType type, void* context) { - Nfc* nfc = context; + NfcApp* instance = context; if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + view_dispatcher_send_custom_event(instance->view_dispatcher, result); } } void nfc_scene_mf_classic_keys_delete_on_enter(void* context) { - Nfc* nfc = context; - MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + NfcApp* instance = context; + uint32_t key_index = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete); - // Setup Custom Widget view - FuriString* key_str; - key_str = furi_string_alloc(); + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicKeysDelete); + FuriString* key_str = furi_string_alloc(); widget_add_string_element( - nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Delete this key?"); + instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Delete this key?"); widget_add_button_element( - nfc->widget, + instance->widget, GuiButtonTypeLeft, "Cancel", nfc_scene_mf_classic_keys_delete_widget_callback, - nfc); + instance); widget_add_button_element( - nfc->widget, + instance->widget, GuiButtonTypeRight, "Delete", nfc_scene_mf_classic_keys_delete_widget_callback, - nfc); + instance); - mf_classic_dict_get_key_at_index_str(dict, key_str, key_index); + mf_user_dict_get_key_str(instance->mf_user_dict, key_index, key_str); widget_add_string_element( - nfc->widget, + instance->widget, 64, 32, AlignCenter, @@ -45,39 +43,35 @@ void nfc_scene_mf_classic_keys_delete_on_enter(void* context) { furi_string_get_cstr(key_str)); furi_string_free(key_str); - mf_classic_dict_free(dict); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); } bool nfc_scene_mf_classic_keys_delete_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* instance = context; bool consumed = false; - uint32_t key_index = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete); if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMfClassicKeys); - } else if(event.event == GuiButtonTypeRight) { - MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); - if(mf_classic_dict_delete_index(dict, key_index)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess); + if(event.event == GuiButtonTypeRight) { + uint32_t key_index = scene_manager_get_scene_state( + instance->scene_manager, NfcSceneMfClassicKeysDelete); + if(mf_user_dict_delete_key(instance->mf_user_dict, key_index)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneDeleteSuccess); } else { - scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMfClassicKeys); + scene_manager_previous_scene(instance->scene_manager); } - mf_classic_dict_free(dict); - consumed = true; + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(instance->scene_manager); } + consumed = true; } return consumed; } void nfc_scene_mf_classic_keys_delete_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; - widget_reset(nfc->widget); + mf_user_dict_free(instance->mf_user_dict); + widget_reset(instance->widget); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c index 57f9fe656247..7370c06840e9 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c @@ -1,100 +1,51 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" #define NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX (100) void nfc_scene_mf_classic_keys_list_submenu_callback(void* context, uint32_t index) { - furi_assert(context); + NfcApp* instance = context; - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); + view_dispatcher_send_custom_event(instance->view_dispatcher, index); } -void nfc_scene_mf_classic_keys_list_popup_callback(void* context) { - furi_assert(context); - - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); -} +void nfc_scene_mf_classic_keys_list_on_enter(void* context) { + NfcApp* instance = context; -void nfc_scene_mf_classic_keys_list_prepare(Nfc* nfc, MfClassicDict* dict) { - Submenu* submenu = nfc->submenu; - uint32_t index = 0; - FuriString* temp_key; - temp_key = furi_string_alloc(); + instance->mf_user_dict = mf_user_dict_alloc(NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX); - submenu_set_header(submenu, "Select key to delete:"); - while(mf_classic_dict_get_next_key_str(dict, temp_key)) { - char* current_key = (char*)malloc(sizeof(char) * 13); - strncpy(current_key, furi_string_get_cstr(temp_key), 12); - MfClassicUserKeys_push_back(nfc->mfc_key_strs, current_key); - FURI_LOG_D("ListKeys", "Key %lu: %s", index, current_key); + submenu_set_header(instance->submenu, "Select key to delete:"); + FuriString* temp_str = furi_string_alloc(); + for(size_t i = 0; i < mf_user_dict_get_keys_cnt(instance->mf_user_dict); i++) { + mf_user_dict_get_key_str(instance->mf_user_dict, i, temp_str); submenu_add_item( - submenu, current_key, index++, nfc_scene_mf_classic_keys_list_submenu_callback, nfc); + instance->submenu, + furi_string_get_cstr(temp_str), + i, + nfc_scene_mf_classic_keys_list_submenu_callback, + instance); } - furi_string_free(temp_key); -} + furi_string_free(temp_str); -void nfc_scene_mf_classic_keys_list_on_enter(void* context) { - Nfc* nfc = context; - MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); - MfClassicUserKeys_init(nfc->mfc_key_strs); - if(dict) { - uint32_t total_user_keys = mf_classic_dict_get_total_keys(dict); - if(total_user_keys < NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX) { - nfc_scene_mf_classic_keys_list_prepare(nfc, dict); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); - } else { - popup_set_header(nfc->popup, "Too many keys!", 64, 0, AlignCenter, AlignTop); - popup_set_text( - nfc->popup, - "Edit user dictionary\nwith file browser", - 64, - 12, - AlignCenter, - AlignTop); - popup_set_callback(nfc->popup, nfc_scene_mf_classic_keys_list_popup_callback); - popup_set_context(nfc->popup, nfc); - popup_set_timeout(nfc->popup, 3000); - popup_enable_timeout(nfc->popup); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - } - mf_classic_dict_free(dict); - } else { - popup_set_header( - nfc->popup, "Failed to load dictionary", 64, 32, AlignCenter, AlignCenter); - popup_set_callback(nfc->popup, nfc_scene_mf_classic_keys_list_popup_callback); - popup_set_context(nfc->popup, nfc); - popup_set_timeout(nfc->popup, 3000); - popup_enable_timeout(nfc->popup); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - } + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); } bool nfc_scene_mf_classic_keys_list_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* instance = context; + bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventViewExit) { - consumed = scene_manager_previous_scene(nfc->scene_manager); - } else { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfClassicKeysDelete, event.event); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysDelete); - consumed = true; - } + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMfClassicKeysDelete, event.event); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeysDelete); + } else if(event.type == SceneManagerEventTypeBack) { + mf_user_dict_free(instance->mf_user_dict); } + return consumed; } void nfc_scene_mf_classic_keys_list_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; - MfClassicUserKeys_it_t it; - for(MfClassicUserKeys_it(it, nfc->mfc_key_strs); !MfClassicUserKeys_end_p(it); - MfClassicUserKeys_next(it)) { - free(*MfClassicUserKeys_ref(it)); - } - MfClassicUserKeys_clear(nfc->mfc_key_strs); - submenu_reset(nfc->submenu); - popup_reset(nfc->popup); + submenu_reset(instance->submenu); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c index ab41989b2c73..991c956c1c00 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c @@ -1,15 +1,16 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_mf_classic_keys_warn_duplicate_popup_callback(void* context) { - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); } void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; // Setup view - Popup* popup = nfc->popup; + Popup* popup = instance->popup; popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48); popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop); popup_set_text( @@ -20,28 +21,29 @@ void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) { 24, AlignLeft, AlignTop); - popup_set_timeout(popup, 5000); - popup_set_context(popup, nfc); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); popup_set_callback(popup, nfc_scene_mf_classic_keys_warn_duplicate_popup_callback); popup_enable_timeout(popup); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); } bool nfc_scene_mf_classic_keys_warn_duplicate_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* instance = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMfClassicKeysAdd); + instance->scene_manager, NfcSceneMfClassicKeysAdd); } } + return consumed; } void nfc_scene_mf_classic_keys_warn_duplicate_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; - popup_reset(nfc->popup); + popup_reset(instance->popup); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c new file mode 100644 index 000000000000..8e07043e25b8 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c @@ -0,0 +1,58 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_mfkey_complete_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_mfkey_complete_on_enter(void* context) { + NfcApp* instance = context; + + widget_add_string_element( + instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Complete!"); + widget_add_string_multiline_element( + instance->widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + "Now use Mfkey32\nto extract keys"); + widget_add_button_element( + instance->widget, + GuiButtonTypeCenter, + "OK", + nfc_scene_mf_classic_mfkey_complete_callback, + instance); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_mfkey_complete_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeCenter) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneStart); + } + } else if(event.type == SceneManagerEventTypeBack) { + const uint32_t prev_scenes[] = {NfcSceneSavedMenu, NfcSceneStart}; + consumed = scene_manager_search_and_switch_to_previous_scene_one_of( + instance->scene_manager, prev_scenes, COUNT_OF(prev_scenes)); + } + + return consumed; +} + +void nfc_scene_mf_classic_mfkey_complete_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_nonces_info.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_nonces_info.c new file mode 100644 index 000000000000..44f3500a7808 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_nonces_info.c @@ -0,0 +1,72 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_mfkey_nonces_info_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_mfkey_nonces_info_on_enter(void* context) { + NfcApp* instance = context; + + FuriString* temp_str = furi_string_alloc(); + + size_t mfkey_params_saved = mfkey32_logger_get_params_num(instance->mfkey32_logger); + furi_string_printf(temp_str, "Nonce pairs saved: %d\n", mfkey_params_saved); + widget_add_string_element( + instance->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(temp_str)); + widget_add_string_element( + instance->widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Authenticated sectors:"); + + mfkey32_logger_get_params_data(instance->mfkey32_logger, temp_str); + widget_add_text_scroll_element( + instance->widget, 0, 22, 128, 42, furi_string_get_cstr(temp_str)); + widget_add_button_element( + instance->widget, + GuiButtonTypeCenter, + "OK", + nfc_scene_mf_classic_mfkey_nonces_info_callback, + instance); + + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_mfkey_nonces_info_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeCenter) { + if(mfkey32_logger_save_params( + instance->mfkey32_logger, NFC_APP_MFKEY32_LOGS_FILE_PATH)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicMfkeyComplete); + } else { + scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneStart); + } + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + const uint32_t prev_scenes[] = {NfcSceneSavedMenu, NfcSceneStart}; + consumed = scene_manager_search_and_switch_to_previous_scene_one_of( + instance->scene_manager, prev_scenes, COUNT_OF(prev_scenes)); + } + + return consumed; +} + +void nfc_scene_mf_classic_mfkey_nonces_info_on_exit(void* context) { + NfcApp* instance = context; + + mfkey32_logger_free(instance->mfkey32_logger); + + // Clear view + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c new file mode 100644 index 000000000000..89aa13bcca3a --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c @@ -0,0 +1,144 @@ +#include "../nfc_app_i.h" + +#include + +enum { + NfcSceneMfClassicUpdateInitialStateCardSearch, + NfcSceneMfClassicUpdateInitialStateCardFound, +}; + +NfcCommand nfc_mf_classic_update_initial_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcCommand command = NfcCommandContinue; + const MfClassicPollerEvent* mfc_event = event.data; + NfcApp* instance = context; + + if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + const MfClassicData* updated_data = nfc_poller_get_data(instance->poller); + const MfClassicData* old_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + if(iso14443_3a_is_equal(updated_data->iso14443_3a_data, old_data->iso14443_3a_data)) { + mfc_event->data->poller_mode.mode = MfClassicPollerModeRead; + } else { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) { + uint8_t sector_num = 0; + MfClassicKey key = {}; + MfClassicKeyType key_type = MfClassicKeyTypeA; + if(mf_classic_key_cahce_get_next_key( + instance->mfc_key_cache, §or_num, &key, &key_type)) { + mfc_event->data->read_sector_request_data.sector_num = sector_num; + mfc_event->data->read_sector_request_data.key = key; + mfc_event->data->read_sector_request_data.key_type = key_type; + mfc_event->data->read_sector_request_data.key_provided = true; + } else { + mfc_event->data->read_sector_request_data.key_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + const MfClassicData* updated_data = nfc_poller_get_data(instance->poller); + nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, updated_data); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWorkerExit); + command = NfcCommandStop; + } + + return command; +} + +static void nfc_scene_mf_classic_update_initial_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + popup_reset(popup); + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicUpdateInitial); + + if(state == NfcSceneMfClassicUpdateInitialStateCardSearch) { + popup_set_text( + instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Updating\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_update_initial_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcEmulate); + + const MfClassicData* mfc_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + mf_classic_key_cache_load_from_data(instance->mfc_key_cache, mfc_data); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardSearch); + nfc_scene_mf_classic_update_initial_setup_view(instance); + + // Setup and start worker + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_mf_classic_update_initial_worker_callback, instance); + nfc_blink_emulate_start(instance); +} + +bool nfc_scene_mf_classic_update_initial_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardFound); + nfc_scene_mf_classic_update_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventCardLost) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardSearch); + nfc_scene_mf_classic_update_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventWrongCard) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcCustomEventWorkerExit) { + if(nfc_save_shadow_file(instance)) { + scene_manager_next_scene( + instance->scene_manager, NfcSceneMfClassicUpdateInitialSuccess); + } else { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } + } + } + + return consumed; +} + +void nfc_scene_mf_classic_update_initial_on_exit(void* context) { + NfcApp* instance = context; + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardSearch); + // Clear view + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c new file mode 100644 index 000000000000..02e307b01baa --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c @@ -0,0 +1,43 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_update_initial_success_popup_callback(void* context) { + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_update_initial_success_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcSave); + + notification_message(instance->notifications, &sequence_success); + + Popup* popup = instance->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_mf_classic_update_initial_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_update_initial_success_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + } + return consumed; +} + +void nfc_scene_mf_classic_update_initial_success_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c new file mode 100644 index 000000000000..4ff65b834dd2 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c @@ -0,0 +1,146 @@ +#include "../nfc_app_i.h" + +#include + +enum { + NfcSceneMfClassicWriteInitialStateCardSearch, + NfcSceneMfClassicWriteInitialStateCardFound, +}; + +NfcCommand + nfc_scene_mf_classic_write_initial_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcCommand command = NfcCommandContinue; + NfcApp* instance = context; + MfClassicPollerEvent* mfc_event = event.data; + const MfClassicData* write_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + + if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + const MfClassicData* tag_data = nfc_poller_get_data(instance->poller); + if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) { + mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite; + } else { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) { + uint8_t sector = mfc_event->data->sec_tr_data.sector_num; + uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector); + if(mf_classic_is_block_read(write_data, sec_tr)) { + mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr]; + mfc_event->data->sec_tr_data.sector_trailer_provided = true; + } else { + mfc_event->data->sec_tr_data.sector_trailer_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) { + uint8_t block_num = mfc_event->data->write_block_data.block_num; + if(mf_classic_is_block_read(write_data, block_num)) { + mfc_event->data->write_block_data.write_block = write_data->block[block_num]; + mfc_event->data->write_block_data.write_block_provided = true; + } else { + mfc_event->data->write_block_data.write_block_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } else if(mfc_event->type == MfClassicPollerEventTypeFail) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } + return command; +} + +static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + popup_reset(popup); + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial); + + if(state == NfcSceneMfClassicWriteInitialStateCardSearch) { + popup_set_text( + instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_write_initial_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardSearch); + nfc_scene_mf_classic_write_initial_setup_view(instance); + + // Setup and start worker + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start( + instance->poller, nfc_scene_mf_classic_write_initial_worker_callback, instance); + + nfc_blink_emulate_start(instance); +} + +bool nfc_scene_mf_classic_write_initial_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardFound); + nfc_scene_mf_classic_write_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventCardLost) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardSearch); + nfc_scene_mf_classic_write_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventWrongCard) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcCustomEventPollerSuccess) { + scene_manager_next_scene( + instance->scene_manager, NfcSceneMfClassicWriteInitialSuccess); + consumed = true; + } else if(event.event == NfcCustomEventPollerFailure) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitialFail); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_write_initial_on_exit(void* context) { + NfcApp* instance = context; + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardSearch); + // Clear view + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c new file mode 100644 index 000000000000..f85e5a80c3f8 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c @@ -0,0 +1,62 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_write_initial_fail_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_write_initial_fail_on_enter(void* context) { + NfcApp* instance = context; + Widget* widget = instance->widget; + + notification_message(instance->notifications, &sequence_error); + + widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Not all sectors\nwere written\ncorrectly."); + + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Finish", + nfc_scene_mf_classic_write_initial_fail_widget_callback, + instance); + + // Setup and start worker + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_write_initial_fail_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + return consumed; +} + +void nfc_scene_mf_classic_write_initial_fail_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c new file mode 100644 index 000000000000..acb75cd2e9f8 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c @@ -0,0 +1,43 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_write_initial_success_popup_callback(void* context) { + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_write_initial_success_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcSave); + + notification_message(instance->notifications, &sequence_success); + + Popup* popup = instance->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_mf_classic_write_initial_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_write_initial_success_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + } + return consumed; +} + +void nfc_scene_mf_classic_write_initial_success_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c index 2c56270e36d2..50025048af43 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c @@ -1,20 +1,20 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_mf_classic_wrong_card_widget_callback( GuiButtonType result, InputType type, void* context) { - Nfc* nfc = context; + NfcApp* instance = context; if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + view_dispatcher_send_custom_event(instance->view_dispatcher, result); } } void nfc_scene_mf_classic_wrong_card_on_enter(void* context) { - Nfc* nfc = context; - Widget* widget = nfc->widget; + NfcApp* instance = context; + Widget* widget = instance->widget; - notification_message(nfc->notifications, &sequence_error); + notification_message(instance->notifications, &sequence_error); widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); widget_add_string_element( @@ -28,26 +28,30 @@ void nfc_scene_mf_classic_wrong_card_on_enter(void* context) { FontSecondary, "Data management\nis only possible\nwith initial card"); widget_add_button_element( - widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_wrong_card_widget_callback, nfc); + widget, + GuiButtonTypeLeft, + "Retry", + nfc_scene_mf_classic_wrong_card_widget_callback, + instance); // Setup and start worker - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); } bool nfc_scene_mf_classic_wrong_card_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* instance = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(nfc->scene_manager); + consumed = scene_manager_previous_scene(instance->scene_manager); } } return consumed; } void nfc_scene_mf_classic_wrong_card_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; - widget_reset(nfc->widget); + widget_reset(instance->widget); } \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c index 882dc5fea8f2..8d6a92b6c7e6 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c @@ -1,103 +1,93 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" -#define TAG "NfcSceneMfDesfireApp" +#include "../helpers/protocol_support/mf_desfire/mf_desfire_render.h" enum SubmenuIndex { SubmenuIndexAppInfo, SubmenuIndexDynamic, // dynamic indexes start here }; -void nfc_scene_mf_desfire_popup_callback(void* context) { - furi_assert(context); +static void nfc_scene_mf_desfire_app_submenu_callback(void* context, uint32_t index) { + NfcApp* nfc = context; - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } -MifareDesfireApplication* nfc_scene_mf_desfire_app_get_app(Nfc* nfc) { - uint32_t app_idx = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> - 1; - MifareDesfireApplication* app = nfc->dev->dev_data.mf_df_data.app_head; - for(uint32_t i = 0; i < app_idx && app; i++) { - app = app->next; - } - return app; -} +void nfc_scene_mf_desfire_app_on_enter(void* context) { + NfcApp* nfc = context; -void nfc_scene_mf_desfire_app_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; + text_box_set_font(nfc->text_box, TextBoxFontHex); + submenu_add_item( + nfc->submenu, + "App info", + SubmenuIndexAppInfo, + nfc_scene_mf_desfire_app_submenu_callback, + nfc); - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} + const uint32_t app_idx = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> 1; -void nfc_scene_mf_desfire_app_on_enter(void* context) { - Nfc* nfc = context; - MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); - if(!app) { - popup_set_icon(nfc->popup, 5, 5, &I_WarningDolphin_45x42); - popup_set_header(nfc->popup, "Empty card!", 55, 12, AlignLeft, AlignBottom); - popup_set_callback(nfc->popup, nfc_scene_mf_desfire_popup_callback); - popup_set_context(nfc->popup, nfc); - popup_set_timeout(nfc->popup, 3000); - popup_enable_timeout(nfc->popup); - popup_set_text(nfc->popup, "No application\nfound.", 55, 15, AlignLeft, AlignTop); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - } else { - text_box_set_font(nfc->text_box, TextBoxFontHex); + const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire); + const MfDesfireApplication* app = simple_array_cget(data->applications, app_idx); + + FuriString* label = furi_string_alloc(); + + for(uint32_t i = 0; i < simple_array_get_count(app->file_ids); ++i) { + const MfDesfireFileId file_id = + *(const MfDesfireFileId*)simple_array_cget(app->file_ids, i); + furi_string_printf(label, "File %d", file_id); submenu_add_item( nfc->submenu, - "App info", - SubmenuIndexAppInfo, + furi_string_get_cstr(label), + i + SubmenuIndexDynamic, nfc_scene_mf_desfire_app_submenu_callback, nfc); + } - FuriString* label = furi_string_alloc(); - int idx = SubmenuIndexDynamic; - for(MifareDesfireFile* file = app->file_head; file; file = file->next) { - furi_string_printf(label, "File %d", file->id); - submenu_add_item( - nfc->submenu, - furi_string_get_cstr(label), - idx++, - nfc_scene_mf_desfire_app_submenu_callback, - nfc); - } - furi_string_free(label); + furi_string_free(label); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); - } + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } bool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp); + + const uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp); if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { consumed = scene_manager_previous_scene(nfc->scene_manager); } else { - MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); + const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire); + + const uint32_t app_index = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> 1; + const MfDesfireApplication* app = simple_array_cget(data->applications, app_index); + TextBox* text_box = nfc->text_box; furi_string_reset(nfc->text_box_store); if(event.event == SubmenuIndexAppInfo) { - mf_df_cat_application_info(app, nfc->text_box_store); + const MfDesfireApplicationId* app_id = + simple_array_cget(data->application_ids, app_index); + nfc_render_mf_desfire_application_id(app_id, nfc->text_box_store); + nfc_render_mf_desfire_application(app, nfc->text_box_store); } else { - uint16_t index = event.event - SubmenuIndexDynamic; - MifareDesfireFile* file = app->file_head; - for(int i = 0; file && i < index; i++) { - file = file->next; - } - if(!file) { - return false; - } - mf_df_cat_file(file, nfc->text_box_store); + const uint32_t file_index = event.event - SubmenuIndexDynamic; + const MfDesfireFileId* file_id = simple_array_cget(app->file_ids, file_index); + const MfDesfireFileSettings* file_settings = + simple_array_cget(app->file_settings, file_index); + const MfDesfireFileData* file_data = simple_array_cget(app->file_data, file_index); + nfc_render_mf_desfire_file_id(file_id, nfc->text_box_store); + nfc_render_mf_desfire_file_settings_data( + file_settings, file_data, nfc->text_box_store); } text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state | 1); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); consumed = true; } + } else if(event.type == SceneManagerEventTypeBack) { if(state & 1) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); @@ -110,10 +100,9 @@ bool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_mf_desfire_app_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clear views - popup_reset(nfc->popup); text_box_reset(nfc->text_box); furi_string_reset(nfc->text_box_store); submenu_reset(nfc->submenu); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_data.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_data.c index c7caee8dc6d3..f26aa06a1bcf 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_data.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_data.c @@ -1,6 +1,6 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" -#define TAG "NfcSceneMfDesfireData" +#include "../helpers/protocol_support/mf_desfire/mf_desfire_render.h" enum { MifareDesfireDataStateMenu, @@ -12,17 +12,19 @@ enum SubmenuIndex { SubmenuIndexDynamic, // dynamic indexes start here }; -void nfc_scene_mf_desfire_data_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = (Nfc*)context; +static void nfc_scene_mf_desfire_data_submenu_callback(void* context, uint32_t index) { + NfcApp* nfc = (NfcApp*)context; view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } void nfc_scene_mf_desfire_data_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; Submenu* submenu = nfc->submenu; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); - MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; + + const uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); + const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire); text_box_set_font(nfc->text_box, TextBoxFontHex); @@ -34,16 +36,19 @@ void nfc_scene_mf_desfire_data_on_enter(void* context) { nfc); FuriString* label = furi_string_alloc(); - int idx = SubmenuIndexDynamic; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - furi_string_printf(label, "App %02x%02x%02x", app->id[0], app->id[1], app->id[2]); + + for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) { + const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i); + furi_string_printf( + label, "App %02x%02x%02x", app_id->data[0], app_id->data[1], app_id->data[2]); submenu_add_item( submenu, furi_string_get_cstr(label), - idx++, + i + SubmenuIndexDynamic, nfc_scene_mf_desfire_data_submenu_callback, nfc); } + furi_string_free(label); if(state >= MifareDesfireDataStateItem) { @@ -57,16 +62,19 @@ void nfc_scene_mf_desfire_data_on_enter(void* context) { } bool nfc_scene_mf_desfire_data_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); - MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; + + const uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); + const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire); if(event.type == SceneManagerEventTypeCustom) { TextBox* text_box = nfc->text_box; furi_string_reset(nfc->text_box_store); + if(event.event == SubmenuIndexCardInfo) { - mf_df_cat_card_info(data, nfc->text_box_store); + nfc_render_mf_desfire_data(data, nfc->text_box_store); text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); scene_manager_set_scene_state( @@ -75,7 +83,7 @@ bool nfc_scene_mf_desfire_data_on_event(void* context, SceneManagerEvent event) MifareDesfireDataStateItem + SubmenuIndexCardInfo); consumed = true; } else { - uint16_t index = event.event - SubmenuIndexDynamic; + const uint32_t index = event.event - SubmenuIndexDynamic; scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateItem + index); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, index << 1); @@ -95,7 +103,7 @@ bool nfc_scene_mf_desfire_data_on_event(void* context, SceneManagerEvent event) } void nfc_scene_mf_desfire_data_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clear views text_box_reset(nfc->text_box); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_capture_pass.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_capture_pass.c new file mode 100644 index 000000000000..95d286689d1e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_capture_pass.c @@ -0,0 +1,58 @@ +#include "../nfc_app_i.h" + +// MfUltralightListenerCommand nfc_scene_mf_ultralight_capture_pass_worker_callback( +// MfUltralightListenerEvent event, +// void* context) { +// NfcApp* nfc = context; + +// if(event.type == MfUltralightListenerEventTypeAuth) { +// nfc->mf_ul_auth->password = event.data->password; +// view_dispatcher_send_custom_event(nfc->view_dispatcher, MfUltralightListenerEventTypeAuth); +// } + +// return MfUltralightListenerCommandContinue; +// } + +void nfc_scene_mf_ultralight_capture_pass_on_enter(void* context) { + NfcApp* nfc = context; + + // Setup view + widget_add_string_multiline_element( + nfc->widget, + 54, + 30, + AlignLeft, + AlignCenter, + FontPrimary, + "Touch the\nreader to get\npassword..."); + widget_add_icon_element(nfc->widget, 0, 15, &I_Modern_reader_18x34); + widget_add_icon_element(nfc->widget, 20, 12, &I_Move_flipper_26x39); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + + // Start worker + + nfc_blink_read_start(nfc); +} + +bool nfc_scene_mf_ultralight_capture_pass_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == MfUltralightListenerEventTypeAuth) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_capture_pass_on_exit(void* context) { + NfcApp* nfc = context; + + // Clear view + widget_reset(nfc->widget); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_key_input.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_key_input.c index 089187d5bc3a..6db6023d274a 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_key_input.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_key_input.c @@ -1,13 +1,13 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_mf_ultralight_key_input_byte_input_callback(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); } void nfc_scene_mf_ultralight_key_input_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Setup view ByteInput* byte_input = nfc->byte_input; @@ -17,13 +17,13 @@ void nfc_scene_mf_ultralight_key_input_on_enter(void* context) { nfc_scene_mf_ultralight_key_input_byte_input_callback, NULL, nfc, - nfc->byte_input_store, + nfc->mf_ul_auth->password.data, 4); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); } bool nfc_scene_mf_ultralight_key_input_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { @@ -36,7 +36,7 @@ bool nfc_scene_mf_ultralight_key_input_on_event(void* context, SceneManagerEvent } void nfc_scene_mf_ultralight_key_input_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clear view byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c index 484629b0bbe1..912150d619bc 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -1,29 +1,29 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" enum SubmenuIndex { - SubmenuIndexMfUlUnlockMenuAuto, + SubmenuIndexMfUlUnlockMenuReader, SubmenuIndexMfUlUnlockMenuAmeebo, SubmenuIndexMfUlUnlockMenuXiaomi, SubmenuIndexMfUlUnlockMenuManual, }; void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; Submenu* submenu = nfc->submenu; uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); - if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { + if(nfc_device_get_protocol(nfc->nfc_device) == NfcProtocolMfUltralight) { submenu_add_item( submenu, "Unlock With Reader", - SubmenuIndexMfUlUnlockMenuAuto, + SubmenuIndexMfUlUnlockMenuReader, nfc_scene_mf_ultralight_unlock_menu_submenu_callback, nfc); } @@ -50,24 +50,25 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { } bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexMfUlUnlockMenuManual) { - nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodManual; + nfc->mf_ul_auth->type = MfUltralightAuthTypeManual; scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightKeyInput); consumed = true; } else if(event.event == SubmenuIndexMfUlUnlockMenuAmeebo) { - nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAmeebo; + nfc->mf_ul_auth->type = MfUltralightAuthTypeAmiibo; scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); consumed = true; } else if(event.event == SubmenuIndexMfUlUnlockMenuXiaomi) { - nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi; + nfc->mf_ul_auth->type = MfUltralightAuthTypeXiaomii; scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); consumed = true; - } else if(event.event == SubmenuIndexMfUlUnlockMenuAuto) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + } else if(event.event == SubmenuIndexMfUlUnlockMenuReader) { + nfc->mf_ul_auth->type = MfUltralightAuthTypeReader; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightCapturePass); consumed = true; } scene_manager_set_scene_state( @@ -77,7 +78,7 @@ bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEve } void nfc_scene_mf_ultralight_unlock_menu_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; submenu_reset(nfc->submenu); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c index 16efae9deaeb..57f9429e130a 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -1,45 +1,41 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" #include void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, result); } void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; DialogEx* dialog_ex = nfc->dialog_ex; - MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method; dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback); - if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) { + MfUltralightAuthType type = nfc->mf_ul_auth->type; + if((type == MfUltralightAuthTypeReader) || (type == MfUltralightAuthTypeManual)) { // Build dialog text - MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth; FuriString* password_str = furi_string_alloc_set_str("Try to unlock the card with\npassword: "); - for(size_t i = 0; i < sizeof(auth->pwd.raw); ++i) { - furi_string_cat_printf(password_str, "%02X ", nfc->byte_input_store[i]); + for(size_t i = 0; i < sizeof(nfc->mf_ul_auth->password); i++) { + furi_string_cat_printf(password_str, "%02X ", nfc->mf_ul_auth->password.data[i]); } furi_string_cat_str(password_str, "?\nCaution, a wrong password\ncan block the card!"); nfc_text_store_set(nfc, furi_string_get_cstr(password_str)); furi_string_free(password_str); - dialog_ex_set_header( - dialog_ex, - auth_method == MfUltralightAuthMethodAuto ? "Password captured!" : "Risky function!", - 64, - 0, - AlignCenter, - AlignTop); + const char* message = (type == MfUltralightAuthTypeReader) ? "Password captured!" : + "Risky function!"; + dialog_ex_set_header(dialog_ex, message, 64, 0, AlignCenter, AlignTop); dialog_ex_set_text(dialog_ex, nfc->text_store, 64, 12, AlignCenter, AlignTop); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); dialog_ex_set_right_button_text(dialog_ex, "Continue"); - if(auth_method == MfUltralightAuthMethodAuto) + if(type == MfUltralightAuthTypeReader) { notification_message(nfc->notifications, &sequence_set_green_255); + } } else { dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); dialog_ex_set_text( @@ -52,19 +48,20 @@ void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { } bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; - MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method; - if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) { + // TODO: Set detected protocol accordingly + MfUltralightAuthType type = nfc->mf_ul_auth->type; + if((type == MfUltralightAuthTypeReader) || (type == MfUltralightAuthTypeManual)) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); - DOLPHIN_DEED(DolphinDeedNfcRead); + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } else if(event.event == DialogExResultLeft) { - if(auth_method == MfUltralightAuthMethodAuto) { + if(type == MfUltralightAuthTypeReader) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); } else { @@ -78,8 +75,10 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve } else { if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultCenter) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); - DOLPHIN_DEED(DolphinDeedNfcRead); + const NfcProtocol mfu_protocol[] = {NfcProtocolMfUltralight}; + nfc_app_set_detected_protocols(nfc, mfu_protocol, COUNT_OF(mfu_protocol)); + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } } @@ -89,7 +88,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve } void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; dialog_ex_reset(nfc->dialog_ex); nfc_text_store_clear(nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c deleted file mode 100644 index 92ad7b56ef41..000000000000 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ /dev/null @@ -1,154 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType type, void* context) { - Nfc* nfc = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_nfc_data_info_on_enter(void* context) { - Nfc* nfc = context; - Widget* widget = nfc->widget; - FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; - NfcDeviceData* dev_data = &nfc->dev->dev_data; - NfcProtocol protocol = dev_data->protocol; - uint8_t text_scroll_height = 0; - if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) || - (protocol == NfcDeviceProtocolMifareClassic)) { - widget_add_button_element( - widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); - text_scroll_height = 52; - } else { - text_scroll_height = 64; - } - - FuriString* temp_str; - temp_str = furi_string_alloc(); - // Set name if present - if(nfc->dev->dev_name[0] != '\0') { - furi_string_printf(temp_str, "\ec%s\n", nfc->dev->dev_name); - } - - // Set tag type - if(protocol == NfcDeviceProtocolEMV) { - furi_string_cat_printf(temp_str, "\e#EMV Bank Card\n"); - } else if(protocol == NfcDeviceProtocolMifareUl) { - furi_string_cat_printf( - temp_str, "\e#%s\n", nfc_mf_ul_type(dev_data->mf_ul_data.type, true)); - } else if(protocol == NfcDeviceProtocolMifareClassic) { - furi_string_cat_printf( - temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); - } else if(protocol == NfcDeviceProtocolMifareDesfire) { - furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n"); - } else { - furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); - } - - // Set tag iso data - char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; - furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); - furi_string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); - } - furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); - furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); - - // Set application specific data - if(protocol == NfcDeviceProtocolMifareDesfire) { - MifareDesfireData* data = &dev_data->mf_df_data; - uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1); - uint32_t bytes_free = data->free_memory ? data->free_memory->bytes : 0; - furi_string_cat_printf(temp_str, "\n%lu", bytes_total); - if(data->version.sw_storage & 1) { - furi_string_push_back(temp_str, '+'); - } - furi_string_cat_printf(temp_str, " bytes, %lu bytes free\n", bytes_free); - - uint16_t n_apps = 0; - uint16_t n_files = 0; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - n_apps++; - for(MifareDesfireFile* file = app->file_head; file; file = file->next) { - n_files++; - } - } - furi_string_cat_printf(temp_str, "%d Application", n_apps); - if(n_apps != 1) { - furi_string_push_back(temp_str, 's'); - } - furi_string_cat_printf(temp_str, ", %d file", n_files); - if(n_files != 1) { - furi_string_push_back(temp_str, 's'); - } - } else if(protocol == NfcDeviceProtocolMifareUl) { - MfUltralightData* data = &dev_data->mf_ul_data; - furi_string_cat_printf( - temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4); - if(data->data_size > data->data_read) { - furi_string_cat_printf(temp_str, "\nPassword-protected"); - } else if(data->auth_success) { - MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); - if(config_pages) { - furi_string_cat_printf( - temp_str, - "\nPassword: %02X %02X %02X %02X", - config_pages->auth_data.pwd.raw[0], - config_pages->auth_data.pwd.raw[1], - config_pages->auth_data.pwd.raw[2], - config_pages->auth_data.pwd.raw[3]); - furi_string_cat_printf( - temp_str, - "\nPACK: %02X %02X", - config_pages->auth_data.pack.raw[0], - config_pages->auth_data.pack.raw[1]); - } - } - } else if(protocol == NfcDeviceProtocolMifareClassic) { - MfClassicData* data = &dev_data->mf_classic_data; - uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); - uint8_t keys_total = sectors_total * 2; - uint8_t keys_found = 0; - uint8_t sectors_read = 0; - mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); - furi_string_cat_printf(temp_str, "\nKeys Found %d/%d", keys_found, keys_total); - furi_string_cat_printf(temp_str, "\nSectors Read %d/%d", sectors_read, sectors_total); - } - - // Add text scroll widget - widget_add_text_scroll_element( - widget, 0, 0, 128, text_scroll_height, furi_string_get_cstr(temp_str)); - furi_string_free(temp_str); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - NfcProtocol protocol = nfc->dev->dev_data.protocol; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeRight) { - if(protocol == NfcDeviceProtocolMifareDesfire) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData); - consumed = true; - } else if(protocol == NfcDeviceProtocolMifareUl) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData); - consumed = true; - } else if(protocol == NfcDeviceProtocolMifareClassic) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); - consumed = true; - } - } - } - - return consumed; -} - -void nfc_scene_nfc_data_info_on_exit(void* context) { - Nfc* nfc = context; - - widget_reset(nfc->widget); -} diff --git a/applications/main/nfc/scenes/nfc_scene_not_implemented.c b/applications/main/nfc/scenes/nfc_scene_not_implemented.c new file mode 100644 index 000000000000..35ae3b8149e6 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_not_implemented.c @@ -0,0 +1,38 @@ +#include "../nfc_app_i.h" + +void nfc_scene_not_implemented_popup_callback(void* context) { + NfcApp* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_not_implemented_on_enter(void* context) { + NfcApp* nfc = context; + + // Setup view + Popup* popup = nfc->popup; + popup_set_header(popup, "Not implemented!", 64, 32, AlignCenter, AlignCenter); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_not_implemented_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_not_implemented_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } + return consumed; +} + +void nfc_scene_not_implemented_on_exit(void* context) { + NfcApp* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 938f2da67546..e9603afd1ad5 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -1,118 +1,13 @@ -#include "../nfc_i.h" -#include - -typedef enum { - NfcSceneReadStateIdle, - NfcSceneReadStateDetecting, - NfcSceneReadStateReading, -} NfcSceneReadState; - -bool nfc_scene_read_worker_callback(NfcWorkerEvent event, void* context) { - Nfc* nfc = context; - bool consumed = false; - if(event == NfcWorkerEventReadMfClassicLoadKeyCache) { - consumed = nfc_device_load_key_cache(nfc->dev); - } else { - view_dispatcher_send_custom_event(nfc->view_dispatcher, event); - consumed = true; - } - return consumed; -} - -void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) { - uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneRead); - if(curr_state != state) { - if(state == NfcSceneReadStateDetecting) { - popup_reset(nfc->popup); - popup_set_text( - nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); - popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); - } else if(state == NfcSceneReadStateReading) { - popup_reset(nfc->popup); - popup_set_header( - nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); - popup_set_icon(nfc->popup, 12, 23, &A_Loading_24); - } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneRead, state); - } -} +#include "../helpers/protocol_support/nfc_protocol_support.h" void nfc_scene_read_on_enter(void* context) { - Nfc* nfc = context; - - nfc_device_clear(nfc->dev); - // Setup view - nfc_scene_read_set_state(nfc, NfcSceneReadStateDetecting); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - // Start worker - nfc_worker_start( - nfc->worker, NfcWorkerStateRead, &nfc->dev->dev_data, nfc_scene_read_worker_callback, nfc); - - nfc_blink_read_start(nfc); + nfc_protocol_support_on_enter(NfcProtocolSupportSceneRead, context); } bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if((event.event == NfcWorkerEventReadUidNfcB) || - (event.event == NfcWorkerEventReadUidNfcF) || - (event.event == NfcWorkerEventReadUidNfcV)) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - consumed = true; - } else if(event.event == NfcWorkerEventReadUidNfcA) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - consumed = true; - } else if(event.event == NfcWorkerEventReadMfUltralight) { - notification_message(nfc->notifications, &sequence_success); - // Set unlock password input to 0xFFFFFFFF only on fresh read - memset(nfc->byte_input_store, 0xFF, sizeof(nfc->byte_input_store)); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - consumed = true; - } else if(event.event == NfcWorkerEventReadMfClassicDone) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - consumed = true; - } else if(event.event == NfcWorkerEventReadMfDesfire) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - consumed = true; - } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { - if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); - } - consumed = true; - } else if(event.event == NfcWorkerEventCardDetected) { - nfc_scene_read_set_state(nfc, NfcSceneReadStateReading); - nfc_blink_detect_start(nfc); - consumed = true; - } else if(event.event == NfcWorkerEventNoCardDetected) { - nfc_scene_read_set_state(nfc, NfcSceneReadStateDetecting); - nfc_blink_read_start(nfc); - consumed = true; - } - } - return consumed; + return nfc_protocol_support_on_event(NfcProtocolSupportSceneRead, context, event); } void nfc_scene_read_on_exit(void* context) { - Nfc* nfc = context; - - // Stop worker - nfc_worker_stop(nfc->worker); - // Clear view - popup_reset(nfc->popup); - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneRead, NfcSceneReadStateIdle); - - nfc_blink_stop(nfc); + nfc_protocol_support_on_exit(NfcProtocolSupportSceneRead, context); } diff --git a/applications/main/nfc/scenes/nfc_scene_read_menu.c b/applications/main/nfc/scenes/nfc_scene_read_menu.c new file mode 100644 index 000000000000..cba07a485ae5 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_read_menu.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_read_menu_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneReadMenu, context); +} + +bool nfc_scene_read_menu_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneReadMenu, context, event); +} + +void nfc_scene_read_menu_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneReadMenu, context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read_success.c b/applications/main/nfc/scenes/nfc_scene_read_success.c new file mode 100644 index 000000000000..5ceada48b5a9 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_read_success.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_read_success_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneReadSuccess, context); +} + +bool nfc_scene_read_success_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneReadSuccess, context, event); +} + +void nfc_scene_read_success_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneReadSuccess, context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original.c b/applications/main/nfc/scenes/nfc_scene_restore_original.c index 3ecf5c048e41..612e6041e69e 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original.c @@ -1,12 +1,12 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_restore_original_popup_callback(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); } void nfc_scene_restore_original_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Setup view Popup* popup = nfc->popup; @@ -20,17 +20,17 @@ void nfc_scene_restore_original_on_enter(void* context) { } bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + if(nfc_load_file(nfc, nfc->file_path, false)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneSavedMenu); } else { consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); + nfc->scene_manager, NfcSceneFileSelect); } } } @@ -38,7 +38,7 @@ bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) } void nfc_scene_restore_original_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clear view popup_reset(nfc->popup); diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c index 730dd41e858b..05a673e61ea3 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c @@ -1,17 +1,17 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_restore_original_confirm_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, result); } void nfc_scene_restore_original_confirm_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); - dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring_38x32); + dialog_ex_set_icon(dialog_ex, 5, 11, &I_ArrowC_1_36x36); dialog_ex_set_text( dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); @@ -23,16 +23,17 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) { } bool nfc_scene_restore_original_confirm_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultRight) { - if(!nfc_device_restore(nfc->dev, true)) { - scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); - } else { + if(nfc_delete_shadow_file(nfc)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginal); + } else { + scene_manager_search_and_switch_to_previous_scene( //TODO: maybe some better handling, for example showing 'Error' scene + nfc->scene_manager, + NfcSceneStart); } consumed = true; } else if(event.event == DialogExResultLeft) { @@ -46,7 +47,7 @@ bool nfc_scene_restore_original_confirm_on_event(void* context, SceneManagerEven } void nfc_scene_restore_original_confirm_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clean view dialog_ex_reset(nfc->dialog_ex); diff --git a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c index 5f4f7985e777..b80f1bdcc143 100644 --- a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c @@ -1,13 +1,13 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_retry_confirm_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, result); } void nfc_scene_retry_confirm_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_set_left_button_text(dialog_ex, "Retry"); @@ -22,15 +22,20 @@ void nfc_scene_retry_confirm_on_enter(void* context) { } bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultRight) { consumed = scene_manager_previous_scene(nfc->scene_manager); } else if(event.event == DialogExResultLeft) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneRead); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneDetect)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneDetect); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneRead)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneRead); + } } } else if(event.type == SceneManagerEventTypeBack) { consumed = true; @@ -40,7 +45,7 @@ bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_retry_confirm_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clean view dialog_ex_reset(nfc->dialog_ex); diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c index 00727422676c..1f7824fd7112 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_name.c +++ b/applications/main/nfc/scenes/nfc_scene_save_name.c @@ -1,75 +1,64 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" #include -#include -#include -#include void nfc_scene_save_name_text_input_callback(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); } void nfc_scene_save_name_on_enter(void* context) { - Nfc* nfc = context; - - // Setup view + NfcApp* nfc = context; + FuriString* folder_path = furi_string_alloc(); TextInput* text_input = nfc->text_input; - bool dev_name_empty = false; - if(!strcmp(nfc->dev->dev_name, "")) { - set_random_name(nfc->text_store, sizeof(nfc->text_store)); - dev_name_empty = true; + + bool name_is_empty = furi_string_empty(nfc->file_name); + if(name_is_empty) { + furi_string_set(nfc->file_path, NFC_APP_FOLDER); + set_random_name(nfc->text_store, NFC_TEXT_STORE_SIZE); + furi_string_set(folder_path, NFC_APP_FOLDER); } else { - nfc_text_store_set(nfc, nfc->dev->dev_name); + nfc_text_store_set(nfc, "%s", furi_string_get_cstr(nfc->file_name)); + path_extract_dirname(furi_string_get_cstr(nfc->file_path), folder_path); } + text_input_set_header_text(text_input, "Name the card"); text_input_set_result_callback( text_input, nfc_scene_save_name_text_input_callback, nfc, nfc->text_store, - NFC_DEV_NAME_MAX_LEN, - dev_name_empty); - - FuriString* folder_path; - folder_path = furi_string_alloc(); - - if(furi_string_end_with(nfc->dev->load_path, NFC_APP_EXTENSION)) { - path_extract_dirname(furi_string_get_cstr(nfc->dev->load_path), folder_path); - } else { - furi_string_set(folder_path, NFC_APP_FOLDER); - } + NFC_NAME_SIZE, + name_is_empty); ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - furi_string_get_cstr(folder_path), NFC_APP_EXTENSION, nfc->dev->dev_name); + furi_string_get_cstr(folder_path), + NFC_APP_EXTENSION, + furi_string_get_cstr(nfc->file_name)); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); - furi_string_free(folder_path); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); } bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventTextInputDone) { - if(strcmp(nfc->dev->dev_name, "") != 0) { - nfc_device_delete(nfc->dev, true); - } - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetUid)) { - nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; + if(!furi_string_empty(nfc->file_name)) { + nfc_delete(nfc); } - strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1); - if(nfc_save_file(nfc)) { + furi_string_set(nfc->file_name, nfc->text_store); + + if(nfc_save(nfc)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); - if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { - // Nothing, do not count editing as saving - } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddSave); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + dolphin_deed(DolphinDeedNfcAddSave); } else { - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); } consumed = true; } else { @@ -78,11 +67,12 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { } } } + return consumed; } void nfc_scene_save_name_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clear view void* validator_context = text_input_get_validator_callback_context(nfc->text_input); diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 34919cbd863a..0cb26c0d45af 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -1,12 +1,12 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_save_success_popup_callback(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); } void nfc_scene_save_success_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Setup view Popup* popup = nfc->popup; @@ -20,7 +20,7 @@ void nfc_scene_save_success_on_enter(void* context) { } bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { @@ -28,9 +28,6 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneMfClassicKeys); - } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneSavedMenu); } else { consumed = scene_manager_search_and_switch_to_another_scene( nfc->scene_manager, NfcSceneFileSelect); @@ -41,7 +38,7 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_save_success_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clear view popup_reset(nfc->popup); diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index ba1f96539848..d367e8ab8610 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -1,180 +1,13 @@ -#include "../nfc_i.h" -#include - -enum SubmenuIndex { - SubmenuIndexEmulate, - SubmenuIndexEditUid, - SubmenuIndexDetectReader, - SubmenuIndexWrite, - SubmenuIndexUpdate, - SubmenuIndexRename, - SubmenuIndexDelete, - SubmenuIndexInfo, - SubmenuIndexRestoreOriginal, - SubmenuIndexMfUlUnlockByReader, - SubmenuIndexMfUlUnlockByPassword, -}; - -void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} +#include "../helpers/protocol_support/nfc_protocol_support.h" void nfc_scene_saved_menu_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - if(nfc->dev->format == NfcDeviceSaveFormatUid || - nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) { - submenu_add_item( - submenu, - "Emulate UID", - SubmenuIndexEmulate, - nfc_scene_saved_menu_submenu_callback, - nfc); - if(nfc->dev->dev_data.protocol == NfcDeviceProtocolUnknown) { - submenu_add_item( - submenu, - "Edit UID", - SubmenuIndexEditUid, - nfc_scene_saved_menu_submenu_callback, - nfc); - } - } else if( - nfc->dev->format == NfcDeviceSaveFormatMifareUl || - nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { - submenu_add_item( - submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); - } - if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { - if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) { - submenu_add_item( - submenu, - "Detect Reader", - SubmenuIndexDetectReader, - nfc_scene_saved_menu_submenu_callback, - nfc); - } - submenu_add_item( - submenu, - "Write to Initial Card", - SubmenuIndexWrite, - nfc_scene_saved_menu_submenu_callback, - nfc); - submenu_add_item( - submenu, - "Update from Initial Card", - SubmenuIndexUpdate, - nfc_scene_saved_menu_submenu_callback, - nfc); - } - submenu_add_item( - submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); - if(nfc->dev->format == NfcDeviceSaveFormatMifareUl && - !mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) { - submenu_add_item( - submenu, - "Unlock with Reader", - SubmenuIndexMfUlUnlockByReader, - nfc_scene_saved_menu_submenu_callback, - nfc); - submenu_add_item( - submenu, - "Unlock with Password", - SubmenuIndexMfUlUnlockByPassword, - nfc_scene_saved_menu_submenu_callback, - nfc); - } - if(nfc->dev->shadow_file_exist) { - submenu_add_item( - submenu, - "Restore to original", - SubmenuIndexRestoreOriginal, - nfc_scene_saved_menu_submenu_callback, - nfc); - } - submenu_add_item( - submenu, "Rename", SubmenuIndexRename, nfc_scene_saved_menu_submenu_callback, nfc); - submenu_add_item( - submenu, "Delete", SubmenuIndexDelete, nfc_scene_saved_menu_submenu_callback, nfc); - submenu_set_selected_item( - nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSavedMenu)); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + nfc_protocol_support_on_enter(NfcProtocolSupportSceneSavedMenu, context); } bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - NfcDeviceData* dev_data = &nfc->dev->dev_data; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, event.event); - if(event.event == SubmenuIndexEmulate) { - if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); - } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); - } - DOLPHIN_DEED(DolphinDeedNfcEmulate); - consumed = true; - } else if(event.event == SubmenuIndexDetectReader) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); - DOLPHIN_DEED(DolphinDeedNfcDetectReader); - consumed = true; - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); - consumed = true; - } else if(event.event == SubmenuIndexUpdate) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdate); - consumed = true; - } else if(event.event == SubmenuIndexRename) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - } else if(event.event == SubmenuIndexEditUid) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); - consumed = true; - } else if(event.event == SubmenuIndexDelete) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDelete); - consumed = true; - } else if(event.event == SubmenuIndexInfo) { - bool application_info_present = false; - if(dev_data->protocol == NfcDeviceProtocolEMV) { - application_info_present = true; - } else if( - dev_data->protocol == NfcDeviceProtocolMifareClassic || - dev_data->protocol == NfcDeviceProtocolMifareUl) { - application_info_present = nfc_supported_card_verify_and_parse(dev_data); - } - - FURI_LOG_I("nfc", "application_info_present: %d", application_info_present); - - if(application_info_present) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); - } - consumed = true; - } else if(event.event == SubmenuIndexRestoreOriginal) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm); - consumed = true; - } else if(event.event == SubmenuIndexMfUlUnlockByReader) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); - consumed = true; - } else if(event.event == SubmenuIndexMfUlUnlockByPassword) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); - consumed = true; - } - } - - return consumed; + return nfc_protocol_support_on_event(NfcProtocolSupportSceneSavedMenu, context, event); } void nfc_scene_saved_menu_on_exit(void* context) { - Nfc* nfc = context; - - submenu_reset(nfc->submenu); + nfc_protocol_support_on_exit(NfcProtocolSupportSceneSavedMenu, context); } diff --git a/applications/main/nfc/scenes/nfc_scene_select_protocol.c b/applications/main/nfc/scenes/nfc_scene_select_protocol.c new file mode 100644 index 000000000000..502f91772097 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_select_protocol.c @@ -0,0 +1,67 @@ +#include "../nfc_app_i.h" + +void nfc_scene_select_protocol_submenu_callback(void* context, uint32_t index) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +void nfc_scene_select_protocol_on_enter(void* context) { + NfcApp* instance = context; + Submenu* submenu = instance->submenu; + + FuriString* temp_str = furi_string_alloc(); + const char* prefix; + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneExtraActions)) { + prefix = "Read"; + instance->protocols_detected_num = NfcProtocolNum; + for(size_t i = 0; i < NfcProtocolNum; i++) { + instance->protocols_detected[i] = i; + } + } else { + prefix = "Read as"; + submenu_set_header(submenu, "Multi-protocol card"); + notification_message(instance->notifications, &sequence_single_vibro); + } + + for(size_t i = 0; i < instance->protocols_detected_num; i++) { + furi_string_printf( + temp_str, + "%s %s", + prefix, + nfc_device_get_protocol_name(instance->protocols_detected[i])); + submenu_add_item( + submenu, + furi_string_get_cstr(temp_str), + i, + nfc_scene_select_protocol_submenu_callback, + instance); + } + furi_string_free(temp_str); + + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneSelectProtocol); + submenu_set_selected_item(submenu, state); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_select_protocol_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + instance->protocols_detected_idx = event.event; + scene_manager_next_scene(instance->scene_manager, NfcSceneRead); + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneSelectProtocol, event.event); + consumed = true; + } + return consumed; +} + +void nfc_scene_select_protocol_on_exit(void* context) { + NfcApp* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_set_atqa.c b/applications/main/nfc/scenes/nfc_scene_set_atqa.c index d079b3804754..f0b399b2c765 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_atqa.c +++ b/applications/main/nfc/scenes/nfc_scene_set_atqa.c @@ -1,34 +1,34 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_set_atqa_byte_input_callback(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone); } void nfc_scene_set_atqa_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; // Setup view - ByteInput* byte_input = nfc->byte_input; + ByteInput* byte_input = instance->byte_input; byte_input_set_header_text(byte_input, "Enter ATQA in hex"); byte_input_set_result_callback( byte_input, nfc_scene_set_atqa_byte_input_callback, NULL, - nfc, - nfc->dev->dev_data.nfc_data.atqa, + instance, + instance->iso14443_3a_edit_data->atqa, 2); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput); } bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* instance = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); consumed = true; } } @@ -36,9 +36,9 @@ bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_set_atqa_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; // Clear view - byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(nfc->byte_input, ""); + byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(instance->byte_input, ""); } diff --git a/applications/main/nfc/scenes/nfc_scene_set_sak.c b/applications/main/nfc/scenes/nfc_scene_set_sak.c index 60a1e1494b84..9968e3b1c6a8 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_sak.c +++ b/applications/main/nfc/scenes/nfc_scene_set_sak.c @@ -1,44 +1,45 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_set_sak_byte_input_callback(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone); } void nfc_scene_set_sak_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; // Setup view - ByteInput* byte_input = nfc->byte_input; + ByteInput* byte_input = instance->byte_input; byte_input_set_header_text(byte_input, "Enter SAK in hex"); byte_input_set_result_callback( byte_input, nfc_scene_set_sak_byte_input_callback, NULL, - nfc, - &nfc->dev->dev_data.nfc_data.sak, + instance, + &instance->iso14443_3a_edit_data->sak, 1); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput); } bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* instance = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqa); + scene_manager_next_scene(instance->scene_manager, NfcSceneSetAtqa); consumed = true; } } + return consumed; } void nfc_scene_set_sak_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; // Clear view - byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(nfc->byte_input, ""); + byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(instance->byte_input, ""); } diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index cadf2eb69ef3..6b8f2c3f92c5 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -1,68 +1,69 @@ -#include "../nfc_i.h" -#include "lib/nfc/helpers/nfc_generators.h" +#include "../nfc_app_i.h" enum SubmenuIndex { - SubmenuIndexNFCA4, - SubmenuIndexNFCA7, SubmenuIndexGeneratorsStart, + SubmenuIndexNFCA4 = NfcDataGeneratorTypeNum, + SubmenuIndexNFCA7, }; void nfc_scene_set_type_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; + NfcApp* instance = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); + view_dispatcher_send_custom_event(instance->view_dispatcher, index); } void nfc_scene_set_type_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - // Clear device name - nfc_device_set_name(nfc->dev, ""); - furi_string_set(nfc->dev->load_path, ""); + NfcApp* instance = context; + + Submenu* submenu = instance->submenu; submenu_add_item( - submenu, "NFC-A 7-bytes UID", SubmenuIndexNFCA7, nfc_scene_set_type_submenu_callback, nfc); + submenu, + "NFC-A 7-bytes UID", + SubmenuIndexNFCA7, + nfc_scene_set_type_submenu_callback, + instance); submenu_add_item( - submenu, "NFC-A 4-bytes UID", SubmenuIndexNFCA4, nfc_scene_set_type_submenu_callback, nfc); + submenu, + "NFC-A 4-bytes UID", + SubmenuIndexNFCA4, + nfc_scene_set_type_submenu_callback, + instance); - // Generators - int i = SubmenuIndexGeneratorsStart; - for(const NfcGenerator* const* generator = nfc_generators; *generator != NULL; - ++generator, ++i) { - submenu_add_item(submenu, (*generator)->name, i, nfc_scene_set_type_submenu_callback, nfc); + for(size_t i = 0; i < NfcDataGeneratorTypeNum; i++) { + const char* name = nfc_data_generator_get_name(i); + submenu_add_item(submenu, name, i, nfc_scene_set_type_submenu_callback, instance); } - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); } bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* instance = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexNFCA7) { - nfc->dev->dev_data.nfc_data.uid_len = 7; - nfc->dev->format = NfcDeviceSaveFormatUid; - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); + instance->iso14443_3a_edit_data->uid_len = 7; + scene_manager_next_scene(instance->scene_manager, NfcSceneSetSak); consumed = true; } else if(event.event == SubmenuIndexNFCA4) { - nfc->dev->dev_data.nfc_data.uid_len = 4; - nfc->dev->format = NfcDeviceSaveFormatUid; - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); + instance->iso14443_3a_edit_data->uid_len = 4; + scene_manager_next_scene(instance->scene_manager, NfcSceneSetSak); consumed = true; } else { - nfc_device_clear(nfc->dev); - nfc->generator = nfc_generators[event.event - SubmenuIndexGeneratorsStart]; - nfc->generator->generator_func(&nfc->dev->dev_data); - - scene_manager_next_scene(nfc->scene_manager, NfcSceneGenerateInfo); + nfc_data_generator_fill_data(event.event, instance->nfc_device); + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneGenerateInfo, event.event); + scene_manager_next_scene(instance->scene_manager, NfcSceneGenerateInfo); consumed = true; } } + return consumed; } void nfc_scene_set_type_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; - submenu_reset(nfc->submenu); + submenu_reset(instance->submenu); } diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index 54606b68eec7..ea10baf79a23 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -1,42 +1,47 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_set_uid_byte_input_callback(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone); } void nfc_scene_set_uid_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; + + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu)) { + nfc_device_copy_data( + instance->nfc_device, NfcProtocolIso14443_3a, instance->iso14443_3a_edit_data); + } // Setup view - ByteInput* byte_input = nfc->byte_input; + ByteInput* byte_input = instance->byte_input; byte_input_set_header_text(byte_input, "Enter UID in hex"); - nfc->dev_edit_data = nfc->dev->dev_data.nfc_data; byte_input_set_result_callback( byte_input, nfc_scene_set_uid_byte_input_callback, NULL, - nfc, - nfc->dev_edit_data.uid, - nfc->dev_edit_data.uid_len); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); + instance, + instance->iso14443_3a_edit_data->uid, + instance->iso14443_3a_edit_data->uid_len); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput); } bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = (Nfc*)context; + NfcApp* instance = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { - nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; - if(nfc_save_file(nfc)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + nfc_device_set_data( + instance->nfc_device, NfcProtocolIso14443_3a, instance->iso14443_3a_edit_data); + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu)) { + if(nfc_save(instance)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess); consumed = true; } } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName); consumed = true; } } @@ -46,9 +51,9 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_set_uid_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* instance = context; // Clear view - byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(nfc->byte_input, ""); + byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(instance->byte_input, ""); } diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index a01f871ab6ad..e03e5e31ada4 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -1,5 +1,4 @@ -#include "../nfc_i.h" -#include "nfc_worker_i.h" +#include "../nfc_app_i.h" #include enum SubmenuIndex { @@ -12,15 +11,19 @@ enum SubmenuIndex { }; void nfc_scene_start_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, index); } void nfc_scene_start_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; Submenu* submenu = nfc->submenu; + // Clear file name and device contents + furi_string_reset(nfc->file_name); + nfc_device_clear(nfc->nfc_device); + submenu_add_item(submenu, "Read", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); submenu_add_item( submenu, "Detect Reader", SubmenuIndexDetectReader, nfc_scene_start_submenu_callback, nfc); @@ -38,32 +41,23 @@ void nfc_scene_start_on_enter(void* context) { submenu_set_selected_item( submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart)); - nfc_device_clear(nfc->dev); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); - nfc->dev->dev_data.read_mode = NfcReadModeAuto; - scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); - DOLPHIN_DEED(DolphinDeedNfcRead); + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetect); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } else if(event.event == SubmenuIndexDetectReader) { scene_manager_set_scene_state( nfc->scene_manager, NfcSceneStart, SubmenuIndexDetectReader); - bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; - if(sd_exist) { - nfc_device_data_clear(&nfc->dev->dev_data); - scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); - DOLPHIN_DEED(DolphinDeedNfcDetectReader); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); - } + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); consumed = true; } else if(event.event == SubmenuIndexSaved) { // Save the scene state explicitly in each branch, so that @@ -92,7 +86,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_start_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; submenu_reset(nfc->submenu); } diff --git a/applications/main/nfc/scenes/nfc_scene_supported_card.c b/applications/main/nfc/scenes/nfc_scene_supported_card.c new file mode 100644 index 000000000000..cea55b783bcb --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_supported_card.c @@ -0,0 +1,50 @@ +#include "nfc/nfc_app_i.h" + +#include "nfc/helpers/nfc_supported_cards.h" +#include "nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h" + +void nfc_scene_supported_card_on_enter(void* context) { + NfcApp* instance = context; + + FuriString* temp_str = furi_string_alloc(); + + if(nfc_supported_cards_parse(instance->nfc_device, temp_str)) { + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "More", + nfc_protocol_support_common_widget_callback, + instance); + + scene_manager_set_scene_state(instance->scene_manager, NfcSceneSupportedCard, true); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); + + } else { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneSupportedCard, false); + scene_manager_next_scene(instance->scene_manager, NfcSceneInfo); + } + + furi_string_free(temp_str); +} + +bool nfc_scene_supported_card_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneInfo); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_supported_card_on_exit(void* context) { + NfcApp* instance = context; + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c index e5951beb2674..ebcda7caf1b7 100644 --- a/applications/main/nfc/views/detect_reader.c +++ b/applications/main/nfc/views/detect_reader.c @@ -163,7 +163,10 @@ void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t no with_view_model( detect_reader->view, DetectReaderViewModel * model, - { model->nonces = nonces_collected; }, + { + model->nonces = nonces_collected; + model->state = DetectReaderStateReaderDetected; + }, false); } diff --git a/applications/main/nfc/views/dict_attack.c b/applications/main/nfc/views/dict_attack.c index 8f4bd063e8b8..b4a21bc57e13 100644 --- a/applications/main/nfc/views/dict_attack.c +++ b/applications/main/nfc/views/dict_attack.c @@ -2,42 +2,34 @@ #include -typedef enum { - DictAttackStateRead, - DictAttackStateCardRemoved, -} DictAttackState; - struct DictAttack { View* view; DictAttackCallback callback; void* context; - bool card_present; }; typedef struct { - DictAttackState state; - MfClassicType type; FuriString* header; + bool card_detected; uint8_t sectors_total; uint8_t sectors_read; - uint8_t sector_current; - uint8_t keys_total; + uint8_t current_sector; uint8_t keys_found; - uint16_t dict_keys_total; - uint16_t dict_keys_current; + size_t dict_keys_total; + size_t dict_keys_current; bool is_key_attack; uint8_t key_attack_current_sector; } DictAttackViewModel; static void dict_attack_draw_callback(Canvas* canvas, void* model) { DictAttackViewModel* m = model; - if(m->state == DictAttackStateCardRemoved) { + if(!m->card_detected) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!"); canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned( canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); - } else if(m->state == DictAttackStateRead) { + } else { char draw_str[32] = {}; canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( @@ -49,14 +41,14 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { "Reuse key check for sector: %d", m->key_attack_current_sector); } else { - snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current); + snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->current_sector); } canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); float dict_progress = m->dict_keys_total == 0 ? 0 : (float)(m->dict_keys_current) / (float)(m->dict_keys_total); float progress = m->sectors_total == 0 ? 0 : - ((float)(m->sector_current) + dict_progress) / + ((float)(m->current_sector) + dict_progress) / (float)(m->sectors_total); if(progress > 1.0) { progress = 1.0; @@ -70,7 +62,8 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { } elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str); canvas_set_font(canvas, FontSecondary); - snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); + snprintf( + draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->sectors_total * 2); canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str); snprintf( draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total); @@ -80,55 +73,56 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { } static bool dict_attack_input_callback(InputEvent* event, void* context) { - DictAttack* dict_attack = context; + DictAttack* instance = context; bool consumed = false; + if(event->type == InputTypeShort && event->key == InputKeyOk) { - if(dict_attack->callback) { - dict_attack->callback(dict_attack->context); + if(instance->callback) { + instance->callback(DictAttackEventSkipPressed, instance->context); } consumed = true; } + return consumed; } DictAttack* dict_attack_alloc() { - DictAttack* dict_attack = malloc(sizeof(DictAttack)); - dict_attack->view = view_alloc(); - view_allocate_model(dict_attack->view, ViewModelTypeLocking, sizeof(DictAttackViewModel)); - view_set_draw_callback(dict_attack->view, dict_attack_draw_callback); - view_set_input_callback(dict_attack->view, dict_attack_input_callback); - view_set_context(dict_attack->view, dict_attack); + DictAttack* instance = malloc(sizeof(DictAttack)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DictAttackViewModel)); + view_set_draw_callback(instance->view, dict_attack_draw_callback); + view_set_input_callback(instance->view, dict_attack_input_callback); + view_set_context(instance->view, instance); with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, { model->header = furi_string_alloc(); }, false); - return dict_attack; + + return instance; } -void dict_attack_free(DictAttack* dict_attack) { - furi_assert(dict_attack); +void dict_attack_free(DictAttack* instance) { + furi_assert(instance); + with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { furi_string_free(model->header); }, - false); - view_free(dict_attack->view); - free(dict_attack); + instance->view, DictAttackViewModel * model, { furi_string_free(model->header); }, false); + + view_free(instance->view); + free(instance); } -void dict_attack_reset(DictAttack* dict_attack) { - furi_assert(dict_attack); +void dict_attack_reset(DictAttack* instance) { + furi_assert(instance); + with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, { - model->state = DictAttackStateRead; - model->type = MfClassicType1k; + model->card_detected = false; model->sectors_total = 0; model->sectors_read = 0; - model->sector_current = 0; - model->keys_total = 0; + model->current_sector = 0; model->keys_found = 0; model->dict_keys_total = 0; model->dict_keys_current = 0; @@ -138,152 +132,108 @@ void dict_attack_reset(DictAttack* dict_attack) { false); } -View* dict_attack_get_view(DictAttack* dict_attack) { - furi_assert(dict_attack); - return dict_attack->view; +View* dict_attack_get_view(DictAttack* instance) { + furi_assert(instance); + + return instance->view; } -void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) { - furi_assert(dict_attack); +void dict_attack_set_callback(DictAttack* instance, DictAttackCallback callback, void* context) { + furi_assert(instance); furi_assert(callback); - dict_attack->callback = callback; - dict_attack->context = context; + + instance->callback = callback; + instance->context = context; } -void dict_attack_set_header(DictAttack* dict_attack, const char* header) { - furi_assert(dict_attack); +void dict_attack_set_header(DictAttack* instance, const char* header) { + furi_assert(instance); furi_assert(header); with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, { furi_string_set(model->header, header); }, true); } -void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) { - furi_assert(dict_attack); - dict_attack->card_present = true; +void dict_attack_set_card_state(DictAttack* instance, bool detected) { + furi_assert(instance); + with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - model->state = DictAttackStateRead; - model->sectors_total = mf_classic_get_total_sectors_num(type); - model->keys_total = model->sectors_total * 2; - }, - true); + instance->view, DictAttackViewModel * model, { model->card_detected = detected; }, true); } -void dict_attack_set_card_removed(DictAttack* dict_attack) { - furi_assert(dict_attack); - dict_attack->card_present = false; +void dict_attack_set_sectors_total(DictAttack* instance, uint8_t sectors_total) { + furi_assert(instance); + with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, - { model->state = DictAttackStateCardRemoved; }, + { model->sectors_total = sectors_total; }, true); } -bool dict_attack_get_card_state(DictAttack* dict_attack) { - furi_assert(dict_attack); - return dict_attack->card_present; -} +void dict_attack_set_sectors_read(DictAttack* instance, uint8_t sectors_read) { + furi_assert(instance); -void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) { - furi_assert(dict_attack); with_view_model( - dict_attack->view, DictAttackViewModel * model, { model->sectors_read = sec_read; }, true); + instance->view, DictAttackViewModel * model, { model->sectors_read = sectors_read; }, true); } -void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true); -} +void dict_attack_set_keys_found(DictAttack* instance, uint8_t keys_found) { + furi_assert(instance); -void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { - furi_assert(dict_attack); with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - model->sector_current = curr_sec; - model->dict_keys_current = 0; - }, - true); + instance->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true); } -void dict_attack_inc_current_sector(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - if(model->sector_current < model->sectors_total) { - model->sector_current++; - model->dict_keys_current = 0; - } - }, - true); -} +void dict_attack_set_current_sector(DictAttack* instance, uint8_t current_sector) { + furi_assert(instance); -void dict_attack_inc_keys_found(DictAttack* dict_attack) { - furi_assert(dict_attack); with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, - { - if(model->keys_found < model->keys_total) { - model->keys_found++; - } - }, + { model->current_sector = current_sector; }, true); } -void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) { - furi_assert(dict_attack); +void dict_attack_set_total_dict_keys(DictAttack* instance, size_t dict_keys_total) { + furi_assert(instance); + with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, { model->dict_keys_total = dict_keys_total; }, true); } -void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) { - furi_assert(dict_attack); +void dict_attack_set_current_dict_key(DictAttack* instance, size_t cur_key_num) { + furi_assert(instance); + with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, - { - if(model->dict_keys_current + keys_tried < model->dict_keys_total) { - model->dict_keys_current += keys_tried; - } - }, + { model->dict_keys_current = cur_key_num; }, true); } -void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) { - furi_assert(dict_attack); +void dict_attack_set_key_attack(DictAttack* instance, uint8_t sector) { + furi_assert(instance); + with_view_model( - dict_attack->view, + instance->view, DictAttackViewModel * model, { - model->is_key_attack = is_key_attack; + model->is_key_attack = true; model->key_attack_current_sector = sector; }, true); } -void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) { - furi_assert(dict_attack); +void dict_attack_reset_key_attack(DictAttack* instance) { + furi_assert(instance); + with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - if(model->key_attack_current_sector < model->sectors_total) { - model->key_attack_current_sector++; - } - }, - true); + instance->view, DictAttackViewModel * model, { model->is_key_attack = false; }, true); } diff --git a/applications/main/nfc/views/dict_attack.h b/applications/main/nfc/views/dict_attack.h index 73b98a1b827b..54a0220fe590 100644 --- a/applications/main/nfc/views/dict_attack.h +++ b/applications/main/nfc/views/dict_attack.h @@ -1,46 +1,50 @@ #pragma once + #include #include -#include -#include +#ifdef __cplusplus +extern "C" { +#endif typedef struct DictAttack DictAttack; -typedef void (*DictAttackCallback)(void* context); - -DictAttack* dict_attack_alloc(); +typedef enum { + DictAttackEventSkipPressed, +} DictAttackEvent; -void dict_attack_free(DictAttack* dict_attack); +typedef void (*DictAttackCallback)(DictAttackEvent event, void* context); -void dict_attack_reset(DictAttack* dict_attack); +DictAttack* dict_attack_alloc(); -View* dict_attack_get_view(DictAttack* dict_attack); +void dict_attack_free(DictAttack* instance); -void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context); +void dict_attack_reset(DictAttack* instance); -void dict_attack_set_header(DictAttack* dict_attack, const char* header); +View* dict_attack_get_view(DictAttack* instance); -void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type); +void dict_attack_set_callback(DictAttack* instance, DictAttackCallback callback, void* context); -void dict_attack_set_card_removed(DictAttack* dict_attack); +void dict_attack_set_header(DictAttack* instance, const char* header); -bool dict_attack_get_card_state(DictAttack* dict_attack); +void dict_attack_set_card_state(DictAttack* instance, bool detected); -void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read); +void dict_attack_set_sectors_total(DictAttack* instance, uint8_t sectors_total); -void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found); +void dict_attack_set_sectors_read(DictAttack* instance, uint8_t sectors_read); -void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec); +void dict_attack_set_keys_found(DictAttack* instance, uint8_t keys_found); -void dict_attack_inc_current_sector(DictAttack* dict_attack); +void dict_attack_set_current_sector(DictAttack* instance, uint8_t curr_sec); -void dict_attack_inc_keys_found(DictAttack* dict_attack); +void dict_attack_set_total_dict_keys(DictAttack* instance, size_t dict_keys_total); -void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total); +void dict_attack_set_current_dict_key(DictAttack* instance, size_t cur_key_num); -void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried); +void dict_attack_set_key_attack(DictAttack* instance, uint8_t sector); -void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector); +void dict_attack_reset_key_attack(DictAttack* instance); -void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack); \ No newline at end of file +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc_old/application.fam b/applications/main/nfc_old/application.fam new file mode 100644 index 000000000000..9cc346abe5f8 --- /dev/null +++ b/applications/main/nfc_old/application.fam @@ -0,0 +1,21 @@ +App( + appid="nfc_old", + name="NFC", + apptype=FlipperAppType.MENUEXTERNAL, + targets=["f7"], + entry_point="nfc_app", + icon="A_NFC_14", + stack_size=5 * 1024, + order=30, + fap_libs=["assets"], + fap_icon="icon.png", + fap_category="NFC", +) + +App( + appid="nfc_old_start", + targets=["f7"], + apptype=FlipperAppType.STARTUP, + entry_point="nfc_on_system_start", + order=30, +) diff --git a/applications/main/nfc_old/helpers/nfc_custom_event.h b/applications/main/nfc_old/helpers/nfc_custom_event.h new file mode 100644 index 000000000000..aa932a3d857d --- /dev/null +++ b/applications/main/nfc_old/helpers/nfc_custom_event.h @@ -0,0 +1,17 @@ +#pragma once + +enum NfcCustomEvent { + // Reserve first 100 events for button types and indexes, starting from 0 + NfcCustomEventReserved = 100, + + NfcCustomEventViewExit, + NfcCustomEventWorkerExit, + NfcCustomEventByteInputDone, + NfcCustomEventTextInputDone, + NfcCustomEventDictAttackDone, + NfcCustomEventDictAttackSkip, + NfcCustomEventRpcLoad, + NfcCustomEventRpcSessionClose, + NfcCustomEventUpdateLog, + NfcCustomEventSaveShadow, +}; diff --git a/applications/main/nfc_old/helpers/nfc_emv_parser.c b/applications/main/nfc_old/helpers/nfc_emv_parser.c new file mode 100644 index 000000000000..30e102405e07 --- /dev/null +++ b/applications/main/nfc_old/helpers/nfc_emv_parser.c @@ -0,0 +1,82 @@ +#include "nfc_emv_parser.h" +#include + +static const char* nfc_resources_header = "Flipper EMV resources"; +static const uint32_t nfc_resources_file_version = 1; + +static bool nfc_emv_parser_search_data( + Storage* storage, + const char* file_name, + FuriString* key, + FuriString* data) { + bool parsed = false; + FlipperFormat* file = flipper_format_file_alloc(storage); + FuriString* temp_str; + temp_str = furi_string_alloc(); + + do { + // Open file + if(!flipper_format_file_open_existing(file, file_name)) break; + // Read file header and version + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, nfc_resources_header) || + (version != nfc_resources_file_version)) + break; + if(!flipper_format_read_string(file, furi_string_get_cstr(key), data)) break; + parsed = true; + } while(false); + + furi_string_free(temp_str); + flipper_format_free(file); + return parsed; +} + +bool nfc_emv_parser_get_aid_name( + Storage* storage, + uint8_t* aid, + uint8_t aid_len, + FuriString* aid_name) { + furi_assert(storage); + bool parsed = false; + FuriString* key; + key = furi_string_alloc(); + for(uint8_t i = 0; i < aid_len; i++) { + furi_string_cat_printf(key, "%02X", aid[i]); + } + if(nfc_emv_parser_search_data(storage, EXT_PATH("nfc/assets/aid.nfc"), key, aid_name)) { + parsed = true; + } + furi_string_free(key); + return parsed; +} + +bool nfc_emv_parser_get_country_name( + Storage* storage, + uint16_t country_code, + FuriString* country_name) { + bool parsed = false; + FuriString* key; + key = furi_string_alloc_printf("%04X", country_code); + if(nfc_emv_parser_search_data( + storage, EXT_PATH("nfc/assets/country_code.nfc"), key, country_name)) { + parsed = true; + } + furi_string_free(key); + return parsed; +} + +bool nfc_emv_parser_get_currency_name( + Storage* storage, + uint16_t currency_code, + FuriString* currency_name) { + bool parsed = false; + FuriString* key; + key = furi_string_alloc_printf("%04X", currency_code); + if(nfc_emv_parser_search_data( + storage, EXT_PATH("nfc/assets/currency_code.nfc"), key, currency_name)) { + parsed = true; + } + furi_string_free(key); + return parsed; +} diff --git a/applications/main/nfc_old/helpers/nfc_emv_parser.h b/applications/main/nfc_old/helpers/nfc_emv_parser.h new file mode 100644 index 000000000000..abe57f470fd1 --- /dev/null +++ b/applications/main/nfc_old/helpers/nfc_emv_parser.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +/** Get EMV application name by number + * @param storage Storage instance + * @param aid - AID number array + * @param aid_len - AID length + * @param aid_name - string to keep AID name + * @return - true if AID found, false otherwies + */ +bool nfc_emv_parser_get_aid_name( + Storage* storage, + uint8_t* aid, + uint8_t aid_len, + FuriString* aid_name); + +/** Get country name by country code + * @param storage Storage instance + * @param country_code - ISO 3166 country code + * @param country_name - string to keep country name + * @return - true if country found, false otherwies + */ +bool nfc_emv_parser_get_country_name( + Storage* storage, + uint16_t country_code, + FuriString* country_name); + +/** Get currency name by currency code + * @param storage Storage instance + * @param currency_code - ISO 3166 currency code + * @param currency_name - string to keep currency name + * @return - true if currency found, false otherwies + */ +bool nfc_emv_parser_get_currency_name( + Storage* storage, + uint16_t currency_code, + FuriString* currency_name); diff --git a/applications/main/nfc_old/icon.png b/applications/main/nfc_old/icon.png new file mode 100644 index 000000000000..6bc027111a75 Binary files /dev/null and b/applications/main/nfc_old/icon.png differ diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc_old/nfc.c similarity index 96% rename from applications/main/nfc/nfc.c rename to applications/main/nfc_old/nfc.c index 4540f5d9f061..56d98a8c61aa 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc_old/nfc.c @@ -286,15 +286,18 @@ int32_t nfc_app(void* p) { if(nfc_device_load(nfc->dev, p, true)) { if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } } else { // Exit app diff --git a/applications/main/nfc/nfc.h b/applications/main/nfc_old/nfc.h similarity index 100% rename from applications/main/nfc/nfc.h rename to applications/main/nfc_old/nfc.h diff --git a/applications/main/nfc_old/nfc_cli.c b/applications/main/nfc_old/nfc_cli.c new file mode 100644 index 000000000000..0b7e75475483 --- /dev/null +++ b/applications/main/nfc_old/nfc_cli.c @@ -0,0 +1,213 @@ +#include +#include +#include +#include +#include + +#include +#include + +static void nfc_cli_print_usage() { + printf("Usage:\r\n"); + printf("nfc \r\n"); + printf("Cmd list:\r\n"); + printf("\tdetect\t - detect nfc device\r\n"); + printf("\temulate\t - emulate predefined nfca card\r\n"); + printf("\tapdu\t - Send APDU and print response \r\n"); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + printf("\tfield\t - turn field on\r\n"); + } +} + +static void nfc_cli_detect(Cli* cli, FuriString* args) { + UNUSED(args); + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + FuriHalNfcDevData dev_data = {}; + bool cmd_exit = false; + furi_hal_nfc_exit_sleep(); + printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n"); + while(!cmd_exit) { + cmd_exit |= cli_cmd_interrupt_received(cli); + if(furi_hal_nfc_detect(&dev_data, 400)) { + printf("Found: %s ", nfc_get_dev_type(dev_data.type)); + printf("UID length: %d, UID:", dev_data.uid_len); + for(size_t i = 0; i < dev_data.uid_len; i++) { + printf("%02X", dev_data.uid[i]); + } + printf("\r\n"); + break; + } + furi_hal_nfc_sleep(); + furi_delay_ms(50); + } + furi_hal_nfc_sleep(); +} + +static void nfc_cli_emulate(Cli* cli, FuriString* args) { + UNUSED(args); + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + furi_hal_nfc_exit_sleep(); + printf("Emulating NFC-A Type: T2T UID: 36 9C E7 B1 0A C1 34 SAK: 00 ATQA: 00/44\r\n"); + printf("Press Ctrl+C to abort\r\n"); + + FuriHalNfcDevData params = { + .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, + .uid_len = 7, + .atqa = {0x44, 0x00}, + .sak = 0x00, + .type = FuriHalNfcTypeA, + }; + + while(!cli_cmd_interrupt_received(cli)) { + if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 100)) { + printf("Reader detected\r\n"); + furi_hal_nfc_sleep(); + } + furi_delay_ms(50); + } + furi_hal_nfc_sleep(); +} + +static void nfc_cli_field(Cli* cli, FuriString* args) { + UNUSED(args); + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_field_on(); + + printf("Field is on. Don't leave device in this mode for too long.\r\n"); + printf("Press Ctrl+C to abort\r\n"); + + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(50); + } + + furi_hal_nfc_field_off(); + furi_hal_nfc_sleep(); +} + +static void nfc_cli_apdu(Cli* cli, FuriString* args) { + UNUSED(cli); + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + furi_hal_nfc_exit_sleep(); + FuriString* data = NULL; + data = furi_string_alloc(); + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData dev_data = {}; + uint8_t* req_buffer = NULL; + uint8_t* resp_buffer = NULL; + size_t apdu_size = 0; + size_t resp_size = 0; + + do { + if(!args_read_string_and_trim(args, data)) { + printf( + "Use like `nfc apdu 00a404000e325041592e5359532e444446303100 00a4040008a0000003010102` \r\n"); + break; + } + + printf("detecting tag\r\n"); + if(!furi_hal_nfc_detect(&dev_data, 300)) { + printf("Failed to detect tag\r\n"); + break; + } + do { + apdu_size = furi_string_size(data) / 2; + req_buffer = malloc(apdu_size); + hex_chars_to_uint8(furi_string_get_cstr(data), req_buffer); + + memcpy(tx_rx.tx_data, req_buffer, apdu_size); + tx_rx.tx_bits = apdu_size * 8; + tx_rx.tx_rx_type = FuriHalNfcTxRxTypeDefault; + + printf("Sending APDU:%s to Tag\r\n", furi_string_get_cstr(data)); + if(!furi_hal_nfc_tx_rx(&tx_rx, 300)) { + printf("Failed to tx_rx\r\n"); + break; + } + resp_size = (tx_rx.rx_bits / 8) * 2; + if(!resp_size) { + printf("No response\r\n"); + break; + } + resp_buffer = malloc(resp_size); + uint8_to_hex_chars(tx_rx.rx_data, resp_buffer, resp_size); + resp_buffer[resp_size] = 0; + printf("Response: %s\r\n", resp_buffer); + free(req_buffer); + free(resp_buffer); + req_buffer = NULL; + resp_buffer = NULL; + } while(args_read_string_and_trim(args, data)); + } while(false); + + free(req_buffer); + free(resp_buffer); + furi_string_free(data); + furi_hal_nfc_sleep(); +} + +static void nfc_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + FuriString* cmd; + cmd = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + nfc_cli_print_usage(); + break; + } + if(furi_string_cmp_str(cmd, "detect") == 0) { + nfc_cli_detect(cli, args); + break; + } + if(furi_string_cmp_str(cmd, "emulate") == 0) { + nfc_cli_emulate(cli, args); + break; + } + + if(furi_string_cmp_str(cmd, "apdu") == 0) { + nfc_cli_apdu(cli, args); + break; + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + if(furi_string_cmp_str(cmd, "field") == 0) { + nfc_cli_field(cli, args); + break; + } + } + + nfc_cli_print_usage(); + } while(false); + + furi_string_free(cmd); +} + +void nfc_on_system_start() { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL); + furi_record_close(RECORD_CLI); +#else + UNUSED(nfc_cli); +#endif +} diff --git a/applications/main/nfc/nfc_i.h b/applications/main/nfc_old/nfc_i.h similarity index 100% rename from applications/main/nfc/nfc_i.h rename to applications/main/nfc_old/nfc_i.h diff --git a/applications/main/nfc_old/scenes/nfc_scene.c b/applications/main/nfc_old/scenes/nfc_scene.c new file mode 100644 index 000000000000..652c8f8cc65c --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene.c @@ -0,0 +1,30 @@ +#include "nfc_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const nfc_on_enter_handlers[])(void*) = { +#include "nfc_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const nfc_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "nfc_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const nfc_on_exit_handlers[])(void* context) = { +#include "nfc_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers nfc_scene_handlers = { + .on_enter_handlers = nfc_on_enter_handlers, + .on_event_handlers = nfc_on_event_handlers, + .on_exit_handlers = nfc_on_exit_handlers, + .scene_num = NfcSceneNum, +}; diff --git a/applications/main/nfc_old/scenes/nfc_scene.h b/applications/main/nfc_old/scenes/nfc_scene.h new file mode 100644 index 000000000000..3e32224183ca --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) NfcScene##id, +typedef enum { +#include "nfc_scene_config.h" + NfcSceneNum, +} NfcScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers nfc_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "nfc_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "nfc_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "nfc_scene_config.h" +#undef ADD_SCENE diff --git a/applications/main/nfc_old/scenes/nfc_scene_config.h b/applications/main/nfc_old/scenes/nfc_scene_config.h new file mode 100644 index 000000000000..f11d1479838e --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_config.h @@ -0,0 +1,72 @@ +ADD_SCENE(nfc, start, Start) +ADD_SCENE(nfc, read, Read) +ADD_SCENE(nfc, saved_menu, SavedMenu) +ADD_SCENE(nfc, extra_actions, ExtraActions) +ADD_SCENE(nfc, set_type, SetType) +ADD_SCENE(nfc, set_sak, SetSak) +ADD_SCENE(nfc, set_atqa, SetAtqa) +ADD_SCENE(nfc, set_uid, SetUid) +ADD_SCENE(nfc, generate_info, GenerateInfo) +ADD_SCENE(nfc, read_card_success, ReadCardSuccess) +ADD_SCENE(nfc, save_name, SaveName) +ADD_SCENE(nfc, save_success, SaveSuccess) +ADD_SCENE(nfc, file_select, FileSelect) +ADD_SCENE(nfc, emulate_uid, EmulateUid) +ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) +ADD_SCENE(nfc, nfca_menu, NfcaMenu) +ADD_SCENE(nfc, nfcv_menu, NfcVMenu) +ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu) +ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) +ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock) +ADD_SCENE(nfc, nfcv_emulate, NfcVEmulate) +ADD_SCENE(nfc, nfcv_sniff, NfcVSniff) +ADD_SCENE(nfc, nfcv_read_success, NfcVReadSuccess) +ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) +ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) +ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) +ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate) +ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth) +ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult) +ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) +ADD_SCENE(nfc, mf_ultralight_unlock_auto, MfUltralightUnlockAuto) +ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) +ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) +ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) +ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu) +ADD_SCENE(nfc, mf_desfire_data, MfDesfireData) +ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) +ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess) +ADD_SCENE(nfc, mf_classic_data, MfClassicData) +ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu) +ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) +ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) +ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) +ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) +ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete) +ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate) +ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) +ADD_SCENE(nfc, mf_classic_write, MfClassicWrite) +ADD_SCENE(nfc, mf_classic_write_success, MfClassicWriteSuccess) +ADD_SCENE(nfc, mf_classic_write_fail, MfClassicWriteFail) +ADD_SCENE(nfc, mf_classic_update, MfClassicUpdate) +ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess) +ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) +ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) +ADD_SCENE(nfc, emv_menu, EmvMenu) +ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) +ADD_SCENE(nfc, device_info, DeviceInfo) +ADD_SCENE(nfc, delete, Delete) +ADD_SCENE(nfc, delete_success, DeleteSuccess) +ADD_SCENE(nfc, restore_original_confirm, RestoreOriginalConfirm) +ADD_SCENE(nfc, restore_original, RestoreOriginal) +ADD_SCENE(nfc, debug, Debug) +ADD_SCENE(nfc, field, Field) +ADD_SCENE(nfc, dict_not_found, DictNotFound) +ADD_SCENE(nfc, rpc, Rpc) +ADD_SCENE(nfc, exit_confirm, ExitConfirm) +ADD_SCENE(nfc, retry_confirm, RetryConfirm) +ADD_SCENE(nfc, detect_reader, DetectReader) +ADD_SCENE(nfc, mfkey_nonces_info, MfkeyNoncesInfo) +ADD_SCENE(nfc, mfkey_complete, MfkeyComplete) +ADD_SCENE(nfc, nfc_data_info, NfcDataInfo) +ADD_SCENE(nfc, read_card_type, ReadCardType) diff --git a/applications/main/nfc_old/scenes/nfc_scene_debug.c b/applications/main/nfc_old/scenes/nfc_scene_debug.c new file mode 100644 index 000000000000..ed079c2ed9aa --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_debug.c @@ -0,0 +1,54 @@ +#include "../nfc_i.h" + +enum SubmenuDebugIndex { + SubmenuDebugIndexField, + SubmenuDebugIndexApdu, +}; + +void nfc_scene_debug_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_debug_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, "Field", SubmenuDebugIndexField, nfc_scene_debug_submenu_callback, nfc); + submenu_add_item( + submenu, "Apdu", SubmenuDebugIndexApdu, nfc_scene_debug_submenu_callback, nfc); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDebug)); + + nfc_device_clear(nfc->dev); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuDebugIndexField) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneDebug, SubmenuDebugIndexField); + scene_manager_next_scene(nfc->scene_manager, NfcSceneField); + consumed = true; + } else if(event.event == SubmenuDebugIndexApdu) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneDebug, SubmenuDebugIndexApdu); + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateApduSequence); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_debug_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_delete.c b/applications/main/nfc_old/scenes/nfc_scene_delete.c new file mode 100644 index 000000000000..0808db45a321 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_delete.c @@ -0,0 +1,82 @@ +#include "../nfc_i.h" + +void nfc_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_delete_on_enter(void* context) { + Nfc* nfc = context; + FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; + + // Setup Custom Widget view + FuriString* temp_str; + temp_str = furi_string_alloc(); + + furi_string_printf(temp_str, "\e#Delete %s?\e#", nfc->dev->dev_name); + widget_add_text_box_element( + nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, furi_string_get_cstr(temp_str), false); + widget_add_button_element( + nfc->widget, GuiButtonTypeLeft, "Cancel", nfc_scene_delete_widget_callback, nfc); + widget_add_button_element( + nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc); + + furi_string_set(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + widget_add_string_element( + nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + + NfcProtocol protocol = nfc->dev->dev_data.protocol; + const char* nfc_type = "NFC-A"; + + if(protocol == NfcDeviceProtocolEMV) { + furi_string_set(temp_str, "EMV bank card"); + } else if(protocol == NfcDeviceProtocolMifareUl) { + furi_string_set(temp_str, nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, true)); + } else if(protocol == NfcDeviceProtocolMifareClassic) { + furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); + } else if(protocol == NfcDeviceProtocolMifareDesfire) { + furi_string_set(temp_str, "MIFARE DESFire"); + } else if(protocol == NfcDeviceProtocolNfcV) { + furi_string_set(temp_str, "ISO15693 tag"); + nfc_type = "NFC-V"; + } else { + furi_string_set(temp_str, "Unknown ISO tag"); + } + widget_add_string_element( + nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, nfc_type); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + if(nfc_device_delete(nfc->dev, true)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + consumed = true; + } + } + return consumed; +} + +void nfc_scene_delete_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_delete_success.c b/applications/main/nfc_old/scenes/nfc_scene_delete_success.c new file mode 100644 index 000000000000..795363527fcf --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_delete_success.c @@ -0,0 +1,45 @@ +#include "../nfc_i.h" + +void nfc_scene_delete_success_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_delete_success_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + Popup* popup = nfc->popup; + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_delete_success_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } + } + return consumed; +} + +void nfc_scene_delete_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_detect_reader.c b/applications/main/nfc_old/scenes/nfc_scene_detect_reader.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_detect_reader.c rename to applications/main/nfc_old/scenes/nfc_scene_detect_reader.c diff --git a/applications/main/nfc/scenes/nfc_scene_device_info.c b/applications/main/nfc_old/scenes/nfc_scene_device_info.c similarity index 97% rename from applications/main/nfc/scenes/nfc_scene_device_info.c rename to applications/main/nfc_old/scenes/nfc_scene_device_info.c index 9780ffe41d2b..5d51c0816c88 100644 --- a/applications/main/nfc/scenes/nfc_scene_device_info.c +++ b/applications/main/nfc_old/scenes/nfc_scene_device_info.c @@ -52,6 +52,7 @@ void nfc_scene_device_info_on_enter(void* context) { } } else if( dev_data->protocol == NfcDeviceProtocolMifareClassic || + dev_data->protocol == NfcDeviceProtocolMifareDesfire || dev_data->protocol == NfcDeviceProtocolMifareUl) { furi_string_set(temp_str, nfc->dev->dev_data.parsed_data); } diff --git a/applications/main/nfc/scenes/nfc_scene_dict_not_found.c b/applications/main/nfc_old/scenes/nfc_scene_dict_not_found.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_dict_not_found.c rename to applications/main/nfc_old/scenes/nfc_scene_dict_not_found.c diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_apdu_sequence.c b/applications/main/nfc_old/scenes/nfc_scene_emulate_apdu_sequence.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_emulate_apdu_sequence.c rename to applications/main/nfc_old/scenes/nfc_scene_emulate_apdu_sequence.c diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c b/applications/main/nfc_old/scenes/nfc_scene_emulate_uid.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_emulate_uid.c rename to applications/main/nfc_old/scenes/nfc_scene_emulate_uid.c diff --git a/applications/main/nfc/scenes/nfc_scene_emv_menu.c b/applications/main/nfc_old/scenes/nfc_scene_emv_menu.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_emv_menu.c rename to applications/main/nfc_old/scenes/nfc_scene_emv_menu.c diff --git a/applications/main/nfc/scenes/nfc_scene_emv_read_success.c b/applications/main/nfc_old/scenes/nfc_scene_emv_read_success.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_emv_read_success.c rename to applications/main/nfc_old/scenes/nfc_scene_emv_read_success.c diff --git a/applications/main/nfc_old/scenes/nfc_scene_exit_confirm.c b/applications/main/nfc_old/scenes/nfc_scene_exit_confirm.c new file mode 100644 index 000000000000..3ce4f6de839b --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_exit_confirm.c @@ -0,0 +1,52 @@ +#include "../nfc_i.h" + +void nfc_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_exit_confirm_on_enter(void* context) { + Nfc* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_left_button_text(dialog_ex, "Exit"); + dialog_ex_set_right_button_text(dialog_ex, "Stay"); + dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 11, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop); + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_exit_confirm_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else if(event.event == DialogExResultLeft) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneReadCardType)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneReadCardType); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void nfc_scene_exit_confirm_on_exit(void* context) { + Nfc* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_extra_actions.c b/applications/main/nfc_old/scenes/nfc_scene_extra_actions.c new file mode 100644 index 000000000000..7f5bc7e75857 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_extra_actions.c @@ -0,0 +1,92 @@ +#include "../nfc_i.h" + +enum SubmenuIndex { + SubmenuIndexReadCardType, + SubmenuIndexMfClassicKeys, + SubmenuIndexMfUltralightUnlock, + SubmenuIndexNfcVUnlock, + SubmenuIndexNfcVSniff, +}; + +void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_extra_actions_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, + "Read Specific Card Type", + SubmenuIndexReadCardType, + nfc_scene_extra_actions_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Mifare Classic Keys", + SubmenuIndexMfClassicKeys, + nfc_scene_extra_actions_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Unlock NTAG/Ultralight", + SubmenuIndexMfUltralightUnlock, + nfc_scene_extra_actions_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Unlock SLIX-L", + SubmenuIndexNfcVUnlock, + nfc_scene_extra_actions_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Listen NfcV Reader", + SubmenuIndexNfcVSniff, + nfc_scene_extra_actions_submenu_callback, + nfc); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexMfClassicKeys) { + if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeys); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + } + consumed = true; + } else if(event.event == SubmenuIndexMfUltralightUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; + } else if(event.event == SubmenuIndexReadCardType) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); + scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); + consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + consumed = true; + } else if(event.event == SubmenuIndexNfcVSniff) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVSniff); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + } + + return consumed; +} + +void nfc_scene_extra_actions_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_field.c b/applications/main/nfc_old/scenes/nfc_scene_field.c new file mode 100644 index 000000000000..e3eb6a7088b8 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_field.c @@ -0,0 +1,33 @@ +#include "../nfc_i.h" + +void nfc_scene_field_on_enter(void* context) { + Nfc* nfc = context; + + furi_hal_nfc_field_on(); + + Popup* popup = nfc->popup; + popup_set_header( + popup, + "Field is on\nDon't leave device\nin this mode for too long.", + 64, + 11, + AlignCenter, + AlignTop); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + + notification_internal_message(nfc->notifications, &sequence_set_blue_255); +} + +bool nfc_scene_field_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void nfc_scene_field_on_exit(void* context) { + Nfc* nfc = context; + + furi_hal_nfc_field_off(); + notification_internal_message(nfc->notifications, &sequence_reset_blue); + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_file_select.c b/applications/main/nfc_old/scenes/nfc_scene_file_select.c new file mode 100644 index 000000000000..374a933d1c70 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_file_select.c @@ -0,0 +1,28 @@ +#include "../nfc_i.h" +#include "nfc/nfc_device.h" + +void nfc_scene_file_select_on_enter(void* context) { + Nfc* nfc = context; + // Process file_select return + nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc); + if(!furi_string_size(nfc->dev->load_path)) { + furi_string_set_str(nfc->dev->load_path, NFC_APP_FOLDER); + } + if(nfc_file_select(nfc->dev)) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, 0); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu); + } else { + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); + } + nfc_device_set_loading_callback(nfc->dev, NULL, nfc); +} + +bool nfc_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void nfc_scene_file_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_generate_info.c b/applications/main/nfc_old/scenes/nfc_scene_generate_info.c new file mode 100644 index 000000000000..7b84ae43b11f --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_generate_info.c @@ -0,0 +1,60 @@ +#include "../nfc_i.h" +#include "lib/nfc/helpers/nfc_generators.h" + +void nfc_scene_generate_info_dialog_callback(DialogExResult result, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_generate_info_on_enter(void* context) { + Nfc* nfc = context; + + // Setup dialog view + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + DialogEx* dialog_ex = nfc->dialog_ex; + dialog_ex_set_right_button_text(dialog_ex, "More"); + + // Create info text + FuriString* info_str = furi_string_alloc_printf( + "%s\n%s\nUID:", nfc->generator->name, nfc_get_dev_type(data->type)); + + // Append UID + for(int i = 0; i < data->uid_len; ++i) { + furi_string_cat_printf(info_str, " %02X", data->uid[i]); + } + nfc_text_store_set(nfc, furi_string_get_cstr(info_str)); + furi_string_free(info_str); + + dialog_ex_set_text(dialog_ex, nfc->text_store, 0, 0, AlignLeft, AlignTop); + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_generate_info_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_generate_info_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + // Switch either to NfcSceneMfClassicMenu or NfcSceneMfUltralightMenu + if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareClassic) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicMenu); + } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightMenu); + } + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_generate_info_on_exit(void* context) { + Nfc* nfc = context; + + // Clean views + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_data.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_data.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mf_classic_data.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_classic_data.c diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_dict_attack.c new file mode 100644 index 000000000000..5bd24d7eac17 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_dict_attack.c @@ -0,0 +1,186 @@ +#include "../nfc_i.h" +#include + +#define TAG "NfcMfClassicDictAttack" + +typedef enum { + DictAttackStateIdle, + DictAttackStateUserDictInProgress, + DictAttackStateFlipperDictInProgress, +} DictAttackState; + +bool nfc_dict_attack_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + return true; +} + +void nfc_dict_attack_dict_attack_result_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventDictAttackSkip); +} + +static void nfc_scene_mf_classic_dict_attack_update_view(Nfc* nfc) { + MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + uint8_t sectors_read = 0; + uint8_t keys_found = 0; + + // Calculate found keys and read sectors + mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); + dict_attack_set_keys_found(nfc->dict_attack, keys_found); + dict_attack_set_sector_read(nfc->dict_attack, sectors_read); +} + +static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackState state) { + MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; + NfcWorkerState worker_state = NfcWorkerStateReady; + MfClassicDict* dict = NULL; + + // Identify scene state + if(state == DictAttackStateIdle) { + if(mf_classic_dict_check_presence(MfClassicDictTypeUser)) { + state = DictAttackStateUserDictInProgress; + } else { + state = DictAttackStateFlipperDictInProgress; + } + } else if(state == DictAttackStateUserDictInProgress) { + state = DictAttackStateFlipperDictInProgress; + } + + // Setup view + if(state == DictAttackStateUserDictInProgress) { + worker_state = NfcWorkerStateMfClassicDictAttack; + dict_attack_set_header(nfc->dict_attack, "MF Classic User Dictionary"); + dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + + // If failed to load user dictionary - try the system dictionary + if(!dict) { + FURI_LOG_E(TAG, "User dictionary not found"); + state = DictAttackStateFlipperDictInProgress; + } + } + if(state == DictAttackStateFlipperDictInProgress) { + worker_state = NfcWorkerStateMfClassicDictAttack; + dict_attack_set_header(nfc->dict_attack, "MF Classic System Dictionary"); + dict = mf_classic_dict_alloc(MfClassicDictTypeSystem); + if(!dict) { + FURI_LOG_E(TAG, "Flipper dictionary not found"); + // Pass through to let the worker handle the failure + } + } + // Free previous dictionary + if(dict_attack_data->dict) { + mf_classic_dict_free(dict_attack_data->dict); + } + dict_attack_data->dict = dict; + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack, state); + dict_attack_set_callback(nfc->dict_attack, nfc_dict_attack_dict_attack_result_callback, nfc); + dict_attack_set_current_sector(nfc->dict_attack, 0); + dict_attack_set_card_detected(nfc->dict_attack, data->type); + dict_attack_set_total_dict_keys( + nfc->dict_attack, dict ? mf_classic_dict_get_total_keys(dict) : 0); + nfc_scene_mf_classic_dict_attack_update_view(nfc); + nfc_worker_start( + nfc->worker, worker_state, &nfc->dev->dev_data, nfc_dict_attack_worker_callback, nfc); +} + +void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { + Nfc* nfc = context; + nfc_scene_mf_classic_dict_attack_prepare_view(nfc, DictAttackStateIdle); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDictAttack); + nfc_blink_read_start(nfc); + notification_message(nfc->notifications, &sequence_display_backlight_enforce_on); +} + +bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + bool consumed = false; + + uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack); + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventSuccess) { + if(state == DictAttackStateUserDictInProgress) { + nfc_worker_stop(nfc->worker); + nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); + consumed = true; + } else { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } + } else if(event.event == NfcWorkerEventAborted) { + if(state == DictAttackStateUserDictInProgress && + dict_attack_get_card_state(nfc->dict_attack)) { + nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); + consumed = true; + } else { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + // Counting failed attempts too + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } + } else if(event.event == NfcWorkerEventCardDetected) { + dict_attack_set_card_detected(nfc->dict_attack, data->type); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + dict_attack_set_card_removed(nfc->dict_attack); + consumed = true; + } else if(event.event == NfcWorkerEventFoundKeyA) { + dict_attack_inc_keys_found(nfc->dict_attack); + consumed = true; + } else if(event.event == NfcWorkerEventFoundKeyB) { + dict_attack_inc_keys_found(nfc->dict_attack); + consumed = true; + } else if(event.event == NfcWorkerEventNewSector) { + nfc_scene_mf_classic_dict_attack_update_view(nfc); + dict_attack_inc_current_sector(nfc->dict_attack); + consumed = true; + } else if(event.event == NfcWorkerEventNewDictKeyBatch) { + nfc_scene_mf_classic_dict_attack_update_view(nfc); + dict_attack_inc_current_dict_key(nfc->dict_attack, NFC_DICT_KEY_BATCH_SIZE); + consumed = true; + } else if(event.event == NfcCustomEventDictAttackSkip) { + if(state == DictAttackStateUserDictInProgress) { + nfc_worker_stop(nfc->worker); + consumed = true; + } else if(state == DictAttackStateFlipperDictInProgress) { + nfc_worker_stop(nfc->worker); + consumed = true; + } + } else if(event.event == NfcWorkerEventKeyAttackStart) { + dict_attack_set_key_attack( + nfc->dict_attack, + true, + nfc->dev->dev_data.mf_classic_dict_attack_data.current_sector); + } else if(event.event == NfcWorkerEventKeyAttackStop) { + dict_attack_set_key_attack(nfc->dict_attack, false, 0); + } else if(event.event == NfcWorkerEventKeyAttackNextSector) { + dict_attack_inc_key_attack_current_sector(nfc->dict_attack); + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + return consumed; +} + +void nfc_scene_mf_classic_dict_attack_on_exit(void* context) { + Nfc* nfc = context; + NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; + // Stop worker + nfc_worker_stop(nfc->worker); + if(dict_attack_data->dict) { + mf_classic_dict_free(dict_attack_data->dict); + dict_attack_data->dict = NULL; + } + dict_attack_reset(nfc->dict_attack); + nfc_blink_stop(nfc); + notification_message(nfc->notifications, &sequence_display_backlight_enforce_auto); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_emulate.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_classic_emulate.c diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys.c new file mode 100644 index 000000000000..8a7dc2c1839e --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys.c @@ -0,0 +1,71 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_keys_widget_callback(GuiButtonType result, InputType type, void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_keys_on_enter(void* context) { + Nfc* nfc = context; + + // Load flipper dict keys total + uint32_t flipper_dict_keys_total = 0; + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeSystem); + if(dict) { + flipper_dict_keys_total = mf_classic_dict_get_total_keys(dict); + mf_classic_dict_free(dict); + } + // Load user dict keys total + uint32_t user_dict_keys_total = 0; + dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + if(dict) { + user_dict_keys_total = mf_classic_dict_get_total_keys(dict); + mf_classic_dict_free(dict); + } + + widget_add_string_element( + nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MIFARE Classic Keys"); + char temp_str[32]; + snprintf(temp_str, sizeof(temp_str), "System dict: %lu", flipper_dict_keys_total); + widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str); + snprintf(temp_str, sizeof(temp_str), "User dict: %lu", user_dict_keys_total); + widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); + widget_add_button_element( + nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); + widget_add_icon_element(nfc->widget, 87, 13, &I_Keychain_39x36); + if(user_dict_keys_total > 0) { + widget_add_button_element( + nfc->widget, + GuiButtonTypeRight, + "List", + nfc_scene_mf_classic_keys_widget_callback, + nfc); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysList); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_add.c new file mode 100644 index 000000000000..3a999f031144 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_add.c @@ -0,0 +1,60 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_classic_keys_add_byte_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_mf_classic_keys_add_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter the key in hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_mf_classic_keys_add_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 6); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + // Add key to dict + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + if(dict) { + if(mf_classic_dict_is_key_present(dict, nfc->byte_input_store)) { + scene_manager_next_scene( + nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); + } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + dolphin_deed(DolphinDeedNfcMfcAdd); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + } + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + } + mf_classic_dict_free(dict); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_classic_keys_add_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_delete.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_delete.c new file mode 100644 index 000000000000..0ea3f59a45ef --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_delete.c @@ -0,0 +1,83 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_keys_delete_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_keys_delete_on_enter(void* context) { + Nfc* nfc = context; + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + uint32_t key_index = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete); + // Setup Custom Widget view + FuriString* key_str; + key_str = furi_string_alloc(); + + widget_add_string_element( + nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Delete this key?"); + widget_add_button_element( + nfc->widget, + GuiButtonTypeLeft, + "Cancel", + nfc_scene_mf_classic_keys_delete_widget_callback, + nfc); + widget_add_button_element( + nfc->widget, + GuiButtonTypeRight, + "Delete", + nfc_scene_mf_classic_keys_delete_widget_callback, + nfc); + + mf_classic_dict_get_key_at_index_str(dict, key_str, key_index); + widget_add_string_element( + nfc->widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + furi_string_get_cstr(key_str)); + + furi_string_free(key_str); + mf_classic_dict_free(dict); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_keys_delete_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + uint32_t key_index = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicKeysDelete); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(event.event == GuiButtonTypeRight) { + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + if(mf_classic_dict_delete_index(dict, key_index)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } + mf_classic_dict_free(dict); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_delete_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_list.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_list.c new file mode 100644 index 000000000000..57f9fe656247 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_list.c @@ -0,0 +1,100 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX (100) + +void nfc_scene_mf_classic_keys_list_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_classic_keys_list_popup_callback(void* context) { + furi_assert(context); + + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_keys_list_prepare(Nfc* nfc, MfClassicDict* dict) { + Submenu* submenu = nfc->submenu; + uint32_t index = 0; + FuriString* temp_key; + temp_key = furi_string_alloc(); + + submenu_set_header(submenu, "Select key to delete:"); + while(mf_classic_dict_get_next_key_str(dict, temp_key)) { + char* current_key = (char*)malloc(sizeof(char) * 13); + strncpy(current_key, furi_string_get_cstr(temp_key), 12); + MfClassicUserKeys_push_back(nfc->mfc_key_strs, current_key); + FURI_LOG_D("ListKeys", "Key %lu: %s", index, current_key); + submenu_add_item( + submenu, current_key, index++, nfc_scene_mf_classic_keys_list_submenu_callback, nfc); + } + furi_string_free(temp_key); +} + +void nfc_scene_mf_classic_keys_list_on_enter(void* context) { + Nfc* nfc = context; + MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + MfClassicUserKeys_init(nfc->mfc_key_strs); + if(dict) { + uint32_t total_user_keys = mf_classic_dict_get_total_keys(dict); + if(total_user_keys < NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX) { + nfc_scene_mf_classic_keys_list_prepare(nfc, dict); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + } else { + popup_set_header(nfc->popup, "Too many keys!", 64, 0, AlignCenter, AlignTop); + popup_set_text( + nfc->popup, + "Edit user dictionary\nwith file browser", + 64, + 12, + AlignCenter, + AlignTop); + popup_set_callback(nfc->popup, nfc_scene_mf_classic_keys_list_popup_callback); + popup_set_context(nfc->popup, nfc); + popup_set_timeout(nfc->popup, 3000); + popup_enable_timeout(nfc->popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + } + mf_classic_dict_free(dict); + } else { + popup_set_header( + nfc->popup, "Failed to load dictionary", 64, 32, AlignCenter, AlignCenter); + popup_set_callback(nfc->popup, nfc_scene_mf_classic_keys_list_popup_callback); + popup_set_context(nfc->popup, nfc); + popup_set_timeout(nfc->popup, 3000); + popup_enable_timeout(nfc->popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + } +} + +bool nfc_scene_mf_classic_keys_list_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicKeysDelete, event.event); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysDelete); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_classic_keys_list_on_exit(void* context) { + Nfc* nfc = context; + + MfClassicUserKeys_it_t it; + for(MfClassicUserKeys_it(it, nfc->mfc_key_strs); !MfClassicUserKeys_end_p(it); + MfClassicUserKeys_next(it)) { + free(*MfClassicUserKeys_ref(it)); + } + MfClassicUserKeys_clear(nfc->mfc_key_strs); + submenu_reset(nfc->submenu); + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c new file mode 100644 index 000000000000..ab41989b2c73 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c @@ -0,0 +1,47 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_keys_warn_duplicate_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + Popup* popup = nfc->popup; + popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48); + popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop); + popup_set_text( + popup, + "Please enter a\n" + "different key.", + 4, + 24, + AlignLeft, + AlignTop); + popup_set_timeout(popup, 5000); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_mf_classic_keys_warn_duplicate_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_keys_warn_duplicate_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeysAdd); + } + } + return consumed; +} + +void nfc_scene_mf_classic_keys_warn_duplicate_on_exit(void* context) { + Nfc* nfc = context; + + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_menu.c similarity index 94% rename from applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_classic_menu.c index 67b2a85309e4..9c4163676462 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_menu.c @@ -54,14 +54,14 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexDetectReader) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); - DOLPHIN_DEED(DolphinDeedNfcDetectReader); + dolphin_deed(DolphinDeedNfcDetectReader); consumed = true; } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_read_success.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mf_classic_read_success.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_classic_read_success.c diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_update.c similarity index 98% rename from applications/main/nfc/scenes/nfc_scene_mf_classic_update.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_classic_update.c index aacf77f773f0..ffef1b7b9f06 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_update.c @@ -34,7 +34,7 @@ static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) { void nfc_scene_mf_classic_update_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_update_success.c similarity index 97% rename from applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_classic_update_success.c index fef8fd5e9320..fb1868459d4a 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_update_success.c @@ -8,7 +8,7 @@ void nfc_scene_mf_classic_update_success_popup_callback(void* context) { void nfc_scene_mf_classic_update_success_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); notification_message(nfc->notifications, &sequence_success); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_write.c similarity index 98% rename from applications/main/nfc/scenes/nfc_scene_mf_classic_write.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_classic_write.c index 3543cbc5889b..20ebfcc70a2c 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_write.c @@ -34,7 +34,7 @@ static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) { void nfc_scene_mf_classic_write_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_write_fail.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mf_classic_write_fail.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_classic_write_fail.c diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_write_success.c similarity index 97% rename from applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_classic_write_success.c index 2f2a3beb119b..00030d4fe8ca 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_write_success.c @@ -8,7 +8,7 @@ void nfc_scene_mf_classic_write_success_popup_callback(void* context) { void nfc_scene_mf_classic_write_success_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); notification_message(nfc->notifications, &sequence_success); diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_classic_wrong_card.c b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_wrong_card.c new file mode 100644 index 000000000000..2c56270e36d2 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_classic_wrong_card.c @@ -0,0 +1,53 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_wrong_card_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_wrong_card_on_enter(void* context) { + Nfc* nfc = context; + Widget* widget = nfc->widget; + + notification_message(nfc->notifications, &sequence_error); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); + widget_add_string_multiline_element( + widget, + 4, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Data management\nis only possible\nwith initial card"); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_wrong_card_widget_callback, nfc); + + // Setup and start worker + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_wrong_card_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } + return consumed; +} + +void nfc_scene_mf_classic_wrong_card_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} \ No newline at end of file diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_desfire_app.c b/applications/main/nfc_old/scenes/nfc_scene_mf_desfire_app.c new file mode 100644 index 000000000000..882dc5fea8f2 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_desfire_app.c @@ -0,0 +1,120 @@ +#include "../nfc_i.h" + +#define TAG "NfcSceneMfDesfireApp" + +enum SubmenuIndex { + SubmenuIndexAppInfo, + SubmenuIndexDynamic, // dynamic indexes start here +}; + +void nfc_scene_mf_desfire_popup_callback(void* context) { + furi_assert(context); + + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +MifareDesfireApplication* nfc_scene_mf_desfire_app_get_app(Nfc* nfc) { + uint32_t app_idx = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> + 1; + MifareDesfireApplication* app = nfc->dev->dev_data.mf_df_data.app_head; + for(uint32_t i = 0; i < app_idx && app; i++) { + app = app->next; + } + return app; +} + +void nfc_scene_mf_desfire_app_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_desfire_app_on_enter(void* context) { + Nfc* nfc = context; + MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); + if(!app) { + popup_set_icon(nfc->popup, 5, 5, &I_WarningDolphin_45x42); + popup_set_header(nfc->popup, "Empty card!", 55, 12, AlignLeft, AlignBottom); + popup_set_callback(nfc->popup, nfc_scene_mf_desfire_popup_callback); + popup_set_context(nfc->popup, nfc); + popup_set_timeout(nfc->popup, 3000); + popup_enable_timeout(nfc->popup); + popup_set_text(nfc->popup, "No application\nfound.", 55, 15, AlignLeft, AlignTop); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + } else { + text_box_set_font(nfc->text_box, TextBoxFontHex); + submenu_add_item( + nfc->submenu, + "App info", + SubmenuIndexAppInfo, + nfc_scene_mf_desfire_app_submenu_callback, + nfc); + + FuriString* label = furi_string_alloc(); + int idx = SubmenuIndexDynamic; + for(MifareDesfireFile* file = app->file_head; file; file = file->next) { + furi_string_printf(label, "File %d", file->id); + submenu_add_item( + nfc->submenu, + furi_string_get_cstr(label), + idx++, + nfc_scene_mf_desfire_app_submenu_callback, + nfc); + } + furi_string_free(label); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + } +} + +bool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else { + MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); + TextBox* text_box = nfc->text_box; + furi_string_reset(nfc->text_box_store); + if(event.event == SubmenuIndexAppInfo) { + mf_df_cat_application_info(app, nfc->text_box_store); + } else { + uint16_t index = event.event - SubmenuIndexDynamic; + MifareDesfireFile* file = app->file_head; + for(int i = 0; file && i < index; i++) { + file = file->next; + } + if(!file) { + return false; + } + mf_df_cat_file(file, nfc->text_box_store); + } + text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state | 1); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state & 1) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state & ~1); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_desfire_app_on_exit(void* context) { + Nfc* nfc = context; + + // Clear views + popup_reset(nfc->popup); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_desfire_data.c b/applications/main/nfc_old/scenes/nfc_scene_mf_desfire_data.c new file mode 100644 index 000000000000..c7caee8dc6d3 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_desfire_data.c @@ -0,0 +1,104 @@ +#include "../nfc_i.h" + +#define TAG "NfcSceneMfDesfireData" + +enum { + MifareDesfireDataStateMenu, + MifareDesfireDataStateItem, // MUST be last, states >= this correspond with submenu index +}; + +enum SubmenuIndex { + SubmenuIndexCardInfo, + SubmenuIndexDynamic, // dynamic indexes start here +}; + +void nfc_scene_mf_desfire_data_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = (Nfc*)context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_desfire_data_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); + MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; + + text_box_set_font(nfc->text_box, TextBoxFontHex); + + submenu_add_item( + submenu, + "Card info", + SubmenuIndexCardInfo, + nfc_scene_mf_desfire_data_submenu_callback, + nfc); + + FuriString* label = furi_string_alloc(); + int idx = SubmenuIndexDynamic; + for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { + furi_string_printf(label, "App %02x%02x%02x", app->id[0], app->id[1], app->id[2]); + submenu_add_item( + submenu, + furi_string_get_cstr(label), + idx++, + nfc_scene_mf_desfire_data_submenu_callback, + nfc); + } + furi_string_free(label); + + if(state >= MifareDesfireDataStateItem) { + submenu_set_selected_item( + nfc->submenu, state - MifareDesfireDataStateItem + SubmenuIndexDynamic); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateMenu); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_desfire_data_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); + MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; + + if(event.type == SceneManagerEventTypeCustom) { + TextBox* text_box = nfc->text_box; + furi_string_reset(nfc->text_box_store); + if(event.event == SubmenuIndexCardInfo) { + mf_df_cat_card_info(data, nfc->text_box_store); + text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneMfDesfireData, + MifareDesfireDataStateItem + SubmenuIndexCardInfo); + consumed = true; + } else { + uint16_t index = event.event - SubmenuIndexDynamic; + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateItem + index); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, index << 1); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state >= MifareDesfireDataStateItem) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateMenu); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_desfire_data_on_exit(void* context) { + Nfc* nfc = context; + + // Clear views + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c b/applications/main/nfc_old/scenes/nfc_scene_mf_desfire_menu.c similarity index 95% rename from applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_desfire_menu.c index bee63d775bd5..9cebefedfa1b 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_desfire_menu.c @@ -50,9 +50,9 @@ bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexEmulateUid) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexInfo) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_read_success.c b/applications/main/nfc_old/scenes/nfc_scene_mf_desfire_read_success.c similarity index 60% rename from applications/main/nfc/scenes/nfc_scene_mf_desfire_read_success.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_desfire_read_success.c index 39030397fc43..633549eb5d8f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_read_success.c +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_desfire_read_success.c @@ -20,35 +20,40 @@ void nfc_scene_mf_desfire_read_success_on_enter(void* context) { Widget* widget = nfc->widget; // Prepare string for data display - FuriString* temp_str = furi_string_alloc_printf("\e#MIFARE DESfire\n"); - furi_string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); - } + FuriString* temp_str = NULL; + if(furi_string_size(nfc->dev->dev_data.parsed_data)) { + temp_str = furi_string_alloc_set(nfc->dev->dev_data.parsed_data); + } else { + temp_str = furi_string_alloc_printf("\e#MIFARE DESFire\n"); + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } - uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1); - uint32_t bytes_free = data->free_memory ? data->free_memory->bytes : 0; - furi_string_cat_printf(temp_str, "\n%lu", bytes_total); - if(data->version.sw_storage & 1) { - furi_string_push_back(temp_str, '+'); - } - furi_string_cat_printf(temp_str, " bytes, %lu bytes free\n", bytes_free); - - uint16_t n_apps = 0; - uint16_t n_files = 0; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - n_apps++; - for(MifareDesfireFile* file = app->file_head; file; file = file->next) { - n_files++; + uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1); + uint32_t bytes_free = data->free_memory ? data->free_memory->bytes : 0; + furi_string_cat_printf(temp_str, "\n%lu", bytes_total); + if(data->version.sw_storage & 1) { + furi_string_push_back(temp_str, '+'); + } + furi_string_cat_printf(temp_str, " bytes, %lu bytes free\n", bytes_free); + + uint16_t n_apps = 0; + uint16_t n_files = 0; + for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { + n_apps++; + for(MifareDesfireFile* file = app->file_head; file; file = file->next) { + n_files++; + } + } + furi_string_cat_printf(temp_str, "%d Application", n_apps); + if(n_apps != 1) { + furi_string_push_back(temp_str, 's'); + } + furi_string_cat_printf(temp_str, ", %d file", n_files); + if(n_files != 1) { + furi_string_push_back(temp_str, 's'); } - } - furi_string_cat_printf(temp_str, "%d Application", n_apps); - if(n_apps != 1) { - furi_string_push_back(temp_str, 's'); - } - furi_string_cat_printf(temp_str, ", %d file", n_files); - if(n_files != 1) { - furi_string_push_back(temp_str, 's'); } notification_message_block(nfc->notifications, &sequence_set_green_255); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_data.c b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_data.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mf_ultralight_data.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_data.c diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_emulate.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_emulate.c diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_key_input.c b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_key_input.c new file mode 100644 index 000000000000..089187d5bc3a --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_key_input.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_ultralight_key_input_byte_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_mf_ultralight_key_input_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter the password in hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_mf_ultralight_key_input_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 4); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_mf_ultralight_key_input_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_key_input_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_menu.c similarity index 86% rename from applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_menu.c index c511e9dcbfe1..b3bd780f4b4b 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_menu.c @@ -19,7 +19,7 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) { Submenu* submenu = nfc->submenu; MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; - if(!mf_ul_is_full_capture(data)) { + if(!mf_ul_is_full_capture(data) && data->type != MfUltralightTypeULC) { submenu_add_item( submenu, "Unlock", @@ -29,12 +29,14 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) { } submenu_add_item( submenu, "Save", SubmenuIndexSave, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); - submenu_add_item( - submenu, - "Emulate", - SubmenuIndexEmulate, - nfc_scene_mf_ultralight_menu_submenu_callback, - nfc); + if(mf_ul_emulation_supported(data)) { + submenu_add_item( + submenu, + "Emulate", + SubmenuIndexEmulate, + nfc_scene_mf_ultralight_menu_submenu_callback, + nfc); + } submenu_add_item( submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); @@ -58,9 +60,9 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexUnlock) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_read_auth.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_read_auth.c diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_read_auth_result.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_read_auth_result.c diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_read_success.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mf_ultralight_read_success.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_read_success.c diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_unlock_auto.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_auto.c rename to applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_unlock_auto.c diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_unlock_menu.c new file mode 100644 index 000000000000..484629b0bbe1 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -0,0 +1,83 @@ +#include "../nfc_i.h" + +enum SubmenuIndex { + SubmenuIndexMfUlUnlockMenuAuto, + SubmenuIndexMfUlUnlockMenuAmeebo, + SubmenuIndexMfUlUnlockMenuXiaomi, + SubmenuIndexMfUlUnlockMenuManual, +}; + +void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexMfUlUnlockMenuAuto, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + } + submenu_add_item( + submenu, + "Auth As Ameebo", + SubmenuIndexMfUlUnlockMenuAmeebo, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As Xiaomi Air Purifier", + SubmenuIndexMfUlUnlockMenuXiaomi, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Enter Password Manually", + SubmenuIndexMfUlUnlockMenuManual, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexMfUlUnlockMenuManual) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodManual; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuAmeebo) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAmeebo; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuXiaomi) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuAuto) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; + } + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu, event.event); + } + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_menu_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_unlock_warn.c new file mode 100644 index 000000000000..af2eca0ce5d2 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -0,0 +1,98 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { + Nfc* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method; + + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback); + + if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) { + // Build dialog text + MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth; + FuriString* password_str = + furi_string_alloc_set_str("Try to unlock the card with\npassword: "); + for(size_t i = 0; i < sizeof(auth->pwd.raw); ++i) { + furi_string_cat_printf(password_str, "%02X ", nfc->byte_input_store[i]); + } + furi_string_cat_str(password_str, "?\nCaution, a wrong password\ncan block the card!"); + nfc_text_store_set(nfc, furi_string_get_cstr(password_str)); + furi_string_free(password_str); + + dialog_ex_set_header( + dialog_ex, + auth_method == MfUltralightAuthMethodAuto ? "Password captured!" : "Risky function!", + 64, + 0, + AlignCenter, + AlignTop); + dialog_ex_set_text(dialog_ex, nfc->text_store, 64, 12, AlignCenter, AlignTop); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Continue"); + + if(auth_method == MfUltralightAuthMethodAuto) + notification_message(nfc->notifications, &sequence_set_green_255); + } else { + dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); + dialog_ex_set_center_button_text(dialog_ex, "OK"); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + + bool consumed = false; + + MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method; + if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + dolphin_deed(DolphinDeedNfcRead); + consumed = true; + } else if(event.event == DialogExResultLeft) { + if(auth_method == MfUltralightAuthMethodAuto) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } else { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } + } else if(event.type == SceneManagerEventTypeBack) { + // Cannot press back + consumed = true; + } + } else { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + dolphin_deed(DolphinDeedNfcRead); + consumed = true; + } + } + } + + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) { + Nfc* nfc = context; + + dialog_ex_reset(nfc->dialog_ex); + nfc_text_store_clear(nfc); + + notification_message_block(nfc->notifications, &sequence_reset_green); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mfkey_complete.c b/applications/main/nfc_old/scenes/nfc_scene_mfkey_complete.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mfkey_complete.c rename to applications/main/nfc_old/scenes/nfc_scene_mfkey_complete.c diff --git a/applications/main/nfc/scenes/nfc_scene_mfkey_nonces_info.c b/applications/main/nfc_old/scenes/nfc_scene_mfkey_nonces_info.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_mfkey_nonces_info.c rename to applications/main/nfc_old/scenes/nfc_scene_mfkey_nonces_info.c diff --git a/applications/main/nfc_old/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc_old/scenes/nfc_scene_nfc_data_info.c new file mode 100644 index 000000000000..66a9174df476 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_nfc_data_info.c @@ -0,0 +1,309 @@ +#include "../nfc_i.h" + +void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType type, void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_slix_build_string( + FuriString* temp_str, + NfcVData* nfcv_data, + SlixTypeFeatures features, + const char* type) { + furi_string_cat_printf(temp_str, "Type: %s\n", type); + furi_string_cat_printf(temp_str, "Keys:\n"); + if(features & SlixFeatureRead) { + furi_string_cat_printf( + temp_str, + " Read %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyRead) ? "" : " (unset)"); + } + if(features & SlixFeatureWrite) { + furi_string_cat_printf( + temp_str, + " Write %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyWrite) ? "" : " (unset)"); + } + if(features & SlixFeaturePrivacy) { + furi_string_cat_printf( + temp_str, + " Privacy %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyPrivacy) ? "" : " (unset)"); + furi_string_cat_printf( + temp_str, + " Privacy mode %s\n", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ENABLED" : "DISABLED"); + } + if(features & SlixFeatureDestroy) { + furi_string_cat_printf( + temp_str, + " Destroy %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyDestroy) ? "" : " (unset)"); + } + if(features & SlixFeatureEas) { + furi_string_cat_printf( + temp_str, + " EAS %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyEas) ? "" : " (unset)"); + } + if(features & SlixFeatureSignature) { + furi_string_cat_printf( + temp_str, + "Signature %08llX...\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.signature, 4)); + } + furi_string_cat_printf( + temp_str, + "DSFID: %02X %s\n", + nfcv_data->dsfid, + (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "AFI: %02X %s\n", + nfcv_data->afi, + (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "EAS: %s\n", + (nfcv_data->security_status[0] & NfcVLockBitEas) ? "locked" : "not locked"); + + if(features & SlixFeatureProtection) { + furi_string_cat_printf( + temp_str, + "PPL: %s\n", + (nfcv_data->security_status[0] & NfcVLockBitPpl) ? "locked" : "not locked"); + furi_string_cat_printf(temp_str, "Prot.ptr %02X\n", nfcv_data->sub_data.slix.pp_pointer); + furi_string_cat_printf(temp_str, "Prot.con %02X\n", nfcv_data->sub_data.slix.pp_condition); + } +} + +void nfc_scene_nfc_data_info_on_enter(void* context) { + Nfc* nfc = context; + Widget* widget = nfc->widget; + FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; + NfcDeviceData* dev_data = &nfc->dev->dev_data; + NfcProtocol protocol = dev_data->protocol; + uint8_t text_scroll_height = 0; + if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) || + (protocol == NfcDeviceProtocolMifareClassic)) { + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); + text_scroll_height = 52; + } else { + text_scroll_height = 64; + } + + FuriString* temp_str; + temp_str = furi_string_alloc(); + // Set name if present + if(nfc->dev->dev_name[0] != '\0') { + furi_string_printf(temp_str, "\ec%s\n", nfc->dev->dev_name); + } + + // Set tag type + if(protocol == NfcDeviceProtocolEMV) { + furi_string_cat_printf(temp_str, "\e#EMV Bank Card\n"); + } else if(protocol == NfcDeviceProtocolMifareUl) { + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_mf_ul_type(dev_data->mf_ul_data.type, true)); + } else if(protocol == NfcDeviceProtocolMifareClassic) { + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); + } else if(protocol == NfcDeviceProtocolMifareDesfire) { + furi_string_cat_printf(temp_str, "\e#MIFARE DESFire\n"); + } else if(protocol == NfcDeviceProtocolNfcV) { + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "\e#ISO15693\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n"); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n"); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n"); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } + } else { + furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); + } + + // Set tag iso data + if(protocol == NfcDeviceProtocolNfcV) { + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + + furi_string_cat_printf(temp_str, "UID:\n"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf(temp_str, "\n"); + + furi_string_cat_printf(temp_str, "IC Ref: %d\n", nfcv_data->ic_ref); + furi_string_cat_printf(temp_str, "Blocks: %d\n", nfcv_data->block_num); + furi_string_cat_printf(temp_str, "Blocksize: %d\n", nfcv_data->block_size); + + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "Type: Plain\n"); + break; + case NfcVTypeSlix: + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix, "SLIX"); + break; + case NfcVTypeSlixS: + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixS, "SLIX-S"); + break; + case NfcVTypeSlixL: + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixL, "SLIX-L"); + break; + case NfcVTypeSlix2: + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix2, "SLIX2"); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } + + furi_string_cat_printf( + temp_str, "Data (%d byte)\n", nfcv_data->block_num * nfcv_data->block_size); + + int maxBlocks = nfcv_data->block_num; + if(maxBlocks > 32) { + maxBlocks = 32; + furi_string_cat_printf(temp_str, "(truncated to %d blocks)\n", maxBlocks); + } + + for(int block = 0; block < maxBlocks; block++) { + const char* status = (nfcv_data->security_status[block] & 0x01) ? "(lck)" : ""; + for(int pos = 0; pos < nfcv_data->block_size; pos++) { + furi_string_cat_printf( + temp_str, " %02X", nfcv_data->data[block * nfcv_data->block_size + pos]); + } + furi_string_cat_printf(temp_str, " %s\n", status); + } + + } else { + char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; + furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf( + temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); + furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); + } + + // Set application specific data + if(protocol == NfcDeviceProtocolMifareDesfire) { + MifareDesfireData* data = &dev_data->mf_df_data; + uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1); + uint32_t bytes_free = data->free_memory ? data->free_memory->bytes : 0; + furi_string_cat_printf(temp_str, "\n%lu", bytes_total); + if(data->version.sw_storage & 1) { + furi_string_push_back(temp_str, '+'); + } + furi_string_cat_printf(temp_str, " bytes, %lu bytes free\n", bytes_free); + + uint16_t n_apps = 0; + uint16_t n_files = 0; + for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { + n_apps++; + for(MifareDesfireFile* file = app->file_head; file; file = file->next) { + n_files++; + } + } + furi_string_cat_printf(temp_str, "%d Application", n_apps); + if(n_apps != 1) { + furi_string_push_back(temp_str, 's'); + } + furi_string_cat_printf(temp_str, ", %d file", n_files); + if(n_files != 1) { + furi_string_push_back(temp_str, 's'); + } + } else if(protocol == NfcDeviceProtocolMifareUl) { + MfUltralightData* data = &dev_data->mf_ul_data; + furi_string_cat_printf( + temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4); + if(data->data_size > data->data_read) { + furi_string_cat_printf(temp_str, "\nPassword-protected"); + } else if(data->auth_success) { + MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); + if(config_pages) { + furi_string_cat_printf( + temp_str, + "\nPassword: %02X %02X %02X %02X", + config_pages->auth_data.pwd.raw[0], + config_pages->auth_data.pwd.raw[1], + config_pages->auth_data.pwd.raw[2], + config_pages->auth_data.pwd.raw[3]); + furi_string_cat_printf( + temp_str, + "\nPACK: %02X %02X", + config_pages->auth_data.pack.raw[0], + config_pages->auth_data.pack.raw[1]); + } + } + } else if(protocol == NfcDeviceProtocolMifareClassic) { + MfClassicData* data = &dev_data->mf_classic_data; + uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + uint8_t keys_total = sectors_total * 2; + uint8_t keys_found = 0; + uint8_t sectors_read = 0; + mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); + furi_string_cat_printf(temp_str, "\nKeys Found %d/%d", keys_found, keys_total); + furi_string_cat_printf(temp_str, "\nSectors Read %d/%d", sectors_read, sectors_total); + } + + // Add text scroll widget + widget_add_text_scroll_element( + widget, 0, 0, 128, text_scroll_height, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcProtocol protocol = nfc->dev->dev_data.protocol; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + if(protocol == NfcDeviceProtocolMifareDesfire) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData); + consumed = true; + } else if(protocol == NfcDeviceProtocolMifareUl) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData); + consumed = true; + } else if(protocol == NfcDeviceProtocolMifareClassic) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); + } else if(protocol == NfcDeviceProtocolNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); + consumed = true; + } + } + } + + return consumed; +} + +void nfc_scene_nfc_data_info_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c b/applications/main/nfc_old/scenes/nfc_scene_nfca_menu.c similarity index 95% rename from applications/main/nfc/scenes/nfc_scene_nfca_menu.c rename to applications/main/nfc_old/scenes/nfc_scene_nfca_menu.c index 30f63945c622..9779470a387b 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c +++ b/applications/main/nfc_old/scenes/nfc_scene_nfca_menu.c @@ -43,9 +43,9 @@ bool nfc_scene_nfca_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexEmulateUid) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexInfo) { diff --git a/applications/main/nfc/scenes/nfc_scene_nfca_read_success.c b/applications/main/nfc_old/scenes/nfc_scene_nfca_read_success.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_nfca_read_success.c rename to applications/main/nfc_old/scenes/nfc_scene_nfca_read_success.c diff --git a/applications/main/nfc_old/scenes/nfc_scene_nfcv_emulate.c b/applications/main/nfc_old/scenes/nfc_scene_nfcv_emulate.c new file mode 100644 index 000000000000..d812988bdfd2 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_nfcv_emulate.c @@ -0,0 +1,169 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200) + +enum { + NfcSceneNfcVEmulateStateWidget, + NfcSceneNfcVEmulateStateTextBox, +}; + +bool nfc_scene_nfcv_emulate_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + Nfc* nfc = context; + + switch(event) { + case NfcWorkerEventNfcVCommandExecuted: + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); + } + break; + case NfcWorkerEventNfcVContentChanged: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); + break; + default: + break; + } + return true; +} + +void nfc_scene_nfcv_emulate_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_emulate_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) { + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); + widget_add_string_multiline_element( + widget, 87, 13, AlignCenter, AlignTop, FontPrimary, "Emulating\nNFC V"); + if(strcmp(nfc->dev->dev_name, "") != 0) { + furi_string_printf(info_str, "%s", nfc->dev->dev_name); + } else { + for(uint8_t i = 0; i < data->uid_len; i++) { + furi_string_cat_printf(info_str, "%02X ", data->uid[i]); + } + } + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 52, 40, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_emulate_widget_callback, nfc); + } + } +} + +void nfc_scene_nfcv_emulate_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_nfcv_emulate_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + text_box_set_text(text_box, ""); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_scene_nfcv_emulate_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_nfcv_emulate_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVEmulate); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventUpdateLog) { + // Add data button to widget if data is received for the first time + if(strlen(nfcv_data->last_command) > 0) { + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_nfcv_emulate_widget_config(nfc, true); + } + /* use the last n bytes from the log so there's enough space for the new log entry */ + size_t maxSize = + NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); + if(furi_string_size(nfc->text_box_store) >= maxSize) { + furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); + } + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + + /* clear previously logged command */ + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == NfcCustomEventSaveShadow) { + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVEmulateStateWidget) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateTextBox); + } + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVEmulateStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneNfcVEmulateStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_nfcv_emulate_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_nfcv_key_input.c b/applications/main/nfc_old/scenes/nfc_scene_nfcv_key_input.c new file mode 100644 index 000000000000..13d903c4b777 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_nfcv_key_input.c @@ -0,0 +1,48 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_nfcv_key_input_byte_input_callback(void* context) { + Nfc* nfc = context; + NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; + + memcpy(data->key_privacy, nfc->byte_input_store, 4); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_nfcv_key_input_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter The Password In Hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_nfcv_key_input_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 4); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); + dolphin_deed(DolphinDeedNfcRead); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_nfcv_key_input_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc_old/scenes/nfc_scene_nfcv_menu.c new file mode 100644 index 000000000000..60eb354e858d --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_nfcv_menu.c @@ -0,0 +1,68 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexSave, + SubmenuIndexEmulate, + SubmenuIndexInfo, +}; + +void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_nfcv_menu_submenu_callback, nfc); + submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); + submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_nfcv_menu_submenu_callback, nfc); + + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSave) { + nfc->dev->format = NfcDeviceSaveFormatNfcV; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + dolphin_deed(DolphinDeedNfcAddEmulate); + } else { + dolphin_deed(DolphinDeedNfcEmulate); + } + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_nfcv_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_nfcv_read_success.c b/applications/main/nfc_old/scenes/nfc_scene_nfcv_read_success.c new file mode 100644 index 000000000000..04e60611d00f --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_nfcv_read_success.c @@ -0,0 +1,92 @@ +#include "../nfc_i.h" + +void nfc_scene_nfcv_read_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_read_success_on_enter(void* context) { + Nfc* nfc = context; + NfcDeviceData* dev_data = &nfc->dev->dev_data; + FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; + // Setup view + Widget* widget = nfc->widget; + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_nfcv_read_success_widget_callback, nfc); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_scene_nfcv_read_success_widget_callback, nfc); + + FuriString* temp_str = furi_string_alloc(); + + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "\e#ISO15693\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n"); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n"); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n"); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } + furi_string_cat_printf(temp_str, "UID:\n"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf(temp_str, "\n"); + furi_string_cat_printf(temp_str, "(see More->Info for details)\n"); + + widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + notification_message_block(nfc->notifications, &sequence_set_green_255); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_nfcv_read_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + + return consumed; +} + +void nfc_scene_nfcv_read_success_on_exit(void* context) { + Nfc* nfc = context; + + notification_message_block(nfc->notifications, &sequence_reset_green); + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_nfcv_sniff.c b/applications/main/nfc_old/scenes/nfc_scene_nfcv_sniff.c new file mode 100644 index 000000000000..2c0f17981b46 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_nfcv_sniff.c @@ -0,0 +1,155 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (800) + +enum { + NfcSceneNfcVSniffStateWidget, + NfcSceneNfcVSniffStateTextBox, +}; + +bool nfc_scene_nfcv_sniff_worker_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + furi_assert(context); + Nfc* nfc = context; + + switch(event) { + case NfcWorkerEventNfcVCommandExecuted: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); + break; + case NfcWorkerEventNfcVContentChanged: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); + break; + default: + break; + } + return true; +} + +void nfc_scene_nfcv_sniff_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_sniff_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +static void nfc_scene_nfcv_sniff_widget_config(Nfc* nfc, bool data_received) { + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); + widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Listen NfcV"); + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_sniff_widget_callback, nfc); + } +} + +void nfc_scene_nfcv_sniff_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_nfcv_sniff_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + text_box_set_text(text_box, ""); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVSniff, + &nfc->dev->dev_data, + nfc_scene_nfcv_sniff_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_nfcv_sniff_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVSniff); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventUpdateLog) { + // Add data button to widget if data is received for the first time + if(strlen(nfcv_data->last_command) > 0) { + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_nfcv_sniff_widget_config(nfc, true); + } + /* use the last n bytes from the log so there's enough space for the new log entry */ + size_t maxSize = + NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); + if(furi_string_size(nfc->text_box_store) >= maxSize) { + furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); + } + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + + /* clear previously logged command */ + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == NfcCustomEventSaveShadow) { + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVSniffStateWidget) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateTextBox); + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVSniffStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneNfcVSniffStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_nfcv_sniff_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc_old/scenes/nfc_scene_nfcv_unlock.c new file mode 100644 index 000000000000..38d7ad563d8d --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_nfcv_unlock.c @@ -0,0 +1,154 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneNfcVUnlockStateIdle, + NfcSceneNfcVUnlockStateDetecting, + NfcSceneNfcVUnlockStateUnlocked, + NfcSceneNfcVUnlockStateAlreadyUnlocked, + NfcSceneNfcVUnlockStateNotSupportedCard, +} NfcSceneNfcVUnlockState; + +static bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; + + if(event == NfcWorkerEventNfcVPassKey) { + memcpy(data->key_privacy, nfc->byte_input_store, 4); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + } + return true; +} + +void nfc_scene_nfcv_unlock_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { + FuriHalNfcDevData* nfc_data = &(nfc->dev->dev_data.nfc_data); + NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data); + + uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock); + if(curr_state != state) { + Popup* popup = nfc->popup; + if(state == NfcSceneNfcVUnlockStateDetecting) { + popup_reset(popup); + popup_set_text( + popup, "Put figurine on\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50); + } else if(state == NfcSceneNfcVUnlockStateUnlocked) { + popup_reset(popup); + + if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { + snprintf( + nfc->dev->dev_name, + sizeof(nfc->dev->dev_name), + "SLIX_%02X%02X%02X%02X%02X%02X%02X%02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + + nfc->dev->format = NfcDeviceSaveFormatNfcV; + + if(nfc_save_file(nfc)) { + popup_set_header(popup, "Successfully\nsaved", 94, 3, AlignCenter, AlignTop); + } else { + popup_set_header( + popup, "Unlocked but\nsave failed!", 94, 3, AlignCenter, AlignTop); + } + } else { + popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop); + } + + notification_message(nfc->notifications, &sequence_single_vibro); + //notification_message(nfc->notifications, &sequence_success); + + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + dolphin_deed(DolphinDeedNfcReadSuccess); + + } else if(state == NfcSceneNfcVUnlockStateAlreadyUnlocked) { + popup_reset(popup); + + popup_set_header(popup, "Already\nUnlocked!", 94, 3, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + } else if(state == NfcSceneNfcVUnlockStateNotSupportedCard) { + popup_reset(popup); + popup_set_header(popup, "Wrong Type Of Card!", 64, 3, AlignCenter, AlignTop); + popup_set_text(popup, nfcv_data->error, 4, 22, AlignLeft, AlignTop); + popup_set_icon(popup, 73, 20, &I_DolphinCommon_56x48); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock, state); + } +} + +void nfc_scene_nfcv_unlock_on_enter(void* context) { + Nfc* nfc = context; + + nfc_device_clear(nfc->dev); + // Setup view + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVUnlockAndSave, + &nfc->dev->dev_data, + nfc_scene_nfcv_unlock_worker_callback, + nfc); + + nfc_blink_read_start(nfc); +} + +bool nfc_scene_nfcv_unlock_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventAborted) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateAlreadyUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateNotSupportedCard); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneNfcVUnlockMenu); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + nfc_blink_stop(nfc); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVUnlock, NfcSceneNfcVUnlockStateIdle); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_nfcv_unlock_menu.c b/applications/main/nfc_old/scenes/nfc_scene_nfcv_unlock_menu.c new file mode 100644 index 000000000000..2f7367256745 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_nfcv_unlock_menu.c @@ -0,0 +1,60 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexNfcVUnlockMenuManual, + SubmenuIndexNfcVUnlockMenuTonieBox, +}; + +void nfc_scene_nfcv_unlock_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_unlock_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + submenu_add_item( + submenu, + "Enter PWD Manually", + SubmenuIndexNfcVUnlockMenuManual, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As TonieBox", + SubmenuIndexNfcVUnlockMenuTonieBox, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNfcVUnlockMenuManual) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodManual; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); + dolphin_deed(DolphinDeedNfcRead); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu, event.event); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_menu_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_read.c b/applications/main/nfc_old/scenes/nfc_scene_read.c new file mode 100644 index 000000000000..1690a9557572 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_read.c @@ -0,0 +1,123 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneReadStateIdle, + NfcSceneReadStateDetecting, + NfcSceneReadStateReading, +} NfcSceneReadState; + +bool nfc_scene_read_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + bool consumed = false; + if(event == NfcWorkerEventReadMfClassicLoadKeyCache) { + consumed = nfc_device_load_key_cache(nfc->dev); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + consumed = true; + } + return consumed; +} + +void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) { + uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneRead); + if(curr_state != state) { + if(state == NfcSceneReadStateDetecting) { + popup_reset(nfc->popup); + popup_set_text( + nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); + } else if(state == NfcSceneReadStateReading) { + popup_reset(nfc->popup); + popup_set_header( + nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); + popup_set_icon(nfc->popup, 12, 23, &A_Loading_24); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneRead, state); + } +} + +void nfc_scene_read_on_enter(void* context) { + Nfc* nfc = context; + + nfc_device_clear(nfc->dev); + // Setup view + nfc_scene_read_set_state(nfc, NfcSceneReadStateDetecting); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + // Start worker + nfc_worker_start( + nfc->worker, NfcWorkerStateRead, &nfc->dev->dev_data, nfc_scene_read_worker_callback, nfc); + + nfc_blink_read_start(nfc); +} + +bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if((event.event == NfcWorkerEventReadUidNfcB) || + (event.event == NfcWorkerEventReadUidNfcF) || + (event.event == NfcWorkerEventReadUidNfcV)) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventReadUidNfcA) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventReadNfcV) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventReadMfUltralight) { + notification_message(nfc->notifications, &sequence_success); + // Set unlock password input to 0xFFFFFFFF only on fresh read + memset(nfc->byte_input_store, 0xFF, sizeof(nfc->byte_input_store)); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventReadMfClassicDone) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventReadMfDesfire) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { + if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + } + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + nfc_scene_read_set_state(nfc, NfcSceneReadStateReading); + nfc_blink_detect_start(nfc); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_read_set_state(nfc, NfcSceneReadStateDetecting); + nfc_blink_read_start(nfc); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_read_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneRead, NfcSceneReadStateIdle); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_success.c b/applications/main/nfc_old/scenes/nfc_scene_read_card_success.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_read_card_success.c rename to applications/main/nfc_old/scenes/nfc_scene_read_card_success.c diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_type.c b/applications/main/nfc_old/scenes/nfc_scene_read_card_type.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_read_card_type.c rename to applications/main/nfc_old/scenes/nfc_scene_read_card_type.c diff --git a/applications/main/nfc_old/scenes/nfc_scene_restore_original.c b/applications/main/nfc_old/scenes/nfc_scene_restore_original.c new file mode 100644 index 000000000000..3ecf5c048e41 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_restore_original.c @@ -0,0 +1,45 @@ +#include "../nfc_i.h" + +void nfc_scene_restore_original_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_restore_original_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + Popup* popup = nfc->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Original file\nrestored", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_restore_original_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneSavedMenu); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + } + } + return consumed; +} + +void nfc_scene_restore_original_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_restore_original_confirm.c b/applications/main/nfc_old/scenes/nfc_scene_restore_original_confirm.c new file mode 100644 index 000000000000..16b0953f8096 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_restore_original_confirm.c @@ -0,0 +1,53 @@ +#include "../nfc_i.h" + +void nfc_scene_restore_original_confirm_dialog_callback(DialogExResult result, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_restore_original_confirm_on_enter(void* context) { + Nfc* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_icon(dialog_ex, 5, 11, &I_ArrowC_1_36x36); + dialog_ex_set_text( + dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Restore"); + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_restore_original_confirm_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_restore_original_confirm_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + if(!nfc_device_restore(nfc->dev, true)) { + scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginal); + } + consumed = true; + } else if(event.event == DialogExResultLeft) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void nfc_scene_restore_original_confirm_on_exit(void* context) { + Nfc* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_retry_confirm.c b/applications/main/nfc_old/scenes/nfc_scene_retry_confirm.c new file mode 100644 index 000000000000..5f4f7985e777 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_retry_confirm.c @@ -0,0 +1,47 @@ +#include "../nfc_i.h" + +void nfc_scene_retry_confirm_dialog_callback(DialogExResult result, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_retry_confirm_on_enter(void* context) { + Nfc* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_left_button_text(dialog_ex, "Retry"); + dialog_ex_set_right_button_text(dialog_ex, "Stay"); + dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop); + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else if(event.event == DialogExResultLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneRead); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void nfc_scene_retry_confirm_on_exit(void* context) { + Nfc* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc_old/scenes/nfc_scene_rpc.c similarity index 90% rename from applications/main/nfc/scenes/nfc_scene_rpc.c rename to applications/main/nfc_old/scenes/nfc_scene_rpc.c index 60d01a30da66..d06ee7564658 100644 --- a/applications/main/nfc/scenes/nfc_scene_rpc.c +++ b/applications/main/nfc_old/scenes/nfc_scene_rpc.c @@ -55,6 +55,13 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { &nfc->dev->dev_data, nfc_scene_rpc_emulate_callback, nfc); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_scene_rpc_emulate_callback, + nfc); } else { nfc_worker_start( nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); diff --git a/applications/main/nfc_old/scenes/nfc_scene_save_name.c b/applications/main/nfc_old/scenes/nfc_scene_save_name.c new file mode 100644 index 000000000000..a7b97aac06b3 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_save_name.c @@ -0,0 +1,93 @@ +#include "../nfc_i.h" +#include +#include +#include +#include + +void nfc_scene_save_name_text_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); +} + +void nfc_scene_save_name_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + TextInput* text_input = nfc->text_input; + bool dev_name_empty = false; + if(!strcmp(nfc->dev->dev_name, "")) { + set_random_name(nfc->text_store, sizeof(nfc->text_store)); + dev_name_empty = true; + } else { + nfc_text_store_set(nfc, nfc->dev->dev_name); + } + text_input_set_header_text(text_input, "Name the card"); + text_input_set_result_callback( + text_input, + nfc_scene_save_name_text_input_callback, + nfc, + nfc->text_store, + NFC_DEV_NAME_MAX_LEN, + dev_name_empty); + + FuriString* folder_path; + folder_path = furi_string_alloc(); + + if(furi_string_end_with(nfc->dev->load_path, NFC_APP_EXTENSION)) { + path_extract_dirname(furi_string_get_cstr(nfc->dev->load_path), folder_path); + } else { + furi_string_set(folder_path, NFC_APP_FOLDER); + } + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + furi_string_get_cstr(folder_path), NFC_APP_EXTENSION, nfc->dev->dev_name); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); + + furi_string_free(folder_path); +} + +bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventTextInputDone) { + if(strcmp(nfc->dev->dev_name, "") != 0) { + nfc_device_delete(nfc->dev, true); + } + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetUid)) { + nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; + } + strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1); + if(nfc_save_file(nfc)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + // Nothing, do not count editing as saving + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + dolphin_deed(DolphinDeedNfcAddSave); + } else { + dolphin_deed(DolphinDeedNfcSave); + } + consumed = true; + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + } + } + return consumed; +} + +void nfc_scene_save_name_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + void* validator_context = text_input_get_validator_callback_context(nfc->text_input); + text_input_set_validator(nfc->text_input, NULL, NULL); + validator_is_file_free(validator_context); + + text_input_reset(nfc->text_input); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_save_success.c b/applications/main/nfc_old/scenes/nfc_scene_save_success.c new file mode 100644 index 000000000000..34919cbd863a --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_save_success.c @@ -0,0 +1,48 @@ +#include "../nfc_i.h" + +void nfc_scene_save_success_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_save_success_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + Popup* popup = nfc->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_save_success_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneSavedMenu); + } else { + consumed = scene_manager_search_and_switch_to_another_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } + } + return consumed; +} + +void nfc_scene_save_success_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_saved_menu.c b/applications/main/nfc_old/scenes/nfc_scene_saved_menu.c new file mode 100644 index 000000000000..b3205554a431 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_saved_menu.c @@ -0,0 +1,186 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexEmulate, + SubmenuIndexEditUid, + SubmenuIndexDetectReader, + SubmenuIndexWrite, + SubmenuIndexUpdate, + SubmenuIndexRename, + SubmenuIndexDelete, + SubmenuIndexInfo, + SubmenuIndexRestoreOriginal, + SubmenuIndexMfUlUnlockByReader, + SubmenuIndexMfUlUnlockByPassword, +}; + +void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_saved_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + if(nfc->dev->format == NfcDeviceSaveFormatUid || + nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) { + submenu_add_item( + submenu, + "Emulate UID", + SubmenuIndexEmulate, + nfc_scene_saved_menu_submenu_callback, + nfc); + if(nfc->dev->dev_data.protocol == NfcDeviceProtocolUnknown) { + submenu_add_item( + submenu, + "Edit UID", + SubmenuIndexEditUid, + nfc_scene_saved_menu_submenu_callback, + nfc); + } + } else if( + (nfc->dev->format == NfcDeviceSaveFormatMifareUl && + mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) || + nfc->dev->format == NfcDeviceSaveFormatNfcV || + nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); + } + if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) { + submenu_add_item( + submenu, + "Detect Reader", + SubmenuIndexDetectReader, + nfc_scene_saved_menu_submenu_callback, + nfc); + } + submenu_add_item( + submenu, + "Write to Initial Card", + SubmenuIndexWrite, + nfc_scene_saved_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Update from Initial Card", + SubmenuIndexUpdate, + nfc_scene_saved_menu_submenu_callback, + nfc); + } + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); + if(nfc->dev->format == NfcDeviceSaveFormatMifareUl && + nfc->dev->dev_data.mf_ul_data.type != MfUltralightTypeULC && + !mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) { + submenu_add_item( + submenu, + "Unlock with Reader", + SubmenuIndexMfUlUnlockByReader, + nfc_scene_saved_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Unlock with Password", + SubmenuIndexMfUlUnlockByPassword, + nfc_scene_saved_menu_submenu_callback, + nfc); + } + if(nfc->dev->shadow_file_exist) { + submenu_add_item( + submenu, + "Restore to original", + SubmenuIndexRestoreOriginal, + nfc_scene_saved_menu_submenu_callback, + nfc); + } + submenu_add_item( + submenu, "Rename", SubmenuIndexRename, nfc_scene_saved_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Delete", SubmenuIndexDelete, nfc_scene_saved_menu_submenu_callback, nfc); + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSavedMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcDeviceData* dev_data = &nfc->dev->dev_data; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, event.event); + if(event.event == SubmenuIndexEmulate) { + if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); + } + dolphin_deed(DolphinDeedNfcEmulate); + consumed = true; + } else if(event.event == SubmenuIndexDetectReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + dolphin_deed(DolphinDeedNfcDetectReader); + consumed = true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); + consumed = true; + } else if(event.event == SubmenuIndexUpdate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdate); + consumed = true; + } else if(event.event == SubmenuIndexRename) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEditUid) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + consumed = true; + } else if(event.event == SubmenuIndexDelete) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDelete); + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + bool application_info_present = false; + if(dev_data->protocol == NfcDeviceProtocolEMV) { + application_info_present = true; + } else if( + dev_data->protocol == NfcDeviceProtocolMifareClassic || + dev_data->protocol == NfcDeviceProtocolMifareDesfire || + dev_data->protocol == NfcDeviceProtocolMifareUl) { + application_info_present = nfc_supported_card_verify_and_parse(dev_data); + } + + FURI_LOG_I("nfc", "application_info_present: %d", application_info_present); + + if(application_info_present) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); + } + consumed = true; + } else if(event.event == SubmenuIndexRestoreOriginal) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByReader) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockByPassword) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_saved_menu_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_set_atqa.c b/applications/main/nfc_old/scenes/nfc_scene_set_atqa.c new file mode 100644 index 000000000000..d079b3804754 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_set_atqa.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" + +void nfc_scene_set_atqa_byte_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_set_atqa_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter ATQA in hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_set_atqa_byte_input_callback, + NULL, + nfc, + nfc->dev->dev_data.nfc_data.atqa, + 2); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_set_atqa_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_set_sak.c b/applications/main/nfc_old/scenes/nfc_scene_set_sak.c new file mode 100644 index 000000000000..60a1e1494b84 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_set_sak.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" + +void nfc_scene_set_sak_byte_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_set_sak_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter SAK in hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_set_sak_byte_input_callback, + NULL, + nfc, + &nfc->dev->dev_data.nfc_data.sak, + 1); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqa); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_set_sak_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_set_type.c b/applications/main/nfc_old/scenes/nfc_scene_set_type.c new file mode 100644 index 000000000000..cadf2eb69ef3 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_set_type.c @@ -0,0 +1,68 @@ +#include "../nfc_i.h" +#include "lib/nfc/helpers/nfc_generators.h" + +enum SubmenuIndex { + SubmenuIndexNFCA4, + SubmenuIndexNFCA7, + SubmenuIndexGeneratorsStart, +}; + +void nfc_scene_set_type_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_set_type_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + furi_string_set(nfc->dev->load_path, ""); + submenu_add_item( + submenu, "NFC-A 7-bytes UID", SubmenuIndexNFCA7, nfc_scene_set_type_submenu_callback, nfc); + submenu_add_item( + submenu, "NFC-A 4-bytes UID", SubmenuIndexNFCA4, nfc_scene_set_type_submenu_callback, nfc); + + // Generators + int i = SubmenuIndexGeneratorsStart; + for(const NfcGenerator* const* generator = nfc_generators; *generator != NULL; + ++generator, ++i) { + submenu_add_item(submenu, (*generator)->name, i, nfc_scene_set_type_submenu_callback, nfc); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNFCA7) { + nfc->dev->dev_data.nfc_data.uid_len = 7; + nfc->dev->format = NfcDeviceSaveFormatUid; + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); + consumed = true; + } else if(event.event == SubmenuIndexNFCA4) { + nfc->dev->dev_data.nfc_data.uid_len = 4; + nfc->dev->format = NfcDeviceSaveFormatUid; + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); + consumed = true; + } else { + nfc_device_clear(nfc->dev); + nfc->generator = nfc_generators[event.event - SubmenuIndexGeneratorsStart]; + nfc->generator->generator_func(&nfc->dev->dev_data); + + scene_manager_next_scene(nfc->scene_manager, NfcSceneGenerateInfo); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_set_type_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_set_uid.c b/applications/main/nfc_old/scenes/nfc_scene_set_uid.c new file mode 100644 index 000000000000..54606b68eec7 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_set_uid.c @@ -0,0 +1,54 @@ +#include "../nfc_i.h" + +void nfc_scene_set_uid_byte_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_set_uid_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter UID in hex"); + nfc->dev_edit_data = nfc->dev->dev_data.nfc_data; + byte_input_set_result_callback( + byte_input, + nfc_scene_set_uid_byte_input_callback, + NULL, + nfc, + nfc->dev_edit_data.uid, + nfc->dev_edit_data.uid_len); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = (Nfc*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; + if(nfc_save_file(nfc)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + consumed = true; + } + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } + } + } + + return consumed; +} + +void nfc_scene_set_uid_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/main/nfc_old/scenes/nfc_scene_start.c b/applications/main/nfc_old/scenes/nfc_scene_start.c new file mode 100644 index 000000000000..c9e8bf78cf59 --- /dev/null +++ b/applications/main/nfc_old/scenes/nfc_scene_start.c @@ -0,0 +1,98 @@ +#include "../nfc_i.h" +#include "nfc_worker_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexRead, + SubmenuIndexDetectReader, + SubmenuIndexSaved, + SubmenuIndexExtraAction, + SubmenuIndexAddManually, + SubmenuIndexDebug, +}; + +void nfc_scene_start_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_start_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item(submenu, "Read", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); + submenu_add_item( + submenu, "Detect Reader", SubmenuIndexDetectReader, nfc_scene_start_submenu_callback, nfc); + submenu_add_item(submenu, "Saved", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc); + submenu_add_item( + submenu, "Extra Actions", SubmenuIndexExtraAction, nfc_scene_start_submenu_callback, nfc); + submenu_add_item( + submenu, "Add Manually", SubmenuIndexAddManually, nfc_scene_start_submenu_callback, nfc); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + submenu_add_item( + submenu, "Debug", SubmenuIndexDebug, nfc_scene_start_submenu_callback, nfc); + } + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart)); + + nfc_device_clear(nfc->dev); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); + nfc->dev->dev_data.read_mode = NfcReadModeAuto; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + dolphin_deed(DolphinDeedNfcRead); + consumed = true; + } else if(event.event == SubmenuIndexDetectReader) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexDetectReader); + bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; + if(sd_exist) { + nfc_device_data_clear(&nfc->dev->dev_data); + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + dolphin_deed(DolphinDeedNfcDetectReader); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + } + consumed = true; + } else if(event.event == SubmenuIndexSaved) { + // Save the scene state explicitly in each branch, so that + // if the user cancels loading a file, the Saved menu item + // is properly reselected. + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved); + scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); + consumed = true; + } else if(event.event == SubmenuIndexExtraAction) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexExtraAction); + scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions); + consumed = true; + } else if(event.event == SubmenuIndexAddManually) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); + consumed = true; + } else if(event.event == SubmenuIndexDebug) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug); + scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_start_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc_old/views/detect_reader.c b/applications/main/nfc_old/views/detect_reader.c new file mode 100644 index 000000000000..e5951beb2674 --- /dev/null +++ b/applications/main/nfc_old/views/detect_reader.c @@ -0,0 +1,190 @@ +#include "detect_reader.h" +#include +#include + +#define DETECT_READER_UID_MAX_LEN (10) + +struct DetectReader { + View* view; + DetectReaderDoneCallback callback; + void* context; +}; + +typedef struct { + uint16_t nonces; + uint16_t nonces_max; + DetectReaderState state; + FuriString* uid_str; +} DetectReaderViewModel; + +static void detect_reader_draw_callback(Canvas* canvas, void* model) { + DetectReaderViewModel* m = model; + char text[32] = {}; + + // Draw header and icon + canvas_draw_icon(canvas, 0, 16, &I_Modern_reader_18x34); + if(m->state == DetectReaderStateStart) { + snprintf(text, sizeof(text), "Touch the reader"); + canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39); + if(furi_string_size(m->uid_str)) { + elements_multiline_text_aligned( + canvas, 64, 64, AlignCenter, AlignBottom, furi_string_get_cstr(m->uid_str)); + } + } else if(m->state == DetectReaderStateReaderDetected) { + snprintf(text, sizeof(text), "Move the Flipper away"); + canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15); + } else if(m->state == DetectReaderStateReaderLost) { + snprintf(text, sizeof(text), "Touch the reader again"); + canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39); + } + + canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, text); + + // Draw collected nonces + if(m->state == DetectReaderStateStart) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Emulating..."); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 51, 35, AlignLeft, AlignTop, "MIFARE MFkey32"); + } else { + if(m->state == DetectReaderStateDone) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Completed!"); + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Collecting..."); + } + canvas_set_font(canvas, FontSecondary); + snprintf(text, sizeof(text), "Nonce pairs: %d/%d", m->nonces, m->nonces_max); + canvas_draw_str_aligned(canvas, 51, 35, AlignLeft, AlignTop, text); + } + // Draw button + if(m->nonces > 0) { + elements_button_center(canvas, "Done"); + } +} + +static bool detect_reader_input_callback(InputEvent* event, void* context) { + DetectReader* detect_reader = context; + furi_assert(detect_reader->callback); + bool consumed = false; + + uint8_t nonces = 0; + with_view_model( + detect_reader->view, DetectReaderViewModel * model, { nonces = model->nonces; }, false); + + if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + if(nonces > 0) { + detect_reader->callback(detect_reader->context); + consumed = true; + } + } + } + + return consumed; +} + +DetectReader* detect_reader_alloc() { + DetectReader* detect_reader = malloc(sizeof(DetectReader)); + detect_reader->view = view_alloc(); + view_allocate_model(detect_reader->view, ViewModelTypeLocking, sizeof(DetectReaderViewModel)); + view_set_draw_callback(detect_reader->view, detect_reader_draw_callback); + view_set_input_callback(detect_reader->view, detect_reader_input_callback); + view_set_context(detect_reader->view, detect_reader); + + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { model->uid_str = furi_string_alloc(); }, + false); + + return detect_reader; +} + +void detect_reader_free(DetectReader* detect_reader) { + furi_assert(detect_reader); + + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { furi_string_free(model->uid_str); }, + false); + + view_free(detect_reader->view); + free(detect_reader); +} + +void detect_reader_reset(DetectReader* detect_reader) { + furi_assert(detect_reader); + + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { + model->nonces = 0; + model->nonces_max = 0; + model->state = DetectReaderStateStart; + furi_string_reset(model->uid_str); + }, + false); +} + +View* detect_reader_get_view(DetectReader* detect_reader) { + furi_assert(detect_reader); + + return detect_reader->view; +} + +void detect_reader_set_callback( + DetectReader* detect_reader, + DetectReaderDoneCallback callback, + void* context) { + furi_assert(detect_reader); + furi_assert(callback); + + detect_reader->callback = callback; + detect_reader->context = context; +} + +void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_max) { + furi_assert(detect_reader); + + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { model->nonces_max = nonces_max; }, + false); +} + +void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected) { + furi_assert(detect_reader); + + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { model->nonces = nonces_collected; }, + false); +} + +void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state) { + furi_assert(detect_reader); + with_view_model( + detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true); +} + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len) { + furi_assert(detect_reader); + furi_assert(uid); + furi_assert(uid_len < DETECT_READER_UID_MAX_LEN); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { + furi_string_set_str(model->uid_str, "UID:"); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(model->uid_str, " %02X", uid[i]); + } + }, + true); +} diff --git a/applications/main/nfc_old/views/detect_reader.h b/applications/main/nfc_old/views/detect_reader.h new file mode 100644 index 000000000000..6481216b4cf4 --- /dev/null +++ b/applications/main/nfc_old/views/detect_reader.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include + +typedef struct DetectReader DetectReader; + +typedef enum { + DetectReaderStateStart, + DetectReaderStateReaderDetected, + DetectReaderStateReaderLost, + DetectReaderStateDone, +} DetectReaderState; + +typedef void (*DetectReaderDoneCallback)(void* context); + +DetectReader* detect_reader_alloc(); + +void detect_reader_free(DetectReader* detect_reader); + +void detect_reader_reset(DetectReader* detect_reader); + +View* detect_reader_get_view(DetectReader* detect_reader); + +void detect_reader_set_callback( + DetectReader* detect_reader, + DetectReaderDoneCallback callback, + void* context); + +void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_max); + +void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected); + +void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state); + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len); diff --git a/applications/main/nfc_old/views/dict_attack.c b/applications/main/nfc_old/views/dict_attack.c new file mode 100644 index 000000000000..8f4bd063e8b8 --- /dev/null +++ b/applications/main/nfc_old/views/dict_attack.c @@ -0,0 +1,289 @@ +#include "dict_attack.h" + +#include + +typedef enum { + DictAttackStateRead, + DictAttackStateCardRemoved, +} DictAttackState; + +struct DictAttack { + View* view; + DictAttackCallback callback; + void* context; + bool card_present; +}; + +typedef struct { + DictAttackState state; + MfClassicType type; + FuriString* header; + uint8_t sectors_total; + uint8_t sectors_read; + uint8_t sector_current; + uint8_t keys_total; + uint8_t keys_found; + uint16_t dict_keys_total; + uint16_t dict_keys_current; + bool is_key_attack; + uint8_t key_attack_current_sector; +} DictAttackViewModel; + +static void dict_attack_draw_callback(Canvas* canvas, void* model) { + DictAttackViewModel* m = model; + if(m->state == DictAttackStateCardRemoved) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!"); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned( + canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); + } else if(m->state == DictAttackStateRead) { + char draw_str[32] = {}; + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); + if(m->is_key_attack) { + snprintf( + draw_str, + sizeof(draw_str), + "Reuse key check for sector: %d", + m->key_attack_current_sector); + } else { + snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current); + } + canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); + float dict_progress = m->dict_keys_total == 0 ? + 0 : + (float)(m->dict_keys_current) / (float)(m->dict_keys_total); + float progress = m->sectors_total == 0 ? 0 : + ((float)(m->sector_current) + dict_progress) / + (float)(m->sectors_total); + if(progress > 1.0) { + progress = 1.0; + } + if(m->dict_keys_current == 0) { + // Cause when people see 0 they think it's broken + snprintf(draw_str, sizeof(draw_str), "%d/%d", 1, m->dict_keys_total); + } else { + snprintf( + draw_str, sizeof(draw_str), "%d/%d", m->dict_keys_current, m->dict_keys_total); + } + elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str); + canvas_set_font(canvas, FontSecondary); + snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); + canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str); + snprintf( + draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total); + canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str); + } + elements_button_center(canvas, "Skip"); +} + +static bool dict_attack_input_callback(InputEvent* event, void* context) { + DictAttack* dict_attack = context; + bool consumed = false; + if(event->type == InputTypeShort && event->key == InputKeyOk) { + if(dict_attack->callback) { + dict_attack->callback(dict_attack->context); + } + consumed = true; + } + return consumed; +} + +DictAttack* dict_attack_alloc() { + DictAttack* dict_attack = malloc(sizeof(DictAttack)); + dict_attack->view = view_alloc(); + view_allocate_model(dict_attack->view, ViewModelTypeLocking, sizeof(DictAttackViewModel)); + view_set_draw_callback(dict_attack->view, dict_attack_draw_callback); + view_set_input_callback(dict_attack->view, dict_attack_input_callback); + view_set_context(dict_attack->view, dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { model->header = furi_string_alloc(); }, + false); + return dict_attack; +} + +void dict_attack_free(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { furi_string_free(model->header); }, + false); + view_free(dict_attack->view); + free(dict_attack); +} + +void dict_attack_reset(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + model->state = DictAttackStateRead; + model->type = MfClassicType1k; + model->sectors_total = 0; + model->sectors_read = 0; + model->sector_current = 0; + model->keys_total = 0; + model->keys_found = 0; + model->dict_keys_total = 0; + model->dict_keys_current = 0; + model->is_key_attack = false; + furi_string_reset(model->header); + }, + false); +} + +View* dict_attack_get_view(DictAttack* dict_attack) { + furi_assert(dict_attack); + return dict_attack->view; +} + +void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) { + furi_assert(dict_attack); + furi_assert(callback); + dict_attack->callback = callback; + dict_attack->context = context; +} + +void dict_attack_set_header(DictAttack* dict_attack, const char* header) { + furi_assert(dict_attack); + furi_assert(header); + + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { furi_string_set(model->header, header); }, + true); +} + +void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) { + furi_assert(dict_attack); + dict_attack->card_present = true; + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + model->state = DictAttackStateRead; + model->sectors_total = mf_classic_get_total_sectors_num(type); + model->keys_total = model->sectors_total * 2; + }, + true); +} + +void dict_attack_set_card_removed(DictAttack* dict_attack) { + furi_assert(dict_attack); + dict_attack->card_present = false; + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { model->state = DictAttackStateCardRemoved; }, + true); +} + +bool dict_attack_get_card_state(DictAttack* dict_attack) { + furi_assert(dict_attack); + return dict_attack->card_present; +} + +void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, DictAttackViewModel * model, { model->sectors_read = sec_read; }, true); +} + +void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true); +} + +void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + model->sector_current = curr_sec; + model->dict_keys_current = 0; + }, + true); +} + +void dict_attack_inc_current_sector(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + if(model->sector_current < model->sectors_total) { + model->sector_current++; + model->dict_keys_current = 0; + } + }, + true); +} + +void dict_attack_inc_keys_found(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + if(model->keys_found < model->keys_total) { + model->keys_found++; + } + }, + true); +} + +void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { model->dict_keys_total = dict_keys_total; }, + true); +} + +void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + if(model->dict_keys_current + keys_tried < model->dict_keys_total) { + model->dict_keys_current += keys_tried; + } + }, + true); +} + +void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + model->is_key_attack = is_key_attack; + model->key_attack_current_sector = sector; + }, + true); +} + +void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, + DictAttackViewModel * model, + { + if(model->key_attack_current_sector < model->sectors_total) { + model->key_attack_current_sector++; + } + }, + true); +} diff --git a/applications/main/nfc_old/views/dict_attack.h b/applications/main/nfc_old/views/dict_attack.h new file mode 100644 index 000000000000..73b98a1b827b --- /dev/null +++ b/applications/main/nfc_old/views/dict_attack.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include + +#include + +typedef struct DictAttack DictAttack; + +typedef void (*DictAttackCallback)(void* context); + +DictAttack* dict_attack_alloc(); + +void dict_attack_free(DictAttack* dict_attack); + +void dict_attack_reset(DictAttack* dict_attack); + +View* dict_attack_get_view(DictAttack* dict_attack); + +void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context); + +void dict_attack_set_header(DictAttack* dict_attack, const char* header); + +void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type); + +void dict_attack_set_card_removed(DictAttack* dict_attack); + +bool dict_attack_get_card_state(DictAttack* dict_attack); + +void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read); + +void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found); + +void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec); + +void dict_attack_inc_current_sector(DictAttack* dict_attack); + +void dict_attack_inc_keys_found(DictAttack* dict_attack); + +void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total); + +void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried); + +void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector); + +void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack); \ No newline at end of file diff --git a/applications/main/nfc_rpc/application.fam b/applications/main/nfc_rpc/application.fam new file mode 100644 index 000000000000..e28b18a98fe0 --- /dev/null +++ b/applications/main/nfc_rpc/application.fam @@ -0,0 +1,13 @@ +App( + appid="nfc_rpc", + name="NfcRpc", + apptype=FlipperAppType.APP, + targets=["f7"], + entry_point="nfc_rpc_app", + requires=[ + "gui", + ], + stack_size=8 * 1024, + order=30, + icon="A_NFC_14", +) diff --git a/applications/main/nfc_rpc/assets/compiled/main.pb.c b/applications/main/nfc_rpc/assets/compiled/main.pb.c new file mode 100644 index 000000000000..9b574c9d6f71 --- /dev/null +++ b/applications/main/nfc_rpc/assets/compiled/main.pb.c @@ -0,0 +1,16 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8-dev */ + +#include "main.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(Nfc_Empty, Nfc_Empty, AUTO) + + +PB_BIND(Nfc_Main, Nfc_Main, 2) + + + + diff --git a/applications/main/nfc_rpc/assets/compiled/main.pb.h b/applications/main/nfc_rpc/assets/compiled/main.pb.h new file mode 100644 index 000000000000..130e4ca6a30e --- /dev/null +++ b/applications/main/nfc_rpc/assets/compiled/main.pb.h @@ -0,0 +1,218 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8-dev */ + +#ifndef PB_NFC_MAIN_PB_H_INCLUDED +#define PB_NFC_MAIN_PB_H_INCLUDED +#include +#include "nfca.pb.h" +#include "mf_ultralight.pb.h" +#include "mf_classic.pb.h" + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _Nfc_CommandStatus { + Nfc_CommandStatus_OK = 0, + Nfc_CommandStatus_ERROR = 1, /* *< Unknown error */ + Nfc_CommandStatus_ERROR_NOT_IMPLEMENTED = 3, /* *< Command succesfully decoded, but not +implemented (deprecated or not yet implemented) */ + Nfc_CommandStatus_ERROR_BUSY = 4 /* *< Somebody took global lock, so not all commands are available */ +} Nfc_CommandStatus; + +/* Struct definitions */ +typedef struct _Nfc_Empty { + char dummy_field; +} Nfc_Empty; + +typedef struct _Nfc_Main { + Nfc_CommandStatus command_status; + pb_callback_t cb_content; + pb_size_t which_content; + union { + Nfc_Empty empty; + PB_Nfca_ReadRequest nfca_read_req; + PB_Nfca_ReadResponse nfca_read_resp; + PB_Nfca_EmulateStartRequest nfca_emulate_start_req; + PB_Nfca_EmulateStartResponse nfca_emulate_start_resp; + PB_Nfca_EmulateStopRequest nfca_emulate_stop_req; + PB_Nfca_EmulateStopResponse nfca_emulate_stop_resp; + PB_MfUltralight_ReadPageRequest mf_ultralight_read_page_req; + PB_MfUltralight_ReadPageResponse mf_ultralight_read_page_resp; + PB_MfUltralight_ReadVersionRequest mf_ultralight_read_version_req; + PB_MfUltralight_ReadVersionResponse mf_ultralight_read_version_resp; + PB_MfUltralight_WritePageRequest mf_ultralight_write_page_req; + PB_MfUltralight_WritePageResponse mf_ultralight_write_page_resp; + PB_MfUltralight_ReadSignatureRequest mf_ultralight_read_signature_req; + PB_MfUltralight_ReadSignatureResponse mf_ultralight_read_signature_resp; + PB_MfUltralight_ReadCounterRequest mf_ultralight_read_counter_req; + PB_MfUltralight_ReadCounterResponse mf_ultralight_read_counter_resp; + PB_MfUltralight_ReadTearingFlagRequest mf_ultralight_read_tearing_flag_req; + PB_MfUltralight_ReadTearingFlagResponse mf_ultralight_read_tearing_flag_resp; + PB_MfUltralight_EmulateStartRequest mf_ultralight_emulate_start_req; + PB_MfUltralight_EmulateStartResponse mf_ultralight_emulate_start_resp; + PB_MfUltralight_EmulateStopRequest mf_ultralight_emulate_stop_req; + PB_MfUltralight_EmulateStopResponse mf_ultralight_emulate_stop_resp; + PB_MfClassic_AuthRequest mf_classic_auth_req; + PB_MfClassic_AuthResponse mf_classic_auth_resp; + PB_MfClassic_ReadBlockRequest mf_classic_read_block_req; + PB_MfClassic_ReadBlockResponse mf_classic_read_block_resp; + PB_MfClassic_WriteBlockRequest mf_classic_write_block_req; + PB_MfClassic_WriteBlockResponse mf_classic_write_block_resp; + PB_MfClassic_ReadValueRequest mf_classic_read_value_req; + PB_MfClassic_ReadValueResponse mf_classic_read_value_resp; + PB_MfClassic_ChangeValueRequest mf_classic_change_value_req; + PB_MfClassic_ChangeValueResponse mf_classic_change_value_resp; + } content; +} Nfc_Main; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _Nfc_CommandStatus_MIN Nfc_CommandStatus_OK +#define _Nfc_CommandStatus_MAX Nfc_CommandStatus_ERROR_BUSY +#define _Nfc_CommandStatus_ARRAYSIZE ((Nfc_CommandStatus)(Nfc_CommandStatus_ERROR_BUSY+1)) + + +#define Nfc_Main_command_status_ENUMTYPE Nfc_CommandStatus + + +/* Initializer values for message structs */ +#define Nfc_Empty_init_default {0} +#define Nfc_Main_init_default {_Nfc_CommandStatus_MIN, {{NULL}, NULL}, 0, {Nfc_Empty_init_default}} +#define Nfc_Empty_init_zero {0} +#define Nfc_Main_init_zero {_Nfc_CommandStatus_MIN, {{NULL}, NULL}, 0, {Nfc_Empty_init_zero}} + +/* Field tags (for use in manual encoding/decoding) */ +#define Nfc_Main_command_status_tag 1 +#define Nfc_Main_empty_tag 2 +#define Nfc_Main_nfca_read_req_tag 3 +#define Nfc_Main_nfca_read_resp_tag 4 +#define Nfc_Main_nfca_emulate_start_req_tag 5 +#define Nfc_Main_nfca_emulate_start_resp_tag 6 +#define Nfc_Main_nfca_emulate_stop_req_tag 7 +#define Nfc_Main_nfca_emulate_stop_resp_tag 8 +#define Nfc_Main_mf_ultralight_read_page_req_tag 9 +#define Nfc_Main_mf_ultralight_read_page_resp_tag 10 +#define Nfc_Main_mf_ultralight_read_version_req_tag 11 +#define Nfc_Main_mf_ultralight_read_version_resp_tag 12 +#define Nfc_Main_mf_ultralight_write_page_req_tag 13 +#define Nfc_Main_mf_ultralight_write_page_resp_tag 14 +#define Nfc_Main_mf_ultralight_read_signature_req_tag 15 +#define Nfc_Main_mf_ultralight_read_signature_resp_tag 16 +#define Nfc_Main_mf_ultralight_read_counter_req_tag 17 +#define Nfc_Main_mf_ultralight_read_counter_resp_tag 18 +#define Nfc_Main_mf_ultralight_read_tearing_flag_req_tag 19 +#define Nfc_Main_mf_ultralight_read_tearing_flag_resp_tag 20 +#define Nfc_Main_mf_ultralight_emulate_start_req_tag 21 +#define Nfc_Main_mf_ultralight_emulate_start_resp_tag 22 +#define Nfc_Main_mf_ultralight_emulate_stop_req_tag 23 +#define Nfc_Main_mf_ultralight_emulate_stop_resp_tag 24 +#define Nfc_Main_mf_classic_auth_req_tag 25 +#define Nfc_Main_mf_classic_auth_resp_tag 26 +#define Nfc_Main_mf_classic_read_block_req_tag 27 +#define Nfc_Main_mf_classic_read_block_resp_tag 28 +#define Nfc_Main_mf_classic_write_block_req_tag 29 +#define Nfc_Main_mf_classic_write_block_resp_tag 30 +#define Nfc_Main_mf_classic_read_value_req_tag 31 +#define Nfc_Main_mf_classic_read_value_resp_tag 32 +#define Nfc_Main_mf_classic_change_value_req_tag 33 +#define Nfc_Main_mf_classic_change_value_resp_tag 34 + +/* Struct field encoding specification for nanopb */ +#define Nfc_Empty_FIELDLIST(X, a) \ + +#define Nfc_Empty_CALLBACK NULL +#define Nfc_Empty_DEFAULT NULL + +#define Nfc_Main_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, command_status, 1) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,empty,content.empty), 2) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,nfca_read_req,content.nfca_read_req), 3) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,nfca_read_resp,content.nfca_read_resp), 4) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,nfca_emulate_start_req,content.nfca_emulate_start_req), 5) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,nfca_emulate_start_resp,content.nfca_emulate_start_resp), 6) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,nfca_emulate_stop_req,content.nfca_emulate_stop_req), 7) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,nfca_emulate_stop_resp,content.nfca_emulate_stop_resp), 8) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_read_page_req,content.mf_ultralight_read_page_req), 9) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_read_page_resp,content.mf_ultralight_read_page_resp), 10) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_read_version_req,content.mf_ultralight_read_version_req), 11) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_read_version_resp,content.mf_ultralight_read_version_resp), 12) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_write_page_req,content.mf_ultralight_write_page_req), 13) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_write_page_resp,content.mf_ultralight_write_page_resp), 14) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_read_signature_req,content.mf_ultralight_read_signature_req), 15) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_read_signature_resp,content.mf_ultralight_read_signature_resp), 16) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_read_counter_req,content.mf_ultralight_read_counter_req), 17) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_read_counter_resp,content.mf_ultralight_read_counter_resp), 18) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_read_tearing_flag_req,content.mf_ultralight_read_tearing_flag_req), 19) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_read_tearing_flag_resp,content.mf_ultralight_read_tearing_flag_resp), 20) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_emulate_start_req,content.mf_ultralight_emulate_start_req), 21) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_emulate_start_resp,content.mf_ultralight_emulate_start_resp), 22) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_emulate_stop_req,content.mf_ultralight_emulate_stop_req), 23) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_ultralight_emulate_stop_resp,content.mf_ultralight_emulate_stop_resp), 24) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_classic_auth_req,content.mf_classic_auth_req), 25) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_classic_auth_resp,content.mf_classic_auth_resp), 26) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_classic_read_block_req,content.mf_classic_read_block_req), 27) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_classic_read_block_resp,content.mf_classic_read_block_resp), 28) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_classic_write_block_req,content.mf_classic_write_block_req), 29) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_classic_write_block_resp,content.mf_classic_write_block_resp), 30) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_classic_read_value_req,content.mf_classic_read_value_req), 31) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_classic_read_value_resp,content.mf_classic_read_value_resp), 32) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_classic_change_value_req,content.mf_classic_change_value_req), 33) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,mf_classic_change_value_resp,content.mf_classic_change_value_resp), 34) +#define Nfc_Main_CALLBACK NULL +#define Nfc_Main_DEFAULT NULL +#define Nfc_Main_content_empty_MSGTYPE Nfc_Empty +#define Nfc_Main_content_nfca_read_req_MSGTYPE PB_Nfca_ReadRequest +#define Nfc_Main_content_nfca_read_resp_MSGTYPE PB_Nfca_ReadResponse +#define Nfc_Main_content_nfca_emulate_start_req_MSGTYPE PB_Nfca_EmulateStartRequest +#define Nfc_Main_content_nfca_emulate_start_resp_MSGTYPE PB_Nfca_EmulateStartResponse +#define Nfc_Main_content_nfca_emulate_stop_req_MSGTYPE PB_Nfca_EmulateStopRequest +#define Nfc_Main_content_nfca_emulate_stop_resp_MSGTYPE PB_Nfca_EmulateStopResponse +#define Nfc_Main_content_mf_ultralight_read_page_req_MSGTYPE PB_MfUltralight_ReadPageRequest +#define Nfc_Main_content_mf_ultralight_read_page_resp_MSGTYPE PB_MfUltralight_ReadPageResponse +#define Nfc_Main_content_mf_ultralight_read_version_req_MSGTYPE PB_MfUltralight_ReadVersionRequest +#define Nfc_Main_content_mf_ultralight_read_version_resp_MSGTYPE PB_MfUltralight_ReadVersionResponse +#define Nfc_Main_content_mf_ultralight_write_page_req_MSGTYPE PB_MfUltralight_WritePageRequest +#define Nfc_Main_content_mf_ultralight_write_page_resp_MSGTYPE PB_MfUltralight_WritePageResponse +#define Nfc_Main_content_mf_ultralight_read_signature_req_MSGTYPE PB_MfUltralight_ReadSignatureRequest +#define Nfc_Main_content_mf_ultralight_read_signature_resp_MSGTYPE PB_MfUltralight_ReadSignatureResponse +#define Nfc_Main_content_mf_ultralight_read_counter_req_MSGTYPE PB_MfUltralight_ReadCounterRequest +#define Nfc_Main_content_mf_ultralight_read_counter_resp_MSGTYPE PB_MfUltralight_ReadCounterResponse +#define Nfc_Main_content_mf_ultralight_read_tearing_flag_req_MSGTYPE PB_MfUltralight_ReadTearingFlagRequest +#define Nfc_Main_content_mf_ultralight_read_tearing_flag_resp_MSGTYPE PB_MfUltralight_ReadTearingFlagResponse +#define Nfc_Main_content_mf_ultralight_emulate_start_req_MSGTYPE PB_MfUltralight_EmulateStartRequest +#define Nfc_Main_content_mf_ultralight_emulate_start_resp_MSGTYPE PB_MfUltralight_EmulateStartResponse +#define Nfc_Main_content_mf_ultralight_emulate_stop_req_MSGTYPE PB_MfUltralight_EmulateStopRequest +#define Nfc_Main_content_mf_ultralight_emulate_stop_resp_MSGTYPE PB_MfUltralight_EmulateStopResponse +#define Nfc_Main_content_mf_classic_auth_req_MSGTYPE PB_MfClassic_AuthRequest +#define Nfc_Main_content_mf_classic_auth_resp_MSGTYPE PB_MfClassic_AuthResponse +#define Nfc_Main_content_mf_classic_read_block_req_MSGTYPE PB_MfClassic_ReadBlockRequest +#define Nfc_Main_content_mf_classic_read_block_resp_MSGTYPE PB_MfClassic_ReadBlockResponse +#define Nfc_Main_content_mf_classic_write_block_req_MSGTYPE PB_MfClassic_WriteBlockRequest +#define Nfc_Main_content_mf_classic_write_block_resp_MSGTYPE PB_MfClassic_WriteBlockResponse +#define Nfc_Main_content_mf_classic_read_value_req_MSGTYPE PB_MfClassic_ReadValueRequest +#define Nfc_Main_content_mf_classic_read_value_resp_MSGTYPE PB_MfClassic_ReadValueResponse +#define Nfc_Main_content_mf_classic_change_value_req_MSGTYPE PB_MfClassic_ChangeValueRequest +#define Nfc_Main_content_mf_classic_change_value_resp_MSGTYPE PB_MfClassic_ChangeValueResponse + +extern const pb_msgdesc_t Nfc_Empty_msg; +extern const pb_msgdesc_t Nfc_Main_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define Nfc_Empty_fields &Nfc_Empty_msg +#define Nfc_Main_fields &Nfc_Main_msg + +/* Maximum encoded size of messages (where known) */ +#define Nfc_Empty_size 0 +#define Nfc_Main_size 2057 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/applications/main/nfc_rpc/assets/compiled/mf_classic.pb.c b/applications/main/nfc_rpc/assets/compiled/mf_classic.pb.c new file mode 100644 index 000000000000..b58f0167f7e9 --- /dev/null +++ b/applications/main/nfc_rpc/assets/compiled/mf_classic.pb.c @@ -0,0 +1,41 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8-dev */ + +#include "mf_classic.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(PB_MfClassic_AuthRequest, PB_MfClassic_AuthRequest, AUTO) + + +PB_BIND(PB_MfClassic_AuthResponse, PB_MfClassic_AuthResponse, AUTO) + + +PB_BIND(PB_MfClassic_ReadBlockRequest, PB_MfClassic_ReadBlockRequest, AUTO) + + +PB_BIND(PB_MfClassic_ReadBlockResponse, PB_MfClassic_ReadBlockResponse, AUTO) + + +PB_BIND(PB_MfClassic_WriteBlockRequest, PB_MfClassic_WriteBlockRequest, AUTO) + + +PB_BIND(PB_MfClassic_WriteBlockResponse, PB_MfClassic_WriteBlockResponse, AUTO) + + +PB_BIND(PB_MfClassic_ReadValueRequest, PB_MfClassic_ReadValueRequest, AUTO) + + +PB_BIND(PB_MfClassic_ReadValueResponse, PB_MfClassic_ReadValueResponse, AUTO) + + +PB_BIND(PB_MfClassic_ChangeValueRequest, PB_MfClassic_ChangeValueRequest, AUTO) + + +PB_BIND(PB_MfClassic_ChangeValueResponse, PB_MfClassic_ChangeValueResponse, AUTO) + + + + + diff --git a/applications/main/nfc_rpc/assets/compiled/mf_classic.pb.h b/applications/main/nfc_rpc/assets/compiled/mf_classic.pb.h new file mode 100644 index 000000000000..19edf9893acf --- /dev/null +++ b/applications/main/nfc_rpc/assets/compiled/mf_classic.pb.h @@ -0,0 +1,305 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8-dev */ + +#ifndef PB_PB_MFCLASSIC_MF_CLASSIC_PB_H_INCLUDED +#define PB_PB_MFCLASSIC_MF_CLASSIC_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _PB_MfClassic_Error { + PB_MfClassic_Error_None = 0, + PB_MfClassic_Error_NotPresent = 1, + PB_MfClassic_Error_Protocol = 2, + PB_MfClassic_Error_Auth = 3, + PB_MfClassic_Error_Timeout = 4 +} PB_MfClassic_Error; + +typedef enum _PB_MfClassic_KeyType { + PB_MfClassic_KeyType_KeyTypeA = 0, + PB_MfClassic_KeyType_KeyTypeB = 1 +} PB_MfClassic_KeyType; + +/* Struct definitions */ +typedef PB_BYTES_ARRAY_T(6) PB_MfClassic_AuthRequest_key_t; +typedef struct _PB_MfClassic_AuthRequest { + uint8_t block; + PB_MfClassic_AuthRequest_key_t key; + PB_MfClassic_KeyType key_type; +} PB_MfClassic_AuthRequest; + +typedef PB_BYTES_ARRAY_T(6) PB_MfClassic_AuthResponse_key_t; +typedef PB_BYTES_ARRAY_T(4) PB_MfClassic_AuthResponse_nt_t; +typedef PB_BYTES_ARRAY_T(4) PB_MfClassic_AuthResponse_nr_t; +typedef PB_BYTES_ARRAY_T(4) PB_MfClassic_AuthResponse_ar_t; +typedef PB_BYTES_ARRAY_T(4) PB_MfClassic_AuthResponse_at_t; +typedef struct _PB_MfClassic_AuthResponse { + PB_MfClassic_Error error; + uint8_t block; + PB_MfClassic_AuthResponse_key_t key; + PB_MfClassic_KeyType key_type; + PB_MfClassic_AuthResponse_nt_t nt; + PB_MfClassic_AuthResponse_nr_t nr; + PB_MfClassic_AuthResponse_ar_t ar; + PB_MfClassic_AuthResponse_at_t at; +} PB_MfClassic_AuthResponse; + +typedef PB_BYTES_ARRAY_T(6) PB_MfClassic_ReadBlockRequest_key_t; +typedef struct _PB_MfClassic_ReadBlockRequest { + uint8_t block; + PB_MfClassic_ReadBlockRequest_key_t key; + PB_MfClassic_KeyType key_type; +} PB_MfClassic_ReadBlockRequest; + +typedef PB_BYTES_ARRAY_T(16) PB_MfClassic_ReadBlockResponse_data_t; +typedef struct _PB_MfClassic_ReadBlockResponse { + PB_MfClassic_Error error; + PB_MfClassic_ReadBlockResponse_data_t data; +} PB_MfClassic_ReadBlockResponse; + +typedef PB_BYTES_ARRAY_T(6) PB_MfClassic_WriteBlockRequest_key_t; +typedef PB_BYTES_ARRAY_T(16) PB_MfClassic_WriteBlockRequest_data_t; +typedef struct _PB_MfClassic_WriteBlockRequest { + uint8_t block; + PB_MfClassic_WriteBlockRequest_key_t key; + PB_MfClassic_KeyType key_type; + PB_MfClassic_WriteBlockRequest_data_t data; +} PB_MfClassic_WriteBlockRequest; + +typedef struct _PB_MfClassic_WriteBlockResponse { + PB_MfClassic_Error error; +} PB_MfClassic_WriteBlockResponse; + +typedef PB_BYTES_ARRAY_T(6) PB_MfClassic_ReadValueRequest_key_t; +typedef struct _PB_MfClassic_ReadValueRequest { + uint8_t block; + PB_MfClassic_ReadValueRequest_key_t key; + PB_MfClassic_KeyType key_type; +} PB_MfClassic_ReadValueRequest; + +typedef struct _PB_MfClassic_ReadValueResponse { + PB_MfClassic_Error error; + int32_t value; +} PB_MfClassic_ReadValueResponse; + +typedef PB_BYTES_ARRAY_T(6) PB_MfClassic_ChangeValueRequest_key_t; +typedef struct _PB_MfClassic_ChangeValueRequest { + uint8_t block; + PB_MfClassic_ChangeValueRequest_key_t key; + PB_MfClassic_KeyType key_type; + int32_t data; +} PB_MfClassic_ChangeValueRequest; + +typedef struct _PB_MfClassic_ChangeValueResponse { + PB_MfClassic_Error error; + int32_t value; +} PB_MfClassic_ChangeValueResponse; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _PB_MfClassic_Error_MIN PB_MfClassic_Error_None +#define _PB_MfClassic_Error_MAX PB_MfClassic_Error_Timeout +#define _PB_MfClassic_Error_ARRAYSIZE ((PB_MfClassic_Error)(PB_MfClassic_Error_Timeout+1)) + +#define _PB_MfClassic_KeyType_MIN PB_MfClassic_KeyType_KeyTypeA +#define _PB_MfClassic_KeyType_MAX PB_MfClassic_KeyType_KeyTypeB +#define _PB_MfClassic_KeyType_ARRAYSIZE ((PB_MfClassic_KeyType)(PB_MfClassic_KeyType_KeyTypeB+1)) + +#define PB_MfClassic_AuthRequest_key_type_ENUMTYPE PB_MfClassic_KeyType + +#define PB_MfClassic_AuthResponse_error_ENUMTYPE PB_MfClassic_Error +#define PB_MfClassic_AuthResponse_key_type_ENUMTYPE PB_MfClassic_KeyType + +#define PB_MfClassic_ReadBlockRequest_key_type_ENUMTYPE PB_MfClassic_KeyType + +#define PB_MfClassic_ReadBlockResponse_error_ENUMTYPE PB_MfClassic_Error + +#define PB_MfClassic_WriteBlockRequest_key_type_ENUMTYPE PB_MfClassic_KeyType + +#define PB_MfClassic_WriteBlockResponse_error_ENUMTYPE PB_MfClassic_Error + +#define PB_MfClassic_ReadValueRequest_key_type_ENUMTYPE PB_MfClassic_KeyType + +#define PB_MfClassic_ReadValueResponse_error_ENUMTYPE PB_MfClassic_Error + +#define PB_MfClassic_ChangeValueRequest_key_type_ENUMTYPE PB_MfClassic_KeyType + +#define PB_MfClassic_ChangeValueResponse_error_ENUMTYPE PB_MfClassic_Error + + +/* Initializer values for message structs */ +#define PB_MfClassic_AuthRequest_init_default {0, {0, {0}}, _PB_MfClassic_KeyType_MIN} +#define PB_MfClassic_AuthResponse_init_default {_PB_MfClassic_Error_MIN, 0, {0, {0}}, _PB_MfClassic_KeyType_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}} +#define PB_MfClassic_ReadBlockRequest_init_default {0, {0, {0}}, _PB_MfClassic_KeyType_MIN} +#define PB_MfClassic_ReadBlockResponse_init_default {_PB_MfClassic_Error_MIN, {0, {0}}} +#define PB_MfClassic_WriteBlockRequest_init_default {0, {0, {0}}, _PB_MfClassic_KeyType_MIN, {0, {0}}} +#define PB_MfClassic_WriteBlockResponse_init_default {_PB_MfClassic_Error_MIN} +#define PB_MfClassic_ReadValueRequest_init_default {0, {0, {0}}, _PB_MfClassic_KeyType_MIN} +#define PB_MfClassic_ReadValueResponse_init_default {_PB_MfClassic_Error_MIN, 0} +#define PB_MfClassic_ChangeValueRequest_init_default {0, {0, {0}}, _PB_MfClassic_KeyType_MIN, 0} +#define PB_MfClassic_ChangeValueResponse_init_default {_PB_MfClassic_Error_MIN, 0} +#define PB_MfClassic_AuthRequest_init_zero {0, {0, {0}}, _PB_MfClassic_KeyType_MIN} +#define PB_MfClassic_AuthResponse_init_zero {_PB_MfClassic_Error_MIN, 0, {0, {0}}, _PB_MfClassic_KeyType_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}} +#define PB_MfClassic_ReadBlockRequest_init_zero {0, {0, {0}}, _PB_MfClassic_KeyType_MIN} +#define PB_MfClassic_ReadBlockResponse_init_zero {_PB_MfClassic_Error_MIN, {0, {0}}} +#define PB_MfClassic_WriteBlockRequest_init_zero {0, {0, {0}}, _PB_MfClassic_KeyType_MIN, {0, {0}}} +#define PB_MfClassic_WriteBlockResponse_init_zero {_PB_MfClassic_Error_MIN} +#define PB_MfClassic_ReadValueRequest_init_zero {0, {0, {0}}, _PB_MfClassic_KeyType_MIN} +#define PB_MfClassic_ReadValueResponse_init_zero {_PB_MfClassic_Error_MIN, 0} +#define PB_MfClassic_ChangeValueRequest_init_zero {0, {0, {0}}, _PB_MfClassic_KeyType_MIN, 0} +#define PB_MfClassic_ChangeValueResponse_init_zero {_PB_MfClassic_Error_MIN, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define PB_MfClassic_AuthRequest_block_tag 1 +#define PB_MfClassic_AuthRequest_key_tag 2 +#define PB_MfClassic_AuthRequest_key_type_tag 3 +#define PB_MfClassic_AuthResponse_error_tag 1 +#define PB_MfClassic_AuthResponse_block_tag 2 +#define PB_MfClassic_AuthResponse_key_tag 3 +#define PB_MfClassic_AuthResponse_key_type_tag 4 +#define PB_MfClassic_AuthResponse_nt_tag 5 +#define PB_MfClassic_AuthResponse_nr_tag 6 +#define PB_MfClassic_AuthResponse_ar_tag 7 +#define PB_MfClassic_AuthResponse_at_tag 8 +#define PB_MfClassic_ReadBlockRequest_block_tag 1 +#define PB_MfClassic_ReadBlockRequest_key_tag 2 +#define PB_MfClassic_ReadBlockRequest_key_type_tag 3 +#define PB_MfClassic_ReadBlockResponse_error_tag 1 +#define PB_MfClassic_ReadBlockResponse_data_tag 2 +#define PB_MfClassic_WriteBlockRequest_block_tag 1 +#define PB_MfClassic_WriteBlockRequest_key_tag 2 +#define PB_MfClassic_WriteBlockRequest_key_type_tag 3 +#define PB_MfClassic_WriteBlockRequest_data_tag 4 +#define PB_MfClassic_WriteBlockResponse_error_tag 1 +#define PB_MfClassic_ReadValueRequest_block_tag 1 +#define PB_MfClassic_ReadValueRequest_key_tag 2 +#define PB_MfClassic_ReadValueRequest_key_type_tag 3 +#define PB_MfClassic_ReadValueResponse_error_tag 1 +#define PB_MfClassic_ReadValueResponse_value_tag 2 +#define PB_MfClassic_ChangeValueRequest_block_tag 1 +#define PB_MfClassic_ChangeValueRequest_key_tag 2 +#define PB_MfClassic_ChangeValueRequest_key_type_tag 3 +#define PB_MfClassic_ChangeValueRequest_data_tag 4 +#define PB_MfClassic_ChangeValueResponse_error_tag 1 +#define PB_MfClassic_ChangeValueResponse_value_tag 2 + +/* Struct field encoding specification for nanopb */ +#define PB_MfClassic_AuthRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, block, 1) \ +X(a, STATIC, SINGULAR, BYTES, key, 2) \ +X(a, STATIC, SINGULAR, UENUM, key_type, 3) +#define PB_MfClassic_AuthRequest_CALLBACK NULL +#define PB_MfClassic_AuthRequest_DEFAULT NULL + +#define PB_MfClassic_AuthResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) \ +X(a, STATIC, SINGULAR, UINT32, block, 2) \ +X(a, STATIC, SINGULAR, BYTES, key, 3) \ +X(a, STATIC, SINGULAR, UENUM, key_type, 4) \ +X(a, STATIC, SINGULAR, BYTES, nt, 5) \ +X(a, STATIC, SINGULAR, BYTES, nr, 6) \ +X(a, STATIC, SINGULAR, BYTES, ar, 7) \ +X(a, STATIC, SINGULAR, BYTES, at, 8) +#define PB_MfClassic_AuthResponse_CALLBACK NULL +#define PB_MfClassic_AuthResponse_DEFAULT NULL + +#define PB_MfClassic_ReadBlockRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, block, 1) \ +X(a, STATIC, SINGULAR, BYTES, key, 2) \ +X(a, STATIC, SINGULAR, UENUM, key_type, 3) +#define PB_MfClassic_ReadBlockRequest_CALLBACK NULL +#define PB_MfClassic_ReadBlockRequest_DEFAULT NULL + +#define PB_MfClassic_ReadBlockResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) \ +X(a, STATIC, SINGULAR, BYTES, data, 2) +#define PB_MfClassic_ReadBlockResponse_CALLBACK NULL +#define PB_MfClassic_ReadBlockResponse_DEFAULT NULL + +#define PB_MfClassic_WriteBlockRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, block, 1) \ +X(a, STATIC, SINGULAR, BYTES, key, 2) \ +X(a, STATIC, SINGULAR, UENUM, key_type, 3) \ +X(a, STATIC, SINGULAR, BYTES, data, 4) +#define PB_MfClassic_WriteBlockRequest_CALLBACK NULL +#define PB_MfClassic_WriteBlockRequest_DEFAULT NULL + +#define PB_MfClassic_WriteBlockResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) +#define PB_MfClassic_WriteBlockResponse_CALLBACK NULL +#define PB_MfClassic_WriteBlockResponse_DEFAULT NULL + +#define PB_MfClassic_ReadValueRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, block, 1) \ +X(a, STATIC, SINGULAR, BYTES, key, 2) \ +X(a, STATIC, SINGULAR, UENUM, key_type, 3) +#define PB_MfClassic_ReadValueRequest_CALLBACK NULL +#define PB_MfClassic_ReadValueRequest_DEFAULT NULL + +#define PB_MfClassic_ReadValueResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) \ +X(a, STATIC, SINGULAR, INT32, value, 2) +#define PB_MfClassic_ReadValueResponse_CALLBACK NULL +#define PB_MfClassic_ReadValueResponse_DEFAULT NULL + +#define PB_MfClassic_ChangeValueRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, block, 1) \ +X(a, STATIC, SINGULAR, BYTES, key, 2) \ +X(a, STATIC, SINGULAR, UENUM, key_type, 3) \ +X(a, STATIC, SINGULAR, INT32, data, 4) +#define PB_MfClassic_ChangeValueRequest_CALLBACK NULL +#define PB_MfClassic_ChangeValueRequest_DEFAULT NULL + +#define PB_MfClassic_ChangeValueResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) \ +X(a, STATIC, SINGULAR, INT32, value, 2) +#define PB_MfClassic_ChangeValueResponse_CALLBACK NULL +#define PB_MfClassic_ChangeValueResponse_DEFAULT NULL + +extern const pb_msgdesc_t PB_MfClassic_AuthRequest_msg; +extern const pb_msgdesc_t PB_MfClassic_AuthResponse_msg; +extern const pb_msgdesc_t PB_MfClassic_ReadBlockRequest_msg; +extern const pb_msgdesc_t PB_MfClassic_ReadBlockResponse_msg; +extern const pb_msgdesc_t PB_MfClassic_WriteBlockRequest_msg; +extern const pb_msgdesc_t PB_MfClassic_WriteBlockResponse_msg; +extern const pb_msgdesc_t PB_MfClassic_ReadValueRequest_msg; +extern const pb_msgdesc_t PB_MfClassic_ReadValueResponse_msg; +extern const pb_msgdesc_t PB_MfClassic_ChangeValueRequest_msg; +extern const pb_msgdesc_t PB_MfClassic_ChangeValueResponse_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define PB_MfClassic_AuthRequest_fields &PB_MfClassic_AuthRequest_msg +#define PB_MfClassic_AuthResponse_fields &PB_MfClassic_AuthResponse_msg +#define PB_MfClassic_ReadBlockRequest_fields &PB_MfClassic_ReadBlockRequest_msg +#define PB_MfClassic_ReadBlockResponse_fields &PB_MfClassic_ReadBlockResponse_msg +#define PB_MfClassic_WriteBlockRequest_fields &PB_MfClassic_WriteBlockRequest_msg +#define PB_MfClassic_WriteBlockResponse_fields &PB_MfClassic_WriteBlockResponse_msg +#define PB_MfClassic_ReadValueRequest_fields &PB_MfClassic_ReadValueRequest_msg +#define PB_MfClassic_ReadValueResponse_fields &PB_MfClassic_ReadValueResponse_msg +#define PB_MfClassic_ChangeValueRequest_fields &PB_MfClassic_ChangeValueRequest_msg +#define PB_MfClassic_ChangeValueResponse_fields &PB_MfClassic_ChangeValueResponse_msg + +/* Maximum encoded size of messages (where known) */ +#define PB_MfClassic_AuthRequest_size 13 +#define PB_MfClassic_AuthResponse_size 39 +#define PB_MfClassic_ChangeValueRequest_size 24 +#define PB_MfClassic_ChangeValueResponse_size 13 +#define PB_MfClassic_ReadBlockRequest_size 13 +#define PB_MfClassic_ReadBlockResponse_size 20 +#define PB_MfClassic_ReadValueRequest_size 13 +#define PB_MfClassic_ReadValueResponse_size 13 +#define PB_MfClassic_WriteBlockRequest_size 31 +#define PB_MfClassic_WriteBlockResponse_size 2 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/applications/main/nfc_rpc/assets/compiled/mf_ultralight.pb.c b/applications/main/nfc_rpc/assets/compiled/mf_ultralight.pb.c new file mode 100644 index 000000000000..a16a9020aaf3 --- /dev/null +++ b/applications/main/nfc_rpc/assets/compiled/mf_ultralight.pb.c @@ -0,0 +1,58 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8-dev */ + +#include "mf_ultralight.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(PB_MfUltralight_ReadPageRequest, PB_MfUltralight_ReadPageRequest, AUTO) + + +PB_BIND(PB_MfUltralight_ReadPageResponse, PB_MfUltralight_ReadPageResponse, AUTO) + + +PB_BIND(PB_MfUltralight_ReadVersionRequest, PB_MfUltralight_ReadVersionRequest, AUTO) + + +PB_BIND(PB_MfUltralight_ReadVersionResponse, PB_MfUltralight_ReadVersionResponse, AUTO) + + +PB_BIND(PB_MfUltralight_WritePageRequest, PB_MfUltralight_WritePageRequest, AUTO) + + +PB_BIND(PB_MfUltralight_WritePageResponse, PB_MfUltralight_WritePageResponse, AUTO) + + +PB_BIND(PB_MfUltralight_ReadSignatureRequest, PB_MfUltralight_ReadSignatureRequest, AUTO) + + +PB_BIND(PB_MfUltralight_ReadSignatureResponse, PB_MfUltralight_ReadSignatureResponse, AUTO) + + +PB_BIND(PB_MfUltralight_ReadCounterRequest, PB_MfUltralight_ReadCounterRequest, AUTO) + + +PB_BIND(PB_MfUltralight_ReadCounterResponse, PB_MfUltralight_ReadCounterResponse, AUTO) + + +PB_BIND(PB_MfUltralight_ReadTearingFlagRequest, PB_MfUltralight_ReadTearingFlagRequest, AUTO) + + +PB_BIND(PB_MfUltralight_ReadTearingFlagResponse, PB_MfUltralight_ReadTearingFlagResponse, AUTO) + + +PB_BIND(PB_MfUltralight_EmulateStartRequest, PB_MfUltralight_EmulateStartRequest, 2) + + +PB_BIND(PB_MfUltralight_EmulateStartResponse, PB_MfUltralight_EmulateStartResponse, AUTO) + + +PB_BIND(PB_MfUltralight_EmulateStopRequest, PB_MfUltralight_EmulateStopRequest, AUTO) + + +PB_BIND(PB_MfUltralight_EmulateStopResponse, PB_MfUltralight_EmulateStopResponse, AUTO) + + + + diff --git a/applications/main/nfc_rpc/assets/compiled/mf_ultralight.pb.h b/applications/main/nfc_rpc/assets/compiled/mf_ultralight.pb.h new file mode 100644 index 000000000000..087ff3819553 --- /dev/null +++ b/applications/main/nfc_rpc/assets/compiled/mf_ultralight.pb.h @@ -0,0 +1,365 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8-dev */ + +#ifndef PB_PB_MFULTRALIGHT_MF_ULTRALIGHT_PB_H_INCLUDED +#define PB_PB_MFULTRALIGHT_MF_ULTRALIGHT_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _PB_MfUltralight_Error { + PB_MfUltralight_Error_None = 0, + PB_MfUltralight_Error_NotPresent = 1, + PB_MfUltralight_Error_Protocol = 2, + PB_MfUltralight_Error_Auth = 3, + PB_MfUltralight_Error_Timeout = 4 +} PB_MfUltralight_Error; + +/* Struct definitions */ +typedef struct _PB_MfUltralight_ReadPageRequest { + uint16_t page; +} PB_MfUltralight_ReadPageRequest; + +typedef PB_BYTES_ARRAY_T(4) PB_MfUltralight_ReadPageResponse_data_t; +typedef struct _PB_MfUltralight_ReadPageResponse { + PB_MfUltralight_Error error; + uint16_t page; + PB_MfUltralight_ReadPageResponse_data_t data; +} PB_MfUltralight_ReadPageResponse; + +typedef struct _PB_MfUltralight_ReadVersionRequest { + char dummy_field; +} PB_MfUltralight_ReadVersionRequest; + +typedef struct _PB_MfUltralight_ReadVersionResponse { + PB_MfUltralight_Error error; + uint32_t header; + uint32_t vendor_id; + uint32_t prod_type; + uint32_t prod_subtype; + uint32_t prod_ver_major; + uint32_t prod_ver_minor; + uint32_t storage_size; + uint32_t protocol_type; +} PB_MfUltralight_ReadVersionResponse; + +typedef PB_BYTES_ARRAY_T(4) PB_MfUltralight_WritePageRequest_data_t; +typedef struct _PB_MfUltralight_WritePageRequest { + uint16_t page; + PB_MfUltralight_WritePageRequest_data_t data; +} PB_MfUltralight_WritePageRequest; + +typedef struct _PB_MfUltralight_WritePageResponse { + PB_MfUltralight_Error error; + bool result; +} PB_MfUltralight_WritePageResponse; + +typedef struct _PB_MfUltralight_ReadSignatureRequest { + char dummy_field; +} PB_MfUltralight_ReadSignatureRequest; + +typedef PB_BYTES_ARRAY_T(32) PB_MfUltralight_ReadSignatureResponse_data_t; +typedef struct _PB_MfUltralight_ReadSignatureResponse { + PB_MfUltralight_Error error; + PB_MfUltralight_ReadSignatureResponse_data_t data; +} PB_MfUltralight_ReadSignatureResponse; + +typedef struct _PB_MfUltralight_ReadCounterRequest { + uint32_t counter_num; +} PB_MfUltralight_ReadCounterRequest; + +typedef PB_BYTES_ARRAY_T(3) PB_MfUltralight_ReadCounterResponse_data_t; +typedef struct _PB_MfUltralight_ReadCounterResponse { + PB_MfUltralight_Error error; + uint32_t counter_num; + PB_MfUltralight_ReadCounterResponse_data_t data; +} PB_MfUltralight_ReadCounterResponse; + +typedef struct _PB_MfUltralight_ReadTearingFlagRequest { + uint32_t flag_num; +} PB_MfUltralight_ReadTearingFlagRequest; + +typedef PB_BYTES_ARRAY_T(1) PB_MfUltralight_ReadTearingFlagResponse_data_t; +typedef struct _PB_MfUltralight_ReadTearingFlagResponse { + PB_MfUltralight_Error error; + uint32_t flag_num; + PB_MfUltralight_ReadTearingFlagResponse_data_t data; +} PB_MfUltralight_ReadTearingFlagResponse; + +typedef PB_BYTES_ARRAY_T(2048) PB_MfUltralight_EmulateStartRequest_data_t; +typedef struct _PB_MfUltralight_EmulateStartRequest { + PB_MfUltralight_EmulateStartRequest_data_t data; +} PB_MfUltralight_EmulateStartRequest; + +typedef struct _PB_MfUltralight_EmulateStartResponse { + PB_MfUltralight_Error error; +} PB_MfUltralight_EmulateStartResponse; + +typedef struct _PB_MfUltralight_EmulateStopRequest { + char dummy_field; +} PB_MfUltralight_EmulateStopRequest; + +typedef struct _PB_MfUltralight_EmulateStopResponse { + PB_MfUltralight_Error error; +} PB_MfUltralight_EmulateStopResponse; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _PB_MfUltralight_Error_MIN PB_MfUltralight_Error_None +#define _PB_MfUltralight_Error_MAX PB_MfUltralight_Error_Timeout +#define _PB_MfUltralight_Error_ARRAYSIZE ((PB_MfUltralight_Error)(PB_MfUltralight_Error_Timeout+1)) + + +#define PB_MfUltralight_ReadPageResponse_error_ENUMTYPE PB_MfUltralight_Error + + +#define PB_MfUltralight_ReadVersionResponse_error_ENUMTYPE PB_MfUltralight_Error + + +#define PB_MfUltralight_WritePageResponse_error_ENUMTYPE PB_MfUltralight_Error + + +#define PB_MfUltralight_ReadSignatureResponse_error_ENUMTYPE PB_MfUltralight_Error + + +#define PB_MfUltralight_ReadCounterResponse_error_ENUMTYPE PB_MfUltralight_Error + + +#define PB_MfUltralight_ReadTearingFlagResponse_error_ENUMTYPE PB_MfUltralight_Error + + +#define PB_MfUltralight_EmulateStartResponse_error_ENUMTYPE PB_MfUltralight_Error + + +#define PB_MfUltralight_EmulateStopResponse_error_ENUMTYPE PB_MfUltralight_Error + + +/* Initializer values for message structs */ +#define PB_MfUltralight_ReadPageRequest_init_default {0} +#define PB_MfUltralight_ReadPageResponse_init_default {_PB_MfUltralight_Error_MIN, 0, {0, {0}}} +#define PB_MfUltralight_ReadVersionRequest_init_default {0} +#define PB_MfUltralight_ReadVersionResponse_init_default {_PB_MfUltralight_Error_MIN, 0, 0, 0, 0, 0, 0, 0, 0} +#define PB_MfUltralight_WritePageRequest_init_default {0, {0, {0}}} +#define PB_MfUltralight_WritePageResponse_init_default {_PB_MfUltralight_Error_MIN, 0} +#define PB_MfUltralight_ReadSignatureRequest_init_default {0} +#define PB_MfUltralight_ReadSignatureResponse_init_default {_PB_MfUltralight_Error_MIN, {0, {0}}} +#define PB_MfUltralight_ReadCounterRequest_init_default {0} +#define PB_MfUltralight_ReadCounterResponse_init_default {_PB_MfUltralight_Error_MIN, 0, {0, {0}}} +#define PB_MfUltralight_ReadTearingFlagRequest_init_default {0} +#define PB_MfUltralight_ReadTearingFlagResponse_init_default {_PB_MfUltralight_Error_MIN, 0, {0, {0}}} +#define PB_MfUltralight_EmulateStartRequest_init_default {{0, {0}}} +#define PB_MfUltralight_EmulateStartResponse_init_default {_PB_MfUltralight_Error_MIN} +#define PB_MfUltralight_EmulateStopRequest_init_default {0} +#define PB_MfUltralight_EmulateStopResponse_init_default {_PB_MfUltralight_Error_MIN} +#define PB_MfUltralight_ReadPageRequest_init_zero {0} +#define PB_MfUltralight_ReadPageResponse_init_zero {_PB_MfUltralight_Error_MIN, 0, {0, {0}}} +#define PB_MfUltralight_ReadVersionRequest_init_zero {0} +#define PB_MfUltralight_ReadVersionResponse_init_zero {_PB_MfUltralight_Error_MIN, 0, 0, 0, 0, 0, 0, 0, 0} +#define PB_MfUltralight_WritePageRequest_init_zero {0, {0, {0}}} +#define PB_MfUltralight_WritePageResponse_init_zero {_PB_MfUltralight_Error_MIN, 0} +#define PB_MfUltralight_ReadSignatureRequest_init_zero {0} +#define PB_MfUltralight_ReadSignatureResponse_init_zero {_PB_MfUltralight_Error_MIN, {0, {0}}} +#define PB_MfUltralight_ReadCounterRequest_init_zero {0} +#define PB_MfUltralight_ReadCounterResponse_init_zero {_PB_MfUltralight_Error_MIN, 0, {0, {0}}} +#define PB_MfUltralight_ReadTearingFlagRequest_init_zero {0} +#define PB_MfUltralight_ReadTearingFlagResponse_init_zero {_PB_MfUltralight_Error_MIN, 0, {0, {0}}} +#define PB_MfUltralight_EmulateStartRequest_init_zero {{0, {0}}} +#define PB_MfUltralight_EmulateStartResponse_init_zero {_PB_MfUltralight_Error_MIN} +#define PB_MfUltralight_EmulateStopRequest_init_zero {0} +#define PB_MfUltralight_EmulateStopResponse_init_zero {_PB_MfUltralight_Error_MIN} + +/* Field tags (for use in manual encoding/decoding) */ +#define PB_MfUltralight_ReadPageRequest_page_tag 1 +#define PB_MfUltralight_ReadPageResponse_error_tag 1 +#define PB_MfUltralight_ReadPageResponse_page_tag 2 +#define PB_MfUltralight_ReadPageResponse_data_tag 3 +#define PB_MfUltralight_ReadVersionResponse_error_tag 1 +#define PB_MfUltralight_ReadVersionResponse_header_tag 2 +#define PB_MfUltralight_ReadVersionResponse_vendor_id_tag 3 +#define PB_MfUltralight_ReadVersionResponse_prod_type_tag 4 +#define PB_MfUltralight_ReadVersionResponse_prod_subtype_tag 5 +#define PB_MfUltralight_ReadVersionResponse_prod_ver_major_tag 6 +#define PB_MfUltralight_ReadVersionResponse_prod_ver_minor_tag 7 +#define PB_MfUltralight_ReadVersionResponse_storage_size_tag 8 +#define PB_MfUltralight_ReadVersionResponse_protocol_type_tag 9 +#define PB_MfUltralight_WritePageRequest_page_tag 1 +#define PB_MfUltralight_WritePageRequest_data_tag 2 +#define PB_MfUltralight_WritePageResponse_error_tag 1 +#define PB_MfUltralight_WritePageResponse_result_tag 2 +#define PB_MfUltralight_ReadSignatureResponse_error_tag 1 +#define PB_MfUltralight_ReadSignatureResponse_data_tag 2 +#define PB_MfUltralight_ReadCounterRequest_counter_num_tag 1 +#define PB_MfUltralight_ReadCounterResponse_error_tag 1 +#define PB_MfUltralight_ReadCounterResponse_counter_num_tag 2 +#define PB_MfUltralight_ReadCounterResponse_data_tag 3 +#define PB_MfUltralight_ReadTearingFlagRequest_flag_num_tag 1 +#define PB_MfUltralight_ReadTearingFlagResponse_error_tag 1 +#define PB_MfUltralight_ReadTearingFlagResponse_flag_num_tag 2 +#define PB_MfUltralight_ReadTearingFlagResponse_data_tag 3 +#define PB_MfUltralight_EmulateStartRequest_data_tag 1 +#define PB_MfUltralight_EmulateStartResponse_error_tag 1 +#define PB_MfUltralight_EmulateStopResponse_error_tag 1 + +/* Struct field encoding specification for nanopb */ +#define PB_MfUltralight_ReadPageRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, page, 1) +#define PB_MfUltralight_ReadPageRequest_CALLBACK NULL +#define PB_MfUltralight_ReadPageRequest_DEFAULT NULL + +#define PB_MfUltralight_ReadPageResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) \ +X(a, STATIC, SINGULAR, UINT32, page, 2) \ +X(a, STATIC, SINGULAR, BYTES, data, 3) +#define PB_MfUltralight_ReadPageResponse_CALLBACK NULL +#define PB_MfUltralight_ReadPageResponse_DEFAULT NULL + +#define PB_MfUltralight_ReadVersionRequest_FIELDLIST(X, a) \ + +#define PB_MfUltralight_ReadVersionRequest_CALLBACK NULL +#define PB_MfUltralight_ReadVersionRequest_DEFAULT NULL + +#define PB_MfUltralight_ReadVersionResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) \ +X(a, STATIC, SINGULAR, UINT32, header, 2) \ +X(a, STATIC, SINGULAR, UINT32, vendor_id, 3) \ +X(a, STATIC, SINGULAR, UINT32, prod_type, 4) \ +X(a, STATIC, SINGULAR, UINT32, prod_subtype, 5) \ +X(a, STATIC, SINGULAR, UINT32, prod_ver_major, 6) \ +X(a, STATIC, SINGULAR, UINT32, prod_ver_minor, 7) \ +X(a, STATIC, SINGULAR, UINT32, storage_size, 8) \ +X(a, STATIC, SINGULAR, UINT32, protocol_type, 9) +#define PB_MfUltralight_ReadVersionResponse_CALLBACK NULL +#define PB_MfUltralight_ReadVersionResponse_DEFAULT NULL + +#define PB_MfUltralight_WritePageRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, page, 1) \ +X(a, STATIC, SINGULAR, BYTES, data, 2) +#define PB_MfUltralight_WritePageRequest_CALLBACK NULL +#define PB_MfUltralight_WritePageRequest_DEFAULT NULL + +#define PB_MfUltralight_WritePageResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) \ +X(a, STATIC, SINGULAR, BOOL, result, 2) +#define PB_MfUltralight_WritePageResponse_CALLBACK NULL +#define PB_MfUltralight_WritePageResponse_DEFAULT NULL + +#define PB_MfUltralight_ReadSignatureRequest_FIELDLIST(X, a) \ + +#define PB_MfUltralight_ReadSignatureRequest_CALLBACK NULL +#define PB_MfUltralight_ReadSignatureRequest_DEFAULT NULL + +#define PB_MfUltralight_ReadSignatureResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) \ +X(a, STATIC, SINGULAR, BYTES, data, 2) +#define PB_MfUltralight_ReadSignatureResponse_CALLBACK NULL +#define PB_MfUltralight_ReadSignatureResponse_DEFAULT NULL + +#define PB_MfUltralight_ReadCounterRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, counter_num, 1) +#define PB_MfUltralight_ReadCounterRequest_CALLBACK NULL +#define PB_MfUltralight_ReadCounterRequest_DEFAULT NULL + +#define PB_MfUltralight_ReadCounterResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) \ +X(a, STATIC, SINGULAR, UINT32, counter_num, 2) \ +X(a, STATIC, SINGULAR, BYTES, data, 3) +#define PB_MfUltralight_ReadCounterResponse_CALLBACK NULL +#define PB_MfUltralight_ReadCounterResponse_DEFAULT NULL + +#define PB_MfUltralight_ReadTearingFlagRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, flag_num, 1) +#define PB_MfUltralight_ReadTearingFlagRequest_CALLBACK NULL +#define PB_MfUltralight_ReadTearingFlagRequest_DEFAULT NULL + +#define PB_MfUltralight_ReadTearingFlagResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) \ +X(a, STATIC, SINGULAR, UINT32, flag_num, 2) \ +X(a, STATIC, SINGULAR, BYTES, data, 3) +#define PB_MfUltralight_ReadTearingFlagResponse_CALLBACK NULL +#define PB_MfUltralight_ReadTearingFlagResponse_DEFAULT NULL + +#define PB_MfUltralight_EmulateStartRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BYTES, data, 1) +#define PB_MfUltralight_EmulateStartRequest_CALLBACK NULL +#define PB_MfUltralight_EmulateStartRequest_DEFAULT NULL + +#define PB_MfUltralight_EmulateStartResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) +#define PB_MfUltralight_EmulateStartResponse_CALLBACK NULL +#define PB_MfUltralight_EmulateStartResponse_DEFAULT NULL + +#define PB_MfUltralight_EmulateStopRequest_FIELDLIST(X, a) \ + +#define PB_MfUltralight_EmulateStopRequest_CALLBACK NULL +#define PB_MfUltralight_EmulateStopRequest_DEFAULT NULL + +#define PB_MfUltralight_EmulateStopResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) +#define PB_MfUltralight_EmulateStopResponse_CALLBACK NULL +#define PB_MfUltralight_EmulateStopResponse_DEFAULT NULL + +extern const pb_msgdesc_t PB_MfUltralight_ReadPageRequest_msg; +extern const pb_msgdesc_t PB_MfUltralight_ReadPageResponse_msg; +extern const pb_msgdesc_t PB_MfUltralight_ReadVersionRequest_msg; +extern const pb_msgdesc_t PB_MfUltralight_ReadVersionResponse_msg; +extern const pb_msgdesc_t PB_MfUltralight_WritePageRequest_msg; +extern const pb_msgdesc_t PB_MfUltralight_WritePageResponse_msg; +extern const pb_msgdesc_t PB_MfUltralight_ReadSignatureRequest_msg; +extern const pb_msgdesc_t PB_MfUltralight_ReadSignatureResponse_msg; +extern const pb_msgdesc_t PB_MfUltralight_ReadCounterRequest_msg; +extern const pb_msgdesc_t PB_MfUltralight_ReadCounterResponse_msg; +extern const pb_msgdesc_t PB_MfUltralight_ReadTearingFlagRequest_msg; +extern const pb_msgdesc_t PB_MfUltralight_ReadTearingFlagResponse_msg; +extern const pb_msgdesc_t PB_MfUltralight_EmulateStartRequest_msg; +extern const pb_msgdesc_t PB_MfUltralight_EmulateStartResponse_msg; +extern const pb_msgdesc_t PB_MfUltralight_EmulateStopRequest_msg; +extern const pb_msgdesc_t PB_MfUltralight_EmulateStopResponse_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define PB_MfUltralight_ReadPageRequest_fields &PB_MfUltralight_ReadPageRequest_msg +#define PB_MfUltralight_ReadPageResponse_fields &PB_MfUltralight_ReadPageResponse_msg +#define PB_MfUltralight_ReadVersionRequest_fields &PB_MfUltralight_ReadVersionRequest_msg +#define PB_MfUltralight_ReadVersionResponse_fields &PB_MfUltralight_ReadVersionResponse_msg +#define PB_MfUltralight_WritePageRequest_fields &PB_MfUltralight_WritePageRequest_msg +#define PB_MfUltralight_WritePageResponse_fields &PB_MfUltralight_WritePageResponse_msg +#define PB_MfUltralight_ReadSignatureRequest_fields &PB_MfUltralight_ReadSignatureRequest_msg +#define PB_MfUltralight_ReadSignatureResponse_fields &PB_MfUltralight_ReadSignatureResponse_msg +#define PB_MfUltralight_ReadCounterRequest_fields &PB_MfUltralight_ReadCounterRequest_msg +#define PB_MfUltralight_ReadCounterResponse_fields &PB_MfUltralight_ReadCounterResponse_msg +#define PB_MfUltralight_ReadTearingFlagRequest_fields &PB_MfUltralight_ReadTearingFlagRequest_msg +#define PB_MfUltralight_ReadTearingFlagResponse_fields &PB_MfUltralight_ReadTearingFlagResponse_msg +#define PB_MfUltralight_EmulateStartRequest_fields &PB_MfUltralight_EmulateStartRequest_msg +#define PB_MfUltralight_EmulateStartResponse_fields &PB_MfUltralight_EmulateStartResponse_msg +#define PB_MfUltralight_EmulateStopRequest_fields &PB_MfUltralight_EmulateStopRequest_msg +#define PB_MfUltralight_EmulateStopResponse_fields &PB_MfUltralight_EmulateStopResponse_msg + +/* Maximum encoded size of messages (where known) */ +#define PB_MfUltralight_EmulateStartRequest_size 2051 +#define PB_MfUltralight_EmulateStartResponse_size 2 +#define PB_MfUltralight_EmulateStopRequest_size 0 +#define PB_MfUltralight_EmulateStopResponse_size 2 +#define PB_MfUltralight_ReadCounterRequest_size 6 +#define PB_MfUltralight_ReadCounterResponse_size 13 +#define PB_MfUltralight_ReadPageRequest_size 4 +#define PB_MfUltralight_ReadPageResponse_size 12 +#define PB_MfUltralight_ReadSignatureRequest_size 0 +#define PB_MfUltralight_ReadSignatureResponse_size 36 +#define PB_MfUltralight_ReadTearingFlagRequest_size 6 +#define PB_MfUltralight_ReadTearingFlagResponse_size 11 +#define PB_MfUltralight_ReadVersionRequest_size 0 +#define PB_MfUltralight_ReadVersionResponse_size 50 +#define PB_MfUltralight_WritePageRequest_size 10 +#define PB_MfUltralight_WritePageResponse_size 4 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/applications/main/nfc_rpc/assets/compiled/nfca.pb.c b/applications/main/nfc_rpc/assets/compiled/nfca.pb.c new file mode 100644 index 000000000000..44e2f02579da --- /dev/null +++ b/applications/main/nfc_rpc/assets/compiled/nfca.pb.c @@ -0,0 +1,28 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8-dev */ + +#include "nfca.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(PB_Nfca_ReadRequest, PB_Nfca_ReadRequest, AUTO) + + +PB_BIND(PB_Nfca_ReadResponse, PB_Nfca_ReadResponse, AUTO) + + +PB_BIND(PB_Nfca_EmulateStartRequest, PB_Nfca_EmulateStartRequest, AUTO) + + +PB_BIND(PB_Nfca_EmulateStartResponse, PB_Nfca_EmulateStartResponse, AUTO) + + +PB_BIND(PB_Nfca_EmulateStopRequest, PB_Nfca_EmulateStopRequest, AUTO) + + +PB_BIND(PB_Nfca_EmulateStopResponse, PB_Nfca_EmulateStopResponse, AUTO) + + + + diff --git a/applications/main/nfc_rpc/assets/compiled/nfca.pb.h b/applications/main/nfc_rpc/assets/compiled/nfca.pb.h new file mode 100644 index 000000000000..df751cbd0c92 --- /dev/null +++ b/applications/main/nfc_rpc/assets/compiled/nfca.pb.h @@ -0,0 +1,174 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8-dev */ + +#ifndef PB_PB_NFCA_NFCA_PB_H_INCLUDED +#define PB_PB_NFCA_NFCA_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _PB_Nfca_Error { + PB_Nfca_Error_None = 0, + PB_Nfca_Error_NotPresent = 1, + PB_Nfca_Error_ColResFailed = 2, + PB_Nfca_Error_BufferOverflow = 3, + PB_Nfca_Error_Communication = 4, + PB_Nfca_Error_FieldOff = 5, + PB_Nfca_Error_WrongCrc = 6, + PB_Nfca_Error_Timeout = 7 +} PB_Nfca_Error; + +/* Struct definitions */ +typedef struct _PB_Nfca_ReadRequest { + char dummy_field; +} PB_Nfca_ReadRequest; + +typedef PB_BYTES_ARRAY_T(10) PB_Nfca_ReadResponse_uid_t; +typedef PB_BYTES_ARRAY_T(1) PB_Nfca_ReadResponse_sak_t; +typedef PB_BYTES_ARRAY_T(2) PB_Nfca_ReadResponse_atqa_t; +typedef struct _PB_Nfca_ReadResponse { + PB_Nfca_Error error; + uint32_t uid_len; + PB_Nfca_ReadResponse_uid_t uid; + PB_Nfca_ReadResponse_sak_t sak; + PB_Nfca_ReadResponse_atqa_t atqa; +} PB_Nfca_ReadResponse; + +typedef PB_BYTES_ARRAY_T(10) PB_Nfca_EmulateStartRequest_uid_t; +typedef PB_BYTES_ARRAY_T(1) PB_Nfca_EmulateStartRequest_sak_t; +typedef PB_BYTES_ARRAY_T(2) PB_Nfca_EmulateStartRequest_atqa_t; +typedef struct _PB_Nfca_EmulateStartRequest { + uint32_t uid_len; + PB_Nfca_EmulateStartRequest_uid_t uid; + PB_Nfca_EmulateStartRequest_sak_t sak; + PB_Nfca_EmulateStartRequest_atqa_t atqa; +} PB_Nfca_EmulateStartRequest; + +typedef struct _PB_Nfca_EmulateStartResponse { + PB_Nfca_Error error; +} PB_Nfca_EmulateStartResponse; + +typedef struct _PB_Nfca_EmulateStopRequest { + char dummy_field; +} PB_Nfca_EmulateStopRequest; + +typedef struct _PB_Nfca_EmulateStopResponse { + PB_Nfca_Error error; +} PB_Nfca_EmulateStopResponse; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _PB_Nfca_Error_MIN PB_Nfca_Error_None +#define _PB_Nfca_Error_MAX PB_Nfca_Error_Timeout +#define _PB_Nfca_Error_ARRAYSIZE ((PB_Nfca_Error)(PB_Nfca_Error_Timeout+1)) + + +#define PB_Nfca_ReadResponse_error_ENUMTYPE PB_Nfca_Error + + +#define PB_Nfca_EmulateStartResponse_error_ENUMTYPE PB_Nfca_Error + + +#define PB_Nfca_EmulateStopResponse_error_ENUMTYPE PB_Nfca_Error + + +/* Initializer values for message structs */ +#define PB_Nfca_ReadRequest_init_default {0} +#define PB_Nfca_ReadResponse_init_default {_PB_Nfca_Error_MIN, 0, {0, {0}}, {0, {0}}, {0, {0}}} +#define PB_Nfca_EmulateStartRequest_init_default {0, {0, {0}}, {0, {0}}, {0, {0}}} +#define PB_Nfca_EmulateStartResponse_init_default {_PB_Nfca_Error_MIN} +#define PB_Nfca_EmulateStopRequest_init_default {0} +#define PB_Nfca_EmulateStopResponse_init_default {_PB_Nfca_Error_MIN} +#define PB_Nfca_ReadRequest_init_zero {0} +#define PB_Nfca_ReadResponse_init_zero {_PB_Nfca_Error_MIN, 0, {0, {0}}, {0, {0}}, {0, {0}}} +#define PB_Nfca_EmulateStartRequest_init_zero {0, {0, {0}}, {0, {0}}, {0, {0}}} +#define PB_Nfca_EmulateStartResponse_init_zero {_PB_Nfca_Error_MIN} +#define PB_Nfca_EmulateStopRequest_init_zero {0} +#define PB_Nfca_EmulateStopResponse_init_zero {_PB_Nfca_Error_MIN} + +/* Field tags (for use in manual encoding/decoding) */ +#define PB_Nfca_ReadResponse_error_tag 1 +#define PB_Nfca_ReadResponse_uid_len_tag 2 +#define PB_Nfca_ReadResponse_uid_tag 3 +#define PB_Nfca_ReadResponse_sak_tag 4 +#define PB_Nfca_ReadResponse_atqa_tag 5 +#define PB_Nfca_EmulateStartRequest_uid_len_tag 1 +#define PB_Nfca_EmulateStartRequest_uid_tag 2 +#define PB_Nfca_EmulateStartRequest_sak_tag 3 +#define PB_Nfca_EmulateStartRequest_atqa_tag 4 +#define PB_Nfca_EmulateStartResponse_error_tag 1 +#define PB_Nfca_EmulateStopResponse_error_tag 1 + +/* Struct field encoding specification for nanopb */ +#define PB_Nfca_ReadRequest_FIELDLIST(X, a) \ + +#define PB_Nfca_ReadRequest_CALLBACK NULL +#define PB_Nfca_ReadRequest_DEFAULT NULL + +#define PB_Nfca_ReadResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) \ +X(a, STATIC, SINGULAR, UINT32, uid_len, 2) \ +X(a, STATIC, SINGULAR, BYTES, uid, 3) \ +X(a, STATIC, SINGULAR, BYTES, sak, 4) \ +X(a, STATIC, SINGULAR, BYTES, atqa, 5) +#define PB_Nfca_ReadResponse_CALLBACK NULL +#define PB_Nfca_ReadResponse_DEFAULT NULL + +#define PB_Nfca_EmulateStartRequest_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, uid_len, 1) \ +X(a, STATIC, SINGULAR, BYTES, uid, 2) \ +X(a, STATIC, SINGULAR, BYTES, sak, 3) \ +X(a, STATIC, SINGULAR, BYTES, atqa, 4) +#define PB_Nfca_EmulateStartRequest_CALLBACK NULL +#define PB_Nfca_EmulateStartRequest_DEFAULT NULL + +#define PB_Nfca_EmulateStartResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) +#define PB_Nfca_EmulateStartResponse_CALLBACK NULL +#define PB_Nfca_EmulateStartResponse_DEFAULT NULL + +#define PB_Nfca_EmulateStopRequest_FIELDLIST(X, a) \ + +#define PB_Nfca_EmulateStopRequest_CALLBACK NULL +#define PB_Nfca_EmulateStopRequest_DEFAULT NULL + +#define PB_Nfca_EmulateStopResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, error, 1) +#define PB_Nfca_EmulateStopResponse_CALLBACK NULL +#define PB_Nfca_EmulateStopResponse_DEFAULT NULL + +extern const pb_msgdesc_t PB_Nfca_ReadRequest_msg; +extern const pb_msgdesc_t PB_Nfca_ReadResponse_msg; +extern const pb_msgdesc_t PB_Nfca_EmulateStartRequest_msg; +extern const pb_msgdesc_t PB_Nfca_EmulateStartResponse_msg; +extern const pb_msgdesc_t PB_Nfca_EmulateStopRequest_msg; +extern const pb_msgdesc_t PB_Nfca_EmulateStopResponse_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define PB_Nfca_ReadRequest_fields &PB_Nfca_ReadRequest_msg +#define PB_Nfca_ReadResponse_fields &PB_Nfca_ReadResponse_msg +#define PB_Nfca_EmulateStartRequest_fields &PB_Nfca_EmulateStartRequest_msg +#define PB_Nfca_EmulateStartResponse_fields &PB_Nfca_EmulateStartResponse_msg +#define PB_Nfca_EmulateStopRequest_fields &PB_Nfca_EmulateStopRequest_msg +#define PB_Nfca_EmulateStopResponse_fields &PB_Nfca_EmulateStopResponse_msg + +/* Maximum encoded size of messages (where known) */ +#define PB_Nfca_EmulateStartRequest_size 25 +#define PB_Nfca_EmulateStartResponse_size 2 +#define PB_Nfca_EmulateStopRequest_size 0 +#define PB_Nfca_EmulateStopResponse_size 2 +#define PB_Nfca_ReadRequest_size 0 +#define PB_Nfca_ReadResponse_size 27 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/applications/main/nfc_rpc/assets/protobuf/main.options b/applications/main/nfc_rpc/assets/protobuf/main.options new file mode 100644 index 000000000000..1a401050e8a2 --- /dev/null +++ b/applications/main/nfc_rpc/assets/protobuf/main.options @@ -0,0 +1 @@ +Nfc.Main submsg_callback:true \ No newline at end of file diff --git a/applications/main/nfc_rpc/assets/protobuf/main.proto b/applications/main/nfc_rpc/assets/protobuf/main.proto new file mode 100644 index 000000000000..1c9c3c73e9b8 --- /dev/null +++ b/applications/main/nfc_rpc/assets/protobuf/main.proto @@ -0,0 +1,65 @@ +syntax = "proto3"; +import "nfca.proto"; +import "mf_ultralight.proto"; +import "mf_classic.proto"; + + +package Nfc; +option java_package = "com.flipperdevices.nfc.protobuf"; + +enum CommandStatus { + OK = 0; + + ERROR = 1; /**< Unknown error */ + ERROR_NOT_IMPLEMENTED = 3; /**< Command succesfully decoded, but not + implemented (deprecated or not yet implemented) */ + ERROR_BUSY = + 4; /**< Somebody took global lock, so not all commands are available */ +} + +message Empty {} + +message Main { + CommandStatus command_status = 1; + oneof content { + Empty empty = 2; + .PB_Nfca.ReadRequest nfca_read_req = 3; + .PB_Nfca.ReadResponse nfca_read_resp = 4; + .PB_Nfca.EmulateStartRequest nfca_emulate_start_req = 5; + .PB_Nfca.EmulateStartResponse nfca_emulate_start_resp = 6; + .PB_Nfca.EmulateStopRequest nfca_emulate_stop_req = 7; + .PB_Nfca.EmulateStopResponse nfca_emulate_stop_resp = 8; + + .PB_MfUltralight.ReadPageRequest mf_ultralight_read_page_req = 9; + .PB_MfUltralight.ReadPageResponse mf_ultralight_read_page_resp = 10; + .PB_MfUltralight.ReadVersionRequest mf_ultralight_read_version_req = 11; + .PB_MfUltralight.ReadVersionResponse mf_ultralight_read_version_resp = 12; + .PB_MfUltralight.WritePageRequest mf_ultralight_write_page_req = 13; + .PB_MfUltralight.WritePageResponse mf_ultralight_write_page_resp = 14; + .PB_MfUltralight.ReadSignatureRequest mf_ultralight_read_signature_req = 15; + .PB_MfUltralight.ReadSignatureResponse mf_ultralight_read_signature_resp = + 16; + .PB_MfUltralight.ReadCounterRequest mf_ultralight_read_counter_req = 17; + .PB_MfUltralight.ReadCounterResponse mf_ultralight_read_counter_resp = 18; + .PB_MfUltralight.ReadTearingFlagRequest + mf_ultralight_read_tearing_flag_req = 19; + .PB_MfUltralight.ReadTearingFlagResponse + mf_ultralight_read_tearing_flag_resp = 20; + .PB_MfUltralight.EmulateStartRequest mf_ultralight_emulate_start_req = 21; + .PB_MfUltralight.EmulateStartResponse mf_ultralight_emulate_start_resp = 22; + .PB_MfUltralight.EmulateStopRequest mf_ultralight_emulate_stop_req = 23; + .PB_MfUltralight.EmulateStopResponse mf_ultralight_emulate_stop_resp = 24; + + .PB_MfClassic.AuthRequest mf_classic_auth_req = 25; + .PB_MfClassic.AuthResponse mf_classic_auth_resp = 26; + .PB_MfClassic.ReadBlockRequest mf_classic_read_block_req = 27; + .PB_MfClassic.ReadBlockResponse mf_classic_read_block_resp = 28; + .PB_MfClassic.WriteBlockRequest mf_classic_write_block_req = 29; + .PB_MfClassic.WriteBlockResponse mf_classic_write_block_resp = 30; + .PB_MfClassic.ReadValueRequest mf_classic_read_value_req = 31; + .PB_MfClassic.ReadValueResponse mf_classic_read_value_resp = 32; + .PB_MfClassic.ChangeValueRequest mf_classic_change_value_req = 33; + .PB_MfClassic.ChangeValueResponse mf_classic_change_value_resp = 34; + + } +} diff --git a/applications/main/nfc_rpc/assets/protobuf/mf_classic.options b/applications/main/nfc_rpc/assets/protobuf/mf_classic.options new file mode 100644 index 000000000000..dad52776630b --- /dev/null +++ b/applications/main/nfc_rpc/assets/protobuf/mf_classic.options @@ -0,0 +1,19 @@ +PB_MfClassic.AuthRequest.block int_size:IS_8 +PB_MfClassic.AuthRequest.key max_size: 6 +PB_MfClassic.AuthResponse.block int_size:IS_8 +PB_MfClassic.AuthResponse.key max_size: 6 +PB_MfClassic.AuthResponse.nt max_size: 4 +PB_MfClassic.AuthResponse.nr max_size: 4 +PB_MfClassic.AuthResponse.ar max_size: 4 +PB_MfClassic.AuthResponse.at max_size: 4 +PB_MfClassic.ReadBlockRequest.block int_size:IS_8 +PB_MfClassic.ReadBlockRequest.key max_size: 6 +PB_MfClassic.ReadBlockResponse.data max_size: 16 +PB_MfClassic.WriteBlockRequest.block int_size:IS_8 +PB_MfClassic.WriteBlockRequest.key max_size: 6 +PB_MfClassic.WriteBlockRequest.data max_size: 16 +PB_MfClassic.ReadValueRequest.block int_size:IS_8 +PB_MfClassic.ReadValueRequest.key max_size: 6 +PB_MfClassic.ChangeValueRequest.block int_size:IS_8 +PB_MfClassic.ChangeValueRequest.key max_size: 6 + diff --git a/applications/main/nfc_rpc/assets/protobuf/mf_classic.proto b/applications/main/nfc_rpc/assets/protobuf/mf_classic.proto new file mode 100644 index 000000000000..c9e75e8c9ec7 --- /dev/null +++ b/applications/main/nfc_rpc/assets/protobuf/mf_classic.proto @@ -0,0 +1,79 @@ +syntax = "proto3"; + +package PB_MfClassic; +option java_package = "com.flipperdevices.nfc.protobuf"; + +enum Error { + None = 0; + NotPresent = 1; + Protocol = 2; + Auth = 3; + Timeout = 4; +} + +enum KeyType { + KeyTypeA = 0; + KeyTypeB = 1; +} + +message AuthRequest { + uint32 block = 1; + bytes key = 2; + KeyType key_type = 3; +} + +message AuthResponse { + Error error = 1; + uint32 block = 2; + bytes key = 3; + KeyType key_type = 4; + bytes nt = 5; + bytes nr = 6; + bytes ar = 7; + bytes at = 8; +} + +message ReadBlockRequest { + uint32 block = 1; + bytes key = 2; + KeyType key_type = 3; +} + +message ReadBlockResponse { + Error error = 1; + bytes data = 2; +} + +message WriteBlockRequest { + uint32 block = 1; + bytes key = 2; + KeyType key_type = 3; + bytes data = 4; +} + +message WriteBlockResponse { + Error error = 1; +} + +message ReadValueRequest { + uint32 block = 1; + bytes key = 2; + KeyType key_type = 3; +} + +message ReadValueResponse { + Error error = 1; + int32 value = 2; +} + +message ChangeValueRequest { + uint32 block = 1; + bytes key = 2; + KeyType key_type = 3; + int32 data = 4; +} + +message ChangeValueResponse { + Error error = 1; + int32 value = 2; +} diff --git a/applications/main/nfc_rpc/assets/protobuf/mf_ultralight.options b/applications/main/nfc_rpc/assets/protobuf/mf_ultralight.options new file mode 100644 index 000000000000..a7b4fab9a8b4 --- /dev/null +++ b/applications/main/nfc_rpc/assets/protobuf/mf_ultralight.options @@ -0,0 +1,9 @@ +PB_MfUltralight.ReadPageRequest.page int_size:IS_16 +PB_MfUltralight.ReadPageResponse.page int_size:IS_16 +PB_MfUltralight.ReadPageResponse.data max_size: 4 +PB_MfUltralight.WritePageRequest.page int_size:IS_16 +PB_MfUltralight.WritePageRequest.data max_size: 4 +PB_MfUltralight.ReadSignatureResponse.data max_size: 32 +PB_MfUltralight.ReadCounterResponse.data max_size: 3 +PB_MfUltralight.ReadTearingFlagResponse.data max_size: 1 +PB_MfUltralight.EmulateStartRequest.data max_size: 2048 \ No newline at end of file diff --git a/applications/main/nfc_rpc/assets/protobuf/mf_ultralight.proto b/applications/main/nfc_rpc/assets/protobuf/mf_ultralight.proto new file mode 100644 index 000000000000..959dd5be7c6b --- /dev/null +++ b/applications/main/nfc_rpc/assets/protobuf/mf_ultralight.proto @@ -0,0 +1,75 @@ +syntax = "proto3"; + +package PB_MfUltralight; +option java_package = "com.flipperdevices.nfc.protobuf"; + +enum Error { + None = 0; + NotPresent = 1; + Protocol = 2; + Auth = 3; + Timeout = 4; +} + +message ReadPageRequest { uint32 page = 1; } + +message ReadPageResponse { + Error error = 1; + uint32 page = 2; + bytes data = 3; +} + +message ReadVersionRequest {} + +message ReadVersionResponse { + Error error = 1; + uint32 header = 2; + uint32 vendor_id = 3; + uint32 prod_type = 4; + uint32 prod_subtype = 5; + uint32 prod_ver_major = 6; + uint32 prod_ver_minor = 7; + uint32 storage_size = 8; + uint32 protocol_type = 9; +} + +message WritePageRequest { + uint32 page = 1; + bytes data = 2; +} + +message WritePageResponse { + Error error = 1; + bool result = 2; +} + +message ReadSignatureRequest {} + +message ReadSignatureResponse { + Error error = 1; + bytes data = 2; +} + +message ReadCounterRequest { uint32 counter_num = 1; } + +message ReadCounterResponse { + Error error = 1; + uint32 counter_num = 2; + bytes data = 3; +} + +message ReadTearingFlagRequest { uint32 flag_num = 1; } + +message ReadTearingFlagResponse { + Error error = 1; + uint32 flag_num = 2; + bytes data = 3; +} + +message EmulateStartRequest { bytes data = 1; } + +message EmulateStartResponse { Error error = 1; } + +message EmulateStopRequest {} + +message EmulateStopResponse { Error error = 1; } diff --git a/applications/main/nfc_rpc/assets/protobuf/nfca.options b/applications/main/nfc_rpc/assets/protobuf/nfca.options new file mode 100644 index 000000000000..5da79e05678c --- /dev/null +++ b/applications/main/nfc_rpc/assets/protobuf/nfca.options @@ -0,0 +1,6 @@ +PB_Nfca.ReadResponse.uid max_size: 10 +PB_Nfca.ReadResponse.sak max_size: 1 +PB_Nfca.ReadResponse.atqa max_size: 2 +PB_Nfca.EmulateStartRequest.uid max_size: 10 +PB_Nfca.EmulateStartRequest.sak max_size: 1 +PB_Nfca.EmulateStartRequest.atqa max_size: 2 \ No newline at end of file diff --git a/applications/main/nfc_rpc/assets/protobuf/nfca.proto b/applications/main/nfc_rpc/assets/protobuf/nfca.proto new file mode 100644 index 000000000000..96a540c306a1 --- /dev/null +++ b/applications/main/nfc_rpc/assets/protobuf/nfca.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package PB_Nfca; +option java_package = "com.flipperdevices.nfc.protobuf"; + +enum Error { + None = 0; + NotPresent = 1; + ColResFailed = 2; + BufferOverflow = 3; + Communication = 4; + FieldOff = 5; + WrongCrc = 6; + Timeout = 7; +} + +message ReadRequest {} + +message ReadResponse { + Error error = 1; + uint32 uid_len = 2; + bytes uid = 3; + bytes sak = 4; + bytes atqa = 5; +} + +message EmulateStartRequest { + uint32 uid_len = 1; + bytes uid = 2; + bytes sak = 3; + bytes atqa = 4; +} + +message EmulateStartResponse { Error error = 1; } + +message EmulateStopRequest {} + +message EmulateStopResponse { Error error = 1; } \ No newline at end of file diff --git a/applications/main/nfc_rpc/nfc_rpc.c b/applications/main/nfc_rpc/nfc_rpc.c new file mode 100644 index 000000000000..b4d9138b12eb --- /dev/null +++ b/applications/main/nfc_rpc/nfc_rpc.c @@ -0,0 +1,213 @@ +#include "nfc_rpc_i.h" + +#define TAG "NfcRpc" + +static const NfcRpcCallbacks nfc_rpc_callbacks[] = { + { + .alloc = nfc_rpc_nfca_alloc, + .free = NULL, + }, + { + .alloc = nfc_rpc_mf_ultralight_alloc, + .free = NULL, + }, + { + .alloc = nfc_rpc_mf_classic_alloc, + .free = NULL, + }}; + +uint32_t nfc_rpc_exit_callback(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +void nfc_rpc_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 32, 0, "NFC RPC Actor running..."); +} + +bool nfc_rpc_input_callback(InputEvent* event, void* context) { + UNUSED(context); + UNUSED(event); + return false; +} + +static void + nfc_rpc_receive_data_exchange_callback(const uint8_t* data, size_t data_size, void* context) { + NfcRpc* app = context; + NfcRpcEvent event; + FURI_LOG_I(TAG, "Received %d bytes", data_size); + event.type = NfcRpcEventDataExchange; + event.data = data; + event.data_size = data_size; + furi_message_queue_put(app->queue, &event, FuriWaitForever); + view_dispatcher_send_custom_event(app->view_dispatcher, NfcRpcCustomEventMessageReceived); +} + +static void nfc_rpc_rpc_command_callback(RpcAppSystemEvent event, void* context) { + furi_assert(context); + NfcRpc* app = context; + + if(event == RpcAppEventSessionClose) { + rpc_system_app_set_callback(app->rpc, NULL, NULL); + rpc_system_app_set_data_exchange_callback(app->rpc, NULL, NULL); + view_dispatcher_stop(app->view_dispatcher); + app->rpc = NULL; + } + if(event == RpcAppEventAppExit) { + view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_NONE); + view_dispatcher_stop(app->view_dispatcher); + rpc_system_app_confirm(app->rpc, RpcAppEventAppExit, true); + } +} + +static bool nfc_rpc_init_rpc(NfcRpc* app, const char* args) { + bool ret = false; + if(args && strlen(args)) { + uint32_t rpc = 0; + if(sscanf(args, "RPC %lX", &rpc) == 1) { + app->rpc = (RpcAppSystem*)rpc; + rpc_system_app_set_callback(app->rpc, nfc_rpc_rpc_command_callback, app); + rpc_system_app_send_started(app->rpc); + ret = true; + } + } + return ret; +} + +void nfc_rpc_add_handler(NfcRpc* instance, pb_size_t message_tag, NfcRpcHandler handler) { + furi_assert(instance); + furi_assert(instance->handlers); + furi_assert(handler); + furi_assert(NfcRpcHandlerDict_get(instance->handlers, message_tag) == NULL); + + NfcRpcHandlerDict_set_at(instance->handlers, message_tag, handler); +} + +static void nfc_rpc_message_receive_handler(NfcRpc* instance) { + furi_assert(instance); + furi_assert(instance->queue); + furi_assert(instance->rpc); + NfcRpcEvent event; + pb_istream_t istream; + pb_ostream_t ostream; + uint8_t response_buffer[512] = {}; + + if(furi_message_queue_get(instance->queue, &event, FuriWaitForever) == FuriStatusOk) { + Nfc_Main cmd = Nfc_Main_init_default; + istream = pb_istream_from_buffer(event.data, event.data_size); + if(pb_decode(&istream, Nfc_Main_fields, &cmd)) { + FURI_LOG_I(TAG, "Which: %d", cmd.which_content); + NfcRpcHandler handler = *NfcRpcHandlerDict_get(instance->handlers, cmd.which_content); + if(handler) { + handler(&cmd, instance); + } else { + cmd.command_status = Nfc_CommandStatus_ERROR_NOT_IMPLEMENTED; + } + ostream = pb_ostream_from_buffer(response_buffer, sizeof(response_buffer)); + pb_encode(&ostream, Nfc_Main_fields, &cmd); + FURI_LOG_D("NfcRpc", "Sending %d bytes", ostream.bytes_written); + rpc_system_app_exchange_data(instance->rpc, response_buffer, ostream.bytes_written); + } else { + FURI_LOG_E(TAG, "Failed to decode message: %s", istream.errmsg); + Nfc_Main cmd = Nfc_Main_init_default; + cmd.command_status = Nfc_CommandStatus_ERROR; + ostream = pb_ostream_from_buffer(response_buffer, sizeof(response_buffer)); + pb_encode(&ostream, Nfc_Main_fields, &cmd); + rpc_system_app_exchange_data(instance->rpc, response_buffer, ostream.bytes_written); + } + } +} + +bool nfc_rpc_custom_event_callback(void* context, uint32_t event) { + NfcRpc* instance = context; + if(event == NfcRpcCustomEventMessageReceived) { + nfc_rpc_message_receive_handler(instance); + } + + return true; +} + +static NfcRpc* nfc_rpc_app_alloc() { + NfcRpc* instance = malloc(sizeof(NfcRpc)); + + instance->nfc = nfc_alloc(); + + NfcRpcHandlerDict_init(instance->handlers); + for(size_t i = 0; i < COUNT_OF(nfc_rpc_callbacks); i++) { + if(nfc_rpc_callbacks[i].alloc) { + nfc_rpc_callbacks[i].alloc(instance); + } + } + + instance->gui = furi_record_open(RECORD_GUI); + instance->view_dispatcher = view_dispatcher_alloc(); + instance->view = view_alloc(); + + view_set_previous_callback(instance->view, nfc_rpc_exit_callback); + view_set_input_callback(instance->view, nfc_rpc_input_callback); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, nfc_rpc_draw_callback); + + view_dispatcher_enable_queue(instance->view_dispatcher); + view_dispatcher_attach_to_gui( + instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_add_view(instance->view_dispatcher, NfcRpcViewMain, instance->view); + + view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance); + view_dispatcher_set_custom_event_callback( + instance->view_dispatcher, nfc_rpc_custom_event_callback); + + instance->queue = furi_message_queue_alloc(8, sizeof(NfcRpcEvent)); + + return instance; +} + +void nfc_rpc_app_free(NfcRpc* instance) { + furi_assert(instance); + + nfc_free(instance->nfc); + + for(size_t i = 0; i < COUNT_OF(nfc_rpc_callbacks); i++) { + if(nfc_rpc_callbacks[i].free) { + nfc_rpc_callbacks[i].free(instance); + } + } + NfcRpcHandlerDict_clear(instance->handlers); + + view_dispatcher_remove_view(instance->view_dispatcher, NfcRpcViewMain); + view_free(instance->view); + view_dispatcher_free(instance->view_dispatcher); + furi_message_queue_free(instance->queue); + furi_record_close(RECORD_GUI); + instance->gui = NULL; + instance->view_dispatcher = NULL; + + if(instance->rpc) { + rpc_system_app_set_callback(instance->rpc, NULL, NULL); + rpc_system_app_send_exited(instance->rpc); + instance->rpc = NULL; + } + + free(instance); +} + +int32_t nfc_rpc_app(void* p) { + UNUSED(p); + NfcRpc* app = nfc_rpc_app_alloc(); + + nfc_rpc_init_rpc(app, p); + + rpc_system_app_set_data_exchange_callback( + app->rpc, nfc_rpc_receive_data_exchange_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRpcViewMain); + view_dispatcher_run(app->view_dispatcher); + + nfc_rpc_app_free(app); + + return 0; +} diff --git a/applications/main/nfc_rpc/nfc_rpc.h b/applications/main/nfc_rpc/nfc_rpc.h new file mode 100644 index 000000000000..39969837eff6 --- /dev/null +++ b/applications/main/nfc_rpc/nfc_rpc.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct NfcRpc NfcRpc; diff --git a/applications/main/nfc_rpc/nfc_rpc_i.h b/applications/main/nfc_rpc/nfc_rpc_i.h new file mode 100644 index 000000000000..69750a2685e3 --- /dev/null +++ b/applications/main/nfc_rpc/nfc_rpc_i.h @@ -0,0 +1,71 @@ +#pragma once + +#include "nfc_rpc.h" + +#include "furi.h" +#include "furi_hal.h" +#include +#include +#include +#include + +#include +#include + +#include "assets/compiled/main.pb.h" +#include + +#include +#include +#include +#include + +#include + +typedef void (*NfcRpcHandler)(Nfc_Main* cmd, void* context); + +DICT_DEF2(NfcRpcHandlerDict, pb_size_t, M_DEFAULT_OPLIST, NfcRpcHandler, M_POD_OPLIST) + +typedef void (*NfcRpcHandlersAlloc)(void* context); +typedef void (*NfcRpcHandlersFree)(void* context); + +typedef enum { + NfcRpcViewMain, +} NfcRpcView; + +typedef enum { + NfcRpcCustomEventMessageReceived, +} NfcRpcCustomEvent; + +typedef enum { + NfcRpcEventDataExchange, +} NfcRpcEventType; + +typedef struct { + NfcRpcEventType type; + const uint8_t* data; + size_t data_size; +} NfcRpcEvent; + +struct NfcRpc { + Gui* gui; + RpcAppSystem* rpc; + FuriMessageQueue* queue; + ViewDispatcher* view_dispatcher; + View* view; + NfcRpcHandlerDict_t handlers; + + Nfc* nfc; + NfcListener* listener; +}; + +typedef struct { + NfcRpcHandlersAlloc alloc; + NfcRpcHandlersFree free; +} NfcRpcCallbacks; + +void nfc_rpc_add_handler(NfcRpc* instance, pb_size_t message_tag, NfcRpcHandler handler); + +void nfc_rpc_nfca_alloc(void* context); +void nfc_rpc_mf_ultralight_alloc(void* context); +void nfc_rpc_mf_classic_alloc(void* context); diff --git a/applications/main/nfc_rpc/nfc_rpc_mf_classic.c b/applications/main/nfc_rpc/nfc_rpc_mf_classic.c new file mode 100644 index 000000000000..86227b455b69 --- /dev/null +++ b/applications/main/nfc_rpc/nfc_rpc_mf_classic.c @@ -0,0 +1,196 @@ +#include "nfc_rpc_i.h" + +#include "assets/compiled/mf_classic.pb.h" +#include + +#define TAG "NfcRpcMfClassic" + +static PB_MfClassic_Error nfc_rpc_mf_classic_process_error(MfClassicError error) { + PB_MfClassic_Error ret = PB_MfClassic_Error_None; + + switch(error) { + case MfClassicErrorNone: + ret = PB_MfClassic_Error_None; + break; + case MfClassicErrorNotPresent: + ret = PB_MfClassic_Error_NotPresent; + break; + case MfClassicErrorProtocol: + ret = PB_MfClassic_Error_Protocol; + break; + case MfClassicErrorAuth: + ret = PB_MfClassic_Error_Auth; + break; + case MfClassicErrorTimeout: + ret = PB_MfClassic_Error_Timeout; + break; + + default: + ret = PB_MfClassic_Error_Timeout; + break; + } + + return ret; +} + +static void nfc_rpc_mf_classic_auth(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfClassic_AuthResponse pb_mf_classic_auth_resp = PB_MfClassic_AuthResponse_init_default; + + PB_MfClassic_AuthRequest* req = &cmd->content.mf_classic_auth_req; + MfClassicAuthContext auth_context = {}; + MfClassicKey key = {}; + memcpy(key.data, req->key.bytes, sizeof(MfClassicKey)); + MfClassicKeyType key_type = + (req->key_type == PB_MfClassic_KeyType_KeyTypeB) ? MfClassicKeyTypeB : MfClassicKeyTypeA; + MfClassicError error = + mf_classic_poller_auth(instance->nfc, req->block, &key, key_type, &auth_context); + + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_classic_auth_resp_tag; + + pb_mf_classic_auth_resp.error = nfc_rpc_mf_classic_process_error(error); + if(pb_mf_classic_auth_resp.error == PB_MfClassic_Error_None) { + pb_mf_classic_auth_resp.block = auth_context.block_num; + memcpy(pb_mf_classic_auth_resp.key.bytes, auth_context.key.data, sizeof(MfClassicKey)); + pb_mf_classic_auth_resp.key.size = sizeof(MfClassicKey); + pb_mf_classic_auth_resp.key_type = (auth_context.key_type == MfClassicKeyTypeB) ? + PB_MfClassic_KeyType_KeyTypeB : + PB_MfClassic_KeyType_KeyTypeA; + memcpy(pb_mf_classic_auth_resp.nt.bytes, auth_context.nt.data, sizeof(MfClassicNt)); + pb_mf_classic_auth_resp.nt.size = sizeof(MfClassicNt); + memcpy(pb_mf_classic_auth_resp.nr.bytes, auth_context.nr.data, sizeof(MfClassicNr)); + pb_mf_classic_auth_resp.nr.size = sizeof(MfClassicNr); + memcpy(pb_mf_classic_auth_resp.ar.bytes, auth_context.ar.data, sizeof(MfClassicAr)); + pb_mf_classic_auth_resp.ar.size = sizeof(MfClassicAr); + memcpy(pb_mf_classic_auth_resp.at.bytes, auth_context.at.data, sizeof(MfClassicAt)); + pb_mf_classic_auth_resp.at.size = sizeof(MfClassicAt); + } + cmd->content.mf_classic_auth_resp = pb_mf_classic_auth_resp; +} + +static void nfc_rpc_mf_classic_read_block(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfClassic_ReadBlockResponse pb_mf_classic_read_block_resp = + PB_MfClassic_ReadBlockResponse_init_default; + + PB_MfClassic_ReadBlockRequest* req = &cmd->content.mf_classic_read_block_req; + MfClassicKey key = {}; + memcpy(key.data, req->key.bytes, sizeof(MfClassicKey)); + MfClassicKeyType key_type = + (req->key_type == PB_MfClassic_KeyType_KeyTypeB) ? MfClassicKeyTypeB : MfClassicKeyTypeA; + MfClassicBlock block = {}; + MfClassicError error = + mf_classic_poller_read_block(instance->nfc, req->block, &key, key_type, &block); + + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_classic_read_block_resp_tag; + + pb_mf_classic_read_block_resp.error = nfc_rpc_mf_classic_process_error(error); + if(pb_mf_classic_read_block_resp.error == PB_MfClassic_Error_None) { + memcpy(pb_mf_classic_read_block_resp.data.bytes, block.data, sizeof(MfClassicBlock)); + pb_mf_classic_read_block_resp.data.size = sizeof(MfClassicBlock); + } + cmd->content.mf_classic_read_block_resp = pb_mf_classic_read_block_resp; +} + +static void nfc_rpc_mf_classic_write_block(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfClassic_WriteBlockResponse pb_mf_classic_write_block_resp = + PB_MfClassic_WriteBlockResponse_init_default; + + PB_MfClassic_WriteBlockRequest* req = &cmd->content.mf_classic_write_block_req; + MfClassicKey key = {}; + memcpy(key.data, req->key.bytes, sizeof(MfClassicKey)); + MfClassicKeyType key_type = + (req->key_type == PB_MfClassic_KeyType_KeyTypeB) ? MfClassicKeyTypeB : MfClassicKeyTypeA; + MfClassicBlock block = {}; + memcpy(block.data, req->data.bytes, sizeof(MfClassicBlock)); + MfClassicError error = + mf_classic_poller_write_block(instance->nfc, req->block, &key, key_type, &block); + + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_classic_write_block_resp_tag; + + pb_mf_classic_write_block_resp.error = nfc_rpc_mf_classic_process_error(error); + cmd->content.mf_classic_write_block_resp = pb_mf_classic_write_block_resp; +} + +static void nfc_rpc_mf_classic_read_value(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfClassic_ReadValueResponse pb_mf_classic_read_value_resp = + PB_MfClassic_ReadValueResponse_init_default; + + PB_MfClassic_ReadValueRequest* req = &cmd->content.mf_classic_read_value_req; + MfClassicKey key = {}; + memcpy(key.data, req->key.bytes, sizeof(MfClassicKey)); + MfClassicKeyType key_type = + (req->key_type == PB_MfClassic_KeyType_KeyTypeB) ? MfClassicKeyTypeB : MfClassicKeyTypeA; + int32_t value = 0; + MfClassicError error = + mf_classic_poller_read_value(instance->nfc, req->block, &key, key_type, &value); + + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_classic_read_value_resp_tag; + + pb_mf_classic_read_value_resp.error = nfc_rpc_mf_classic_process_error(error); + if(pb_mf_classic_read_value_resp.error == PB_MfClassic_Error_None) { + pb_mf_classic_read_value_resp.value = value; + } + cmd->content.mf_classic_read_value_resp = pb_mf_classic_read_value_resp; +} + +static void nfc_rpc_mf_classic_change_value(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfClassic_ChangeValueResponse pb_mf_classic_change_value_resp = + PB_MfClassic_ChangeValueResponse_init_default; + + PB_MfClassic_ChangeValueRequest* req = &cmd->content.mf_classic_change_value_req; + MfClassicKey key = {}; + memcpy(key.data, req->key.bytes, sizeof(MfClassicKey)); + MfClassicKeyType key_type = + (req->key_type == PB_MfClassic_KeyType_KeyTypeB) ? MfClassicKeyTypeB : MfClassicKeyTypeA; + int32_t data = req->data; + int32_t new_value = 0; + MfClassicError error = mf_classic_poller_change_value( + instance->nfc, req->block, &key, key_type, data, &new_value); + + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_classic_change_value_resp_tag; + + pb_mf_classic_change_value_resp.error = nfc_rpc_mf_classic_process_error(error); + if(pb_mf_classic_change_value_resp.error == PB_MfClassic_Error_None) { + pb_mf_classic_change_value_resp.value = new_value; + } + cmd->content.mf_classic_change_value_resp = pb_mf_classic_change_value_resp; +} + +void nfc_rpc_mf_classic_alloc(void* context) { + furi_assert(context); + + NfcRpc* instance = context; + nfc_rpc_add_handler(instance, Nfc_Main_mf_classic_auth_req_tag, nfc_rpc_mf_classic_auth); + nfc_rpc_add_handler( + instance, Nfc_Main_mf_classic_read_block_req_tag, nfc_rpc_mf_classic_read_block); + nfc_rpc_add_handler( + instance, Nfc_Main_mf_classic_write_block_req_tag, nfc_rpc_mf_classic_write_block); + nfc_rpc_add_handler( + instance, Nfc_Main_mf_classic_read_value_req_tag, nfc_rpc_mf_classic_read_value); + nfc_rpc_add_handler( + instance, Nfc_Main_mf_classic_change_value_req_tag, nfc_rpc_mf_classic_change_value); +} diff --git a/applications/main/nfc_rpc/nfc_rpc_mf_ultralight.c b/applications/main/nfc_rpc/nfc_rpc_mf_ultralight.c new file mode 100644 index 000000000000..ddab5b7fdec0 --- /dev/null +++ b/applications/main/nfc_rpc/nfc_rpc_mf_ultralight.c @@ -0,0 +1,275 @@ +#include "nfc_rpc_i.h" + +#include "assets/compiled/mf_ultralight.pb.h" +#include + +#define TAG "NfcRpcMfUltralight" + +static PB_MfUltralight_Error nfc_rpc_mf_ultralight_process_error(MfUltralightError error) { + PB_MfUltralight_Error ret = PB_MfUltralight_Error_None; + + switch(error) { + case MfUltralightErrorNone: + ret = PB_MfUltralight_Error_None; + break; + case MfUltralightErrorNotPresent: + ret = PB_MfUltralight_Error_NotPresent; + break; + case MfUltralightErrorProtocol: + ret = PB_MfUltralight_Error_Protocol; + break; + case MfUltralightErrorAuth: + ret = PB_MfUltralight_Error_Auth; + break; + case MfUltralightErrorTimeout: + ret = PB_MfUltralight_Error_Timeout; + break; + + default: + ret = PB_MfUltralight_Error_Timeout; + break; + } + + return ret; +} + +static void nfc_rpc_mf_ultralight_read_page(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfUltralight_ReadPageResponse pb_mf_ul_read_page_resp = + PB_MfUltralight_ReadPageResponse_init_default; + + MfUltralightPage page = {}; + MfUltralightError error = mf_ultralight_poller_read_page( + instance->nfc, cmd->content.mf_ultralight_read_page_req.page, &page); + + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_ultralight_read_page_resp_tag; + pb_mf_ul_read_page_resp.error = nfc_rpc_mf_ultralight_process_error(error); + if(pb_mf_ul_read_page_resp.error == PB_MfUltralight_Error_None) { + pb_mf_ul_read_page_resp.page = cmd->content.mf_ultralight_read_page_req.page; + memcpy(pb_mf_ul_read_page_resp.data.bytes, &page, sizeof(MfUltralightPage)); + pb_mf_ul_read_page_resp.data.size = sizeof(MfUltralightPage); + } + cmd->content.mf_ultralight_read_page_resp = pb_mf_ul_read_page_resp; +} + +static void nfc_rpc_mf_ultralight_write_page(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfUltralight_WritePageResponse pb_mf_ul_write_page_resp = + PB_MfUltralight_WritePageResponse_init_default; + + MfUltralightPage data = {}; + memcpy(&data, cmd->content.mf_ultralight_write_page_req.data.bytes, sizeof(MfUltralightPage)); + uint16_t page = cmd->content.mf_ultralight_write_page_req.page; + MfUltralightError error = mf_ultralight_poller_write_page(instance->nfc, page, &data); + + cmd->which_content = Nfc_Main_mf_ultralight_write_page_resp_tag; + cmd->command_status = Nfc_CommandStatus_OK; + pb_mf_ul_write_page_resp.error = nfc_rpc_mf_ultralight_process_error(error); + cmd->content.mf_ultralight_write_page_resp = pb_mf_ul_write_page_resp; +} + +static void nfc_rpc_mf_ultralight_read_version(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfUltralight_ReadVersionResponse pb_mf_ul_version = + PB_MfUltralight_ReadVersionResponse_init_default; + + MfUltralightVersion data = {}; + MfUltralightError error = mf_ultralight_poller_read_version(instance->nfc, &data); + + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_ultralight_read_version_resp_tag; + pb_mf_ul_version.error = nfc_rpc_mf_ultralight_process_error(error); + if(pb_mf_ul_version.error == PB_MfUltralight_Error_None) { + pb_mf_ul_version.header = data.header; + pb_mf_ul_version.prod_subtype = data.prod_subtype; + pb_mf_ul_version.prod_type = data.prod_type; + pb_mf_ul_version.prod_ver_major = data.prod_ver_major; + pb_mf_ul_version.prod_ver_minor = data.prod_ver_minor; + pb_mf_ul_version.protocol_type = data.protocol_type; + pb_mf_ul_version.storage_size = data.storage_size; + pb_mf_ul_version.vendor_id = data.vendor_id; + } + cmd->content.mf_ultralight_read_version_resp = pb_mf_ul_version; +} + +static void nfc_rpc_mf_ultralight_read_signature(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfUltralight_ReadSignatureResponse pb_mf_ul_signature = + PB_MfUltralight_ReadSignatureResponse_init_default; + + MfUltralightSignature data = {}; + MfUltralightError error = mf_ultralight_poller_read_signature(instance->nfc, &data); + + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_ultralight_read_signature_resp_tag; + pb_mf_ul_signature.error = nfc_rpc_mf_ultralight_process_error(error); + if(pb_mf_ul_signature.error == PB_MfUltralight_Error_None) { + memcpy(pb_mf_ul_signature.data.bytes, data.data, sizeof(MfUltralightSignature)); + pb_mf_ul_signature.data.size = sizeof(MfUltralightSignature); + } + cmd->content.mf_ultralight_read_signature_resp = pb_mf_ul_signature; +} + +static void nfc_rpc_mf_ultralight_read_counter(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfUltralight_ReadCounterResponse pb_mf_ul_read_counter_resp = + PB_MfUltralight_ReadPageResponse_init_default; + + MfUltralightCounter data = {}; + MfUltralightError error = mf_ultralight_poller_read_counter( + instance->nfc, cmd->content.mf_ultralight_read_counter_req.counter_num, &data); + + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_ultralight_read_counter_resp_tag; + pb_mf_ul_read_counter_resp.error = nfc_rpc_mf_ultralight_process_error(error); + if(pb_mf_ul_read_counter_resp.error == PB_MfUltralight_Error_None) { + pb_mf_ul_read_counter_resp.counter_num = + cmd->content.mf_ultralight_read_counter_req.counter_num; + memcpy(pb_mf_ul_read_counter_resp.data.bytes, data.data, sizeof(MfUltralightCounter)); + pb_mf_ul_read_counter_resp.data.size = sizeof(MfUltralightCounter); + } + cmd->content.mf_ultralight_read_counter_resp = pb_mf_ul_read_counter_resp; +} + +static void nfc_rpc_mf_ultralight_read_tearing_flag(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfUltralight_ReadTearingFlagResponse pb_mf_ul_read_tearing_flag_resp = + PB_MfUltralight_ReadTearingFlagResponse_init_default; + + MfUltralightTearingFlag data = {}; + MfUltralightError error = mf_ultralight_poller_read_tearing_flag( + instance->nfc, cmd->content.mf_ultralight_read_tearing_flag_req.flag_num, &data); + + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_ultralight_read_tearing_flag_resp_tag; + pb_mf_ul_read_tearing_flag_resp.error = nfc_rpc_mf_ultralight_process_error(error); + if(pb_mf_ul_read_tearing_flag_resp.error == PB_MfUltralight_Error_None) { + pb_mf_ul_read_tearing_flag_resp.flag_num = + cmd->content.mf_ultralight_read_tearing_flag_req.flag_num; + memcpy( + pb_mf_ul_read_tearing_flag_resp.data.bytes, + data.data, + sizeof(MfUltralightTearingFlag)); + pb_mf_ul_read_tearing_flag_resp.data.size = sizeof(MfUltralightTearingFlag); + } + cmd->content.mf_ultralight_read_tearing_flag_resp = pb_mf_ul_read_tearing_flag_resp; + FURI_LOG_D( + TAG, "Tearing flag %ld: %02X", pb_mf_ul_read_tearing_flag_resp.flag_num, data.data[0]); +} + +// TODO DELETE! +static void init_mf_ul_data(MfUltralightData* data) { + Iso14443_3aData* iso14443_3a_data = data->iso14443_3a_data; + uint8_t uid[7] = {0x44, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; + uint8_t atqa[2] = {0x44, 0x00}; + iso14443_3a_data->uid_len = sizeof(uid); + memcpy(iso14443_3a_data->uid, uid, sizeof(uid)); + memcpy(iso14443_3a_data->atqa, atqa, sizeof(atqa)); + iso14443_3a_data->sak = 0x00; + + data->type = MfUltralightTypeUnknown; + data->pages_total = 16; + MfUltralightVersion version = { + .header = 1, + .protocol_type = 228, + }; + data->version = version; + MfUltralightSignature sig = {.data = {0x01, 0x02, 0x03}}; + data->signature = sig; + for(size_t i = 0; i < 16; i++) { + data->page[i].data[0] = i; + } +} + +void nfc_rpc_mf_ultralight_emulate_start(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfUltralight_EmulateStartResponse pb_mf_ultralight_emulate_start_resp = + PB_MfUltralight_EmulateStartResponse_init_default; + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_ultralight_emulate_start_resp_tag; + if(instance->listener == NULL) { + MfUltralightData* mf_ul_data = mf_ultralight_alloc(); + // TODO initialize data from rpc message + init_mf_ul_data(mf_ul_data); + instance->listener = + nfc_listener_alloc(instance->nfc, NfcProtocolMfUltralight, mf_ul_data); + nfc_listener_start(instance->listener, NULL, NULL); + pb_mf_ultralight_emulate_start_resp.error = PB_MfUltralight_Error_None; + } else { + // TODO add Busy error + pb_mf_ultralight_emulate_start_resp.error = PB_MfUltralight_Error_NotPresent; + } + cmd->content.mf_ultralight_emulate_start_resp = pb_mf_ultralight_emulate_start_resp; +} + +void nfc_rpc_mf_ultralight_emulate_stop(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_MfUltralight_EmulateStopResponse pb_mf_ultralight_emulate_stop_resp = + PB_MfUltralight_EmulateStopResponse_init_default; + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_mf_ultralight_emulate_stop_resp_tag; + if(instance->listener) { + // Stop before free + nfc_listener_stop(instance->listener); + nfc_listener_free(instance->listener); + instance->listener = NULL; + pb_mf_ultralight_emulate_stop_resp.error = PB_MfUltralight_Error_None; + } else { + // TODO emulation not started error + pb_mf_ultralight_emulate_stop_resp.error = PB_MfUltralight_Error_NotPresent; + } + cmd->content.mf_ultralight_emulate_stop_resp = pb_mf_ultralight_emulate_stop_resp; +} + +void nfc_rpc_mf_ultralight_alloc(void* context) { + furi_assert(context); + + NfcRpc* instance = context; + nfc_rpc_add_handler( + instance, Nfc_Main_mf_ultralight_read_page_req_tag, nfc_rpc_mf_ultralight_read_page); + nfc_rpc_add_handler( + instance, Nfc_Main_mf_ultralight_read_version_req_tag, nfc_rpc_mf_ultralight_read_version); + nfc_rpc_add_handler( + instance, Nfc_Main_mf_ultralight_write_page_req_tag, nfc_rpc_mf_ultralight_write_page); + nfc_rpc_add_handler( + instance, + Nfc_Main_mf_ultralight_read_signature_req_tag, + nfc_rpc_mf_ultralight_read_signature); + nfc_rpc_add_handler( + instance, Nfc_Main_mf_ultralight_read_counter_req_tag, nfc_rpc_mf_ultralight_read_counter); + nfc_rpc_add_handler( + instance, + Nfc_Main_mf_ultralight_read_tearing_flag_req_tag, + nfc_rpc_mf_ultralight_read_tearing_flag); + nfc_rpc_add_handler( + instance, + Nfc_Main_mf_ultralight_emulate_start_req_tag, + nfc_rpc_mf_ultralight_emulate_start); + nfc_rpc_add_handler( + instance, Nfc_Main_mf_ultralight_emulate_stop_req_tag, nfc_rpc_mf_ultralight_emulate_stop); +} diff --git a/applications/main/nfc_rpc/nfc_rpc_nfca.c b/applications/main/nfc_rpc/nfc_rpc_nfca.c new file mode 100644 index 000000000000..986e909d0bdf --- /dev/null +++ b/applications/main/nfc_rpc/nfc_rpc_nfca.c @@ -0,0 +1,127 @@ +#include "nfc_rpc_i.h" + +#include "assets/compiled/nfca.pb.h" +#include + +#define TAG "NfcRpcIso14443_3a" + +static PB_Nfca_Error nfc_rpc_nfca_process_error(Iso14443_3aError error) { + PB_Nfca_Error ret = PB_Nfca_Error_None; + switch(error) { + case Iso14443_3aErrorNone: + ret = PB_Nfca_Error_None; + break; + case Iso14443_3aErrorNotPresent: + ret = PB_Nfca_Error_NotPresent; + break; + case Iso14443_3aErrorColResFailed: + ret = PB_Nfca_Error_ColResFailed; + break; + case Iso14443_3aErrorBufferOverflow: + ret = PB_Nfca_Error_BufferOverflow; + break; + case Iso14443_3aErrorCommunication: + ret = PB_Nfca_Error_Communication; + break; + case Iso14443_3aErrorFieldOff: + ret = PB_Nfca_Error_FieldOff; + break; + case Iso14443_3aErrorWrongCrc: + ret = PB_Nfca_Error_WrongCrc; + break; + case Iso14443_3aErrorTimeout: + ret = PB_Nfca_Error_Timeout; + break; + + default: + ret = PB_Nfca_Error_Timeout; + break; + } + + return ret; +} + +static void nfc_rpc_nfca_read(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_Nfca_ReadResponse pb_nfca_read_resp = PB_Nfca_ReadResponse_init_default; + + Iso14443_3aData iso14443_3a_data = {}; + Iso14443_3aError error = iso14443_3a_poller_read(instance->nfc, &iso14443_3a_data); + + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_nfca_read_resp_tag; + pb_nfca_read_resp.error = nfc_rpc_nfca_process_error(error); + if(pb_nfca_read_resp.error == PB_Nfca_Error_None) { + pb_nfca_read_resp.uid_len = iso14443_3a_data.uid_len; + memcpy(pb_nfca_read_resp.uid.bytes, iso14443_3a_data.uid, iso14443_3a_data.uid_len); + pb_nfca_read_resp.uid.size = iso14443_3a_data.uid_len; + memcpy(pb_nfca_read_resp.sak.bytes, &iso14443_3a_data.sak, sizeof(iso14443_3a_data.sak)); + pb_nfca_read_resp.sak.size = sizeof(iso14443_3a_data.sak); + memcpy(pb_nfca_read_resp.atqa.bytes, iso14443_3a_data.atqa, sizeof(iso14443_3a_data.atqa)); + pb_nfca_read_resp.atqa.size = sizeof(iso14443_3a_data.atqa); + } + cmd->content.nfca_read_resp = pb_nfca_read_resp; +} + +static void nfc_rpc_nfca_emulate_start(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_Nfca_EmulateStartResponse pb_nfca_emulate_start_resp = + PB_Nfca_EmulateStartResponse_init_default; + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_nfca_emulate_start_resp_tag; + if(instance->listener == NULL) { + Iso14443_3aData iso14443_3a_data = {}; + iso14443_3a_data.uid_len = cmd->content.nfca_emulate_start_req.uid_len; + memcpy( + iso14443_3a_data.uid, + cmd->content.nfca_emulate_start_req.uid.bytes, + iso14443_3a_data.uid_len); + memcpy(iso14443_3a_data.atqa, cmd->content.nfca_emulate_start_req.atqa.bytes, 2); + memcpy(&iso14443_3a_data.sak, cmd->content.nfca_emulate_start_req.sak.bytes, 1); + + instance->listener = + nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_3a, &iso14443_3a_data); + nfc_listener_start(instance->listener, NULL, NULL); + pb_nfca_emulate_start_resp.error = PB_Nfca_Error_None; + } else { + // TODO add Busy error + pb_nfca_emulate_start_resp.error = PB_Nfca_Error_NotPresent; + } + cmd->content.nfca_emulate_start_resp = pb_nfca_emulate_start_resp; +} + +static void nfc_rpc_nfca_emulate_stop(Nfc_Main* cmd, void* context) { + furi_assert(cmd); + furi_assert(context); + + NfcRpc* instance = context; + PB_Nfca_EmulateStopResponse pb_nfca_emulate_stop_resp = + PB_Nfca_EmulateStopResponse_init_default; + cmd->command_status = Nfc_CommandStatus_OK; + cmd->which_content = Nfc_Main_nfca_emulate_stop_resp_tag; + if(instance->listener) { + nfc_listener_stop(instance->listener); + nfc_listener_free(instance->listener); + instance->listener = NULL; + pb_nfca_emulate_stop_resp.error = PB_Nfca_Error_None; + } else { + // TODO add Busy error + pb_nfca_emulate_stop_resp.error = PB_Nfca_Error_NotPresent; + } + cmd->content.nfca_emulate_stop_resp = pb_nfca_emulate_stop_resp; +} + +void nfc_rpc_nfca_alloc(void* context) { + furi_assert(context); + + NfcRpc* instance = context; + nfc_rpc_add_handler(instance, Nfc_Main_nfca_read_req_tag, nfc_rpc_nfca_read); + nfc_rpc_add_handler(instance, Nfc_Main_nfca_emulate_start_req_tag, nfc_rpc_nfca_emulate_start); + nfc_rpc_add_handler(instance, Nfc_Main_nfca_emulate_stop_req_tag, nfc_rpc_nfca_emulate_stop); +} diff --git a/applications/main/onewire/application.fam b/applications/main/onewire/application.fam index 68d4f6716939..3d35abce948f 100644 --- a/applications/main/onewire/application.fam +++ b/applications/main/onewire/application.fam @@ -1,14 +1,6 @@ -App( - appid="onewire", - name="1-Wire", - apptype=FlipperAppType.METAPACKAGE, - provides=["onewire_start"], -) - App( appid="onewire_start", apptype=FlipperAppType.STARTUP, entry_point="onewire_on_system_start", - requires=["onewire"], order=60, ) diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index f0dc66e89ccd..4f21cb6c4e90 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -1,25 +1,21 @@ App( appid="subghz", name="Sub-GHz", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.MENUEXTERNAL, targets=["f7"], entry_point="subghz_app", - cdefines=["APP_SUBGHZ"], - requires=[ - "gui", - "cli", - "dialogs", - ], - provides=["subghz_start"], icon="A_Sub1ghz_14", stack_size=3 * 1024, order=10, + fap_libs=["assets", "hwdrivers"], + fap_icon="icon.png", + fap_category="Sub-GHz", ) App( appid="subghz_start", + targets=["f7"], apptype=FlipperAppType.STARTUP, entry_point="subghz_on_system_start", - requires=["subghz"], order=40, ) diff --git a/applications/main/subghz/helpers/subghz_chat.c b/applications/main/subghz/helpers/subghz_chat.c index dbf34c97051a..bbe219fd2f74 100644 --- a/applications/main/subghz/helpers/subghz_chat.c +++ b/applications/main/subghz/helpers/subghz_chat.c @@ -76,12 +76,15 @@ void subghz_chat_worker_free(SubGhzChatWorker* instance) { free(instance); } -bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency) { +bool subghz_chat_worker_start( + SubGhzChatWorker* instance, + const SubGhzDevice* device, + uint32_t frequency) { furi_assert(instance); furi_assert(!instance->worker_running); bool res = false; - if(subghz_tx_rx_worker_start(instance->subghz_txrx, frequency)) { + if(subghz_tx_rx_worker_start(instance->subghz_txrx, device, frequency)) { furi_message_queue_reset(instance->event_queue); subghz_tx_rx_worker_set_callback_have_read( instance->subghz_txrx, subghz_chat_worker_update_rx_event_chat, instance); diff --git a/applications/main/subghz/helpers/subghz_chat.h b/applications/main/subghz/helpers/subghz_chat.h index b418bbdbffce..2c454b75d981 100644 --- a/applications/main/subghz/helpers/subghz_chat.h +++ b/applications/main/subghz/helpers/subghz_chat.h @@ -1,5 +1,6 @@ #pragma once #include "../subghz_i.h" +#include #include typedef struct SubGhzChatWorker SubGhzChatWorker; @@ -20,7 +21,10 @@ typedef struct { SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli); void subghz_chat_worker_free(SubGhzChatWorker* instance); -bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency); +bool subghz_chat_worker_start( + SubGhzChatWorker* instance, + const SubGhzDevice* device, + uint32_t frequency); void subghz_chat_worker_stop(SubGhzChatWorker* instance); bool subghz_chat_worker_is_running(SubGhzChatWorker* instance); SubGhzChatEvent subghz_chat_worker_get_event_chat(SubGhzChatWorker* instance); diff --git a/applications/main/subghz/helpers/subghz_error_type.h b/applications/main/subghz/helpers/subghz_error_type.h index e481aa4be846..0f86d6ea7d16 100644 --- a/applications/main/subghz/helpers/subghz_error_type.h +++ b/applications/main/subghz/helpers/subghz_error_type.h @@ -10,4 +10,5 @@ typedef enum { 1, /** File parsing error, or wrong file structure, or missing required parameters. more accurate data can be obtained through the debug port */ SubGhzErrorTypeOnlyRX = 2, /** Transmission on this frequency is blocked by regional settings */ + SubGhzErrorTypeParserOthers = 3, /** Error in protocol parameters description */ } SubGhzErrorType; diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h index b94ebe3809df..df53143d23f4 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h @@ -27,10 +27,15 @@ TUPLE_DEF2( (rssi_max, uint8_t)) /* Register globally the oplist */ #define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \ - TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_POD_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) + TUPLE_OPLIST( \ + SubGhzFrequencyAnalyzerLogItem, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST) /* Define the array, register the oplist and define further algorithms on it */ -ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) +ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) //-V779 #define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_t() \ ARRAY_OPLIST(SubGhzFrequencyAnalyzerLogItemArray, M_OPL_SubGhzFrequencyAnalyzerLogItem_t()) ALGO_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItemArray_t) diff --git a/applications/main/subghz/helpers/subghz_threshold_rssi.c b/applications/main/subghz/helpers/subghz_threshold_rssi.c index 04a06bc1736c..07d7bccf93df 100644 --- a/applications/main/subghz/helpers/subghz_threshold_rssi.c +++ b/applications/main/subghz/helpers/subghz_threshold_rssi.c @@ -32,9 +32,8 @@ float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance) { return instance->threshold_rssi; } -SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance) { +SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance, float rssi) { furi_assert(instance); - float rssi = furi_hal_subghz_get_rssi(); SubGhzThresholdRssiData ret = {.rssi = rssi, .is_above = false}; if(float_is_equal(instance->threshold_rssi, SUBGHZ_RAW_THRESHOLD_MIN)) { diff --git a/applications/main/subghz/helpers/subghz_threshold_rssi.h b/applications/main/subghz/helpers/subghz_threshold_rssi.h index e28092acbc00..1d588e271b3a 100644 --- a/applications/main/subghz/helpers/subghz_threshold_rssi.h +++ b/applications/main/subghz/helpers/subghz_threshold_rssi.h @@ -38,6 +38,7 @@ float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance); /** Check threshold * * @param instance Pointer to a SubGhzThresholdRssi + * @param rssi Current RSSI * @return SubGhzThresholdRssiData */ -SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance); +SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance, float rssi); diff --git a/applications/main/subghz/helpers/subghz_txrx.c b/applications/main/subghz/helpers/subghz_txrx.c index 1517cb99892c..cbd47f5e5ee6 100644 --- a/applications/main/subghz/helpers/subghz_txrx.c +++ b/applications/main/subghz/helpers/subghz_txrx.c @@ -1,9 +1,32 @@ #include "subghz_txrx_i.h" #include +#include +#include #define TAG "SubGhz" +static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) { + UNUSED(instance); + uint8_t attempts = 5; + while(--attempts > 0) { + if(furi_hal_power_enable_otg()) break; + } + if(attempts == 0) { + if(furi_hal_power_get_usb_voltage() < 4.5f) { + FURI_LOG_E( + TAG, + "Error power otg enable. BQ2589 check otg fault = %d", + furi_hal_power_check_otg_fault() ? 1 : 0); + } + } +} + +static void subghz_txrx_radio_device_power_off(SubGhzTxRx* instance) { + UNUSED(instance); + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); +} + SubGhzTxRx* subghz_txrx_alloc() { SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx)); instance->setting = subghz_setting_alloc(); @@ -23,16 +46,15 @@ SubGhzTxRx* subghz_txrx_alloc() { instance->fff_data = flipper_format_string_alloc(); instance->environment = subghz_environment_alloc(); - instance->is_database_loaded = subghz_environment_load_keystore( - instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); - subghz_environment_load_keystore( - instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); + instance->is_database_loaded = + subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_NAME); + subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); subghz_environment_set_came_atomo_rainbow_table_file_name( - instance->environment, EXT_PATH("subghz/assets/came_atomo")); + instance->environment, SUBGHZ_CAME_ATOMO_DIR_NAME); subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - instance->environment, EXT_PATH("subghz/assets/alutech_at_4n")); + instance->environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( - instance->environment, EXT_PATH("subghz/assets/nice_flor_s")); + instance->environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); subghz_environment_set_protocol_registry( instance->environment, (void*)&subghz_protocol_registry); instance->receiver = subghz_receiver_alloc_init(instance->environment); @@ -43,18 +65,32 @@ SubGhzTxRx* subghz_txrx_alloc() { instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); subghz_worker_set_context(instance->worker, instance->receiver); + //set default device External + subghz_devices_init(); + instance->radio_device_type = SubGhzRadioDeviceTypeInternal; + instance->radio_device_type = + subghz_txrx_radio_device_set(instance, SubGhzRadioDeviceTypeExternalCC1101); + return instance; } void subghz_txrx_free(SubGhzTxRx* instance) { furi_assert(instance); + if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) { + subghz_txrx_radio_device_power_off(instance); + subghz_devices_end(instance->radio_device); + } + + subghz_devices_deinit(); + subghz_worker_free(instance->worker); subghz_receiver_free(instance->receiver); subghz_environment_free(instance->environment); flipper_format_free(instance->fff_data); furi_string_free(instance->preset->name); subghz_setting_free(instance->setting); + free(instance->preset); free(instance); } @@ -122,29 +158,26 @@ void subghz_txrx_get_frequency_and_modulation( static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) { furi_assert(instance); - furi_hal_subghz_reset(); - furi_hal_subghz_idle(); - furi_hal_subghz_load_custom_preset(preset_data); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + subghz_devices_reset(instance->radio_device); + subghz_devices_idle(instance->radio_device); + subghz_devices_load_preset(instance->radio_device, FuriHalSubGhzPresetCustom, preset_data); instance->txrx_state = SubGhzTxRxStateIDLE; } static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) { furi_assert(instance); - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - furi_crash("SubGhz: Incorrect RX frequency."); - } + furi_assert( instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep); - furi_hal_subghz_idle(); - uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); - furi_hal_subghz_flush_rx(); + subghz_devices_idle(instance->radio_device); + + uint32_t value = subghz_devices_set_frequency(instance->radio_device, frequency); + subghz_devices_flush_rx(instance->radio_device); subghz_txrx_speaker_on(instance); - furi_hal_subghz_rx(); - furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, instance->worker); + subghz_devices_start_async_rx( + instance->radio_device, subghz_worker_rx_callback, instance->worker); subghz_worker_start(instance->worker); instance->txrx_state = SubGhzTxRxStateRx; return value; @@ -153,7 +186,7 @@ static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) { static void subghz_txrx_idle(SubGhzTxRx* instance) { furi_assert(instance); furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); - furi_hal_subghz_idle(); + subghz_devices_idle(instance->radio_device); subghz_txrx_speaker_off(instance); instance->txrx_state = SubGhzTxRxStateIDLE; } @@ -164,30 +197,26 @@ static void subghz_txrx_rx_end(SubGhzTxRx* instance) { if(subghz_worker_is_running(instance->worker)) { subghz_worker_stop(instance->worker); - furi_hal_subghz_stop_async_rx(); + subghz_devices_stop_async_rx(instance->radio_device); } - furi_hal_subghz_idle(); + subghz_devices_idle(instance->radio_device); subghz_txrx_speaker_off(instance); instance->txrx_state = SubGhzTxRxStateIDLE; } void subghz_txrx_sleep(SubGhzTxRx* instance) { furi_assert(instance); - furi_hal_subghz_sleep(); + subghz_devices_sleep(instance->radio_device); instance->txrx_state = SubGhzTxRxStateSleep; } static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) { furi_assert(instance); - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - furi_crash("SubGhz: Incorrect TX frequency."); - } furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); - furi_hal_subghz_idle(); - furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_write(&gpio_cc1101_g0, false); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - bool ret = furi_hal_subghz_tx(); + subghz_devices_idle(instance->radio_device); + subghz_devices_set_frequency(instance->radio_device, frequency); + + bool ret = subghz_devices_set_tx(instance->radio_device); if(ret) { subghz_txrx_speaker_on(instance); instance->txrx_state = SubGhzTxRxStateTx; @@ -249,8 +278,8 @@ SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* if(ret == SubGhzTxRxStartTxStateOk) { //Start TX - furi_hal_subghz_start_async_tx( - subghz_transmitter_yield, instance->transmitter); + subghz_devices_start_async_tx( + instance->radio_device, subghz_transmitter_yield, instance->transmitter); } } else { ret = SubGhzTxRxStartTxStateErrorParserOthers; @@ -293,7 +322,7 @@ static void subghz_txrx_tx_stop(SubGhzTxRx* instance) { furi_assert(instance); furi_assert(instance->txrx_state == SubGhzTxRxStateTx); //Stop TX - furi_hal_subghz_stop_async_tx(); + subghz_devices_stop_async_tx(instance->radio_device); subghz_transmitter_stop(instance->transmitter); subghz_transmitter_free(instance->transmitter); @@ -306,7 +335,6 @@ static void subghz_txrx_tx_stop(SubGhzTxRx* instance) { subghz_txrx_idle(instance); subghz_txrx_speaker_off(instance); //Todo: Show message - // notification_message(notifications, &sequence_reset_red); } FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) { @@ -356,7 +384,7 @@ void subghz_txrx_hopper_update(SubGhzTxRx* instance) { float rssi = -127.0f; if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) { // See RSSI Calculation timings in CC1101 17.3 RSSI - rssi = furi_hal_subghz_get_rssi(); + rssi = subghz_devices_get_rssi(instance->radio_device); // Stay if RSSI is high enough if(rssi > -90.0f) { @@ -414,7 +442,7 @@ void subghz_txrx_speaker_on(SubGhzTxRx* instance) { furi_assert(instance); if(instance->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_acquire(30)) { - furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker); } else { instance->speaker_state = SubGhzSpeakerStateDisable; } @@ -425,7 +453,7 @@ void subghz_txrx_speaker_off(SubGhzTxRx* instance) { furi_assert(instance); if(instance->speaker_state != SubGhzSpeakerStateDisable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(NULL); + subghz_devices_set_async_mirror_pin(instance->radio_device, NULL); furi_hal_speaker_release(); if(instance->speaker_state == SubGhzSpeakerStateShutdown) instance->speaker_state = SubGhzSpeakerStateDisable; @@ -437,7 +465,7 @@ void subghz_txrx_speaker_mute(SubGhzTxRx* instance) { furi_assert(instance); if(instance->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(NULL); + subghz_devices_set_async_mirror_pin(instance->radio_device, NULL); } } } @@ -446,7 +474,7 @@ void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) { furi_assert(instance); if(instance->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker); } } } @@ -519,3 +547,66 @@ void subghz_txrx_set_raw_file_encoder_worker_callback_end( callback, context); } + +bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name) { + furi_assert(instance); + + bool is_connect = false; + bool is_otg_enabled = furi_hal_power_is_otg_enabled(); + + if(!is_otg_enabled) { + subghz_txrx_radio_device_power_on(instance); + } + + const SubGhzDevice* device = subghz_devices_get_by_name(name); + if(device) { + is_connect = subghz_devices_is_connect(device); + } + + if(!is_otg_enabled) { + subghz_txrx_radio_device_power_off(instance); + } + return is_connect; +} + +SubGhzRadioDeviceType + subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type) { + furi_assert(instance); + + if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 && + subghz_txrx_radio_device_is_external_connected(instance, SUBGHZ_DEVICE_CC1101_EXT_NAME)) { + subghz_txrx_radio_device_power_on(instance); + instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); + subghz_devices_begin(instance->radio_device); + instance->radio_device_type = SubGhzRadioDeviceTypeExternalCC1101; + } else { + subghz_txrx_radio_device_power_off(instance); + if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) { + subghz_devices_end(instance->radio_device); + } + instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + instance->radio_device_type = SubGhzRadioDeviceTypeInternal; + } + + return instance->radio_device_type; +} + +SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->radio_device_type; +} + +float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance) { + furi_assert(instance); + return subghz_devices_get_rssi(instance->radio_device); +} + +const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance) { + furi_assert(instance); + return subghz_devices_get_name(instance->radio_device); +} + +bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency) { + furi_assert(instance); + return subghz_devices_is_frequency_valid(instance->radio_device, frequency); +} \ No newline at end of file diff --git a/applications/main/subghz/helpers/subghz_txrx.h b/applications/main/subghz/helpers/subghz_txrx.h index 0f2daf05d49f..e49789206f0f 100644 --- a/applications/main/subghz/helpers/subghz_txrx.h +++ b/applications/main/subghz/helpers/subghz_txrx.h @@ -7,6 +7,7 @@ #include #include #include +#include typedef struct SubGhzTxRx SubGhzTxRx; @@ -288,3 +289,48 @@ void subghz_txrx_set_raw_file_encoder_worker_callback_end( SubGhzTxRx* instance, SubGhzProtocolEncoderRAWCallbackEnd callback, void* context); + +/* Checking if an external radio device is connected +* +* @param instance Pointer to a SubGhzTxRx +* @param name Name of external radio device +* @return bool True if is connected to the external radio device +*/ +bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name); + +/* Set the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @param radio_device_type Radio device type +* @return SubGhzRadioDeviceType Type of installed radio device +*/ +SubGhzRadioDeviceType + subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type); + +/* Get the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return SubGhzRadioDeviceType Type of installed radio device +*/ +SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance); + +/* Get RSSI the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return float RSSI +*/ +float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance); + +/* Get name the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return const char* Name of installed radio device +*/ +const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance); + +/* Get get intelligence whether frequency the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return bool True if the frequency is valid +*/ +bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency); \ No newline at end of file diff --git a/applications/main/subghz/helpers/subghz_txrx_i.h b/applications/main/subghz/helpers/subghz_txrx_i.h index bd0ad8b7be34..b7d74fd492f5 100644 --- a/applications/main/subghz/helpers/subghz_txrx_i.h +++ b/applications/main/subghz/helpers/subghz_txrx_i.h @@ -21,6 +21,8 @@ struct SubGhzTxRx { SubGhzTxRxState txrx_state; SubGhzSpeakerState speaker_state; + const SubGhzDevice* radio_device; + SubGhzRadioDeviceType radio_device_type; SubGhzTxRxNeedSaveCallback need_save_callback; void* need_save_context; diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 46bf940f4690..e71c22dd5648 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -35,6 +35,13 @@ typedef enum { SubGhzSpeakerStateEnable, } SubGhzSpeakerState; +/** SubGhzRadioDeviceType */ +typedef enum { + SubGhzRadioDeviceTypeAuto, + SubGhzRadioDeviceTypeInternal, + SubGhzRadioDeviceTypeExternalCC1101, +} SubGhzRadioDeviceType; + /** SubGhzRxKeyState state */ typedef enum { SubGhzRxKeyStateIDLE, @@ -73,9 +80,6 @@ typedef enum { SubGhzViewIdFrequencyAnalyzer, SubGhzViewIdReadRAW, - SubGhzViewIdStatic, - SubGhzViewIdTestCarrier, - SubGhzViewIdTestPacket, } SubGhzViewId; /** SubGhz load type file */ diff --git a/applications/main/subghz/icon.png b/applications/main/subghz/icon.png new file mode 100644 index 000000000000..5a25fdf4ef1c Binary files /dev/null and b/applications/main/subghz/icon.png differ diff --git a/applications/main/subghz/scenes/subghz_scene_config.h b/applications/main/subghz/scenes/subghz_scene_config.h index 86a307317923..47655958b4b7 100644 --- a/applications/main/subghz/scenes/subghz_scene_config.h +++ b/applications/main/subghz/scenes/subghz_scene_config.h @@ -12,10 +12,6 @@ ADD_SCENE(subghz, show_only_rx, ShowOnlyRx) ADD_SCENE(subghz, saved_menu, SavedMenu) ADD_SCENE(subghz, delete, Delete) ADD_SCENE(subghz, delete_success, DeleteSuccess) -ADD_SCENE(subghz, test, Test) -ADD_SCENE(subghz, test_static, TestStatic) -ADD_SCENE(subghz, test_carrier, TestCarrier) -ADD_SCENE(subghz, test_packet, TestPacket) ADD_SCENE(subghz, set_type, SetType) ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer) ADD_SCENE(subghz, read_raw, ReadRAW) @@ -24,3 +20,4 @@ ADD_SCENE(subghz, delete_raw, DeleteRAW) ADD_SCENE(subghz, need_saving, NeedSaving) ADD_SCENE(subghz, rpc, Rpc) ADD_SCENE(subghz, region_info, RegionInfo) +ADD_SCENE(subghz, radio_settings, RadioSettings) diff --git a/applications/main/subghz/scenes/subghz_scene_radio_setting.c b/applications/main/subghz/scenes/subghz_scene_radio_setting.c new file mode 100644 index 000000000000..0a47d5bfdbd4 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_radio_setting.c @@ -0,0 +1,70 @@ +#include "../subghz_i.h" +#include +#include + +enum SubGhzRadioSettingIndex { + SubGhzRadioSettingIndexDevice, +}; + +#define RADIO_DEVICE_COUNT 2 +const char* const radio_device_text[RADIO_DEVICE_COUNT] = { + "Internal", + "External", +}; + +const uint32_t radio_device_value[RADIO_DEVICE_COUNT] = { + SubGhzRadioDeviceTypeInternal, + SubGhzRadioDeviceTypeExternalCC1101, +}; + +static void subghz_scene_radio_settings_set_device(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + if(!subghz_txrx_radio_device_is_external_connected( + subghz->txrx, SUBGHZ_DEVICE_CC1101_EXT_NAME) && + radio_device_value[index] == SubGhzRadioDeviceTypeExternalCC1101) { + //ToDo correct if there is more than 1 module + index = 0; + } + variable_item_set_current_value_text(item, radio_device_text[index]); + subghz_txrx_radio_device_set(subghz->txrx, radio_device_value[index]); +} + +void subghz_scene_radio_settings_on_enter(void* context) { + SubGhz* subghz = context; + VariableItem* item; + uint8_t value_index; + + uint8_t value_count_device = RADIO_DEVICE_COUNT; + if(subghz_txrx_radio_device_get(subghz->txrx) == SubGhzRadioDeviceTypeInternal && + !subghz_txrx_radio_device_is_external_connected(subghz->txrx, SUBGHZ_DEVICE_CC1101_EXT_NAME)) + value_count_device = 1; + item = variable_item_list_add( + subghz->variable_item_list, + "Module", + value_count_device, + subghz_scene_radio_settings_set_device, + subghz); + value_index = value_index_uint32( + subghz_txrx_radio_device_get(subghz->txrx), radio_device_value, value_count_device); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, radio_device_text[value_index]); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); +} + +bool subghz_scene_radio_settings_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + bool consumed = false; + UNUSED(subghz); + UNUSED(event); + + return consumed; +} + +void subghz_scene_radio_settings_on_exit(void* context) { + SubGhz* subghz = context; + variable_item_list_set_selected_item(subghz->variable_item_list, 0); + variable_item_list_reset(subghz->variable_item_list); +} diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 6e576a861838..58e4b042954c 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -48,6 +48,9 @@ static void subghz_scene_read_raw_update_statusbar(void* context) { furi_string_free(frequency_str); furi_string_free(modulation_str); + + subghz_read_raw_set_radio_device_type( + subghz->subghz_read_raw, subghz_txrx_radio_device_get(subghz->txrx)); } void subghz_scene_read_raw_callback(SubGhzCustomEvent event, void* context) { @@ -204,7 +207,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { } else { if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) || !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) { - DOLPHIN_DEED(DolphinDeedSubGhzSend); + dolphin_deed(DolphinDeedSubGhzSend); } // set callback end tx subghz_txrx_set_raw_file_encoder_worker_callback_end( @@ -238,7 +241,9 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { furi_string_printf( temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION); subghz_protocol_raw_gen_fff_data( - subghz_txrx_get_fff_data(subghz->txrx), furi_string_get_cstr(temp_str)); + subghz_txrx_get_fff_data(subghz->txrx), + furi_string_get_cstr(temp_str), + subghz_txrx_radio_device_get_name(subghz->txrx)); furi_string_free(temp_str); if(spl_count > 0) { @@ -259,7 +264,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { } else { SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) { - DOLPHIN_DEED(DolphinDeedSubGhzRawRec); + dolphin_deed(DolphinDeedSubGhzRawRec); subghz_txrx_rx_start(subghz->txrx); subghz->state_notifications = SubGhzNotificationStateRx; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); @@ -298,8 +303,8 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz_read_raw_update_sample_write( subghz->subghz_read_raw, subghz_protocol_raw_get_sample_write(decoder_raw)); - SubGhzThresholdRssiData ret_rssi = - subghz_threshold_get_rssi_data(subghz->threshold_rssi); + SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( + subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); subghz_read_raw_add_data_rssi( subghz->subghz_read_raw, ret_rssi.rssi, ret_rssi.is_above); subghz_protocol_raw_save_to_file_pause(decoder_raw, !ret_rssi.is_above); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index dcc22b91cc79..6ab443579bbb 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -56,6 +56,9 @@ static void subghz_scene_receiver_update_statusbar(void* context) { subghz->state_notifications = SubGhzNotificationStateIDLE; } furi_string_free(history_stat_str); + + subghz_view_receiver_set_radio_device_type( + subghz->subghz_receiver, subghz_txrx_radio_device_get(subghz->txrx)); } void subghz_scene_receiver_callback(SubGhzCustomEvent event, void* context) { @@ -163,7 +166,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { case SubGhzCustomEventViewReceiverOK: subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); - DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); + dolphin_deed(DolphinDeedSubGhzReceiverInfo); consumed = true; break; case SubGhzCustomEventViewReceiverConfig: @@ -189,7 +192,8 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_scene_receiver_update_statusbar(subghz); } - SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data(subghz->threshold_rssi); + SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( + subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi); subghz_protocol_decoder_bin_raw_data_input_rssi( diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index aa6f132d761d..d4bf3e808eb8 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -40,15 +40,26 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { bool result = false; if((state == SubGhzRpcStateLoaded)) { - result = subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx)); - state = SubGhzRpcStateTx; - if(result) subghz_blink_start(subghz); - } - if(!result) { - rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeOnlyRX); - rpc_system_app_set_error_text( - subghz->rpc_ctx, - "Transmission on this frequency is restricted in your region"); + switch( + subghz_txrx_tx_start(subghz->txrx, subghz_txrx_get_fff_data(subghz->txrx))) { + case SubGhzTxRxStartTxStateErrorOnlyRx: + rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeOnlyRX); + rpc_system_app_set_error_text( + subghz->rpc_ctx, + "Transmission on this frequency is restricted in your region"); + break; + case SubGhzTxRxStartTxStateErrorParserOthers: + rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeParserOthers); + rpc_system_app_set_error_text( + subghz->rpc_ctx, "Error in protocol parameters description"); + break; + + default: //if(SubGhzTxRxStartTxStateOk) + result = true; + subghz_blink_start(subghz); + state = SubGhzRpcStateTx; + break; + } } rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { @@ -56,9 +67,9 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { if(state == SubGhzRpcStateTx) { subghz_txrx_stop(subghz->txrx); subghz_blink_stop(subghz); - state = SubGhzRpcStateIdle; result = true; } + state = SubGhzRpcStateIdle; rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result); } else if(event.event == SubGhzCustomEventSceneRpcLoad) { bool result = false; @@ -95,7 +106,7 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { void subghz_scene_rpc_on_exit(void* context) { SubGhz* subghz = context; SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc); - if(state != SubGhzRpcStateIdle) { + if(state == SubGhzRpcStateTx) { subghz_txrx_stop(subghz->txrx); subghz_blink_stop(subghz); } diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 2a292a1ef399..86eddfe8e97f 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -122,7 +122,8 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { SubGhzCustomEventManagerNoSet) { subghz_protocol_raw_gen_fff_data( subghz_txrx_get_fff_data(subghz->txrx), - furi_string_get_cstr(subghz->file_path)); + furi_string_get_cstr(subghz->file_path), + subghz_txrx_radio_device_get_name(subghz->txrx)); scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); } else { @@ -137,9 +138,9 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { // Ditto, for RAW signals } else if(scene_manager_has_previous_scene( subghz->scene_manager, SubGhzSceneSetType)) { - DOLPHIN_DEED(DolphinDeedSubGhzAddManually); + dolphin_deed(DolphinDeedSubGhzAddManually); } else { - DOLPHIN_DEED(DolphinDeedSubGhzSave); + dolphin_deed(DolphinDeedSubGhzSave); } return true; } else { diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index a41e4b06f4ac..951c756d8430 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -4,11 +4,11 @@ enum SubmenuIndex { SubmenuIndexRead = 10, SubmenuIndexSaved, - SubmenuIndexTest, SubmenuIndexAddManually, SubmenuIndexFrequencyAnalyzer, SubmenuIndexReadRAW, - SubmenuIndexShowRegionInfo + SubmenuIndexShowRegionInfo, + SubmenuIndexRadioSetting, }; void subghz_scene_start_submenu_callback(void* context, uint32_t index) { @@ -49,10 +49,12 @@ void subghz_scene_start_on_enter(void* context) { SubmenuIndexShowRegionInfo, subghz_scene_start_submenu_callback, subghz); - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - submenu_add_item( - subghz->submenu, "Test", SubmenuIndexTest, subghz_scene_start_submenu_callback, subghz); - } + submenu_add_item( + subghz->submenu, + "Radio Settings", + SubmenuIndexRadioSetting, + subghz_scene_start_submenu_callback, + subghz); submenu_set_selected_item( subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneStart)); @@ -92,18 +94,18 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); - DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); - return true; - } else if(event.event == SubmenuIndexTest) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTest); + dolphin_deed(DolphinDeedSubGhzFrequencyAnalyzer); return true; } else if(event.event == SubmenuIndexShowRegionInfo) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexShowRegionInfo); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRegionInfo); return true; + } else if(event.event == SubmenuIndexRadioSetting) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRadioSetting); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRadioSettings); + return true; } } return false; diff --git a/applications/main/subghz/scenes/subghz_scene_test.c b/applications/main/subghz/scenes/subghz_scene_test.c deleted file mode 100644 index 65f9bbdefade..000000000000 --- a/applications/main/subghz/scenes/subghz_scene_test.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "../subghz_i.h" - -enum SubmenuIndex { - SubmenuIndexCarrier, - SubmenuIndexPacket, - SubmenuIndexStatic, -}; - -void subghz_scene_test_submenu_callback(void* context, uint32_t index) { - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, index); -} - -void subghz_scene_test_on_enter(void* context) { - SubGhz* subghz = context; - - submenu_add_item( - subghz->submenu, - "Carrier", - SubmenuIndexCarrier, - subghz_scene_test_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, "Packet", SubmenuIndexPacket, subghz_scene_test_submenu_callback, subghz); - submenu_add_item( - subghz->submenu, "Static", SubmenuIndexStatic, subghz_scene_test_submenu_callback, subghz); - - submenu_set_selected_item( - subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneTest)); - - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu); -} - -bool subghz_scene_test_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexCarrier) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneTest, SubmenuIndexCarrier); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTestCarrier); - return true; - } else if(event.event == SubmenuIndexPacket) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneTest, SubmenuIndexPacket); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTestPacket); - return true; - } else if(event.event == SubmenuIndexStatic) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneTest, SubmenuIndexStatic); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTestStatic); - return true; - } - } - return false; -} - -void subghz_scene_test_on_exit(void* context) { - SubGhz* subghz = context; - submenu_reset(subghz->submenu); -} diff --git a/applications/main/subghz/scenes/subghz_scene_test_carrier.c b/applications/main/subghz/scenes/subghz_scene_test_carrier.c deleted file mode 100644 index 9677792ba319..000000000000 --- a/applications/main/subghz/scenes/subghz_scene_test_carrier.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "../subghz_i.h" -#include "../views/subghz_test_carrier.h" - -void subghz_scene_test_carrier_callback(SubGhzTestCarrierEvent event, void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, event); -} - -void subghz_scene_test_carrier_on_enter(void* context) { - SubGhz* subghz = context; - subghz_test_carrier_set_callback( - subghz->subghz_test_carrier, subghz_scene_test_carrier_callback, subghz); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTestCarrier); -} - -bool subghz_scene_test_carrier_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzTestCarrierEventOnlyRx) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); - return true; - } - } - return false; -} - -void subghz_scene_test_carrier_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/main/subghz/scenes/subghz_scene_test_packet.c b/applications/main/subghz/scenes/subghz_scene_test_packet.c deleted file mode 100644 index 99f0ab1791fd..000000000000 --- a/applications/main/subghz/scenes/subghz_scene_test_packet.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "../subghz_i.h" -#include "../views/subghz_test_packet.h" - -void subghz_scene_test_packet_callback(SubGhzTestPacketEvent event, void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, event); -} - -void subghz_scene_test_packet_on_enter(void* context) { - SubGhz* subghz = context; - subghz_test_packet_set_callback( - subghz->subghz_test_packet, subghz_scene_test_packet_callback, subghz); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); -} - -bool subghz_scene_test_packet_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzTestPacketEventOnlyRx) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); - return true; - } - } - return false; -} - -void subghz_scene_test_packet_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/main/subghz/scenes/subghz_scene_test_static.c b/applications/main/subghz/scenes/subghz_scene_test_static.c deleted file mode 100644 index 10e6d02a1da9..000000000000 --- a/applications/main/subghz/scenes/subghz_scene_test_static.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "../subghz_i.h" -#include "../views/subghz_test_static.h" - -void subghz_scene_test_static_callback(SubGhzTestStaticEvent event, void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, event); -} - -void subghz_scene_test_static_on_enter(void* context) { - SubGhz* subghz = context; - subghz_test_static_set_callback( - subghz->subghz_test_static, subghz_scene_test_static_callback, subghz); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdStatic); -} - -bool subghz_scene_test_static_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzTestStaticEventOnlyRx) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); - return true; - } - } - return false; -} - -void subghz_scene_test_static_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 1c193c1794d8..f83e44a0a1d5 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -35,6 +35,8 @@ bool subghz_scene_transmitter_update_data_show(void* context) { furi_string_free(modulation_str); furi_string_free(key_str); } + subghz_view_transmitter_set_radio_device_type( + subghz->subghz_transmitter, subghz_txrx_radio_device_get(subghz->txrx)); return ret; } @@ -61,7 +63,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { subghz->state_notifications = SubGhzNotificationStateTx; subghz_scene_transmitter_update_data_show(subghz); - DOLPHIN_DEED(DolphinDeedSubGhzSend); + dolphin_deed(DolphinDeedSubGhzSend); } return true; } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 09963584afcf..e8148798ef4a 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -129,27 +129,6 @@ SubGhz* subghz_alloc() { SubGhzViewIdReadRAW, subghz_read_raw_get_view(subghz->subghz_read_raw)); - // Carrier Test Module - subghz->subghz_test_carrier = subghz_test_carrier_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, - SubGhzViewIdTestCarrier, - subghz_test_carrier_get_view(subghz->subghz_test_carrier)); - - // Packet Test - subghz->subghz_test_packet = subghz_test_packet_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, - SubGhzViewIdTestPacket, - subghz_test_packet_get_view(subghz->subghz_test_packet)); - - // Static send - subghz->subghz_test_static = subghz_test_static_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, - SubGhzViewIdStatic, - subghz_test_static_get_view(subghz->subghz_test_static)); - //init threshold rssi subghz->threshold_rssi = subghz_threshold_rssi_alloc(); @@ -183,18 +162,6 @@ void subghz_free(SubGhz* subghz) { subghz_txrx_stop(subghz->txrx); subghz_txrx_sleep(subghz->txrx); - // Packet Test - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); - subghz_test_packet_free(subghz->subghz_test_packet); - - // Carrier Test - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestCarrier); - subghz_test_carrier_free(subghz->subghz_test_carrier); - - // Static - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdStatic); - subghz_test_static_free(subghz->subghz_test_static); - // Receiver view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdReceiver); subghz_view_receiver_free(subghz->subghz_receiver); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index ac19d65b4994..fe97c8a06713 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -10,6 +10,10 @@ #include #include #include +#include +#include +#include +#include #include "helpers/subghz_chat.h" @@ -24,6 +28,27 @@ #define SUBGHZ_REGION_FILENAME "/int/.region_data" +#define TAG "SubGhz CLI" + +static void subghz_cli_radio_device_power_on() { + uint8_t attempts = 5; + while(--attempts > 0) { + if(furi_hal_power_enable_otg()) break; + } + if(attempts == 0) { + if(furi_hal_power_get_usb_voltage() < 4.5f) { + FURI_LOG_E( + "TAG", + "Error power otg enable. BQ2589 check otg fault = %d", + furi_hal_power_check_otg_fault() ? 1 : 0); + } + } +} + +static void subghz_cli_radio_device_power_off() { + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); +} + void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; @@ -44,7 +69,7 @@ void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { } furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); frequency = furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); @@ -88,7 +113,7 @@ void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) { } furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); frequency = furi_hal_subghz_set_frequency_and_path(frequency); printf("Receiving at frequency %lu Hz\r\n", frequency); printf("Press CTRL+C to stop\r\n"); @@ -109,44 +134,76 @@ void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) { furi_hal_subghz_sleep(); } +static const SubGhzDevice* subghz_cli_command_get_device(uint32_t* device_ind) { + const SubGhzDevice* device = NULL; + switch(*device_ind) { + case 1: + subghz_cli_radio_device_power_on(); + device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); + break; + + default: + device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + break; + } + //check if the device is connected + if(!subghz_devices_is_connect(device)) { + subghz_cli_radio_device_power_off(); + device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + *device_ind = 0; + } + return device; +} + void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; uint32_t key = 0x0074BADE; uint32_t repeat = 10; uint32_t te = 403; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = - sscanf(furi_string_get_cstr(args), "%lx %lu %lu %lu", &key, &frequency, &te, &repeat); - if(ret != 4) { + int ret = sscanf( + furi_string_get_cstr(args), + "%lx %lu %lu %lu %lu", + &key, + &frequency, + &te, + &repeat, + &device_ind); + if(ret != 5) { printf( - "sscanf returned %d, key: %lx, frequency: %lu, te:%lu, repeat: %lu\r\n", + "sscanf returned %d, key: %lx, frequency: %lu, te: %lu, repeat: %lu, device: %lu\r\n ", ret, key, frequency, te, - repeat); + repeat, + device_ind); cli_print_usage( "subghz tx", - "<3 Byte Key: in hex> ", + "<3 Byte Key: in hex> ", furi_string_get_cstr(args)); return; } - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - printf( - "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", - frequency); - return; - } } - + subghz_devices_init(); + const SubGhzDevice* device = subghz_cli_command_get_device(&device_ind); + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + return; + } printf( - "Transmitting at %lu, key %lx, te %lu, repeat %lu. Press CTRL+C to stop\r\n", + "Transmitting at %lu, key %lx, te %lu, repeat %lu device %lu. Press CTRL+C to stop\r\n", frequency, key, te, - repeat); + repeat, + device_ind); FuriString* flipper_format_string = furi_string_alloc_printf( "Protocol: Princeton\n" @@ -170,21 +227,28 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton"); subghz_transmitter_deserialize(transmitter, flipper_format); - furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - frequency = furi_hal_subghz_set_frequency_and_path(frequency); + subghz_devices_begin(device); + subghz_devices_reset(device); + subghz_devices_load_preset(device, FuriHalSubGhzPresetOok650Async, NULL); + frequency = subghz_devices_set_frequency(device, frequency); furi_hal_power_suppress_charge_enter(); + if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) { + while(!(subghz_devices_is_async_complete_tx(device) || cli_cmd_interrupt_received(cli))) { + printf("."); + fflush(stdout); + furi_delay_ms(333); + } + subghz_devices_stop_async_tx(device); - furi_hal_subghz_start_async_tx(subghz_transmitter_yield, transmitter); - - while(!(furi_hal_subghz_is_async_tx_complete() || cli_cmd_interrupt_received(cli))) { - printf("."); - fflush(stdout); - furi_delay_ms(333); + } else { + printf("Transmission on this frequency is restricted in your region\r\n"); } - furi_hal_subghz_stop_async_tx(); - furi_hal_subghz_sleep(); + + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); furi_hal_power_suppress_charge_exit(); @@ -230,21 +294,29 @@ static void subghz_cli_command_rx_callback( void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); - cli_print_usage("subghz rx", "", furi_string_get_cstr(args)); - return; - } - if(!furi_hal_subghz_is_frequency_valid(frequency)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &frequency, &device_ind); + if(ret != 2) { printf( - "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", - frequency); + "sscanf returned %d, frequency: %lu device: %lu\r\n", ret, frequency, device_ind); + cli_print_usage( + "subghz rx", + " ", + furi_string_get_cstr(args)); return; } } + subghz_devices_init(); + const SubGhzDevice* device = subghz_cli_command_get_device(&device_ind); + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + return; + } // Allocate context and buffers SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); @@ -253,14 +325,14 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { furi_check(instance->stream); SubGhzEnvironment* environment = subghz_environment_alloc(); - subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); - subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); + subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME); + subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/came_atomo")); + environment, SUBGHZ_CAME_ATOMO_DIR_NAME); subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/alutech_at_4n")); + environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/nice_flor_s")); + environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); @@ -268,18 +340,21 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance); // Configure radio - furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - frequency = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + subghz_devices_begin(device); + subghz_devices_reset(device); + subghz_devices_load_preset(device, FuriHalSubGhzPresetOok650Async, NULL); + frequency = subghz_devices_set_frequency(device, frequency); furi_hal_power_suppress_charge_enter(); // Prepare and start RX - furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance); + subghz_devices_start_async_rx(device, subghz_cli_command_rx_capture_callback, instance); // Wait for packets to arrive - printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency); + printf( + "Listening at frequency: %lu device: %lu. Press CTRL+C to stop\r\n", + frequency, + device_ind); LevelDuration level_duration; while(!cli_cmd_interrupt_received(cli)) { int ret = furi_stream_buffer_receive( @@ -297,8 +372,11 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { } // Shutdown radio - furi_hal_subghz_stop_async_rx(); - furi_hal_subghz_sleep(); + subghz_devices_stop_async_rx(device); + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); furi_hal_power_suppress_charge_exit(); @@ -338,7 +416,7 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { // Configure radio furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok270Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_270khz_async_regs); frequency = furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); @@ -386,6 +464,7 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { furi_stream_buffer_free(instance->stream); free(instance); } + void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { UNUSED(context); FuriString* file_name; @@ -439,25 +518,23 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); SubGhzEnvironment* environment = subghz_environment_alloc(); - if(subghz_environment_load_keystore( - environment, EXT_PATH("subghz/assets/keeloq_mfcodes"))) { + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); } else { printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); } - if(subghz_environment_load_keystore( - environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"))) { + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); } else { printf( "SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n"); } subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/came_atomo")); + environment, SUBGHZ_CAME_ATOMO_DIR_NAME); subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/alutech_at_4n")); + environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/nice_flor_s")); + environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); @@ -465,7 +542,8 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance); SubGhzFileEncoderWorker* file_worker_encoder = subghz_file_encoder_worker_alloc(); - if(subghz_file_encoder_worker_start(file_worker_encoder, furi_string_get_cstr(file_name))) { + if(subghz_file_encoder_worker_start( + file_worker_encoder, furi_string_get_cstr(file_name), NULL)) { //the worker needs a file in order to open and read part of the file furi_delay_ms(100); } @@ -507,10 +585,11 @@ static void subghz_cli_command_print_usage() { printf("subghz \r\n"); printf("Cmd list:\r\n"); - printf("\tchat \t - Chat with other Flippers\r\n"); printf( - "\ttx <3 byte Key: in hex> \t - Transmitting key\r\n"); - printf("\trx \t - Receive\r\n"); + "\tchat \t - Chat with other Flippers\r\n"); + printf( + "\ttx <3 byte Key: in hex> \t - Transmitting key\r\n"); + printf("\trx \t - Receive\r\n"); printf("\trx_raw \t - Receive RAW\r\n"); printf("\tdecode_raw \t - Testing\r\n"); @@ -608,21 +687,29 @@ static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) { static void subghz_cli_command_chat(Cli* cli, FuriString* args) { uint32_t frequency = 433920000; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); - cli_print_usage("subghz chat", "", furi_string_get_cstr(args)); - return; - } - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - printf( - "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", - frequency); + int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &frequency, &device_ind); + if(ret != 2) { + printf("sscanf returned %d, Frequency: %lu\r\n", ret, frequency); + printf("sscanf returned %d, Device: %lu\r\n", ret, device_ind); + cli_print_usage( + "subghz chat", + " ", + furi_string_get_cstr(args)); return; } } + subghz_devices_init(); + const SubGhzDevice* device = subghz_cli_command_get_device(&device_ind); + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + return; + } if(!furi_hal_region_is_frequency_allowed(frequency)) { printf( "In your region, only reception on this frequency (%lu) is allowed,\r\n" @@ -632,7 +719,8 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { } SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli); - if(!subghz_chat_worker_start(subghz_chat, frequency)) { + + if(!subghz_chat_worker_start(subghz_chat, device, frequency)) { printf("Startup error SubGhzChatWorker\r\n"); if(subghz_chat_worker_is_running(subghz_chat)) { @@ -778,6 +866,10 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { furi_string_free(name); furi_string_free(output); furi_string_free(sysmsg); + + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + furi_hal_power_suppress_charge_exit(); furi_record_close(RECORD_NOTIFICATION); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index 8036ed5f7ac4..55036846cae1 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -115,7 +115,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { break; } - if(!furi_hal_subghz_is_frequency_valid(temp_data32)) { + if(!subghz_txrx_radio_device_is_frequecy_valid(subghz->txrx, temp_data32)) { FURI_LOG_E(TAG, "Frequency not supported"); break; } @@ -163,7 +163,8 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { //if RAW subghz->load_type_file = SubGhzLoadTypeFileRaw; - subghz_protocol_raw_gen_fff_data(fff_data, file_path); + subghz_protocol_raw_gen_fff_data( + fff_data, file_path, subghz_txrx_radio_device_get_name(subghz->txrx)); } else { subghz->load_type_file = SubGhzLoadTypeFileKey; stream_copy_full( diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index fc3404c07e73..9e58f3947dc7 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -9,10 +9,6 @@ #include "views/subghz_frequency_analyzer.h" #include "views/subghz_read_raw.h" -#include "views/subghz_test_static.h" -#include "views/subghz_test_carrier.h" -#include "views/subghz_test_packet.h" - #include #include #include @@ -64,9 +60,6 @@ struct SubGhz { SubGhzFrequencyAnalyzer* subghz_frequency_analyzer; SubGhzReadRAW* subghz_read_raw; - SubGhzTestStatic* subghz_test_static; - SubGhzTestCarrier* subghz_test_carrier; - SubGhzTestPacket* subghz_test_packet; SubGhzProtocolFlag filter; FuriString* error_str; diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index f84ddfed086f..e1014b811095 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -62,6 +62,7 @@ typedef struct { uint16_t history_item; SubGhzViewReceiverBarShow bar_show; uint8_t u_rssi; + SubGhzRadioDeviceType device_type; } SubGhzViewReceiverModel; void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { @@ -173,6 +174,17 @@ void subghz_view_receiver_add_data_statusbar( true); } +void subghz_view_receiver_set_radio_device_type( + SubGhzViewReceiver* subghz_receiver, + SubGhzRadioDeviceType device_type) { + furi_assert(subghz_receiver); + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->device_type = device_type; }, + true); +} + static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { canvas_set_color(canvas, ColorBlack); canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); @@ -190,9 +202,9 @@ static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool s static void subghz_view_rssi_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { for(uint8_t i = 1; i < model->u_rssi; i++) { if(i % 5) { - canvas_draw_dot(canvas, 46 + i, 50); - canvas_draw_dot(canvas, 47 + i, 51); canvas_draw_dot(canvas, 46 + i, 52); + canvas_draw_dot(canvas, 47 + i, 53); + canvas_draw_dot(canvas, 46 + i, 54); } } } @@ -232,22 +244,28 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { canvas_set_color(canvas, ColorBlack); if(model->history_item == 0) { - canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); + canvas_draw_icon(canvas, 0, 0, &I_Scanning_short_96x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 44, "Scanning..."); canvas_set_font(canvas, FontSecondary); } + if(model->device_type == SubGhzRadioDeviceTypeInternal) { + canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); + } else { + canvas_draw_icon(canvas, 108, 0, &I_External_antenna_20x12); + } + subghz_view_rssi_draw(canvas, model); switch(model->bar_show) { case SubGhzViewReceiverBarShowLock: - canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); - canvas_draw_str(canvas, 74, 62, "Locked"); + canvas_draw_icon(canvas, 64, 56, &I_Lock_7x8); + canvas_draw_str(canvas, 74, 64, "Locked"); break; case SubGhzViewReceiverBarShowToUnlockPress: - canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + canvas_draw_str(canvas, 44, 64, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 79, 64, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 97, 64, furi_string_get_cstr(model->history_stat_str)); canvas_set_font(canvas, FontSecondary); elements_bold_rounded_frame(canvas, 14, 8, 99, 48); elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); @@ -258,13 +276,13 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { canvas_draw_dot(canvas, 17, 61); break; case SubGhzViewReceiverBarShowUnlock: - canvas_draw_icon(canvas, 64, 55, &I_Unlock_7x8); - canvas_draw_str(canvas, 74, 62, "Unlocked"); + canvas_draw_icon(canvas, 64, 56, &I_Unlock_7x8); + canvas_draw_str(canvas, 74, 64, "Unlocked"); break; default: - canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + canvas_draw_str(canvas, 44, 64, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 79, 64, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 97, 64, furi_string_get_cstr(model->history_stat_str)); break; } } diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index 5119105e93ba..c91c069386cb 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -29,6 +29,10 @@ void subghz_view_receiver_add_data_statusbar( const char* preset_str, const char* history_stat_str); +void subghz_view_receiver_set_radio_device_type( + SubGhzViewReceiver* subghz_receiver, + SubGhzRadioDeviceType device_type); + void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 325664f4a9c5..d90401678a85 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -177,7 +177,8 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel } subghz_frequency_analyzer_log_frequency_draw(canvas, model); } else { - canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); + canvas_draw_str(canvas, 0, 8, "Frequency Analyzer"); + canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); canvas_draw_str(canvas, 0, 64, "RSSI"); subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20, 64); diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index 2ff598b60584..88ac129ca9ec 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -29,6 +29,7 @@ typedef struct { uint8_t ind_sin; SubGhzReadRAWStatus status; float raw_threshold_rssi; + SubGhzRadioDeviceType device_type; } SubGhzReadRAWModel; void subghz_read_raw_set_callback( @@ -56,6 +57,14 @@ void subghz_read_raw_add_data_statusbar( true); } +void subghz_read_raw_set_radio_device_type( + SubGhzReadRAW* instance, + SubGhzRadioDeviceType device_type) { + furi_assert(instance); + with_view_model( + instance->view, SubGhzReadRAWModel * model, { model->device_type = device_type; }, true); +} + void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace) { furi_assert(instance); uint8_t u_rssi = 0; @@ -279,11 +288,16 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { uint8_t graphics_mode = 1; canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 5, 7, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 40, 7, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 0, 9, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 35, 9, furi_string_get_cstr(model->preset_str)); canvas_draw_str_aligned( - canvas, 126, 0, AlignRight, AlignTop, furi_string_get_cstr(model->sample_write)); + canvas, 106, 2, AlignRight, AlignTop, furi_string_get_cstr(model->sample_write)); + if(model->device_type == SubGhzRadioDeviceTypeInternal) { + canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); + } else { + canvas_draw_icon(canvas, 108, 0, &I_External_antenna_20x12); + } canvas_draw_line(canvas, 0, 14, 115, 14); canvas_draw_line(canvas, 0, 48, 115, 48); canvas_draw_line(canvas, 115, 14, 115, 48); diff --git a/applications/main/subghz/views/subghz_read_raw.h b/applications/main/subghz/views/subghz_read_raw.h index 31aa9db6fdd1..83403e9750c9 100644 --- a/applications/main/subghz/views/subghz_read_raw.h +++ b/applications/main/subghz/views/subghz_read_raw.h @@ -1,6 +1,7 @@ #pragma once #include +#include "../helpers/subghz_types.h" #include "../helpers/subghz_custom_event.h" #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f @@ -36,6 +37,10 @@ void subghz_read_raw_add_data_statusbar( const char* frequency_str, const char* preset_str); +void subghz_read_raw_set_radio_device_type( + SubGhzReadRAW* instance, + SubGhzRadioDeviceType device_type); + void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample); void subghz_read_raw_stop_send(SubGhzReadRAW* instance); diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index 86dc17a38f7f..2a876f8c268b 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -15,6 +15,7 @@ typedef struct { FuriString* preset_str; FuriString* key_str; bool show_button; + SubGhzRadioDeviceType device_type; } SubGhzViewTransmitterModel; void subghz_view_transmitter_set_callback( @@ -46,6 +47,17 @@ void subghz_view_transmitter_add_data_to_show( true); } +void subghz_view_transmitter_set_radio_device_type( + SubGhzViewTransmitter* subghz_transmitter, + SubGhzRadioDeviceType device_type) { + furi_assert(subghz_transmitter); + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { model->device_type = device_type; }, + true); +} + static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str) { const uint8_t button_height = 12; const uint8_t vertical_offset = 3; @@ -56,7 +68,7 @@ static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_offset; const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; - const uint8_t x = (canvas_width(canvas) - button_width) / 2 + 40; + const uint8_t x = (canvas_width(canvas) - button_width) / 2 + 44; const uint8_t y = canvas_height(canvas); canvas_draw_box(canvas, x, y - button_height, button_width, button_height); @@ -88,7 +100,14 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str)); canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str)); canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str)); - if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send"); + if(model->show_button) { + if(model->device_type == SubGhzRadioDeviceTypeInternal) { + canvas_draw_icon(canvas, 108, 39, &I_Internal_antenna_20x12); + } else { + canvas_draw_icon(canvas, 108, 39, &I_External_antenna_20x12); + } + subghz_view_transmitter_button_right(canvas, "Send"); + } } bool subghz_view_transmitter_input(InputEvent* event, void* context) { diff --git a/applications/main/subghz/views/transmitter.h b/applications/main/subghz/views/transmitter.h index 06aae7c6bf74..19da3145c95c 100644 --- a/applications/main/subghz/views/transmitter.h +++ b/applications/main/subghz/views/transmitter.h @@ -1,6 +1,7 @@ #pragma once #include +#include "../helpers/subghz_types.h" #include "../helpers/subghz_custom_event.h" typedef struct SubGhzViewTransmitter SubGhzViewTransmitter; @@ -12,6 +13,10 @@ void subghz_view_transmitter_set_callback( SubGhzViewTransmitterCallback callback, void* context); +void subghz_view_transmitter_set_radio_device_type( + SubGhzViewTransmitter* subghz_transmitter, + SubGhzRadioDeviceType device_type); + SubGhzViewTransmitter* subghz_view_transmitter_alloc(); void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter); diff --git a/applications/main/u2f/application.fam b/applications/main/u2f/application.fam index 82010ffb4397..8167e6277b34 100644 --- a/applications/main/u2f/application.fam +++ b/applications/main/u2f/application.fam @@ -1,15 +1,12 @@ App( appid="u2f", name="U2F", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.MENUEXTERNAL, entry_point="u2f_app", - cdefines=["APP_U2F"], - requires=[ - "gui", - "dialogs", - ], stack_size=2 * 1024, icon="A_U2F_14", order=80, fap_libs=["assets"], + fap_category="USB", + fap_icon="icon.png", ) diff --git a/applications/main/u2f/icon.png b/applications/main/u2f/icon.png new file mode 100644 index 000000000000..fcd87a2ef56d Binary files /dev/null and b/applications/main/u2f/icon.png differ diff --git a/applications/main/u2f/scenes/u2f_scene_main.c b/applications/main/u2f/scenes/u2f_scene_main.c index 251bc4d991ae..992236e7a8b9 100644 --- a/applications/main/u2f/scenes/u2f_scene_main.c +++ b/applications/main/u2f/scenes/u2f_scene_main.c @@ -68,7 +68,7 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { notification_message(app->notifications, &sequence_blink_magenta_10); } else if(event.event == U2fCustomEventAuthSuccess) { notification_message_block(app->notifications, &sequence_set_green_255); - DOLPHIN_DEED(DolphinDeedU2fAuthorized); + dolphin_deed(DolphinDeedU2fAuthorized); furi_timer_start(app->timer, U2F_SUCCESS_TIMEOUT); app->event_cur = U2fCustomEventNone; u2f_view_set_state(app->u2f_view, U2fMsgSuccess); diff --git a/applications/services/applications.h b/applications/services/applications.h index 85f736742326..426d6d2d241c 100644 --- a/applications/services/applications.h +++ b/applications/services/applications.h @@ -4,9 +4,9 @@ #include typedef enum { - FlipperApplicationFlagDefault = 0, - FlipperApplicationFlagInsomniaSafe = (1 << 0), -} FlipperApplicationFlag; + FlipperInternalApplicationFlagDefault = 0, + FlipperInternalApplicationFlagInsomniaSafe = (1 << 0), +} FlipperInternalApplicationFlag; typedef struct { const FuriThreadCallback app; @@ -14,48 +14,53 @@ typedef struct { const char* appid; const size_t stack_size; const Icon* icon; - const FlipperApplicationFlag flags; -} FlipperApplication; + const FlipperInternalApplicationFlag flags; +} FlipperInternalApplication; -typedef void (*FlipperOnStartHook)(void); +typedef struct { + const char* name; + const Icon* icon; + const char* path; +} FlipperExternalApplication; + +typedef void (*FlipperInternalOnStartHook)(void); extern const char* FLIPPER_AUTORUN_APP_NAME; /* Services list * Spawned on startup */ -extern const FlipperApplication FLIPPER_SERVICES[]; +extern const FlipperInternalApplication FLIPPER_SERVICES[]; extern const size_t FLIPPER_SERVICES_COUNT; /* Apps list * Spawned by loader */ -extern const FlipperApplication FLIPPER_APPS[]; +extern const FlipperInternalApplication FLIPPER_APPS[]; extern const size_t FLIPPER_APPS_COUNT; /* On system start hooks * Called by loader, after OS initialization complete */ -extern const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[]; +extern const FlipperInternalOnStartHook FLIPPER_ON_SYSTEM_START[]; extern const size_t FLIPPER_ON_SYSTEM_START_COUNT; /* System apps * Can only be spawned by loader by name */ -extern const FlipperApplication FLIPPER_SYSTEM_APPS[]; +extern const FlipperInternalApplication FLIPPER_SYSTEM_APPS[]; extern const size_t FLIPPER_SYSTEM_APPS_COUNT; -/* Separate scene app holder - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_SCENE; -extern const FlipperApplication FLIPPER_SCENE_APPS[]; -extern const size_t FLIPPER_SCENE_APPS_COUNT; - -extern const FlipperApplication FLIPPER_ARCHIVE; +extern const FlipperInternalApplication FLIPPER_ARCHIVE; /* Settings list * Spawned by loader */ -extern const FlipperApplication FLIPPER_SETTINGS_APPS[]; +extern const FlipperInternalApplication FLIPPER_SETTINGS_APPS[]; extern const size_t FLIPPER_SETTINGS_APPS_COUNT; + +/* External Menu Apps list + * Spawned by loader + */ +extern const FlipperExternalApplication FLIPPER_EXTERNAL_APPS[]; +extern const size_t FLIPPER_EXTERNAL_APPS_COUNT; diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 2dcea34856cc..1b12ee303c6c 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -1,7 +1,7 @@ #include "bt_i.h" -#include "battery_service.h" #include "bt_keys_storage.h" +#include #include #include #include diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 3f94deebcfb4..467e7c53024c 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -89,6 +89,14 @@ void cli_command_help(Cli* cli, FuriString* args, void* context) { } } +void cli_command_uptime(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(args); + UNUSED(context); + uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency(); + printf("Uptime: %luh%lum%lus", uptime / 60 / 60, uptime / 60 % 60, uptime % 60); +} + void cli_command_date(Cli* cli, FuriString* args, void* context) { UNUSED(cli); UNUSED(context); @@ -165,24 +173,23 @@ void cli_command_log_tx_callback(const uint8_t* buffer, size_t size, void* conte furi_stream_buffer_send(context, buffer, size, 0); } -void cli_command_log_level_set_from_string(FuriString* level) { - if(furi_string_cmpi_str(level, "default") == 0) { - furi_log_set_level(FuriLogLevelDefault); - } else if(furi_string_cmpi_str(level, "none") == 0) { - furi_log_set_level(FuriLogLevelNone); - } else if(furi_string_cmpi_str(level, "error") == 0) { - furi_log_set_level(FuriLogLevelError); - } else if(furi_string_cmpi_str(level, "warn") == 0) { - furi_log_set_level(FuriLogLevelWarn); - } else if(furi_string_cmpi_str(level, "info") == 0) { - furi_log_set_level(FuriLogLevelInfo); - } else if(furi_string_cmpi_str(level, "debug") == 0) { - furi_log_set_level(FuriLogLevelDebug); - } else if(furi_string_cmpi_str(level, "trace") == 0) { - furi_log_set_level(FuriLogLevelTrace); +bool cli_command_log_level_set_from_string(FuriString* level) { + FuriLogLevel log_level; + if(furi_log_level_from_string(furi_string_get_cstr(level), &log_level)) { + furi_log_set_level(log_level); + return true; } else { - printf("Unknown log level\r\n"); + printf(" — start logging using the current level from the system settings\r\n"); + printf(" — only critical errors and other important messages\r\n"); + printf(" — non-critical errors and warnings including \r\n"); + printf(" — non-critical information including \r\n"); + printf(" — the default system log level (equivalent to )\r\n"); + printf( + " — debug information including (may impact system performance)\r\n"); + printf( + " — system traces including (may impact system performance)\r\n"); } + return false; } void cli_command_log(Cli* cli, FuriString* args, void* context) { @@ -193,12 +200,20 @@ void cli_command_log(Cli* cli, FuriString* args, void* context) { bool restore_log_level = false; if(furi_string_size(args) > 0) { - cli_command_log_level_set_from_string(args); + if(!cli_command_log_level_set_from_string(args)) { + furi_stream_buffer_free(ring); + return; + } restore_log_level = true; } + const char* current_level; + furi_log_level_to_string(furi_log_get_level(), ¤t_level); + printf("Current log level: %s\r\n", current_level); + furi_hal_console_set_tx_callback(cli_command_log_tx_callback, ring); + printf("Use to list available log levels\r\n"); printf("Press CTRL+C to stop...\r\n"); while(!cli_cmd_interrupt_received(cli)) { size_t ret = furi_stream_buffer_receive(ring, buffer, CLI_COMMAND_LOG_BUFFER_SIZE, 50); @@ -444,6 +459,7 @@ void cli_commands_init(Cli* cli) { cli_add_command(cli, "?", CliCommandFlagParallelSafe, cli_command_help, NULL); cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL); + cli_add_command(cli, "uptime", CliCommandFlagDefault, cli_command_uptime, NULL); cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL); cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL); cli_add_command(cli, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL); diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 28d09cc0d9d1..1233af8938fe 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -36,6 +36,7 @@ static void desktop_loader_callback(const void* message, void* context) { view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished); } } + static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) { UNUSED(context); furi_assert(canvas); @@ -135,7 +136,6 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) { void desktop_lock(Desktop* desktop) { furi_hal_rtc_set_flag(FuriHalRtcFlagLock); - furi_hal_rtc_set_pin_fails(0); if(desktop->settings.pin_code.length) { Cli* cli = furi_record_open(RECORD_CLI); @@ -148,6 +148,9 @@ void desktop_lock(Desktop* desktop) { desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); notification_message(desktop->notification, &sequence_display_backlight_off_delay_1000); + + DesktopStatus status = {.locked = true}; + furi_pubsub_publish(desktop->status_pubsub, &status); } void desktop_unlock(Desktop* desktop) { @@ -159,12 +162,16 @@ void desktop_unlock(Desktop* desktop) { scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); desktop_auto_lock_arm(desktop); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + furi_hal_rtc_set_pin_fails(0); if(desktop->settings.pin_code.length) { Cli* cli = furi_record_open(RECORD_CLI); cli_session_open(cli, &cli_vcp); furi_record_close(RECORD_CLI); } + + DesktopStatus status = {.locked = false}; + furi_pubsub_publish(desktop->status_pubsub, &status); } void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) { @@ -308,63 +315,13 @@ Desktop* desktop_alloc() { desktop->auto_lock_timer = furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop); + desktop->status_pubsub = furi_pubsub_alloc(); + furi_record_create(RECORD_DESKTOP, desktop); return desktop; } -void desktop_free(Desktop* desktop) { - furi_assert(desktop); - furi_check(furi_record_destroy(RECORD_DESKTOP)); - - furi_pubsub_unsubscribe( - loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription); - - if(desktop->input_events_subscription) { - furi_pubsub_unsubscribe(desktop->input_events_pubsub, desktop->input_events_subscription); - desktop->input_events_subscription = NULL; - } - - desktop->loader = NULL; - desktop->input_events_pubsub = NULL; - furi_record_close(RECORD_LOADER); - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_INPUT_EVENTS); - - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdMain); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLocked); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdDebug); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdHwMismatch); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinInput); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinTimeout); - - view_dispatcher_free(desktop->view_dispatcher); - scene_manager_free(desktop->scene_manager); - - animation_manager_free(desktop->animation_manager); - view_stack_free(desktop->main_view_stack); - desktop_main_free(desktop->main_view); - view_stack_free(desktop->locked_view_stack); - desktop_view_locked_free(desktop->locked_view); - desktop_lock_menu_free(desktop->lock_menu); - desktop_view_locked_free(desktop->locked_view); - desktop_debug_free(desktop->debug_view); - popup_free(desktop->hw_mismatch_popup); - desktop_view_pin_timeout_free(desktop->pin_timeout_view); - - furi_record_close(RECORD_GUI); - desktop->gui = NULL; - - furi_thread_free(desktop->scene_thread); - - furi_record_close("menu"); - - furi_timer_free(desktop->auto_lock_timer); - - free(desktop); -} - static bool desktop_check_file_flag(const char* flag_path) { Storage* storage = furi_record_open(RECORD_STORAGE); bool exists = storage_common_stat(storage, flag_path, NULL) == FSE_OK; @@ -383,6 +340,11 @@ void desktop_api_unlock(Desktop* instance) { view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked); } +FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance) { + furi_assert(instance); + return instance->status_pubsub; +} + int32_t desktop_srv(void* p) { UNUSED(p); @@ -427,7 +389,8 @@ int32_t desktop_srv(void* p) { } view_dispatcher_run(desktop->view_dispatcher); - desktop_free(desktop); + + furi_crash("That was unexpected"); return 0; } diff --git a/applications/services/desktop/desktop.h b/applications/services/desktop/desktop.h index 5b12647b8a5f..4eab24fcc50c 100644 --- a/applications/services/desktop/desktop.h +++ b/applications/services/desktop/desktop.h @@ -1,5 +1,7 @@ #pragma once +#include + typedef struct Desktop Desktop; #define RECORD_DESKTOP "desktop" @@ -7,3 +9,9 @@ typedef struct Desktop Desktop; bool desktop_api_is_locked(Desktop* instance); void desktop_api_unlock(Desktop* instance); + +typedef struct { + bool locked; +} DesktopStatus; + +FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance); diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index ede6bbcc31c1..0b3d568016b9 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -71,6 +71,8 @@ struct Desktop { FuriPubSubSubscription* input_events_subscription; FuriTimer* auto_lock_timer; + FuriPubSub* status_pubsub; + bool in_transition; }; diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h index 5d1b6126febe..9b88868a8dc0 100644 --- a/applications/services/desktop/desktop_settings.h +++ b/applications/services/desktop/desktop_settings.h @@ -8,7 +8,7 @@ #include #include -#define DESKTOP_SETTINGS_VER (7) +#define DESKTOP_SETTINGS_VER (8) #define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) #define DESKTOP_SETTINGS_MAGIC (0x17) @@ -36,15 +36,12 @@ #define MIN_PIN_SIZE 4 #define MAX_APP_LENGTH 128 -#define FAP_LOADER_APP_NAME "Applications" - typedef struct { InputKey data[MAX_PIN_SIZE]; uint8_t length; } PinCode; typedef struct { - bool is_external; char name_or_path[MAX_APP_LENGTH]; } FavoriteApp; diff --git a/applications/services/desktop/scenes/desktop_scene_debug.c b/applications/services/desktop/scenes/desktop_scene_debug.c index e79c56e111a4..866c736abad7 100644 --- a/applications/services/desktop/scenes/desktop_scene_debug.c +++ b/applications/services/desktop/scenes/desktop_scene_debug.c @@ -14,8 +14,6 @@ void desktop_scene_debug_callback(DesktopEvent event, void* context) { void desktop_scene_debug_on_enter(void* context) { Desktop* desktop = (Desktop*)context; - desktop_debug_get_dolphin_data(desktop->debug_view); - desktop_debug_set_callback(desktop->debug_view, desktop_scene_debug_callback, desktop); view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdDebug); } @@ -32,24 +30,6 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { dolphin_flush(dolphin); consumed = true; break; - - case DesktopDebugEventDeed: - dolphin_deed(dolphin, DolphinDeedTestRight); - desktop_debug_get_dolphin_data(desktop->debug_view); - consumed = true; - break; - - case DesktopDebugEventWrongDeed: - dolphin_deed(dolphin, DolphinDeedTestLeft); - desktop_debug_get_dolphin_data(desktop->debug_view); - consumed = true; - break; - - case DesktopDebugEventSaveState: - dolphin_flush(dolphin); - consumed = true; - break; - default: break; } @@ -60,6 +40,5 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { } void desktop_scene_debug_on_exit(void* context) { - Desktop* desktop = (Desktop*)context; - desktop_debug_reset_screen_idx(desktop->debug_view); + UNUSED(context); } diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index 053ac56f1e71..ae39ec223767 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -38,7 +38,8 @@ static void desktop_scene_main_interact_animation_callback(void* context) { } #ifdef APP_ARCHIVE -static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { +static void + desktop_switch_to_app(Desktop* desktop, const FlipperInternalApplication* flipper_app) { furi_assert(desktop); furi_assert(flipper_app); furi_assert(flipper_app->app); @@ -65,16 +66,17 @@ static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* fl #endif static void desktop_scene_main_open_app_or_profile(Desktop* desktop, const char* path) { - do { - LoaderStatus status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, path); - if(status == LoaderStatusOk) break; - FURI_LOG_E(TAG, "loader_start failed: %d", status); - - status = loader_start(desktop->loader, "Passport", NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - } while(false); + if(loader_start_with_gui_error(desktop->loader, path, NULL) != LoaderStatusOk) { + loader_start(desktop->loader, "Passport", NULL, NULL); + } +} + +static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) { + if(strlen(application->name_or_path) > 0) { + loader_start_with_gui_error(desktop->loader, application->name_or_path, NULL); + } else { + loader_start(desktop->loader, LOADER_APPLICATIONS_NAME, NULL, NULL); + } } void desktop_scene_main_callback(DesktopEvent event, void* context) { @@ -131,50 +133,19 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; case DesktopMainEventOpenPowerOff: { - LoaderStatus status = loader_start(desktop->loader, "Power", "off"); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } + loader_start(desktop->loader, "Power", "off", NULL); consumed = true; break; } case DesktopMainEventOpenFavoritePrimary: DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(desktop->settings.favorite_primary.is_external) { - LoaderStatus status = loader_start( - desktop->loader, - FAP_LOADER_APP_NAME, - desktop->settings.favorite_primary.name_or_path); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - } else { - LoaderStatus status = loader_start( - desktop->loader, desktop->settings.favorite_primary.name_or_path, NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - } + desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_primary); consumed = true; break; case DesktopMainEventOpenFavoriteSecondary: DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(desktop->settings.favorite_secondary.is_external) { - LoaderStatus status = loader_start( - desktop->loader, - FAP_LOADER_APP_NAME, - desktop->settings.favorite_secondary.name_or_path); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - } else { - LoaderStatus status = loader_start( - desktop->loader, desktop->settings.favorite_secondary.name_or_path, NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - } + desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_secondary); consumed = true; break; case DesktopAnimationEventCheckAnimation: @@ -187,18 +158,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; case DesktopAnimationEventInteractAnimation: if(!animation_manager_interact_process(desktop->animation_manager)) { - LoaderStatus status = loader_start(desktop->loader, "Passport", NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } + loader_start(desktop->loader, "Passport", NULL, NULL); } consumed = true; break; case DesktopMainEventOpenPassport: { - LoaderStatus status = loader_start(desktop->loader, "Passport", NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } + loader_start(desktop->loader, "Passport", NULL, NULL); break; } case DesktopMainEventOpenGame: { diff --git a/applications/services/desktop/scenes/desktop_scene_pin_input.c b/applications/services/desktop/scenes/desktop_scene_pin_input.c index 157acebf5401..e062c1b97d27 100644 --- a/applications/services/desktop/scenes/desktop_scene_pin_input.c +++ b/applications/services/desktop/scenes/desktop_scene_pin_input.c @@ -55,7 +55,6 @@ static void desktop_scene_pin_input_back_callback(void* context) { static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) { Desktop* desktop = (Desktop*)context; if(desktop_pin_compare(&desktop->settings.pin_code, pin_code)) { - furi_hal_rtc_set_pin_fails(0); view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked); } else { uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); diff --git a/applications/services/desktop/views/desktop_view_debug.c b/applications/services/desktop/views/desktop_view_debug.c index 7a16c08479a4..35c7dc038861 100644 --- a/applications/services/desktop/views/desktop_view_debug.c +++ b/applications/services/desktop/views/desktop_view_debug.c @@ -18,96 +18,71 @@ void desktop_debug_set_callback( } void desktop_debug_render(Canvas* canvas, void* model) { + UNUSED(model); canvas_clear(canvas); - DesktopDebugViewModel* m = model; const Version* ver; char buffer[64]; - static const char* headers[] = {"Device Info:", "Dolphin Info:"}; - canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 1 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignTop, headers[m->screen]); + + uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency(); + snprintf( + buffer, + sizeof(buffer), + "Uptime: %luh%lum%lus", + uptime / 60 / 60, + uptime / 60 % 60, + uptime % 60); + canvas_draw_str_aligned(canvas, 64, 1 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignTop, buffer); + canvas_set_font(canvas, FontSecondary); - if(m->screen != DesktopViewStatsMeta) { - // Hardware version - const char* my_name = furi_hal_version_get_name_ptr(); - snprintf( - buffer, - sizeof(buffer), - "%d.F%dB%dC%d %s:%s %s", - furi_hal_version_get_hw_version(), - furi_hal_version_get_hw_target(), - furi_hal_version_get_hw_body(), - furi_hal_version_get_hw_connect(), - furi_hal_version_get_hw_region_name(), - furi_hal_region_get_name(), - my_name ? my_name : "Unknown"); - canvas_draw_str(canvas, 0, 19 + STATUS_BAR_Y_SHIFT, buffer); - - ver = furi_hal_version_get_firmware_version(); - const BleGlueC2Info* c2_ver = NULL; + // Hardware version + const char* my_name = furi_hal_version_get_name_ptr(); + snprintf( + buffer, + sizeof(buffer), + "%d.F%dB%dC%d %s:%s %s", + furi_hal_version_get_hw_version(), + furi_hal_version_get_hw_target(), + furi_hal_version_get_hw_body(), + furi_hal_version_get_hw_connect(), + furi_hal_version_get_hw_region_name(), + furi_hal_region_get_name(), + my_name ? my_name : "Unknown"); + canvas_draw_str(canvas, 0, 19 + STATUS_BAR_Y_SHIFT, buffer); + + ver = furi_hal_version_get_firmware_version(); + const BleGlueC2Info* c2_ver = NULL; #ifdef SRV_BT - c2_ver = ble_glue_get_c2_info(); + c2_ver = ble_glue_get_c2_info(); #endif - if(!ver) { //-V1051 - canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, "No info"); - return; - } - - snprintf( - buffer, - sizeof(buffer), - "%s [%s]", - version_get_version(ver), - version_get_builddate(ver)); - canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer); - - uint16_t api_major, api_minor; - furi_hal_info_get_api_version(&api_major, &api_minor); - snprintf( - buffer, - sizeof(buffer), - "%s%s [%d.%d] %s", - version_get_dirty_flag(ver) ? "[!] " : "", - version_get_githash(ver), - api_major, - api_minor, - c2_ver ? c2_ver->StackTypeString : ""); - canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer); - - snprintf( - buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), version_get_gitbranch(ver)); - canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer); - - } else { - Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); - DolphinStats stats = dolphin_stats(dolphin); - furi_record_close(RECORD_DOLPHIN); - - uint32_t current_lvl = stats.level; - uint32_t remaining = dolphin_state_xp_to_levelup(m->icounter); - - canvas_set_font(canvas, FontSecondary); - snprintf(buffer, sizeof(buffer), "Icounter: %lu Butthurt %lu", m->icounter, m->butthurt); - canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer); - - snprintf( - buffer, - sizeof(buffer), - "Level: %lu To level up: %lu", - current_lvl, - (remaining == (uint32_t)(-1) ? remaining : 0)); - canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, buffer); - - // even if timestamp is uint64_t, it's safe to cast it to uint32_t, because furi_hal_rtc_datetime_to_timestamp only returns uint32_t - snprintf(buffer, sizeof(buffer), "%lu", (uint32_t)m->timestamp); - - canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer); - canvas_draw_str(canvas, 0, 49 + STATUS_BAR_Y_SHIFT, "[< >] icounter value [ok] save"); + if(!ver) { //-V1051 + canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, "No info"); + return; } + + snprintf( + buffer, sizeof(buffer), "%s [%s]", version_get_version(ver), version_get_builddate(ver)); + canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer); + + uint16_t api_major, api_minor; + furi_hal_info_get_api_version(&api_major, &api_minor); + snprintf( + buffer, + sizeof(buffer), + "%s%s [%d.%d] %s", + version_get_dirty_flag(ver) ? "[!] " : "", + version_get_githash(ver), + api_major, + api_minor, + c2_ver ? c2_ver->StackTypeString : ""); + canvas_draw_str(canvas, 0, 40 + STATUS_BAR_Y_SHIFT, buffer); + + snprintf( + buffer, sizeof(buffer), "[%d] %s", version_get_target(ver), version_get_gitbranch(ver)); + canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer); } View* desktop_debug_get_view(DesktopDebugView* debug_view) { @@ -115,61 +90,43 @@ View* desktop_debug_get_view(DesktopDebugView* debug_view) { return debug_view->view; } -bool desktop_debug_input(InputEvent* event, void* context) { +static bool desktop_debug_input(InputEvent* event, void* context) { furi_assert(event); furi_assert(context); DesktopDebugView* debug_view = context; - if(event->type != InputTypeShort && event->type != InputTypeRepeat) { - return false; - } - - DesktopViewStatsScreens current = 0; - with_view_model( - debug_view->view, - DesktopDebugViewModel * model, - { -#ifdef SRV_DOLPHIN_STATE_DEBUG - if((event->key == InputKeyDown) || (event->key == InputKeyUp)) { - model->screen = !model->screen; - } -#endif - current = model->screen; - }, - true); - - size_t count = (event->type == InputTypeRepeat) ? 10 : 1; - if(current == DesktopViewStatsMeta) { - if(event->key == InputKeyLeft) { - while(count-- > 0) { - debug_view->callback(DesktopDebugEventWrongDeed, debug_view->context); - } - } else if(event->key == InputKeyRight) { - while(count-- > 0) { - debug_view->callback(DesktopDebugEventDeed, debug_view->context); - } - } else if(event->key == InputKeyOk) { - debug_view->callback(DesktopDebugEventSaveState, debug_view->context); - } else { - return false; - } - } - - if(event->key == InputKeyBack) { + if(event->key == InputKeyBack && event->type == InputTypeShort) { debug_view->callback(DesktopDebugEventExit, debug_view->context); } return true; } +static void desktop_debug_enter(void* context) { + DesktopDebugView* debug_view = context; + furi_timer_start(debug_view->timer, furi_ms_to_ticks(1000)); +} + +static void desktop_debug_exit(void* context) { + DesktopDebugView* debug_view = context; + furi_timer_stop(debug_view->timer); +} +void desktop_debug_timer(void* context) { + DesktopDebugView* debug_view = context; + view_get_model(debug_view->view); + view_commit_model(debug_view->view, true); +} + DesktopDebugView* desktop_debug_alloc() { DesktopDebugView* debug_view = malloc(sizeof(DesktopDebugView)); debug_view->view = view_alloc(); - view_allocate_model(debug_view->view, ViewModelTypeLocking, sizeof(DesktopDebugViewModel)); + debug_view->timer = furi_timer_alloc(desktop_debug_timer, FuriTimerTypePeriodic, debug_view); view_set_context(debug_view->view, debug_view); view_set_draw_callback(debug_view->view, (ViewDrawCallback)desktop_debug_render); view_set_input_callback(debug_view->view, desktop_debug_input); + view_set_enter_callback(debug_view->view, desktop_debug_enter); + view_set_exit_callback(debug_view->view, desktop_debug_exit); return debug_view; } @@ -177,27 +134,7 @@ DesktopDebugView* desktop_debug_alloc() { void desktop_debug_free(DesktopDebugView* debug_view) { furi_assert(debug_view); + furi_timer_free(debug_view->timer); view_free(debug_view->view); free(debug_view); } - -void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view) { - Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); - DolphinStats stats = dolphin_stats(dolphin); - with_view_model( - debug_view->view, - DesktopDebugViewModel * model, - { - model->icounter = stats.icounter; - model->butthurt = stats.butthurt; - model->timestamp = stats.timestamp; - }, - true); - - furi_record_close(RECORD_DOLPHIN); -} - -void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view) { - with_view_model( - debug_view->view, DesktopDebugViewModel * model, { model->screen = 0; }, true); -} diff --git a/applications/services/desktop/views/desktop_view_debug.h b/applications/services/desktop/views/desktop_view_debug.h index f6af16b2e967..fea0c22a080f 100644 --- a/applications/services/desktop/views/desktop_view_debug.h +++ b/applications/services/desktop/views/desktop_view_debug.h @@ -8,26 +8,13 @@ typedef struct DesktopDebugView DesktopDebugView; typedef void (*DesktopDebugViewCallback)(DesktopEvent event, void* context); -// Debug info -typedef enum { - DesktopViewStatsFw, - DesktopViewStatsMeta, - DesktopViewStatsTotalCount, -} DesktopViewStatsScreens; - struct DesktopDebugView { View* view; + FuriTimer* timer; DesktopDebugViewCallback callback; void* context; }; -typedef struct { - uint32_t icounter; - uint32_t butthurt; - uint64_t timestamp; - DesktopViewStatsScreens screen; -} DesktopDebugViewModel; - void desktop_debug_set_callback( DesktopDebugView* debug_view, DesktopDebugViewCallback callback, @@ -36,7 +23,5 @@ void desktop_debug_set_callback( View* desktop_debug_get_view(DesktopDebugView* debug_view); DesktopDebugView* desktop_debug_alloc(); -void desktop_debug_free(DesktopDebugView* debug_view); -void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view); -void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view); +void desktop_debug_free(DesktopDebugView* debug_view); diff --git a/applications/services/dialogs/dialogs.c b/applications/services/dialogs/dialogs.c index 3908ca31b585..10c08a991b39 100644 --- a/applications/services/dialogs/dialogs.c +++ b/applications/services/dialogs/dialogs.c @@ -9,12 +9,13 @@ void dialog_file_browser_set_basic_options( const char* extension, const Icon* icon) { options->extension = extension; + options->base_path = NULL; options->skip_assets = true; + options->hide_dot_files = true; options->icon = icon; options->hide_ext = true; options->item_loader_callback = NULL; options->item_loader_context = NULL; - options->base_path = NULL; } static DialogsApp* dialogs_app_alloc() { diff --git a/applications/services/dialogs/dialogs.h b/applications/services/dialogs/dialogs.h index 4c1b675a6446..39b15c67c2ed 100644 --- a/applications/services/dialogs/dialogs.h +++ b/applications/services/dialogs/dialogs.h @@ -16,7 +16,8 @@ typedef struct DialogsApp DialogsApp; /****************** FILE BROWSER ******************/ /** - * File browser dialog extra options + * File browser dialog extra options. + * This can be default-initialized using {@link dialog_file_browser_set_basic_options}. * @param extension file extension to be offered for selection * @param base_path root folder path for navigation with back key * @param skip_assets true - do not show assets folders @@ -38,8 +39,10 @@ typedef struct { } DialogsFileBrowserOptions; /** - * Initialize file browser dialog options - * and set default values + * Initialize file browser dialog options and set default values. + * This is guaranteed to initialize all fields + * so it is safe to pass pointer to uninitialized {@code options} + * and assume that the data behind it becomes fully initialized after the call. * @param options pointer to options structure * @param extension file extension to filter * @param icon file icon pointer, NULL for default icon diff --git a/applications/services/dialogs/dialogs_i.h b/applications/services/dialogs/dialogs_i.h index 76495d31b0a3..29417b41b52f 100644 --- a/applications/services/dialogs/dialogs_i.h +++ b/applications/services/dialogs/dialogs_i.h @@ -1,7 +1,7 @@ #pragma once #include "dialogs.h" #include "dialogs_message.h" -#include "view_holder.h" +#include #include #ifdef __cplusplus diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index dd8b7105f094..579b400ad013 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -13,12 +13,13 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin); -void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { - furi_assert(dolphin); +void dolphin_deed(DolphinDeed deed) { + Dolphin* dolphin = (Dolphin*)furi_record_open(RECORD_DOLPHIN); DolphinEvent event; event.type = DolphinEventTypeDeed; event.deed = deed; dolphin_event_send_async(dolphin, &event); + furi_record_close(RECORD_DOLPHIN); } DolphinStats dolphin_stats(Dolphin* dolphin) { @@ -89,15 +90,6 @@ Dolphin* dolphin_alloc() { return dolphin; } -void dolphin_free(Dolphin* dolphin) { - furi_assert(dolphin); - - dolphin_state_free(dolphin->state); - furi_message_queue_free(dolphin->event_queue); - - free(dolphin); -} - void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event) { furi_assert(dolphin); furi_assert(event); @@ -204,7 +196,7 @@ int32_t dolphin_srv(void* p) { } } - dolphin_free(dolphin); + furi_crash("That was unexpected"); return 0; } diff --git a/applications/services/dolphin/dolphin.h b/applications/services/dolphin/dolphin.h index 8757e2a37735..1035247e718d 100644 --- a/applications/services/dolphin/dolphin.h +++ b/applications/services/dolphin/dolphin.h @@ -26,18 +26,11 @@ typedef enum { DolphinPubsubEventUpdate, } DolphinPubsubEvent; -#define DOLPHIN_DEED(deed) \ - do { \ - Dolphin* dolphin = (Dolphin*)furi_record_open("dolphin"); \ - dolphin_deed(dolphin, deed); \ - furi_record_close("dolphin"); \ - } while(0) - /** Deed complete notification. Call it on deed completion. * See dolphin_deed.h for available deeds. In futures it will become part of assets. * Thread safe, async */ -void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); +void dolphin_deed(DolphinDeed deed); /** Retrieve dolphin stats * Thread safe, blocking diff --git a/applications/services/dolphin/dolphin_i.h b/applications/services/dolphin/dolphin_i.h index 4bb0df08ee65..ceeff1e1a9a3 100644 --- a/applications/services/dolphin/dolphin_i.h +++ b/applications/services/dolphin/dolphin_i.h @@ -37,8 +37,6 @@ struct Dolphin { Dolphin* dolphin_alloc(); -void dolphin_free(Dolphin* dolphin); - void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event); void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event); diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index d12a00ba5984..91b03ec8aae6 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -118,6 +118,8 @@ typedef struct { const Icon* file_icon; bool hide_ext; size_t scroll_counter; + + uint32_t button_held_for_ticks; } FileBrowserModel; static const Icon* BrowserItemIcons[] = { @@ -301,6 +303,12 @@ static bool browser_is_list_load_required(FileBrowserModel* model) { return false; } +static void browser_list_rollover(FileBrowserModel* model) { + if(!model->list_loading && items_array_size(model->items) < model->item_cnt) { + items_array_reset(model->items); + } +} + static void browser_update_offset(FileBrowser* browser) { furi_assert(browser); @@ -383,7 +391,7 @@ static void browser_list_load_cb(void* context, uint32_t list_load_offset) { } } }, - true); + false); BrowserItem_t_clear(&back_item); } @@ -423,14 +431,15 @@ static void (browser->hide_ext) && (item.type == BrowserItemTypeFile)); } + // We shouldn't update screen on each item if custom callback is not set + // Otherwise it will cause screen flickering + bool instant_update = (browser->item_callback != NULL); with_view_model( browser->view, FileBrowserModel * model, - { - items_array_push_back(model->items, item); - // TODO: calculate if element is visible - }, - true); + { items_array_push_back(model->items, item); }, + instant_update); + furi_string_free(item.display_name); furi_string_free(item.path); if(item.custom_icon_data) { @@ -438,7 +447,18 @@ static void } } else { with_view_model( - browser->view, FileBrowserModel * model, { model->list_loading = false; }, true); + browser->view, + FileBrowserModel * model, + { + model->list_loading = false; + if(browser_is_list_load_required(model)) { + model->list_loading = true; + int32_t load_offset = CLAMP( + model->item_idx - ITEM_LIST_LEN_MAX / 2, (int32_t)model->item_cnt, 0); + file_browser_worker_load(browser->worker, load_offset, ITEM_LIST_LEN_MAX); + } + }, + true); } } @@ -589,9 +609,26 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { browser->view, FileBrowserModel * model, { + int32_t scroll_speed = 1; + if(model->button_held_for_ticks > 5) { + if(model->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = model->button_held_for_ticks > 9 ? 5 : 3; + } + } + if(event->key == InputKeyUp) { - model->item_idx = - ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; + if(model->item_idx < scroll_speed) { + model->button_held_for_ticks = 0; + model->item_idx = model->item_cnt - 1; + browser_list_rollover(model); + } else { + model->item_idx = + ((model->item_idx - scroll_speed) + model->item_cnt) % + model->item_cnt; + } + if(browser_is_list_load_required(model)) { model->list_loading = true; int32_t load_offset = CLAMP( @@ -602,8 +639,17 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { browser->worker, load_offset, ITEM_LIST_LEN_MAX); } model->scroll_counter = 0; + + model->button_held_for_ticks += 1; } else if(event->key == InputKeyDown) { - model->item_idx = (model->item_idx + 1) % model->item_cnt; + if(model->item_idx + scroll_speed >= (int32_t)model->item_cnt) { + model->button_held_for_ticks = 0; + model->item_idx = 0; + browser_list_rollover(model); + } else { + model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; + } + if(browser_is_list_load_required(model)) { model->list_loading = true; int32_t load_offset = CLAMP( @@ -614,11 +660,19 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { browser->worker, load_offset, ITEM_LIST_LEN_MAX); } model->scroll_counter = 0; + + model->button_held_for_ticks += 1; } }, true); browser_update_offset(browser); consumed = true; + } else if(event->type == InputTypeRelease) { + with_view_model( + browser->view, + FileBrowserModel * model, + { model->button_held_for_ticks = 0; }, + true); } } else if(event->key == InputKeyOk) { if(event->type == InputTypeShort) { diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 9d81c30b688a..3ba35edde079 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -63,7 +63,7 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { SubmenuModel* model = _model; const uint8_t item_height = 16; - const uint8_t item_width = 123; + uint8_t item_width = canvas_width(canvas) - 5; canvas_clear(canvas); diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c index 01ccdbf52b5e..0e4aae9446b5 100644 --- a/applications/services/gui/modules/text_box.c +++ b/applications/services/gui/modules/text_box.c @@ -6,6 +6,8 @@ struct TextBox { View* view; + + uint16_t button_held_for_ticks; }; typedef struct { @@ -19,36 +21,52 @@ typedef struct { bool formatted; } TextBoxModel; -static void text_box_process_down(TextBox* text_box) { +static void text_box_process_down(TextBox* text_box, uint8_t lines) { with_view_model( text_box->view, TextBoxModel * model, { - if(model->scroll_pos < model->scroll_num - 1) { - model->scroll_pos++; - // Search next line start - while(*model->text_pos++ != '\n') - ; + if(model->scroll_pos < model->scroll_num - lines) { + model->scroll_pos += lines; + for(uint8_t i = 0; i < lines; i++) { + // Search next line start + while(*model->text_pos++ != '\n') + ; + } + } else if(lines > 1) { + lines = model->scroll_num - model->scroll_pos - 1; + model->scroll_pos = model->scroll_num - 1; + for(uint8_t i = 0; i < lines; i++) { + // Search next line start + while(*model->text_pos++ != '\n') + ; + } } }, true); } -static void text_box_process_up(TextBox* text_box) { +static void text_box_process_up(TextBox* text_box, uint8_t lines) { with_view_model( text_box->view, TextBoxModel * model, { - if(model->scroll_pos > 0) { - model->scroll_pos--; - // Reach last symbol of previous line - model->text_pos--; - // Search previous line start - while((model->text_pos != model->text) && (*(--model->text_pos) != '\n')) - ; - if(*model->text_pos == '\n') { - model->text_pos++; + if(model->scroll_pos > lines - 1) { + model->scroll_pos -= lines; + for(uint8_t i = 0; i < lines; i++) { + // Reach last symbol of previous line + model->text_pos--; + // Search previous line start + while((model->text_pos != model->text) && (*(--model->text_pos) != '\n')) + ; + if(*model->text_pos == '\n') { + model->text_pos++; + } } + } else if(lines > 1) { + lines = model->scroll_pos; + model->scroll_pos = 0; + model->text_pos = (char*)model->text; } }, true); @@ -120,14 +138,28 @@ static bool text_box_view_input_callback(InputEvent* event, void* context) { TextBox* text_box = context; bool consumed = false; - if(event->type == InputTypeShort) { + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + int32_t scroll_speed = 1; + if(text_box->button_held_for_ticks > 5) { + if(text_box->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = text_box->button_held_for_ticks > 9 ? 5 : 3; + } + } + if(event->key == InputKeyDown) { - text_box_process_down(text_box); + text_box_process_down(text_box, scroll_speed); consumed = true; } else if(event->key == InputKeyUp) { - text_box_process_up(text_box); + text_box_process_up(text_box, scroll_speed); consumed = true; } + + text_box->button_held_for_ticks++; + } else if(event->type == InputTypeRelease) { + text_box->button_held_for_ticks = 0; + consumed = true; } return consumed; } diff --git a/applications/services/dialogs/view_holder.c b/applications/services/gui/view_holder.c similarity index 100% rename from applications/services/dialogs/view_holder.c rename to applications/services/gui/view_holder.c diff --git a/applications/services/dialogs/view_holder.h b/applications/services/gui/view_holder.h similarity index 100% rename from applications/services/dialogs/view_holder.h rename to applications/services/gui/view_holder.h diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index f385efdf95ae..41c0f95d420b 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -1,20 +1,41 @@ #include "loader.h" #include "loader_i.h" -#include "loader_menu.h" #include +#include #include +#include + +#include +#include +#include +#include #define TAG "Loader" #define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF -// api -LoaderStatus loader_start(Loader* loader, const char* name, const char* args) { +// helpers + +static const char* loader_find_external_application_by_name(const char* app_name) { + for(size_t i = 0; i < FLIPPER_EXTERNAL_APPS_COUNT; i++) { + if(strcmp(FLIPPER_EXTERNAL_APPS[i].name, app_name) == 0) { + return FLIPPER_EXTERNAL_APPS[i].path; + } + } + + return NULL; +} + +// API + +LoaderStatus + loader_start(Loader* loader, const char* name, const char* args, FuriString* error_message) { LoaderMessage message; LoaderMessageLoaderStatusResult result; message.type = LoaderMessageTypeStartByName; message.start.name = name; message.start.args = args; + message.start.error_message = error_message; message.api_lock = api_lock_alloc_locked(); message.status_value = &result; furi_message_queue_put(loader->queue, &message, FuriWaitForever); @@ -22,6 +43,47 @@ LoaderStatus loader_start(Loader* loader, const char* name, const char* args) { return result.value; } +LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args) { + FuriString* error_message = furi_string_alloc(); + LoaderStatus status = loader_start(loader, name, args, error_message); + + if(status == LoaderStatusErrorUnknownApp && + loader_find_external_application_by_name(name) != NULL) { + // Special case for external apps + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Update needed", 64, 3, AlignCenter, AlignTop); + dialog_message_set_buttons(message, NULL, NULL, NULL); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); + dialog_message_set_text( + message, "Update firmware\nto run this app", 3, 26, AlignLeft, AlignTop); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + } else if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) { + // TODO: we have many places where we can emit a double start, ex: desktop, menu + // so i prefer to not show LoaderStatusErrorAppStarted error message for now + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(message, NULL, NULL, NULL); + + furi_string_replace(error_message, "/ext/apps/", ""); + furi_string_replace(error_message, ", ", "\n"); + furi_string_replace(error_message, ": ", "\n"); + + dialog_message_set_text( + message, furi_string_get_cstr(error_message), 64, 35, AlignCenter, AlignCenter); + + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + } + + furi_string_free(error_message); + return status; +} + bool loader_lock(Loader* loader) { LoaderMessage message; LoaderMessageBoolResult result; @@ -73,27 +135,26 @@ static void loader_menu_closed_callback(void* context) { furi_message_queue_put(loader->queue, &message, FuriWaitForever); } -static void loader_menu_click_callback(const char* name, void* context) { +static void loader_applications_closed_callback(void* context) { Loader* loader = context; - loader_start(loader, name, NULL); + LoaderMessage message; + message.type = LoaderMessageTypeApplicationsClosed; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); } static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { furi_assert(context); Loader* loader = context; - LoaderEvent event; if(thread_state == FuriThreadStateRunning) { + LoaderEvent event; event.type = LoaderEventTypeApplicationStarted; furi_pubsub_publish(loader->pubsub, &event); } else if(thread_state == FuriThreadStateStopped) { LoaderMessage message; message.type = LoaderMessageTypeAppClosed; furi_message_queue_put(loader->queue, &message, FuriWaitForever); - - event.type = LoaderEventTypeApplicationStopped; - furi_pubsub_publish(loader->pubsub, &event); } } @@ -104,16 +165,17 @@ static Loader* loader_alloc() { loader->pubsub = furi_pubsub_alloc(); loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage)); loader->loader_menu = NULL; + loader->loader_applications = NULL; loader->app.args = NULL; - loader->app.name = NULL; loader->app.thread = NULL; loader->app.insomniac = false; + loader->app.fap = NULL; return loader; } -static FlipperApplication const* loader_find_application_by_name_in_list( +static FlipperInternalApplication const* loader_find_application_by_name_in_list( const char* name, - const FlipperApplication* list, + const FlipperInternalApplication* list, const uint32_t n_apps) { for(size_t i = 0; i < n_apps; i++) { if(strcmp(name, list[i].name) == 0) { @@ -123,8 +185,8 @@ static FlipperApplication const* loader_find_application_by_name_in_list( return NULL; } -static const FlipperApplication* loader_find_application_by_name(const char* name) { - const FlipperApplication* application = NULL; +static const FlipperInternalApplication* loader_find_application_by_name(const char* name) { + const FlipperInternalApplication* application = NULL; application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); if(!application) { application = loader_find_application_by_name_in_list( @@ -138,25 +200,7 @@ static const FlipperApplication* loader_find_application_by_name(const char* nam return application; } -static void - loader_start_internal_app(Loader* loader, const FlipperApplication* app, const char* args) { - FURI_LOG_I(TAG, "Starting %s", app->name); - - // store args - furi_assert(loader->app.args == NULL); - if(args && strlen(args) > 0) { - loader->app.args = strdup(args); - } - - // store name - furi_assert(loader->app.name == NULL); - loader->app.name = strdup(app->name); - - // setup app thread - loader->app.thread = - furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args); - furi_thread_set_appid(loader->app.thread, app->appid); - +static void loader_start_app_thread(Loader* loader, FlipperInternalApplicationFlag flags) { // setup heap trace FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); if(mode > FuriHalRtcHeapTrackModeNone) { @@ -166,14 +210,14 @@ static void } // setup insomnia - if(!(app->flags & FlipperApplicationFlagInsomniaSafe)) { + if(!(flags & FlipperInternalApplicationFlagInsomniaSafe)) { furi_hal_power_insomnia_enter(); loader->app.insomniac = true; } else { loader->app.insomniac = false; } - // setup app thread callbacks + // setup thread state callbacks furi_thread_set_state_context(loader->app.thread, loader); furi_thread_set_state_callback(loader->app.thread, loader_thread_state_callback); @@ -181,42 +225,212 @@ static void furi_thread_start(loader->app.thread); } +static void loader_start_internal_app( + Loader* loader, + const FlipperInternalApplication* app, + const char* args) { + FURI_LOG_I(TAG, "Starting %s", app->name); + + // store args + furi_assert(loader->app.args == NULL); + if(args && strlen(args) > 0) { + loader->app.args = strdup(args); + } + + loader->app.thread = + furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args); + furi_thread_set_appid(loader->app.thread, app->appid); + + loader_start_app_thread(loader, app->flags); +} + +static void loader_log_status_error( + LoaderStatus status, + FuriString* error_message, + const char* format, + va_list args) { + if(error_message) { + furi_string_vprintf(error_message, format, args); + FURI_LOG_E(TAG, "Status [%d]: %s", status, furi_string_get_cstr(error_message)); + } else { + FuriString* tmp = furi_string_alloc(); + FURI_LOG_E(TAG, "Status [%d]: %s", status, furi_string_get_cstr(tmp)); + furi_string_free(tmp); + } +} + +static LoaderStatus loader_make_status_error( + LoaderStatus status, + FuriString* error_message, + const char* format, + ...) { + va_list args; + va_start(args, format); + loader_log_status_error(status, error_message, format, args); + va_end(args); + return status; +} + +static LoaderStatus loader_make_success_status(FuriString* error_message) { + if(error_message) { + furi_string_set(error_message, "App started"); + } + + return LoaderStatusOk; +} + +static LoaderStatus loader_start_external_app( + Loader* loader, + Storage* storage, + const char* path, + const char* args, + FuriString* error_message) { + LoaderStatus status = loader_make_success_status(error_message); + + do { + loader->app.fap = flipper_application_alloc(storage, firmware_api_interface); + size_t start = furi_get_tick(); + + FURI_LOG_I(TAG, "Loading %s", path); + + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload(loader->app.fap, path); + if(preload_res != FlipperApplicationPreloadStatusSuccess) { + const char* err_msg = flipper_application_preload_status_to_string(preload_res); + status = loader_make_status_error( + LoaderStatusErrorInternal, error_message, "Preload failed, %s: %s", path, err_msg); + break; + } + + FlipperApplicationLoadStatus load_status = + flipper_application_map_to_memory(loader->app.fap); + if(load_status != FlipperApplicationLoadStatusSuccess) { + const char* err_msg = flipper_application_load_status_to_string(load_status); + status = loader_make_status_error( + LoaderStatusErrorInternal, error_message, "Load failed, %s: %s", path, err_msg); + break; + } + + FURI_LOG_I(TAG, "Loaded in %zums", (size_t)(furi_get_tick() - start)); + + loader->app.thread = flipper_application_alloc_thread(loader->app.fap, args); + FuriString* app_name = furi_string_alloc(); + path_extract_filename_no_ext(path, app_name); + furi_thread_set_appid(loader->app.thread, furi_string_get_cstr(app_name)); + furi_string_free(app_name); + + /* This flag is set by the debugger - to break on app start */ + if(furi_hal_debug_is_gdb_session_active()) { + FURI_LOG_W(TAG, "Triggering BP for debugger"); + /* After hitting this, you can set breakpoints in your .fap's code + * Note that you have to toggle breakpoints that were set before */ + __asm volatile("bkpt 0"); + } + + loader_start_app_thread(loader, FlipperInternalApplicationFlagDefault); + } while(0); + + if(status != LoaderStatusOk) { + flipper_application_free(loader->app.fap); + loader->app.fap = NULL; + } + + return status; +} + // process messages static void loader_do_menu_show(Loader* loader) { if(!loader->loader_menu) { - loader->loader_menu = loader_menu_alloc(); - loader_menu_set_closed_callback(loader->loader_menu, loader_menu_closed_callback, loader); - loader_menu_set_click_callback(loader->loader_menu, loader_menu_click_callback, loader); - loader_menu_start(loader->loader_menu); + loader->loader_menu = loader_menu_alloc(loader_menu_closed_callback, loader); } } static void loader_do_menu_closed(Loader* loader) { if(loader->loader_menu) { - loader_menu_stop(loader->loader_menu); loader_menu_free(loader->loader_menu); loader->loader_menu = NULL; } } +static void loader_do_applications_show(Loader* loader) { + if(!loader->loader_applications) { + loader->loader_applications = + loader_applications_alloc(loader_applications_closed_callback, loader); + } +} + +static void loader_do_applications_closed(Loader* loader) { + if(loader->loader_applications) { + loader_applications_free(loader->loader_applications); + loader->loader_applications = NULL; + } +} + static bool loader_do_is_locked(Loader* loader) { return loader->app.thread != NULL; } -static LoaderStatus loader_do_start_by_name(Loader* loader, const char* name, const char* args) { - if(loader_do_is_locked(loader)) { - return LoaderStatusErrorAppStarted; - } +static LoaderStatus loader_do_start_by_name( + Loader* loader, + const char* name, + const char* args, + FuriString* error_message) { + LoaderStatus status; + do { + // check lock + if(loader_do_is_locked(loader)) { + const char* current_thread_name = + furi_thread_get_name(furi_thread_get_id(loader->app.thread)); + status = loader_make_status_error( + LoaderStatusErrorAppStarted, + error_message, + "Loader is locked, please close the \"%s\" first", + current_thread_name); + break; + } + + // check internal apps + { + const FlipperInternalApplication* app = loader_find_application_by_name(name); + if(app) { + loader_start_internal_app(loader, app, args); + status = loader_make_success_status(error_message); + break; + } + } - const FlipperApplication* app = loader_find_application_by_name(name); + // check Applications + if(strcmp(name, LOADER_APPLICATIONS_NAME) == 0) { + loader_do_applications_show(loader); + status = loader_make_success_status(error_message); + break; + } - if(!app) { - return LoaderStatusErrorUnknownApp; - } + // check External Applications + { + const char* path = loader_find_external_application_by_name(name); + if(path) { + name = path; + } + } - loader_start_internal_app(loader, app, args); - return LoaderStatusOk; + // check Faps + { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_file_exists(storage, name)) { + status = loader_start_external_app(loader, storage, name, args, error_message); + furi_record_close(RECORD_STORAGE); + break; + } + furi_record_close(RECORD_STORAGE); + } + + status = loader_make_status_error( + LoaderStatusErrorUnknownApp, error_message, "Application \"%s\" not found", name); + } while(false); + + return status; } static bool loader_do_lock(Loader* loader) { @@ -229,13 +443,16 @@ static bool loader_do_lock(Loader* loader) { } static void loader_do_unlock(Loader* loader) { - furi_assert(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE); + furi_check(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE); loader->app.thread = NULL; } static void loader_do_app_closed(Loader* loader) { furi_assert(loader->app.thread); - FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); + + furi_thread_join(loader->app.thread); + FURI_LOG_I(TAG, "App returned: %li", furi_thread_get_return_code(loader->app.thread)); + if(loader->app.args) { free(loader->app.args); loader->app.args = NULL; @@ -245,12 +462,20 @@ static void loader_do_app_closed(Loader* loader) { furi_hal_power_insomnia_exit(); } - free(loader->app.name); - loader->app.name = NULL; + if(loader->app.fap) { + flipper_application_free(loader->app.fap); + loader->app.fap = NULL; + loader->app.thread = NULL; + } else { + furi_thread_free(loader->app.thread); + loader->app.thread = NULL; + } - furi_thread_join(loader->app.thread); - furi_thread_free(loader->app.thread); - loader->app.thread = NULL; + FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); + + LoaderEvent event; + event.type = LoaderEventTypeApplicationStopped; + furi_pubsub_publish(loader->pubsub, &event); } // app @@ -266,7 +491,7 @@ int32_t loader_srv(void* p) { } if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { - loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL); + loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL, NULL); } LoaderMessage message; @@ -274,8 +499,8 @@ int32_t loader_srv(void* p) { if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) { switch(message.type) { case LoaderMessageTypeStartByName: - message.status_value->value = - loader_do_start_by_name(loader, message.start.name, message.start.args); + message.status_value->value = loader_do_start_by_name( + loader, message.start.name, message.start.args, message.start.error_message); api_lock_unlock(message.api_lock); break; case LoaderMessageTypeShowMenu: @@ -297,6 +522,10 @@ int32_t loader_srv(void* p) { break; case LoaderMessageTypeUnlock: loader_do_unlock(loader); + break; + case LoaderMessageTypeApplicationsClosed: + loader_do_applications_closed(loader); + break; } } } diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index e3a691b768c5..9fc4059f2d9e 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -6,6 +6,7 @@ extern "C" { #endif #define RECORD_LOADER "loader" +#define LOADER_APPLICATIONS_NAME "Applications" typedef struct Loader Loader; @@ -25,28 +26,57 @@ typedef struct { LoaderEventType type; } LoaderEvent; -/** Start application - * @param name - application name - * @param args - application arguments - * @retval true on success +/** + * @brief Start application + * @param[in] instance loader instance + * @param[in] name application name + * @param[in] args application arguments + * @param[out] error_message detailed error message, can be NULL + * @return LoaderStatus */ -LoaderStatus loader_start(Loader* instance, const char* name, const char* args); +LoaderStatus + loader_start(Loader* instance, const char* name, const char* args, FuriString* error_message); -/** Lock application start - * @retval true on success +/** + * @brief Start application with GUI error message + * @param[in] instance loader instance + * @param[in] name application name + * @param[in] args application arguments + * @return LoaderStatus + */ +LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args); + +/** + * @brief Lock application start + * @param[in] instance loader instance + * @return true on success */ bool loader_lock(Loader* instance); -/** Unlock application start */ +/** + * @brief Unlock application start + * @param[in] instance loader instance + */ void loader_unlock(Loader* instance); -/** Get loader lock status */ +/** + * @brief Check if loader is locked + * @param[in] instance loader instance + * @return true if locked + */ bool loader_is_locked(Loader* instance); -/** Show primary loader */ +/** + * @brief Show loader menu + * @param[in] instance loader instance + */ void loader_show_menu(Loader* instance); -/** Show primary loader */ +/** + * @brief Get loader pubsub + * @param[in] instance loader instance + * @return FuriPubSub* + */ FuriPubSub* loader_get_pubsub(Loader* instance); #ifdef __cplusplus diff --git a/applications/services/loader/loader_applications.c b/applications/services/loader/loader_applications.c new file mode 100644 index 000000000000..8ae91d76408a --- /dev/null +++ b/applications/services/loader/loader_applications.c @@ -0,0 +1,157 @@ +#include "loader.h" +#include "loader_applications.h" +#include +#include +#include +#include +#include +#include + +#define TAG "LoaderApplications" + +struct LoaderApplications { + FuriThread* thread; + void (*closed_cb)(void*); + void* context; +}; + +static int32_t loader_applications_thread(void* p); + +LoaderApplications* loader_applications_alloc(void (*closed_cb)(void*), void* context) { + LoaderApplications* loader_applications = malloc(sizeof(LoaderApplications)); + loader_applications->thread = + furi_thread_alloc_ex(TAG, 768, loader_applications_thread, (void*)loader_applications); + loader_applications->closed_cb = closed_cb; + loader_applications->context = context; + furi_thread_start(loader_applications->thread); + return loader_applications; +} + +void loader_applications_free(LoaderApplications* loader_applications) { + furi_assert(loader_applications); + furi_thread_join(loader_applications->thread); + furi_thread_free(loader_applications->thread); + free(loader_applications); +} + +typedef struct { + FuriString* fap_path; + DialogsApp* dialogs; + Storage* storage; + Loader* loader; + + Gui* gui; + ViewHolder* view_holder; + Loading* loading; +} LoaderApplicationsApp; + +static LoaderApplicationsApp* loader_applications_app_alloc() { + LoaderApplicationsApp* app = malloc(sizeof(LoaderApplicationsApp)); //-V799 + app->fap_path = furi_string_alloc_set(EXT_PATH("apps")); + app->dialogs = furi_record_open(RECORD_DIALOGS); + app->storage = furi_record_open(RECORD_STORAGE); + app->loader = furi_record_open(RECORD_LOADER); + + app->gui = furi_record_open(RECORD_GUI); + app->view_holder = view_holder_alloc(); + app->loading = loading_alloc(); + + view_holder_attach_to_gui(app->view_holder, app->gui); + view_holder_set_view(app->view_holder, loading_get_view(app->loading)); + + return app; +} //-V773 + +static void loader_applications_app_free(LoaderApplicationsApp* app) { + furi_assert(app); + + view_holder_free(app->view_holder); + loading_free(app->loading); + furi_record_close(RECORD_GUI); + + furi_record_close(RECORD_LOADER); + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_STORAGE); + furi_string_free(app->fap_path); + free(app); +} + +static bool loader_applications_item_callback( + FuriString* path, + void* context, + uint8_t** icon_ptr, + FuriString* item_name) { + LoaderApplicationsApp* loader_applications_app = context; + furi_assert(loader_applications_app); + return flipper_application_load_name_and_icon( + path, loader_applications_app->storage, icon_ptr, item_name); +} + +static bool loader_applications_select_app(LoaderApplicationsApp* loader_applications_app) { + const DialogsFileBrowserOptions browser_options = { + .extension = ".fap", + .skip_assets = true, + .icon = &I_unknown_10px, + .hide_ext = true, + .item_loader_callback = loader_applications_item_callback, + .item_loader_context = loader_applications_app, + .base_path = EXT_PATH("apps"), + }; + + return dialog_file_browser_show( + loader_applications_app->dialogs, + loader_applications_app->fap_path, + loader_applications_app->fap_path, + &browser_options); +} + +#define APPLICATION_STOP_EVENT 1 + +static void loader_pubsub_callback(const void* message, void* context) { + const LoaderEvent* event = message; + const FuriThreadId thread_id = (FuriThreadId)context; + + if(event->type == LoaderEventTypeApplicationStopped) { + furi_thread_flags_set(thread_id, APPLICATION_STOP_EVENT); + } +} + +static void loader_applications_start_app(LoaderApplicationsApp* app) { + const char* name = furi_string_get_cstr(app->fap_path); + + // load app + FuriThreadId thread_id = furi_thread_get_current_id(); + FuriPubSubSubscription* subscription = + furi_pubsub_subscribe(loader_get_pubsub(app->loader), loader_pubsub_callback, thread_id); + + LoaderStatus status = loader_start_with_gui_error(app->loader, name, NULL); + + if(status == LoaderStatusOk) { + furi_thread_flags_wait(APPLICATION_STOP_EVENT, FuriFlagWaitAny, FuriWaitForever); + } + + furi_pubsub_unsubscribe(loader_get_pubsub(app->loader), subscription); +} + +static int32_t loader_applications_thread(void* p) { + LoaderApplications* loader_applications = p; + LoaderApplicationsApp* app = loader_applications_app_alloc(); + + // start loading animation + view_holder_start(app->view_holder); + + while(loader_applications_select_app(app)) { + loader_applications_start_app(app); + } + + // stop loading animation + view_holder_stop(app->view_holder); + + loader_applications_app_free(app); + + if(loader_applications->closed_cb) { + loader_applications->closed_cb(loader_applications->context); + } + + return 0; +} \ No newline at end of file diff --git a/applications/services/loader/loader_applications.h b/applications/services/loader/loader_applications.h new file mode 100644 index 000000000000..6b132af0559f --- /dev/null +++ b/applications/services/loader/loader_applications.h @@ -0,0 +1,16 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct LoaderApplications LoaderApplications; + +LoaderApplications* loader_applications_alloc(void (*closed_cb)(void*), void* context); + +void loader_applications_free(LoaderApplications* loader_applications); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/services/loader/loader_cli.c b/applications/services/loader/loader_cli.c index 2d4602215784..af3ebf9e002d 100644 --- a/applications/services/loader/loader_cli.c +++ b/applications/services/loader/loader_cli.c @@ -50,21 +50,11 @@ static void loader_cli_open(FuriString* args, Loader* loader) { const char* app_name_str = furi_string_get_cstr(app_name); - LoaderStatus status = loader_start(loader, app_name_str, args_str); - - switch(status) { - case LoaderStatusOk: - break; - case LoaderStatusErrorAppStarted: - printf("Can't start, application is running"); - break; - case LoaderStatusErrorUnknownApp: - printf("%s doesn't exists\r\n", app_name_str); - break; - case LoaderStatusErrorInternal: - printf("Internal error\r\n"); - break; + FuriString* error_message = furi_string_alloc(); + if(loader_start(loader, app_name_str, args_str, error_message) != LoaderStatusOk) { + printf("%s\r\n", furi_string_get_cstr(error_message)); } + furi_string_free(error_message); } while(false); furi_string_free(app_name); diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index 2e3f10dad02a..688b8fb665c6 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -1,20 +1,23 @@ #pragma once #include #include +#include #include "loader.h" #include "loader_menu.h" +#include "loader_applications.h" typedef struct { char* args; - char* name; FuriThread* thread; bool insomniac; + FlipperApplication* fap; } LoaderAppData; struct Loader { FuriPubSub* pubsub; FuriMessageQueue* queue; LoaderMenu* loader_menu; + LoaderApplications* loader_applications; LoaderAppData app; }; @@ -23,6 +26,7 @@ typedef enum { LoaderMessageTypeAppClosed, LoaderMessageTypeShowMenu, LoaderMessageTypeMenuClosed, + LoaderMessageTypeApplicationsClosed, LoaderMessageTypeLock, LoaderMessageTypeUnlock, LoaderMessageTypeIsLocked, @@ -31,6 +35,7 @@ typedef enum { typedef struct { const char* name; const char* args; + FuriString* error_message; } LoaderMessageStartByName; typedef struct { diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c index ec853661fbbf..149fea72c19a 100644 --- a/applications/services/loader/loader_menu.c +++ b/applications/services/loader/loader_menu.c @@ -5,106 +5,82 @@ #include #include +#include "loader.h" #include "loader_menu.h" #define TAG "LoaderMenu" struct LoaderMenu { - Gui* gui; - ViewDispatcher* view_dispatcher; - Menu* primary_menu; - Submenu* settings_menu; - - void (*closed_callback)(void*); - void* closed_callback_context; - - void (*click_callback)(const char*, void*); - void* click_callback_context; - FuriThread* thread; + void (*closed_cb)(void*); + void* context; }; -typedef enum { - LoaderMenuViewPrimary, - LoaderMenuViewSettings, -} LoaderMenuView; - static int32_t loader_menu_thread(void* p); -LoaderMenu* loader_menu_alloc() { +LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context) { LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu)); - loader_menu->gui = furi_record_open(RECORD_GUI); - loader_menu->view_dispatcher = view_dispatcher_alloc(); - loader_menu->primary_menu = menu_alloc(); - loader_menu->settings_menu = submenu_alloc(); - loader_menu->thread = NULL; + loader_menu->closed_cb = closed_cb; + loader_menu->context = context; + loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu); + furi_thread_start(loader_menu->thread); return loader_menu; } void loader_menu_free(LoaderMenu* loader_menu) { furi_assert(loader_menu); - // check if thread is running - furi_assert(!loader_menu->thread); - - submenu_free(loader_menu->settings_menu); - menu_free(loader_menu->primary_menu); - view_dispatcher_free(loader_menu->view_dispatcher); - furi_record_close(RECORD_GUI); + furi_thread_join(loader_menu->thread); + furi_thread_free(loader_menu->thread); free(loader_menu); } -void loader_menu_start(LoaderMenu* loader_menu) { - furi_assert(loader_menu); - furi_assert(!loader_menu->thread); - loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu); - furi_thread_start(loader_menu->thread); -} +typedef enum { + LoaderMenuViewPrimary, + LoaderMenuViewSettings, +} LoaderMenuView; -void loader_menu_stop(LoaderMenu* loader_menu) { - furi_assert(loader_menu); - furi_assert(loader_menu->thread); - view_dispatcher_stop(loader_menu->view_dispatcher); - furi_thread_join(loader_menu->thread); - furi_thread_free(loader_menu->thread); - loader_menu->thread = NULL; +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Menu* primary_menu; + Submenu* settings_menu; +} LoaderMenuApp; + +static void loader_menu_start(const char* name) { + Loader* loader = furi_record_open(RECORD_LOADER); + loader_start_with_gui_error(loader, name, NULL); + furi_record_close(RECORD_LOADER); } -void loader_menu_set_closed_callback( - LoaderMenu* loader_menu, - void (*callback)(void*), - void* context) { - loader_menu->closed_callback = callback; - loader_menu->closed_callback_context = context; +static void loader_menu_apps_callback(void* context, uint32_t index) { + UNUSED(context); + const char* name = FLIPPER_APPS[index].name; + loader_menu_start(name); } -void loader_menu_set_click_callback( - LoaderMenu* loader_menu, - void (*callback)(const char*, void*), - void* context) { - loader_menu->click_callback = callback; - loader_menu->click_callback_context = context; +static void loader_menu_external_apps_callback(void* context, uint32_t index) { + UNUSED(context); + const char* path = FLIPPER_EXTERNAL_APPS[index].name; + loader_menu_start(path); } -static void loader_menu_callback(void* context, uint32_t index) { - LoaderMenu* loader_menu = context; - const char* name = FLIPPER_APPS[index].name; - if(loader_menu->click_callback) { - loader_menu->click_callback(name, loader_menu->click_callback_context); - } +static void loader_menu_applications_callback(void* context, uint32_t index) { + UNUSED(index); + UNUSED(context); + const char* name = LOADER_APPLICATIONS_NAME; + loader_menu_start(name); } static void loader_menu_settings_menu_callback(void* context, uint32_t index) { - LoaderMenu* loader_menu = context; + UNUSED(context); const char* name = FLIPPER_SETTINGS_APPS[index].name; - if(loader_menu->click_callback) { - loader_menu->click_callback(name, loader_menu->click_callback_context); - } + loader_menu_start(name); } static void loader_menu_switch_to_settings(void* context, uint32_t index) { UNUSED(index); - LoaderMenu* loader_menu = context; - view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewSettings); + LoaderMenuApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, LoaderMenuViewSettings); } static uint32_t loader_menu_switch_to_primary(void* context) { @@ -117,30 +93,43 @@ static uint32_t loader_menu_exit(void* context) { return VIEW_NONE; } -static void loader_menu_build_menu(LoaderMenu* loader_menu) { +static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) { size_t i; + + for(i = 0; i < FLIPPER_EXTERNAL_APPS_COUNT; i++) { + menu_add_item( + app->primary_menu, + FLIPPER_EXTERNAL_APPS[i].name, + FLIPPER_EXTERNAL_APPS[i].icon, + i, + loader_menu_external_apps_callback, + (void*)menu); + } + for(i = 0; i < FLIPPER_APPS_COUNT; i++) { menu_add_item( - loader_menu->primary_menu, + app->primary_menu, FLIPPER_APPS[i].name, FLIPPER_APPS[i].icon, i, - loader_menu_callback, - (void*)loader_menu); + loader_menu_apps_callback, + (void*)menu); } menu_add_item( - loader_menu->primary_menu, - "Settings", - &A_Settings_14, + app->primary_menu, "Settings", &A_Settings_14, i++, loader_menu_switch_to_settings, app); + menu_add_item( + app->primary_menu, + LOADER_APPLICATIONS_NAME, + &A_Plugins_14, i++, - loader_menu_switch_to_settings, - loader_menu); + loader_menu_applications_callback, + (void*)menu); }; -static void loader_menu_build_submenu(LoaderMenu* loader_menu) { +static void loader_menu_build_submenu(LoaderMenuApp* app, LoaderMenu* loader_menu) { for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { submenu_add_item( - loader_menu->settings_menu, + app->settings_menu, FLIPPER_SETTINGS_APPS[i].name, i, loader_menu_settings_menu_callback, @@ -148,40 +137,59 @@ static void loader_menu_build_submenu(LoaderMenu* loader_menu) { } } -static int32_t loader_menu_thread(void* p) { - LoaderMenu* loader_menu = p; - furi_assert(loader_menu); - - loader_menu_build_menu(loader_menu); - loader_menu_build_submenu(loader_menu); +static LoaderMenuApp* loader_menu_app_alloc(LoaderMenu* loader_menu) { + LoaderMenuApp* app = malloc(sizeof(LoaderMenuApp)); + app->gui = furi_record_open(RECORD_GUI); + app->view_dispatcher = view_dispatcher_alloc(); + app->primary_menu = menu_alloc(); + app->settings_menu = submenu_alloc(); - view_dispatcher_attach_to_gui( - loader_menu->view_dispatcher, loader_menu->gui, ViewDispatcherTypeFullscreen); + loader_menu_build_menu(app, loader_menu); + loader_menu_build_submenu(app, loader_menu); // Primary menu - View* primary_view = menu_get_view(loader_menu->primary_menu); - view_set_context(primary_view, loader_menu->primary_menu); + View* primary_view = menu_get_view(app->primary_menu); + view_set_context(primary_view, app->primary_menu); view_set_previous_callback(primary_view, loader_menu_exit); - view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary, primary_view); + view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewPrimary, primary_view); // Settings menu - View* settings_view = submenu_get_view(loader_menu->settings_menu); - view_set_context(settings_view, loader_menu->settings_menu); + View* settings_view = submenu_get_view(app->settings_menu); + view_set_context(settings_view, app->settings_menu); view_set_previous_callback(settings_view, loader_menu_switch_to_primary); - view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewSettings, settings_view); + view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewSettings, settings_view); + + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_switch_to_view(app->view_dispatcher, LoaderMenuViewPrimary); + + return app; +} - view_dispatcher_enable_queue(loader_menu->view_dispatcher); - view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary); +static void loader_menu_app_free(LoaderMenuApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewPrimary); + view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewSettings); + view_dispatcher_free(app->view_dispatcher); - // run view dispatcher - view_dispatcher_run(loader_menu->view_dispatcher); + menu_free(app->primary_menu); + submenu_free(app->settings_menu); + furi_record_close(RECORD_GUI); + free(app); +} + +static int32_t loader_menu_thread(void* p) { + LoaderMenu* loader_menu = p; + furi_assert(loader_menu); - view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary); - view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewSettings); + LoaderMenuApp* app = loader_menu_app_alloc(loader_menu); - if(loader_menu->closed_callback) { - loader_menu->closed_callback(loader_menu->closed_callback_context); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_run(app->view_dispatcher); + + if(loader_menu->closed_cb) { + loader_menu->closed_cb(loader_menu->context); } + loader_menu_app_free(app); + return 0; } \ No newline at end of file diff --git a/applications/services/loader/loader_menu.h b/applications/services/loader/loader_menu.h index 7405b87be772..528fe7d291c2 100644 --- a/applications/services/loader/loader_menu.h +++ b/applications/services/loader/loader_menu.h @@ -7,24 +7,10 @@ extern "C" { typedef struct LoaderMenu LoaderMenu; -LoaderMenu* loader_menu_alloc(); +LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context); void loader_menu_free(LoaderMenu* loader_menu); -void loader_menu_start(LoaderMenu* loader_menu); - -void loader_menu_stop(LoaderMenu* loader_menu); - -void loader_menu_set_closed_callback( - LoaderMenu* loader_menu, - void (*callback)(void*), - void* context); - -void loader_menu_set_click_callback( - LoaderMenu* loader_menu, - void (*callback)(const char*, void*), - void* context); - #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/applications/services/notification/notification.h b/applications/services/notification/notification.h index b38620f0f5f8..0e1c07e5df67 100644 --- a/applications/services/notification/notification.h +++ b/applications/services/notification/notification.h @@ -75,6 +75,8 @@ typedef enum { NotificationMessageTypeForceDisplayBrightnessSetting, NotificationMessageTypeLedBrightnessSettingApply, + + NotificationMessageTypeLcdContrastUpdate, } NotificationMessageType; typedef struct { diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index f91a73f321de..2f947fe8a00d 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -3,6 +3,9 @@ #include #include #include +#include +#include + #include "notification.h" #include "notification_messages.h" #include "notification_app.h" @@ -20,14 +23,14 @@ static const uint8_t reset_sound_mask = 1 << 4; static const uint8_t reset_display_mask = 1 << 5; static const uint8_t reset_blink_mask = 1 << 6; -void notification_vibro_on(bool force); -void notification_vibro_off(); -void notification_sound_on(float freq, float volume, bool force); -void notification_sound_off(); +static void notification_vibro_on(bool force); +static void notification_vibro_off(); +static void notification_sound_on(float freq, float volume, bool force); +static void notification_sound_off(); -uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); -uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); -uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); +static uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); +static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); +static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); void notification_message_save_settings(NotificationApp* app) { NotificationAppMessage m = { @@ -39,7 +42,8 @@ void notification_message_save_settings(NotificationApp* app) { }; // internal layer -void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { +static void + notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { furi_assert(layer); furi_assert(layer->index < LayerMAX); @@ -52,7 +56,13 @@ void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t } } -bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { +static void notification_apply_lcd_contrast(NotificationApp* app) { + Gui* gui = furi_record_open(RECORD_GUI); + u8x8_d_st756x_set_contrast(&gui->canvas->fb.u8x8, app->settings.contrast); + furi_record_close(RECORD_GUI); +} + +static bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { bool result = false; if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) || (app->led[2].index == LayerInternal)) { @@ -67,7 +77,7 @@ bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) } // notification layer -void notification_apply_notification_led_layer( +static void notification_apply_notification_led_layer( NotificationLedLayer* layer, const uint8_t layer_value) { furi_assert(layer); @@ -81,7 +91,7 @@ void notification_apply_notification_led_layer( furi_hal_light_set(layer->light, layer->value[LayerNotification]); } -void notification_reset_notification_led_layer(NotificationLedLayer* layer) { +static void notification_reset_notification_led_layer(NotificationLedLayer* layer) { furi_assert(layer); furi_assert(layer->index < LayerMAX); @@ -94,7 +104,7 @@ void notification_reset_notification_led_layer(NotificationLedLayer* layer) { furi_hal_light_set(layer->light, layer->value[LayerInternal]); } -void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { +static void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { if(reset_mask & reset_blink_mask) { furi_hal_light_blink_stop(); } @@ -130,28 +140,28 @@ uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8 return (value * app->settings.display_brightness); } -uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { +static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { return (value * app->settings.led_brightness); } -uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { +static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { return ( (float)(app->settings.display_off_delay_ms) / (1000.0f / furi_kernel_get_tick_frequency())); } // generics -void notification_vibro_on(bool force) { +static void notification_vibro_on(bool force) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { furi_hal_vibro_on(true); } } -void notification_vibro_off() { +static void notification_vibro_off() { furi_hal_vibro_on(false); } -void notification_sound_on(float freq, float volume, bool force) { +static void notification_sound_on(float freq, float volume, bool force) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { furi_hal_speaker_start(freq, volume); @@ -159,7 +169,7 @@ void notification_sound_on(float freq, float volume, bool force) { } } -void notification_sound_off() { +static void notification_sound_off() { if(furi_hal_speaker_is_mine()) { furi_hal_speaker_stop(); furi_hal_speaker_release(); @@ -174,7 +184,7 @@ static void notification_display_timer(void* ctx) { } // message processing -void notification_process_notification_message( +static void notification_process_notification_message( NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; @@ -333,6 +343,9 @@ void notification_process_notification_message( reset_mask |= reset_green_mask; reset_mask |= reset_blue_mask; break; + case NotificationMessageTypeLcdContrastUpdate: + notification_apply_lcd_contrast(app); + break; } notification_message_index++; notification_message = (*message->sequence)[notification_message_index]; @@ -361,7 +374,8 @@ void notification_process_notification_message( } } -void notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { +static void + notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; const NotificationMessage* notification_message; notification_message = (*message->sequence)[notification_message_index]; @@ -548,6 +562,7 @@ int32_t notification_srv(void* p) { notification_apply_internal_led_layer(&app->led[0], 0x00); notification_apply_internal_led_layer(&app->led[1], 0x00); notification_apply_internal_led_layer(&app->led[2], 0x00); + notification_apply_lcd_contrast(app); furi_record_create(RECORD_NOTIFICATION, app); diff --git a/applications/services/notification/notification_app.h b/applications/services/notification/notification_app.h index 88194bfbd535..cacc17ffb0cd 100644 --- a/applications/services/notification/notification_app.h +++ b/applications/services/notification/notification_app.h @@ -32,7 +32,7 @@ typedef struct { Light light; } NotificationLedLayer; -#define NOTIFICATION_SETTINGS_VERSION 0x01 +#define NOTIFICATION_SETTINGS_VERSION 0x02 #define NOTIFICATION_SETTINGS_PATH INT_PATH(NOTIFICATION_SETTINGS_FILE_NAME) typedef struct { @@ -41,6 +41,7 @@ typedef struct { float led_brightness; float speaker_volume; uint32_t display_off_delay_ms; + int8_t contrast; bool vibro_on; } NotificationSettings; diff --git a/applications/services/notification/notification_messages.c b/applications/services/notification/notification_messages.c index 51f3533c32d2..28ec327c6e67 100644 --- a/applications/services/notification/notification_messages.c +++ b/applications/services/notification/notification_messages.c @@ -197,6 +197,10 @@ const NotificationMessage message_force_display_brightness_setting_1f = { .data.forced_settings.display_brightness = 1.0f, }; +const NotificationMessage message_lcd_contrast_update = { + .type = NotificationMessageTypeLcdContrastUpdate, +}; + /****************************** Message sequences ******************************/ // Reset @@ -566,3 +570,8 @@ const NotificationSequence sequence_audiovisual_alert = { &message_vibro_off, NULL, }; + +const NotificationSequence sequence_lcd_contrast_update = { + &message_lcd_contrast_update, + NULL, +}; diff --git a/applications/services/notification/notification_messages.h b/applications/services/notification/notification_messages.h index 1007969176c0..d87cf74f4ee6 100644 --- a/applications/services/notification/notification_messages.h +++ b/applications/services/notification/notification_messages.h @@ -63,6 +63,9 @@ extern const NotificationMessage message_force_vibro_setting_on; extern const NotificationMessage message_force_vibro_setting_off; extern const NotificationMessage message_force_display_brightness_setting_1f; +// LCD Messages +extern const NotificationMessage message_lcd_contrast_update; + /****************************** Message sequences ******************************/ // Reset @@ -138,6 +141,9 @@ extern const NotificationSequence sequence_success; extern const NotificationSequence sequence_error; extern const NotificationSequence sequence_audiovisual_alert; +// LCD +extern const NotificationSequence sequence_lcd_contrast_update; + #ifdef __cplusplus } #endif diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 56dbd0f87cf6..aadb5f46e406 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -85,30 +85,6 @@ Power* power_alloc() { return power; } -void power_free(Power* power) { - furi_assert(power); - - // Gui - view_dispatcher_remove_view(power->view_dispatcher, PowerViewOff); - power_off_free(power->power_off); - view_dispatcher_remove_view(power->view_dispatcher, PowerViewUnplugUsb); - power_unplug_usb_free(power->power_unplug_usb); - - view_port_free(power->battery_view_port); - - // State - furi_mutex_free(power->api_mtx); - - // FuriPubSub - furi_pubsub_free(power->event_pubsub); - - // Records - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_GUI); - - free(power); -} - static void power_check_charging_state(Power* power) { if(furi_hal_power_is_charging()) { if((power->info.charge == 100) || (furi_hal_power_is_charging_done())) { @@ -141,6 +117,7 @@ static bool power_update_info(Power* power) { info.is_charging = furi_hal_power_is_charging(); info.gauge_is_ok = furi_hal_power_gauge_is_ok(); + info.is_shutdown_requested = furi_hal_power_is_shutdown_requested(); info.charge = furi_hal_power_get_pct(); info.health = furi_hal_power_get_bat_health_pct(); info.capacity_remaining = furi_hal_power_get_battery_remaining_capacity(); @@ -169,7 +146,7 @@ static void power_check_low_battery(Power* power) { } // Check battery charge and vbus voltage - if((power->info.charge == 0) && (power->info.voltage_vbus < 4.0f) && + if((power->info.is_shutdown_requested) && (power->info.voltage_vbus < 4.0f) && power->show_low_bat_level_message) { if(!power->battery_low) { view_dispatcher_send_to_front(power->view_dispatcher); @@ -252,7 +229,7 @@ int32_t power_srv(void* p) { furi_delay_ms(1000); } - power_free(power); + furi_crash("That was unexpected"); return 0; } diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index c7f5d7e35012..fdc5b527a609 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -37,6 +37,7 @@ typedef struct { typedef struct { bool gauge_is_ok; bool is_charging; + bool is_shutdown_requested; float current_charger; float current_gauge; diff --git a/applications/services/rpc/rpc_app.c b/applications/services/rpc/rpc_app.c index cc18b6cec37f..bf44ed2deed0 100644 --- a/applications/services/rpc/rpc_app.c +++ b/applications/services/rpc/rpc_app.c @@ -52,7 +52,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app); app_args = args_temp; } - LoaderStatus status = loader_start(loader, app_name, app_args); + LoaderStatus status = loader_start(loader, app_name, app_args, NULL); if(status == LoaderStatusErrorAppStarted) { result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED; } else if(status == LoaderStatusErrorInternal) { diff --git a/applications/services/rpc/rpc_desktop.c b/applications/services/rpc/rpc_desktop.c index dbf9796ec56b..0d72b43d5516 100644 --- a/applications/services/rpc/rpc_desktop.c +++ b/applications/services/rpc/rpc_desktop.c @@ -8,6 +8,8 @@ typedef struct { RpcSession* session; Desktop* desktop; + FuriPubSub* status_pubsub; + FuriPubSubSubscription* status_subscription; } RpcDesktop; static void rpc_desktop_on_is_locked_request(const PB_Main* request, void* context) { @@ -39,11 +41,63 @@ static void rpc_desktop_on_unlock_request(const PB_Main* request, void* context) rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); } +static void rpc_desktop_on_desktop_pubsub(const void* message, void* context) { + RpcDesktop* rpc_desktop = context; + RpcSession* session = rpc_desktop->session; + const DesktopStatus* status = message; + + PB_Main rpc_message = { + .command_id = 0, + .command_status = PB_CommandStatus_OK, + .has_next = false, + .which_content = PB_Main_desktop_status_tag, + .content.desktop_status.locked = status->locked, + }; + rpc_send_and_release(session, &rpc_message); +} + +static void rpc_desktop_on_status_subscribe_request(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_desktop_status_subscribe_request_tag); + + FURI_LOG_D(TAG, "StatusSubscribeRequest"); + RpcDesktop* rpc_desktop = context; + RpcSession* session = rpc_desktop->session; + + if(rpc_desktop->status_subscription) { + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_ERROR); + } else { + rpc_desktop->status_subscription = furi_pubsub_subscribe( + rpc_desktop->status_pubsub, rpc_desktop_on_desktop_pubsub, rpc_desktop); + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); + } +} + +static void rpc_desktop_on_status_unsubscribe_request(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_desktop_status_unsubscribe_request_tag); + + FURI_LOG_D(TAG, "StatusUnsubscribeRequest"); + RpcDesktop* rpc_desktop = context; + RpcSession* session = rpc_desktop->session; + + if(rpc_desktop->status_subscription) { + furi_pubsub_unsubscribe(rpc_desktop->status_pubsub, rpc_desktop->status_subscription); + rpc_desktop->status_subscription = NULL; + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); + } else { + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_ERROR); + } +} + void* rpc_desktop_alloc(RpcSession* session) { furi_assert(session); RpcDesktop* rpc_desktop = malloc(sizeof(RpcDesktop)); rpc_desktop->desktop = furi_record_open(RECORD_DESKTOP); + rpc_desktop->status_pubsub = desktop_api_get_status_pubsub(rpc_desktop->desktop); rpc_desktop->session = session; RpcHandler rpc_handler = { @@ -58,6 +112,12 @@ void* rpc_desktop_alloc(RpcSession* session) { rpc_handler.message_handler = rpc_desktop_on_unlock_request; rpc_add_handler(session, PB_Main_desktop_unlock_request_tag, &rpc_handler); + rpc_handler.message_handler = rpc_desktop_on_status_subscribe_request; + rpc_add_handler(session, PB_Main_desktop_status_subscribe_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_desktop_on_status_unsubscribe_request; + rpc_add_handler(session, PB_Main_desktop_status_unsubscribe_request_tag, &rpc_handler); + return rpc_desktop; } @@ -65,6 +125,10 @@ void rpc_desktop_free(void* context) { furi_assert(context); RpcDesktop* rpc_desktop = context; + if(rpc_desktop->status_subscription) { + furi_pubsub_unsubscribe(rpc_desktop->status_pubsub, rpc_desktop->status_subscription); + } + furi_assert(rpc_desktop->desktop); furi_record_close(RECORD_DESKTOP); diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h index a1267575fb88..dccf29592c63 100644 --- a/applications/services/storage/storage.h +++ b/applications/services/storage/storage.h @@ -226,7 +226,7 @@ FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* filei */ FS_Error storage_common_remove(Storage* storage, const char* path); -/** Renames file/directory, file/directory must not be open +/** Renames file/directory, file/directory must not be open. Will overwrite existing file. * @param app pointer to the api * @param old_path old path * @param new_path new path diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index bf474bc9d0b7..585ded41444a 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -422,12 +422,41 @@ FS_Error storage_common_remove(Storage* storage, const char* path) { } FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) { - FS_Error error = storage_common_copy(storage, old_path, new_path); - if(error == FSE_OK) { + FS_Error error; + + do { + if(!storage_common_exists(storage, old_path)) { + error = FSE_INVALID_NAME; + break; + } + + if(storage_dir_exists(storage, old_path)) { + FuriString* dir_path = furi_string_alloc_set_str(old_path); + if(!furi_string_end_with_str(dir_path, "/")) { + furi_string_cat_str(dir_path, "/"); + } + const char* dir_path_s = furi_string_get_cstr(dir_path); + if(strncmp(new_path, dir_path_s, strlen(dir_path_s)) == 0) { + error = FSE_INVALID_NAME; + furi_string_free(dir_path); + break; + } + furi_string_free(dir_path); + } + + if(storage_file_exists(storage, new_path)) { + storage_common_remove(storage, new_path); + } + + error = storage_common_copy(storage, old_path, new_path); + if(error != FSE_OK) { + break; + } + if(!storage_simply_remove_recursive(storage, old_path)) { error = FSE_INTERNAL; } - } + } while(false); return error; } diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 688103306887..55bd43e5c589 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -82,7 +82,9 @@ static DialogMessageButton icon1_screen(DialogsApp* dialogs, DialogMessage* mess static DialogMessageButton icon2_screen(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; - dialog_message_set_icon(message, &I_Certification2_98x33, 15, 10); + dialog_message_set_icon(message, &I_Certification2_46x33, 15, 10); + dialog_message_set_text( + message, furi_hal_version_get_mic_id(), 63, 27, AlignLeft, AlignCenter); result = dialog_message_show(dialogs, message); dialog_message_set_icon(message, NULL, 0, 0); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index 94c5ee9f0493..26e7bc5875b2 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -1,9 +1,30 @@ #include "../desktop_settings_app.h" #include "applications.h" #include "desktop_settings_scene.h" +#include #include #include -#include + +#define APPS_COUNT (FLIPPER_APPS_COUNT + FLIPPER_EXTERNAL_APPS_COUNT) + +#define EXTERNAL_BROWSER_NAME ("Applications") +#define EXTERNAL_BROWSER_INDEX (APPS_COUNT + 1) + +#define EXTERNAL_APPLICATION_NAME ("[External Application]") +#define EXTERNAL_APPLICATION_INDEX (APPS_COUNT + 2) + +#define PRESELECTED_SPECIAL 0xffffffff + +static const char* favorite_fap_get_app_name(size_t i) { + const char* name; + if(i < FLIPPER_APPS_COUNT) { + name = FLIPPER_APPS[i].name; + } else { + name = FLIPPER_EXTERNAL_APPS[i - FLIPPER_APPS_COUNT].name; + } + + return name; +} static bool favorite_fap_selector_item_callback( FuriString* file_path, @@ -11,16 +32,9 @@ static bool favorite_fap_selector_item_callback( uint8_t** icon_ptr, FuriString* item_name) { UNUSED(context); -#ifdef APP_FAP_LOADER Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = fap_loader_load_name_and_icon(file_path, storage, icon_ptr, item_name); + bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name); furi_record_close(RECORD_STORAGE); -#else - UNUSED(file_path); - UNUSED(icon_ptr); - UNUSED(item_name); - bool success = false; -#endif return success; } @@ -43,28 +57,42 @@ void desktop_settings_scene_favorite_on_enter(void* context) { uint32_t primary_favorite = scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); - uint32_t pre_select_item = 0; - - for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - submenu_add_item( - submenu, - FLIPPER_APPS[i].name, - i, - desktop_settings_scene_favorite_submenu_callback, - app); - - if(primary_favorite) { // Select favorite item in submenu - if((app->settings.favorite_primary.is_external && - !strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) || - (!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_primary.name_or_path))) { - pre_select_item = i; - } + uint32_t pre_select_item = PRESELECTED_SPECIAL; + FavoriteApp* curr_favorite_app = primary_favorite ? &app->settings.favorite_primary : + &app->settings.favorite_secondary; + + for(size_t i = 0; i < APPS_COUNT; i++) { + const char* name = favorite_fap_get_app_name(i); + + submenu_add_item(submenu, name, i, desktop_settings_scene_favorite_submenu_callback, app); + + // Select favorite item in submenu + if(!strcmp(name, curr_favorite_app->name_or_path)) { + pre_select_item = i; + } + } + + // Special case: Application browser + submenu_add_item( + submenu, + EXTERNAL_BROWSER_NAME, + EXTERNAL_BROWSER_INDEX, + desktop_settings_scene_favorite_submenu_callback, + app); + + // Special case: Specific application + submenu_add_item( + submenu, + EXTERNAL_APPLICATION_NAME, + EXTERNAL_APPLICATION_INDEX, + desktop_settings_scene_favorite_submenu_callback, + app); + + if(pre_select_item == PRESELECTED_SPECIAL) { + if(curr_favorite_app->name_or_path[0] == '\0') { + pre_select_item = EXTERNAL_BROWSER_INDEX; } else { - if((app->settings.favorite_secondary.is_external && - !strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) || - (!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_secondary.name_or_path))) { - pre_select_item = i; - } + pre_select_item = EXTERNAL_APPLICATION_INDEX; } } @@ -82,23 +110,14 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e uint32_t primary_favorite = scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); + FavoriteApp* curr_favorite_app = primary_favorite ? &app->settings.favorite_primary : + &app->settings.favorite_secondary; if(event.type == SceneManagerEventTypeCustom) { - if(strcmp(FLIPPER_APPS[event.event].name, FAP_LOADER_APP_NAME) != 0) { - if(primary_favorite) { - app->settings.favorite_primary.is_external = false; - strncpy( - app->settings.favorite_primary.name_or_path, - FLIPPER_APPS[event.event].name, - MAX_APP_LENGTH); - } else { - app->settings.favorite_secondary.is_external = false; - strncpy( - app->settings.favorite_secondary.name_or_path, - FLIPPER_APPS[event.event].name, - MAX_APP_LENGTH); - } - } else { + if(event.event == EXTERNAL_BROWSER_INDEX) { + curr_favorite_app->name_or_path[0] = '\0'; + consumed = true; + } else if(event.event == EXTERNAL_APPLICATION_INDEX) { const DialogsFileBrowserOptions browser_options = { .extension = ".fap", .icon = &I_unknown_10px, @@ -109,36 +128,27 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e .base_path = EXT_PATH("apps"), }; - if(primary_favorite) { // Select favorite fap in file browser - if(favorite_fap_selector_file_exists( - app->settings.favorite_primary.name_or_path)) { - furi_string_set_str(temp_path, app->settings.favorite_primary.name_or_path); - } - } else { - if(favorite_fap_selector_file_exists( - app->settings.favorite_secondary.name_or_path)) { - furi_string_set_str(temp_path, app->settings.favorite_secondary.name_or_path); - } + // Select favorite fap in file browser + if(favorite_fap_selector_file_exists(curr_favorite_app->name_or_path)) { + furi_string_set_str(temp_path, curr_favorite_app->name_or_path); } - submenu_reset(app->submenu); if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) { - if(primary_favorite) { - app->settings.favorite_primary.is_external = true; - strncpy( - app->settings.favorite_primary.name_or_path, - furi_string_get_cstr(temp_path), - MAX_APP_LENGTH); - } else { - app->settings.favorite_secondary.is_external = true; - strncpy( - app->settings.favorite_secondary.name_or_path, - furi_string_get_cstr(temp_path), - MAX_APP_LENGTH); - } + submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene + strncpy( + curr_favorite_app->name_or_path, + furi_string_get_cstr(temp_path), + MAX_APP_LENGTH); + consumed = true; } + } else { + const char* name = favorite_fap_get_app_name(event.event); + if(name) strncpy(curr_favorite_app->name_or_path, name, MAX_APP_LENGTH); + consumed = true; } - scene_manager_previous_scene(app->scene_manager); + if(consumed) { + scene_manager_previous_scene(app->scene_manager); + }; consumed = true; } diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c index 8efbc5e0844a..450aaee144f9 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -20,6 +20,34 @@ static const NotificationSequence sequence_note_c = { NULL, }; +#define CONTRAST_COUNT 11 +const char* const contrast_text[CONTRAST_COUNT] = { + "-5", + "-4", + "-3", + "-2", + "-1", + "0", + "+1", + "+2", + "+3", + "+4", + "+5", +}; +const int32_t contrast_value[CONTRAST_COUNT] = { + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, +}; + #define BACKLIGHT_COUNT 5 const char* const backlight_text[BACKLIGHT_COUNT] = { "0%", @@ -64,6 +92,15 @@ const char* const vibro_text[VIBRO_COUNT] = { }; const bool vibro_value[VIBRO_COUNT] = {false, true}; +static void contrast_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, contrast_text[index]); + app->notification->settings.contrast = contrast_value[index]; + notification_message(app->notification, &sequence_lcd_contrast_update); +} + static void backlight_changed(VariableItem* item) { NotificationAppSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -136,6 +173,13 @@ static NotificationAppSettings* alloc_settings() { VariableItem* item; uint8_t value_index; + item = variable_item_list_add( + app->variable_item_list, "LCD Contrast", CONTRAST_COUNT, contrast_changed, app); + value_index = + value_index_int32(app->notification->settings.contrast, contrast_value, CONTRAST_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, contrast_text[value_index]); + item = variable_item_list_add( app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); value_index = value_index_float( diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index d56dfc628598..8add60db5ab9 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -54,8 +54,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { (uint32_t)(data->vbus_voltage * 10) % 10, current); } else if(current < -5) { - // Often gauge reports anything in the range 1~5ma as 5ma - // That brings confusion, so we'll treat it as Napping + // 0-5ma deadband snprintf( emote, sizeof(emote), diff --git a/applications/system/storage_move_to_sd/storage_move_to_sd.c b/applications/system/storage_move_to_sd/storage_move_to_sd.c index 9c91b9266809..25893f011061 100644 --- a/applications/system/storage_move_to_sd/storage_move_to_sd.c +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.c @@ -172,7 +172,7 @@ static void storage_move_to_sd_mount_callback(const void* message, void* context if(storage_event->type == StorageEventTypeCardMount) { Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "StorageMoveToSd", NULL); + loader_start(loader, "StorageMoveToSd", NULL, NULL); furi_record_close(RECORD_LOADER); } } diff --git a/applications/system/updater/cli/updater_cli.c b/applications/system/updater/cli/updater_cli.c index 659c431f7017..cebdc4d7cab6 100644 --- a/applications/system/updater/cli/updater_cli.c +++ b/applications/system/updater/cli/updater_cli.c @@ -99,7 +99,7 @@ static void updater_start_app(void* context, uint32_t arg) { * So, accessing its record would cause a deadlock */ Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "UpdaterApp", NULL); + loader_start(loader, "UpdaterApp", NULL, NULL); furi_record_close(RECORD_LOADER); } diff --git a/applications/system/updater/util/update_task.c b/applications/system/updater/util/update_task.c index 708d10ce0400..74d752b434b6 100644 --- a/applications/system/updater/util/update_task.c +++ b/applications/system/updater/util/update_task.c @@ -19,7 +19,7 @@ static const char* update_task_stage_descr[] = { [UpdateTaskStageRadioErase] = "Uninstalling radio FW", [UpdateTaskStageRadioWrite] = "Writing radio FW", [UpdateTaskStageRadioInstall] = "Installing radio FW", - [UpdateTaskStageRadioBusy] = "Radio is updating", + [UpdateTaskStageRadioBusy] = "Core 2 busy", [UpdateTaskStageOBValidation] = "Validating opt. bytes", [UpdateTaskStageLfsBackup] = "Backing up LFS", [UpdateTaskStageLfsRestore] = "Restoring LFS", @@ -30,6 +30,191 @@ static const char* update_task_stage_descr[] = { [UpdateTaskStageOBError] = "OB, report", }; +static const struct { + UpdateTaskStage stage; + uint8_t percent_min, percent_max; + const char* descr; +} update_task_error_detail[] = { + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 0, + .percent_max = 13, + .descr = "Wrong Updater HW", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 14, + .percent_max = 20, + .descr = "Manifest pointer error", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 21, + .percent_max = 30, + .descr = "Manifest load error", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 31, + .percent_max = 40, + .descr = "Wrong package version", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 41, + .percent_max = 50, + .descr = "HW Target mismatch", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 51, + .percent_max = 60, + .descr = "No DFU file", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 61, + .percent_max = 80, + .descr = "No Radio file", + }, +#ifndef FURI_RAM_EXEC + { + .stage = UpdateTaskStageLfsBackup, + .percent_min = 0, + .percent_max = 100, + .descr = "FS R/W error", + }, +#else + { + .stage = UpdateTaskStageRadioImageValidate, + .percent_min = 0, + .percent_max = 98, + .descr = "FS Read error", + }, + { + .stage = UpdateTaskStageRadioImageValidate, + .percent_min = 99, + .percent_max = 100, + .descr = "CRC mismatch", + }, + { + .stage = UpdateTaskStageRadioErase, + .percent_min = 0, + .percent_max = 30, + .descr = "Stack remove: cmd error", + }, + { + .stage = UpdateTaskStageRadioErase, + .percent_min = 31, + .percent_max = 100, + .descr = "Stack remove: wait failed", + }, + { + .stage = UpdateTaskStageRadioWrite, + .percent_min = 0, + .percent_max = 100, + .descr = "Stack write: error", + }, + { + .stage = UpdateTaskStageRadioInstall, + .percent_min = 0, + .percent_max = 10, + .descr = "Stack install: cmd error", + }, + { + .stage = UpdateTaskStageRadioInstall, + .percent_min = 11, + .percent_max = 100, + .descr = "Stack install: wait failed", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 0, + .percent_max = 10, + .descr = "Failed to start C2", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 11, + .percent_max = 20, + .descr = "C2 FUS swich failed", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 21, + .percent_max = 30, + .descr = "FUS operation failed", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 31, + .percent_max = 100, + .descr = "C2 Stach switch failed", + }, + { + .stage = UpdateTaskStageOBValidation, + .percent_min = 0, + .percent_max = 100, + .descr = "Uncorr. value mismatch", + }, + { + .stage = UpdateTaskStageValidateDFUImage, + .percent_min = 0, + .percent_max = 1, + .descr = "Failed to open DFU file", + }, + { + .stage = UpdateTaskStageValidateDFUImage, + .percent_min = 1, + .percent_max = 97, + .descr = "DFU file read error", + }, + { + .stage = UpdateTaskStageValidateDFUImage, + .percent_min = 98, + .percent_max = 100, + .descr = "DFU file CRC mismatch", + }, + { + .stage = UpdateTaskStageFlashWrite, + .percent_min = 0, + .percent_max = 100, + .descr = "Flash write error", + }, + { + .stage = UpdateTaskStageFlashValidate, + .percent_min = 0, + .percent_max = 100, + .descr = "Flash compare error", + }, +#endif +#ifndef FURI_RAM_EXEC + { + .stage = UpdateTaskStageLfsRestore, + .percent_min = 0, + .percent_max = 100, + .descr = "LFS I/O error", + }, + { + .stage = UpdateTaskStageResourcesUpdate, + .percent_min = 0, + .percent_max = 100, + .descr = "SD card I/O error", + }, +#endif +}; + +static const char* update_task_get_error_message(UpdateTaskStage stage, uint8_t percent) { + for(size_t i = 0; i < COUNT_OF(update_task_error_detail); i++) { + if(update_task_error_detail[i].stage == stage && + percent >= update_task_error_detail[i].percent_min && + percent <= update_task_error_detail[i].percent_max) { + return update_task_error_detail[i].descr; + } + } + return "Unknown error"; +} + typedef struct { UpdateTaskStageGroup group; uint8_t weight; @@ -111,8 +296,9 @@ void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, ui if(stage >= UpdateTaskStageError) { furi_string_printf( update_task->state.status, - "%s #[%d-%d]", - update_task_stage_descr[stage], + "%s\n#[%d-%d]", + update_task_get_error_message( + update_task->state.stage, update_task->state.stage_progress), update_task->state.stage, update_task->state.stage_progress); } else { diff --git a/applications/system/updater/util/update_task_i.h b/applications/system/updater/util/update_task_i.h index 0dbeca5f491c..1b664e57e4c8 100644 --- a/applications/system/updater/util/update_task_i.h +++ b/applications/system/updater/util/update_task_i.h @@ -24,3 +24,8 @@ bool update_task_open_file(UpdateTask* update_task, FuriString* filename); int32_t update_task_worker_flash_writer(void* context); int32_t update_task_worker_backup_restore(void* context); + +#define CHECK_RESULT(x) \ + if(!(x)) { \ + break; \ + } diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c index f2c33c2edb45..ef4276fac5ec 100644 --- a/applications/system/updater/util/update_task_worker_backup.c +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -15,11 +15,6 @@ #define TAG "UpdWorkerBackup" -#define CHECK_RESULT(x) \ - if(!(x)) { \ - break; \ - } - static bool update_task_pre_update(UpdateTask* update_task) { bool success = false; FuriString* backup_file_path; diff --git a/applications/system/updater/util/update_task_worker_flasher.c b/applications/system/updater/util/update_task_worker_flasher.c index 5d247746464f..d6dc13e378db 100644 --- a/applications/system/updater/util/update_task_worker_flasher.c +++ b/applications/system/updater/util/update_task_worker_flasher.c @@ -13,11 +13,6 @@ #define TAG "UpdWorkerRAM" -#define CHECK_RESULT(x) \ - if(!(x)) { \ - break; \ - } - #define STM_DFU_VENDOR_ID 0x0483 #define STM_DFU_PRODUCT_ID 0xDF11 /* Written into DFU file by build pipeline */ @@ -137,7 +132,7 @@ static bool update_task_write_stack_data(UpdateTask* update_task) { } static void update_task_wait_for_restart(UpdateTask* update_task) { - update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10); + update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 70); furi_delay_ms(C2_MODE_SWITCH_TIMEOUT); furi_crash("C2 timeout"); } @@ -153,12 +148,12 @@ static bool update_task_write_stack(UpdateTask* update_task) { manifest->radio_crc); CHECK_RESULT(update_task_write_stack_data(update_task)); - update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 0); + update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 10); CHECK_RESULT( ble_glue_fus_stack_install(manifest->radio_address, 0) != BleGlueCommandResultError); - update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 80); + update_task_set_progress(update_task, UpdateTaskStageProgress, 80); CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); - update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 100); + update_task_set_progress(update_task, UpdateTaskStageProgress, 100); /* ...system will restart here. */ update_task_wait_for_restart(update_task); } while(false); @@ -170,9 +165,9 @@ static bool update_task_remove_stack(UpdateTask* update_task) { FURI_LOG_W(TAG, "Removing stack"); update_task_set_progress(update_task, UpdateTaskStageRadioErase, 30); CHECK_RESULT(ble_glue_fus_stack_delete() != BleGlueCommandResultError); - update_task_set_progress(update_task, UpdateTaskStageRadioErase, 80); + update_task_set_progress(update_task, UpdateTaskStageProgress, 80); CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); - update_task_set_progress(update_task, UpdateTaskStageRadioErase, 100); + update_task_set_progress(update_task, UpdateTaskStageProgress, 100); /* ...system will restart here. */ update_task_wait_for_restart(update_task); } while(false); @@ -180,6 +175,7 @@ static bool update_task_remove_stack(UpdateTask* update_task) { } static bool update_task_manage_radiostack(UpdateTask* update_task) { + update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10); bool success = false; do { CHECK_RESULT(ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)); @@ -208,15 +204,17 @@ static bool update_task_manage_radiostack(UpdateTask* update_task) { /* Version or type mismatch. Let's boot to FUS and start updating. */ FURI_LOG_W(TAG, "Restarting to FUS"); furi_hal_rtc_set_flag(FuriHalRtcFlagC2Update); + update_task_set_progress(update_task, UpdateTaskStageProgress, 20); + CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeFUS)); /* ...system will restart here. */ update_task_wait_for_restart(update_task); } } else if(c2_state->mode == BleGlueC2ModeFUS) { /* OK, we're in FUS mode. */ - update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10); FURI_LOG_W(TAG, "Waiting for FUS to settle"); - ble_glue_fus_wait_operation(); + update_task_set_progress(update_task, UpdateTaskStageProgress, 30); + CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); if(stack_version_match) { /* We can't check StackType with FUS, but partial version matches */ if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagC2Update)) { @@ -230,7 +228,7 @@ static bool update_task_manage_radiostack(UpdateTask* update_task) { /* We might just had the stack installed. * Let's start it up to check its version */ FURI_LOG_W(TAG, "Starting stack to check full version"); - update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 40); + update_task_set_progress(update_task, UpdateTaskStageProgress, 50); CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeStack)); /* ...system will restart here. */ update_task_wait_for_restart(update_task); diff --git a/applications/system/updater/views/updater_main.c b/applications/system/updater/views/updater_main.c index 1199cc882aa7..d32d51b7c0bf 100644 --- a/applications/system/updater/views/updater_main.c +++ b/applications/system/updater/views/updater_main.c @@ -81,16 +81,17 @@ static void updater_main_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontPrimary); if(model->failed) { - canvas_draw_str_aligned(canvas, 42, 16, AlignLeft, AlignTop, "Update Failed!"); + canvas_draw_icon(canvas, 2, 22, &I_Warning_30x23); + canvas_draw_str_aligned(canvas, 40, 9, AlignLeft, AlignTop, "Update Failed!"); canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 42, 32, AlignLeft, AlignTop, furi_string_get_cstr(model->status)); - canvas_draw_icon(canvas, 7, 16, &I_Warning_30x23); + elements_multiline_text_aligned( + canvas, 75, 26, AlignCenter, AlignTop, furi_string_get_cstr(model->status)); + canvas_draw_str_aligned( - canvas, 18, 51, AlignLeft, AlignTop, "to retry, hold to abort"); - canvas_draw_icon(canvas, 7, 50, &I_Ok_btn_9x9); - canvas_draw_icon(canvas, 75, 51, &I_Pin_back_arrow_10x8); + canvas, 18, 55, AlignLeft, AlignTop, "to retry, hold to abort"); + canvas_draw_icon(canvas, 7, 54, &I_Ok_btn_9x9); + canvas_draw_icon(canvas, 75, 55, &I_Pin_back_arrow_10x8); } else { canvas_draw_str_aligned(canvas, 55, 14, AlignLeft, AlignTop, "UPDATING"); canvas_set_font(canvas, FontSecondary); diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png new file mode 100644 index 000000000000..8b8dc80bce09 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png new file mode 100644 index 000000000000..956de6170702 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png new file mode 100644 index 000000000000..93fdaa076741 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png new file mode 100644 index 000000000000..a4e194825e11 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png new file mode 100644 index 000000000000..9cbcbd070eb4 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png new file mode 100644 index 000000000000..a745cdb03f1a Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png new file mode 100644 index 000000000000..768f471ec1f1 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png new file mode 100644 index 000000000000..2f50fb8c9492 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png new file mode 100644 index 000000000000..fc7b76696c11 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png new file mode 100644 index 000000000000..013e2008a2f5 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png new file mode 100644 index 000000000000..795120e772fe Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png new file mode 100644 index 000000000000..52061a5a260d Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png new file mode 100644 index 000000000000..10afed391e47 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png new file mode 100644 index 000000000000..52f87f3a88ee Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png new file mode 100644 index 000000000000..9696e0b5ccf1 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png new file mode 100644 index 000000000000..d23ee492b07b Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png new file mode 100644 index 000000000000..9d368900c94f Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png new file mode 100644 index 000000000000..daf0788ad75e Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png new file mode 100644 index 000000000000..b40333654f83 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png new file mode 100644 index 000000000000..9d499f23d22e Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png new file mode 100644 index 000000000000..0291dfb5830a Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png new file mode 100644 index 000000000000..54a889d81512 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png new file mode 100644 index 000000000000..ba79b3b88ffe Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png new file mode 100644 index 000000000000..bedf366c6ccf Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png new file mode 100644 index 000000000000..0731760dffba Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png new file mode 100644 index 000000000000..898efdc4b8fe Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png new file mode 100644 index 000000000000..39f5db8a0131 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png new file mode 100644 index 000000000000..bee4cff08722 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png new file mode 100644 index 000000000000..969b91193d40 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png new file mode 100644 index 000000000000..a72cf1823ee1 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png new file mode 100644 index 000000000000..9a13e7c67db2 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png new file mode 100644 index 000000000000..da3ee77f30b6 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png new file mode 100644 index 000000000000..93da7f4f94b0 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png new file mode 100644 index 000000000000..7510931b44bb Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png new file mode 100644 index 000000000000..f99454b164c5 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png new file mode 100644 index 000000000000..a17a14044a94 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png new file mode 100644 index 000000000000..f763540c917b Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png new file mode 100644 index 000000000000..97a829e9b7ae Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png new file mode 100644 index 000000000000..7eadf75185ec Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png new file mode 100644 index 000000000000..5241195d32bc Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png new file mode 100644 index 000000000000..2a3ea8e23af7 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png new file mode 100644 index 000000000000..f4b263b199c8 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png new file mode 100644 index 000000000000..1563ff39b27a Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png new file mode 100644 index 000000000000..394bb53df4a1 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png new file mode 100644 index 000000000000..3f84ad47e3de Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png new file mode 100644 index 000000000000..8ea650ad0751 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png new file mode 100644 index 000000000000..2c8cf3f5d7c3 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png new file mode 100644 index 000000000000..caf6a90ef61b Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/meta.txt b/assets/dolphin/external/L1_Kaiju_128x64/meta.txt new file mode 100644 index 000000000000..597f4b692a97 --- /dev/null +++ b/assets/dolphin/external/L1_Kaiju_128x64/meta.txt @@ -0,0 +1,50 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 16 +Active frames: 60 +Frames order: 0 1 2 1 0 3 4 5 6 3 7 1 8 1 7 9 0 10 11 12 13 14 15 16 14 12 17 18 19 20 21 22 22 23 22 24 25 26 27 26 25 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 19 18 14 45 14 45 14 45 14 45 14 16 14 14 11 46 47 1 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 83 +Y: 42 +Text: Sup +AlignH: Left +AlignV: Top +StartFrame: 38 +EndFrame: 40 + +Slot: 0 +X: 66 +Y: 35 +Text: What just +AlignH: Left +AlignV: Center +StartFrame: 62 +EndFrame: 65 + +Slot: 0 +X: 66 +Y: 35 +Text: happened? +AlignH: Left +AlignV: Center +StartFrame: 66 +EndFrame: 68 + +Slot: 0 +X: 87 +Y: 38 +Text: Idk +AlignH: Left +AlignV: Top +StartFrame: 70 +EndFrame: 70 diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_0.png b/assets/dolphin/external/L1_My_dude_128x64/frame_0.png new file mode 100644 index 000000000000..bf07d03d6e63 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_1.png b/assets/dolphin/external/L1_My_dude_128x64/frame_1.png new file mode 100644 index 000000000000..4402654c776e Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_10.png b/assets/dolphin/external/L1_My_dude_128x64/frame_10.png new file mode 100644 index 000000000000..10dabe4c5c54 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_11.png b/assets/dolphin/external/L1_My_dude_128x64/frame_11.png new file mode 100644 index 000000000000..878712fe23a0 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_12.png b/assets/dolphin/external/L1_My_dude_128x64/frame_12.png new file mode 100644 index 000000000000..19fc985ac866 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_13.png b/assets/dolphin/external/L1_My_dude_128x64/frame_13.png new file mode 100644 index 000000000000..39172f26f90f Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_14.png b/assets/dolphin/external/L1_My_dude_128x64/frame_14.png new file mode 100644 index 000000000000..9a3a84fff837 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_15.png b/assets/dolphin/external/L1_My_dude_128x64/frame_15.png new file mode 100644 index 000000000000..2472c2729f6d Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_16.png b/assets/dolphin/external/L1_My_dude_128x64/frame_16.png new file mode 100644 index 000000000000..4940aef672f1 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_17.png b/assets/dolphin/external/L1_My_dude_128x64/frame_17.png new file mode 100644 index 000000000000..fd910ce5a61c Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_18.png b/assets/dolphin/external/L1_My_dude_128x64/frame_18.png new file mode 100644 index 000000000000..ed33f18a7072 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_19.png b/assets/dolphin/external/L1_My_dude_128x64/frame_19.png new file mode 100644 index 000000000000..d602a01d5b14 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_2.png b/assets/dolphin/external/L1_My_dude_128x64/frame_2.png new file mode 100644 index 000000000000..b680b4ae0565 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_20.png b/assets/dolphin/external/L1_My_dude_128x64/frame_20.png new file mode 100644 index 000000000000..2dfa931f259d Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_21.png b/assets/dolphin/external/L1_My_dude_128x64/frame_21.png new file mode 100644 index 000000000000..272064d90de9 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_22.png b/assets/dolphin/external/L1_My_dude_128x64/frame_22.png new file mode 100644 index 000000000000..35d0149ad97e Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_23.png b/assets/dolphin/external/L1_My_dude_128x64/frame_23.png new file mode 100644 index 000000000000..83d02ef88a82 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_24.png b/assets/dolphin/external/L1_My_dude_128x64/frame_24.png new file mode 100644 index 000000000000..f3ac44178194 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_25.png b/assets/dolphin/external/L1_My_dude_128x64/frame_25.png new file mode 100644 index 000000000000..832c2bde9f9c Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_26.png b/assets/dolphin/external/L1_My_dude_128x64/frame_26.png new file mode 100644 index 000000000000..3836a3b01b8f Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_27.png b/assets/dolphin/external/L1_My_dude_128x64/frame_27.png new file mode 100644 index 000000000000..ff621b73eeae Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_28.png b/assets/dolphin/external/L1_My_dude_128x64/frame_28.png new file mode 100644 index 000000000000..94e05f94d9d1 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_29.png b/assets/dolphin/external/L1_My_dude_128x64/frame_29.png new file mode 100644 index 000000000000..1ce384b165cb Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_3.png b/assets/dolphin/external/L1_My_dude_128x64/frame_3.png new file mode 100644 index 000000000000..a5056eb4bcff Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_30.png b/assets/dolphin/external/L1_My_dude_128x64/frame_30.png new file mode 100644 index 000000000000..8d42b8b482ba Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_31.png b/assets/dolphin/external/L1_My_dude_128x64/frame_31.png new file mode 100644 index 000000000000..ac926d7be312 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_32.png b/assets/dolphin/external/L1_My_dude_128x64/frame_32.png new file mode 100644 index 000000000000..35070eb6b4a2 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_33.png b/assets/dolphin/external/L1_My_dude_128x64/frame_33.png new file mode 100644 index 000000000000..a6c973f67946 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_34.png b/assets/dolphin/external/L1_My_dude_128x64/frame_34.png new file mode 100644 index 000000000000..3f9407f385c8 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_35.png b/assets/dolphin/external/L1_My_dude_128x64/frame_35.png new file mode 100644 index 000000000000..6059e00d545e Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_36.png b/assets/dolphin/external/L1_My_dude_128x64/frame_36.png new file mode 100644 index 000000000000..d2cd0c970c61 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_37.png b/assets/dolphin/external/L1_My_dude_128x64/frame_37.png new file mode 100644 index 000000000000..e60fd08dee1d Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_38.png b/assets/dolphin/external/L1_My_dude_128x64/frame_38.png new file mode 100644 index 000000000000..70e56b168bf7 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_39.png b/assets/dolphin/external/L1_My_dude_128x64/frame_39.png new file mode 100644 index 000000000000..450b4d4f63db Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_4.png b/assets/dolphin/external/L1_My_dude_128x64/frame_4.png new file mode 100644 index 000000000000..2d9f4e96334e Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_40.png b/assets/dolphin/external/L1_My_dude_128x64/frame_40.png new file mode 100644 index 000000000000..369200345db3 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_41.png b/assets/dolphin/external/L1_My_dude_128x64/frame_41.png new file mode 100644 index 000000000000..e0f882268275 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_41.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_42.png b/assets/dolphin/external/L1_My_dude_128x64/frame_42.png new file mode 100644 index 000000000000..a8a23536a433 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_42.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_43.png b/assets/dolphin/external/L1_My_dude_128x64/frame_43.png new file mode 100644 index 000000000000..6a402b350b0a Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_43.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_44.png b/assets/dolphin/external/L1_My_dude_128x64/frame_44.png new file mode 100644 index 000000000000..f425bcc17940 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_44.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_45.png b/assets/dolphin/external/L1_My_dude_128x64/frame_45.png new file mode 100644 index 000000000000..b0ea1a7e78c0 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_45.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_46.png b/assets/dolphin/external/L1_My_dude_128x64/frame_46.png new file mode 100644 index 000000000000..3113ff2e623d Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_46.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_47.png b/assets/dolphin/external/L1_My_dude_128x64/frame_47.png new file mode 100644 index 000000000000..87403c610aca Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_47.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_48.png b/assets/dolphin/external/L1_My_dude_128x64/frame_48.png new file mode 100644 index 000000000000..2734e2fcd57e Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_48.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_5.png b/assets/dolphin/external/L1_My_dude_128x64/frame_5.png new file mode 100644 index 000000000000..df2255949104 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_6.png b/assets/dolphin/external/L1_My_dude_128x64/frame_6.png new file mode 100644 index 000000000000..4c00552ea2bd Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_7.png b/assets/dolphin/external/L1_My_dude_128x64/frame_7.png new file mode 100644 index 000000000000..9703809405f7 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_8.png b/assets/dolphin/external/L1_My_dude_128x64/frame_8.png new file mode 100644 index 000000000000..86e41e913b7e Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_9.png b/assets/dolphin/external/L1_My_dude_128x64/frame_9.png new file mode 100644 index 000000000000..4334eefafb90 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/meta.txt b/assets/dolphin/external/L1_My_dude_128x64/meta.txt new file mode 100644 index 000000000000..8c326cf42ded --- /dev/null +++ b/assets/dolphin/external/L1_My_dude_128x64/meta.txt @@ -0,0 +1,32 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 19 +Active frames: 51 +Frames order: 0 1 2 3 4 5 6 0 1 2 7 8 9 10 11 12 7 8 9 13 14 15 14 13 14 15 7 8 9 16 17 18 13 14 19 20 21 22 23 24 21 25 26 27 28 29 30 31 32 33 32 34 35 36 35 34 37 38 39 40 41 42 43 44 45 46 17 47 48 7 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 41 +Y: 43 +Text: My dude +AlignH: Right +AlignV: Top +StartFrame: 50 +EndFrame: 50 + +Slot: 0 +X: 59 +Y: 43 +Text: My dude +AlignH: Left +AlignV: Top +StartFrame: 54 +EndFrame: 54 \ No newline at end of file diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_0.png b/assets/dolphin/external/L2_Dj_128x64/frame_0.png new file mode 100644 index 000000000000..95f72f901a5e Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_1.png b/assets/dolphin/external/L2_Dj_128x64/frame_1.png new file mode 100644 index 000000000000..32e13541d83e Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_10.png b/assets/dolphin/external/L2_Dj_128x64/frame_10.png new file mode 100644 index 000000000000..3cce11f998e7 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_11.png b/assets/dolphin/external/L2_Dj_128x64/frame_11.png new file mode 100644 index 000000000000..eca4a1296ee3 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_12.png b/assets/dolphin/external/L2_Dj_128x64/frame_12.png new file mode 100644 index 000000000000..5f92e47fdd7c Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_13.png b/assets/dolphin/external/L2_Dj_128x64/frame_13.png new file mode 100644 index 000000000000..1b1017ce259d Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_14.png b/assets/dolphin/external/L2_Dj_128x64/frame_14.png new file mode 100644 index 000000000000..2cf408c979c7 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_15.png b/assets/dolphin/external/L2_Dj_128x64/frame_15.png new file mode 100644 index 000000000000..9b796498c0c4 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_16.png b/assets/dolphin/external/L2_Dj_128x64/frame_16.png new file mode 100644 index 000000000000..c1545500cbc6 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_17.png b/assets/dolphin/external/L2_Dj_128x64/frame_17.png new file mode 100644 index 000000000000..80863f0b6935 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_18.png b/assets/dolphin/external/L2_Dj_128x64/frame_18.png new file mode 100644 index 000000000000..b4527bc83306 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_19.png b/assets/dolphin/external/L2_Dj_128x64/frame_19.png new file mode 100644 index 000000000000..f1531da4ef7b Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_2.png b/assets/dolphin/external/L2_Dj_128x64/frame_2.png new file mode 100644 index 000000000000..391cfe1e1035 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_20.png b/assets/dolphin/external/L2_Dj_128x64/frame_20.png new file mode 100644 index 000000000000..f63904f29b2f Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_21.png b/assets/dolphin/external/L2_Dj_128x64/frame_21.png new file mode 100644 index 000000000000..076448fa940b Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_22.png b/assets/dolphin/external/L2_Dj_128x64/frame_22.png new file mode 100644 index 000000000000..8651f12f8bd9 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_23.png b/assets/dolphin/external/L2_Dj_128x64/frame_23.png new file mode 100644 index 000000000000..d2d8e7e51446 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_24.png b/assets/dolphin/external/L2_Dj_128x64/frame_24.png new file mode 100644 index 000000000000..6080e7aa3f9a Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_25.png b/assets/dolphin/external/L2_Dj_128x64/frame_25.png new file mode 100644 index 000000000000..ec483addf8ce Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_26.png b/assets/dolphin/external/L2_Dj_128x64/frame_26.png new file mode 100644 index 000000000000..90fc3b1380cd Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_27.png b/assets/dolphin/external/L2_Dj_128x64/frame_27.png new file mode 100644 index 000000000000..39ddf46ab3bd Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_28.png b/assets/dolphin/external/L2_Dj_128x64/frame_28.png new file mode 100644 index 000000000000..f433c969c61b Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_29.png b/assets/dolphin/external/L2_Dj_128x64/frame_29.png new file mode 100644 index 000000000000..263ffe15a83a Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_3.png b/assets/dolphin/external/L2_Dj_128x64/frame_3.png new file mode 100644 index 000000000000..40d1314c95fb Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_30.png b/assets/dolphin/external/L2_Dj_128x64/frame_30.png new file mode 100644 index 000000000000..27c297e8d59c Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_31.png b/assets/dolphin/external/L2_Dj_128x64/frame_31.png new file mode 100644 index 000000000000..f2aefbfae2f1 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_32.png b/assets/dolphin/external/L2_Dj_128x64/frame_32.png new file mode 100644 index 000000000000..852c24233ffd Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_33.png b/assets/dolphin/external/L2_Dj_128x64/frame_33.png new file mode 100644 index 000000000000..665ac5eafd89 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_34.png b/assets/dolphin/external/L2_Dj_128x64/frame_34.png new file mode 100644 index 000000000000..81f133ac5592 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_35.png b/assets/dolphin/external/L2_Dj_128x64/frame_35.png new file mode 100644 index 000000000000..c828207d33ed Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_36.png b/assets/dolphin/external/L2_Dj_128x64/frame_36.png new file mode 100644 index 000000000000..fc923b40236d Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_4.png b/assets/dolphin/external/L2_Dj_128x64/frame_4.png new file mode 100644 index 000000000000..d372ff643b40 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_5.png b/assets/dolphin/external/L2_Dj_128x64/frame_5.png new file mode 100644 index 000000000000..5b52f95175b4 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_6.png b/assets/dolphin/external/L2_Dj_128x64/frame_6.png new file mode 100644 index 000000000000..8a1e84a11eeb Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_7.png b/assets/dolphin/external/L2_Dj_128x64/frame_7.png new file mode 100644 index 000000000000..1fddffaa4953 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_8.png b/assets/dolphin/external/L2_Dj_128x64/frame_8.png new file mode 100644 index 000000000000..14ef1aded643 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_9.png b/assets/dolphin/external/L2_Dj_128x64/frame_9.png new file mode 100644 index 000000000000..05de5d5c698a Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/meta.txt b/assets/dolphin/external/L2_Dj_128x64/meta.txt new file mode 100644 index 000000000000..2d0693ef2cf9 --- /dev/null +++ b/assets/dolphin/external/L2_Dj_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 15 +Active frames: 23 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 26 28 29 30 31 32 33 34 35 36 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 5 + +Bubble slots: 0 \ No newline at end of file diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index 42991523fbd1..4e3dbbf1105a 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -90,6 +90,20 @@ Min butthurt: 0 Max butthurt: 5 Min level: 1 Max level: 3 +Weight: 3 + +Name: L1_Kaiju_128x64 +Min butthurt: 0 +Max butthurt: 10 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L1_My_dude_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 1 +Max level: 3 Weight: 4 Name: L2_Wake_up_128x64 @@ -120,6 +134,13 @@ Min level: 2 Max level: 2 Weight: 3 +Name: L2_Dj_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 2 +Max level: 3 +Weight: 4 + Name: L3_Furippa3_128x64 Min butthurt: 0 Max butthurt: 6 diff --git a/assets/icons/About/Certification2_46x33.png b/assets/icons/About/Certification2_46x33.png new file mode 100644 index 000000000000..d421b8291495 Binary files /dev/null and b/assets/icons/About/Certification2_46x33.png differ diff --git a/assets/icons/About/Certification2_98x33.png b/assets/icons/About/Certification2_98x33.png deleted file mode 100644 index 49c5581c7523..000000000000 Binary files a/assets/icons/About/Certification2_98x33.png and /dev/null differ diff --git a/assets/icons/NFC/Reader_detect_43x40.png b/assets/icons/NFC/Reader_detect_43x40.png deleted file mode 100644 index d833a5277fcf..000000000000 Binary files a/assets/icons/NFC/Reader_detect_43x40.png and /dev/null differ diff --git a/assets/icons/NFC/Tap_reader_36x38.png b/assets/icons/NFC/Tap_reader_36x38.png deleted file mode 100644 index 4e0ba8f05921..000000000000 Binary files a/assets/icons/NFC/Tap_reader_36x38.png and /dev/null differ diff --git a/assets/icons/SubGhz/External_antenna_20x12.png b/assets/icons/SubGhz/External_antenna_20x12.png new file mode 100644 index 000000000000..940087071a1c Binary files /dev/null and b/assets/icons/SubGhz/External_antenna_20x12.png differ diff --git a/assets/icons/SubGhz/Internal_antenna_20x12.png b/assets/icons/SubGhz/Internal_antenna_20x12.png new file mode 100644 index 000000000000..a8a5be09fb87 Binary files /dev/null and b/assets/icons/SubGhz/Internal_antenna_20x12.png differ diff --git a/assets/icons/SubGhz/Scanning_123x52.png b/assets/icons/SubGhz/Scanning_123x52.png deleted file mode 100644 index ec785948d035..000000000000 Binary files a/assets/icons/SubGhz/Scanning_123x52.png and /dev/null differ diff --git a/assets/icons/SubGhz/Scanning_short_96x52.png b/assets/icons/SubGhz/Scanning_short_96x52.png new file mode 100644 index 000000000000..718d0e695a25 Binary files /dev/null and b/assets/icons/SubGhz/Scanning_short_96x52.png differ diff --git a/assets/protobuf b/assets/protobuf index a13c5ddd0397..08a907d95733 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit a13c5ddd0397511bd4c6de4afdd1031a5b6f5bca +Subproject commit 08a907d95733600becc41c0602ef5ee4c4baf782 diff --git a/assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt b/assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt index d11892372455..908889aecf6c 100644 --- a/assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt +++ b/assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt @@ -34,4 +34,8 @@ C1B74D7478053AE2 # default iCLASS RFIDeas 6B65797374726B72 +# CTF key 5C100DF7042EAE64 + +# iCopy-X DRM key (iCE product) +2020666666668888 diff --git a/assets/resources/badusb/Install_qFlipper_windows.txt b/assets/resources/badusb/Install_qFlipper_windows.txt new file mode 100644 index 000000000000..94282321512f --- /dev/null +++ b/assets/resources/badusb/Install_qFlipper_windows.txt @@ -0,0 +1,42 @@ +REM Written by @dexv +DELAY 2000 +GUI r +DELAY 500 +STRING powershell +ENTER +DELAY 1000 +STRING $url = "https://update.flipperzero.one/qFlipper/release/windows-amd64/portable" +ENTER +STRING $output = "$env:USERPROFILE\Documents\qFlipper.zip" +ENTER +STRING $destination = "$env:USERPROFILE\Documents\qFlipper" +ENTER +STRING $shortcutPath = "$env:USERPROFILE\Desktop\qFlipper.lnk" +ENTER +STRING $scriptPath = "$env:USERPROFILE\Documents\qFlipperInstall.ps1" +ENTER +STRING $driverPath = "$destination\STM32 Driver" +ENTER +STRING $installBat = "$driverPath\install.bat" +ENTER +STRING (New-Object System.Net.WebClient).DownloadFile($url, $output) +ENTER +STRING Expand-Archive -Path $output -DestinationPath $destination -Force +ENTER +STRING Set-Location -Path $destination +ENTER +STRING Start-Process -FilePath ".\qFlipper.exe" +ENTER +STRING Start-Process -Wait -FilePath "cmd.exe" -ArgumentList "/c $installBat" +ENTER +STRING $shell = New-Object -ComObject WScript.Shell +ENTER +STRING $shortcut = $shell.CreateShortcut($shortcutPath) +ENTER +STRING $shortcut.TargetPath = "$destination\qFlipper.exe" +ENTER +STRING $shortcut.Save() +ENTER +DELAY 500 +STRING "powershell -ExecutionPolicy Bypass -File $scriptPath" +ENTER diff --git a/assets/resources/badusb/assets/layouts/fr-FR-mac.kl b/assets/resources/badusb/assets/layouts/fr-FR-mac.kl new file mode 100644 index 000000000000..2887aae0fda0 Binary files /dev/null and b/assets/resources/badusb/assets/layouts/fr-FR-mac.kl differ diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 96a2a0f38ebf..6ec6fc82959b 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -297,8 +297,8 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 2320 634 837 637 838 637 838 640 835 642 832 1378 836 645 826 670 809 667 808 1406 806 672 803 674 802 1412 802 1412 800 676 801 675 802 1412 802 674 802 1413 801 1412 801 1413 802 1412 802 50937 2285 671 801 1411 802 51225 2280 696 775 1412 801 51212 2283 671 775 1412 802 -# Model: Daikin FTXM20M. # +# Model: Daikin FTXM20M. name: Off type: raw frequency: 38000 @@ -334,8 +334,8 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 503 365 500 364 501 366 499 365 500 364 502 25049 3535 1660 504 1228 503 390 474 391 473 393 471 1261 469 397 468 397 469 397 469 397 469 1264 468 398 468 1264 468 1264 468 398 468 1265 467 1265 467 1265 467 1265 467 1265 467 399 467 399 467 1266 466 399 467 400 466 400 466 400 466 423 443 423 443 401 465 423 442 424 442 424 442 1290 442 424 442 1290 442 424 442 424 441 424 442 1290 442 1291 441 425 441 424 442 425 441 425 441 1291 441 425 440 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 426 440 1292 440 1292 440 1292 440 426 440 426 440 1292 440 1293 439 1293 439 35480 3503 1696 467 1264 468 398 468 398 467 398 468 1265 467 398 467 399 467 399 466 399 467 1265 467 399 467 1266 466 1267 465 400 466 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 442 1290 442 424 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 425 441 425 441 1291 441 425 441 425 441 425 441 425 440 1292 440 425 441 425 440 426 440 426 440 426 440 426 440 426 440 426 440 426 440 426 439 427 439 426 440 426 440 1293 439 427 439 427 439 427 438 427 439 428 438 1294 438 428 437 428 438 1295 437 1319 413 453 413 35480 3503 1696 468 1265 467 398 468 398 468 398 468 1265 467 398 468 399 466 399 467 399 467 1266 466 399 466 1267 465 1290 442 401 465 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 441 1291 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 424 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 441 1292 440 426 440 426 440 1292 440 426 440 426 440 1293 439 426 440 426 440 1293 439 1293 439 1293 439 427 439 1294 438 427 438 427 439 427 438 428 438 428 438 428 438 428 438 453 413 429 437 453 413 1319 413 1320 412 1320 412 1320 412 454 412 1320 412 454 412 1321 411 1321 411 1321 411 1321 411 1321 411 455 410 455 411 455 411 455 410 456 410 456 410 456 410 481 384 481 385 482 383 482 383 483 358 507 359 1374 358 1374 358 508 358 508 358 508 358 509 357 509 357 535 331 535 331 535 330 535 330 536 330 1403 329 1403 329 563 302 564 301 564 302 564 301 565 301 591 274 619 246 593 273 620 245 620 245 621 245 673 189 -# Model: Mitsubishi SRK63HE. # +# Model: Mitsubishi SRK63HE. name: Off type: raw frequency: 38000 @@ -371,3 +371,213 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 3234 1525 463 333 462 1127 465 332 462 333 436 1153 518 307 488 1073 518 307 488 308 434 1131 459 1155 435 1156 434 362 433 1159 432 363 432 1159 432 1159 432 1159 433 363 432 363 432 363 433 363 432 1159 433 1159 432 363 432 1159 433 1159 432 363 432 363 432 1159 432 363 433 363 432 1159 432 363 432 363 432 1159 432 1159 432 363 432 1160 432 1160 431 1160 432 1160 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 364 431 364 431 364 431 364 431 1160 431 364 431 364 431 364 431 1160 432 1160 431 1160 432 364 431 1160 431 1160 431 1161 431 1161 430 364 431 364 431 364 431 1160 432 364 431 364 431 364 432 364 431 1161 431 1161 431 364 431 364 431 1161 430 364 432 364 431 1161 430 365 431 365 431 1161 430 1161 430 365 431 1161 430 1161 430 365 430 +# +# Model: Airwell Prime DCI Series +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3078 3852 2004 888 1054 1824 1045 894 1051 865 2062 861 1078 860 1080 865 1046 894 1015 1883 1945 899 1013 901 1013 901 1012 902 1012 900 1042 927 986 902 1012 927 987 901 1012 902 1012 927 986 903 1011 902 1012 931 1011 931 1011 904 1010 933 1009 928 985 928 986 1885 1944 927 3017 3943 1943 927 985 1915 984 929 985 929 1943 957 984 929 985 928 985 929 985 1886 1943 899 984 930 983 930 984 957 986 929 985 929 985 930 984 930 983 930 984 930 1013 930 984 959 983 931 982 931 983 930 984 930 984 930 984 960 1011 931 984 1918 1939 929 3016 3917 1940 930 982 1916 954 959 954 959 1913 957 955 933 981 959 928 1015 954 1916 1913 959 953 960 957 986 927 1015 928 1015 953 961 927 987 927 986 927 987 927 986 927 1016 927 987 927 1016 926 1044 928 987 926 1015 928 988 926 987 926 988 926 1946 1883 987 3974 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3060 3870 1026 888 1984 886 1026 888 1053 888 1026 887 1055 858 1055 858 1052 860 1024 891 1044 1854 1016 897 1975 1853 1975 925 1015 898 1015 898 1016 897 1016 898 1015 898 1016 898 1015 898 1015 898 1015 899 1015 899 1014 899 1014 899 1016 927 1014 900 1014 899 1014 1856 1974 926 3048 3883 1015 898 1975 897 1014 899 1015 899 1014 900 1014 899 1015 900 1013 900 1014 899 1014 1857 1014 900 1973 1855 1973 899 1012 901 1013 902 1011 926 987 927 987 926 987 927 987 955 987 926 988 926 987 927 1015 927 987 927 987 926 988 926 1016 927 1015 1884 1945 925 3020 3911 986 928 1946 925 986 927 986 928 986 928 986 927 987 927 987 928 986 928 986 1884 987 956 1946 1882 1946 925 986 928 986 927 986 928 985 928 986 928 986 928 986 928 985 928 985 929 959 954 984 930 984 987 931 955 959 956 958 955 959 1968 1891 980 3982 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3054 3879 1977 892 1020 1846 1083 838 1073 840 2031 859 1051 864 1047 864 1077 896 1014 927 986 928 986 928 986 1885 1943 927 985 929 1013 928 986 929 985 928 985 928 985 928 985 929 985 929 985 957 985 928 1015 928 1015 928 985 929 984 929 985 929 985 1886 1943 927 3017 3914 1943 928 984 1886 985 958 984 929 1972 957 984 958 984 929 984 930 984 929 984 930 984 930 983 1887 1942 929 983 930 984 930 984 930 983 959 984 930 984 930 983 930 984 930 984 930 984 930 983 931 983 931 983 959 984 931 983 931 983 1888 1941 930 3014 3943 1914 931 981 1915 955 959 955 959 1913 958 955 959 954 959 955 959 955 959 955 988 955 960 953 1917 1941 959 953 960 953 961 953 961 953 961 927 987 927 987 927 987 926 987 927 1017 926 988 926 988 925 988 925 988 926 988 925 1018 925 1972 1857 1014 3946 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3080 3850 2007 863 1049 1851 1048 888 1054 887 2012 886 1026 887 1025 888 1050 865 1045 1854 2029 871 1041 872 1040 873 1042 899 1043 899 1043 872 1015 898 1016 899 1041 872 1042 872 1015 926 1017 898 1016 927 1016 926 1016 899 1014 898 1015 926 1016 899 1015 898 1015 1856 1973 897 3048 3910 1947 898 1015 1884 1015 925 988 899 2003 953 988 900 1014 954 988 926 987 1857 1972 896 988 899 1014 900 1014 900 1013 927 987 929 1014 929 1013 926 987 927 986 927 987 927 987 927 987 927 986 927 987 927 986 985 958 928 986 927 986 927 1012 1860 1943 927 3018 3914 1943 955 986 1914 986 929 984 928 1944 955 957 930 984 957 985 956 958 1913 1915 956 957 957 956 957 931 1012 957 930 983 958 955 931 982 958 956 960 983 958 956 958 955 958 930 984 930 984 930 984 930 984 930 1013 930 984 930 985 928 1942 1887 983 3977 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3083 3873 2012 1843 2013 1873 1054 888 2011 887 1025 887 1025 887 1052 890 1048 1852 1017 896 1018 895 1018 896 1976 895 1017 898 1016 898 1015 898 1016 897 1016 897 1016 897 1017 897 1016 897 1017 900 1043 899 1014 897 1016 897 1016 898 1016 898 1015 898 1015 1857 1972 896 3048 3911 1947 1853 1975 1854 1015 898 1973 897 1016 927 1015 926 987 926 1016 1857 1014 926 987 899 1015 901 1970 926 987 927 987 926 988 955 988 1013 958 900 1014 926 987 900 1014 900 1014 900 1013 927 986 927 987 927 1016 955 987 927 987 955 987 1884 1946 928 3045 3912 1974 1883 1974 1883 987 927 1974 926 986 927 987 928 987 956 987 1942 986 956 986 928 986 928 1944 926 986 928 985 929 984 929 985 928 986 956 987 928 986 958 984 929 984 930 984 930 983 929 985 958 984 930 984 957 957 956 957 1887 1971 956 4003 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3108 3851 2062 1793 2006 1821 1103 839 2031 859 1085 829 1081 833 1079 836 1045 1911 1973 897 1015 898 1016 899 1041 871 1016 899 1014 898 1015 899 1015 899 1014 899 1041 872 1015 899 1041 872 1041 872 1015 899 1015 899 1041 873 1014 899 1041 873 1014 899 1014 1883 1975 900 3045 3886 1997 1856 1945 1857 1012 927 1945 900 1013 901 1012 901 1013 901 1012 1859 1999 901 1012 930 1012 903 1011 903 1010 903 1011 902 1012 960 1011 928 986 932 1010 903 1011 928 1015 928 985 929 985 928 1014 928 985 929 985 929 984 929 985 928 986 1915 1971 928 3017 3915 1942 1885 1943 1885 985 930 1971 929 984 930 983 930 984 930 984 1887 1942 929 983 960 983 931 982 931 983 932 981 958 985 958 956 958 984 959 954 931 983 932 981 959 955 932 982 959 954 960 982 961 983 933 955 988 955 985 929 1943 1915 958 4003 +# +# Model: Danby DAC060EB7WDB +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4402 4442 527 1629 529 549 529 1628 530 550 529 548 531 549 530 550 529 1630 528 549 530 549 529 549 530 1630 527 1629 529 1630 528 548 530 550 529 547 531 1628 530 1629 529 1628 530 1628 529 1628 530 1628 530 549 530 1628 530 1628 530 1627 531 1628 530 1627 531 1631 527 1629 529 1628 530 1627 531 1628 530 1628 530 1629 529 1627 531 1627 531 1628 529 1627 531 1627 531 1629 529 1627 530 549 529 549 530 549 530 1628 529 1627 530 5234 4401 4440 530 550 528 1628 529 549 530 1628 529 1629 528 1626 531 1628 529 550 529 1628 530 1627 531 1629 529 549 529 548 530 549 530 1628 529 1628 530 1627 530 547 532 547 532 547 531 548 531 548 530 548 531 1626 531 549 530 547 531 547 531 548 530 548 530 549 530 548 531 546 532 578 500 547 532 548 531 548 531 548 530 548 531 548 530 548 531 547 531 547 532 547 531 1627 531 1627 530 1626 532 547 531 547 532 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4454 4387 586 1573 585 494 584 1571 586 494 584 493 585 524 554 493 531 1626 586 1573 585 494 585 492 586 492 586 494 585 493 585 493 586 1571 586 493 585 1572 531 1627 530 549 584 494 586 1571 587 1572 529 1628 529 1628 530 1627 530 1626 532 1627 531 1626 532 1628 529 1627 531 1626 531 1627 531 1627 587 1569 532 1625 533 1626 532 1626 532 1627 531 1627 530 548 531 1626 532 1627 531 548 531 1627 531 548 531 546 532 547 531 5233 4401 4443 530 548 530 1627 530 547 531 1627 531 1626 531 1626 531 1627 530 549 530 548 530 1627 531 1627 531 1627 530 1627 531 1627 531 1626 531 548 530 1627 530 547 532 547 532 1627 531 1627 532 546 531 547 531 548 530 548 530 548 531 548 531 548 530 548 530 549 530 548 531 548 531 548 530 547 532 549 529 548 530 548 531 547 532 548 530 548 531 1627 530 548 530 548 531 1626 533 546 531 1627 530 1627 532 1626 530 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4492 4354 619 1537 619 459 621 1537 622 455 623 456 623 457 622 456 623 1535 622 1536 622 458 621 456 623 1536 621 1535 623 456 622 456 623 457 621 456 531 1627 621 1536 622 456 622 457 622 455 623 458 621 458 621 1537 621 1536 620 1539 618 1538 531 1628 531 1627 530 1628 530 1628 530 1627 531 1628 530 1628 530 1628 531 1627 530 1629 529 1628 529 1628 530 549 530 1627 531 1628 530 1628 530 1627 586 494 530 1627 530 549 530 5232 4400 4443 586 492 587 1571 587 493 585 1572 530 1628 586 1572 586 1572 586 492 587 493 586 1572 586 1571 586 492 587 493 585 1572 531 1627 585 1573 585 1572 585 492 586 494 585 1572 586 1571 531 1627 531 1628 530 1627 587 491 531 548 587 492 530 548 530 547 532 548 531 547 532 547 531 548 531 547 532 547 531 548 588 491 530 547 589 490 531 547 532 1626 532 548 531 548 531 547 532 547 532 1627 531 548 531 1626 532 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4401 4441 528 1629 529 550 528 1628 529 551 528 550 528 551 527 551 528 1629 529 1629 529 550 529 1630 528 551 528 549 530 550 529 551 528 549 529 550 529 1629 529 1628 530 549 530 1629 529 550 529 1628 529 1629 529 1631 527 1628 530 1628 529 1629 528 1628 530 1629 529 1629 529 1629 529 1629 528 1629 529 1629 529 1630 528 1629 529 1629 529 1628 529 1629 528 551 528 1629 529 550 529 550 530 548 529 1631 527 551 528 1629 529 5235 4402 4439 530 550 528 1629 529 549 530 1628 529 1629 529 1628 530 1629 529 549 530 550 529 1628 530 552 526 1628 529 1628 530 1628 530 1627 531 1628 529 1629 528 551 528 550 529 1628 530 550 528 1628 529 549 529 550 528 550 529 549 530 548 530 551 528 550 528 578 500 550 529 550 529 551 527 549 530 549 529 549 529 550 528 548 530 550 528 549 529 1629 528 550 529 1630 528 1628 530 1628 530 549 530 1628 529 549 529 +# +# Model: Carrier 42QHB12D8S +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4467 4363 599 1556 599 478 599 1556 599 1558 597 505 572 505 572 1583 571 505 572 506 624 1531 653 423 651 426 624 1530 622 1532 572 505 572 1583 571 506 571 1584 571 1583 571 1584 571 1584 571 507 570 1585 570 1585 570 1585 570 508 569 508 569 508 592 485 570 1585 593 484 570 508 569 1586 569 1586 592 1563 593 485 569 508 592 485 592 485 570 508 569 508 570 508 569 508 569 1586 569 1586 569 1586 569 1586 569 1586 569 5187 4461 4371 568 1586 569 508 593 1562 593 1563 569 509 591 486 591 1563 569 508 592 486 592 1563 592 485 592 486 591 1563 592 1563 591 486 592 1564 591 486 592 1563 593 1563 591 1563 592 1564 592 485 592 1563 592 1563 592 1564 591 486 591 486 592 485 592 485 592 1563 592 486 591 486 592 1564 591 1563 592 1563 592 486 592 485 592 486 591 486 592 485 592 486 591 486 591 486 591 1563 592 1563 593 1563 591 1564 591 1563 591 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4440 4390 571 1583 572 505 572 1583 596 1559 571 505 572 505 572 1583 596 481 596 481 597 1559 625 451 654 423 625 1529 595 1560 596 480 572 1583 572 505 596 482 594 483 571 1583 571 1584 571 1584 571 1584 571 1585 569 1585 593 1562 594 1561 593 485 569 508 593 484 592 485 593 484 592 485 592 1563 569 508 592 1562 592 485 570 1586 592 485 592 486 569 1586 592 485 569 1585 570 508 569 1586 569 508 569 1585 570 1586 569 5186 4438 4393 569 1585 593 485 592 1562 569 1585 569 508 569 508 569 1585 591 486 593 484 591 1563 591 486 591 486 590 1564 569 1585 569 508 593 1562 592 486 592 485 569 508 591 1563 592 1562 569 1586 569 1586 591 1563 592 1563 590 1564 591 1563 592 486 569 508 569 508 569 508 569 508 592 486 592 1563 568 508 593 1563 591 486 592 1563 569 508 592 486 592 1563 591 486 592 1563 592 485 592 1563 592 486 592 1563 591 1563 592 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4465 4365 598 1557 598 479 598 1557 598 1557 598 479 598 479 598 1556 599 479 598 507 593 1562 652 424 624 452 595 1559 595 1560 594 483 571 1584 594 1561 593 484 593 1562 593 1562 593 1562 593 1562 593 1562 593 1563 592 485 592 1562 593 484 593 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1562 592 1563 592 1562 593 1562 592 1563 592 1563 592 1562 592 1563 592 5165 4439 4394 569 1586 592 485 569 1586 569 1586 569 508 569 508 593 1562 592 485 570 508 592 1563 592 485 594 484 569 1586 569 1586 569 508 569 1586 592 1563 569 508 592 1563 569 1586 592 1563 592 1563 569 1586 569 1585 569 508 569 1586 569 508 569 508 569 508 569 508 592 485 569 508 592 485 569 508 593 485 569 508 569 508 569 508 593 484 570 508 569 1586 593 1562 570 1586 569 1586 592 1563 569 1586 592 1563 592 1563 592 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4465 4364 599 1556 599 479 598 1556 599 1556 599 478 599 479 598 1559 596 505 572 506 571 1584 653 424 624 452 571 1583 572 1583 571 506 571 1583 571 1584 571 506 571 1584 571 1585 570 1585 570 1585 592 1563 592 1562 593 485 592 1563 592 485 593 485 592 485 592 485 592 485 592 485 593 485 592 1563 592 485 592 1563 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 5164 4460 4372 592 1563 592 485 593 1563 592 1563 592 486 591 486 591 1563 592 485 592 485 592 1563 592 485 592 485 592 1563 592 1563 592 485 592 1563 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 1563 592 1564 591 486 591 1563 591 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 486 591 485 592 486 591 485 592 1563 592 485 592 1563 592 485 592 1564 591 1563 592 1563 592 1563 592 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4440 4391 572 1583 572 505 572 1584 571 1583 572 505 572 505 572 1583 596 481 573 505 596 1559 626 451 655 422 625 1529 596 1559 572 505 572 1583 572 1582 572 505 572 1583 572 1583 571 1584 571 1584 571 1585 570 1584 594 484 593 1562 592 485 592 485 592 485 593 485 593 484 593 484 593 1562 593 484 593 1562 593 1563 592 1562 592 1563 592 485 593 484 592 485 593 1562 593 484 593 485 592 485 592 485 592 1562 593 1563 592 5163 4462 4370 592 1563 592 485 592 1563 592 1563 592 485 592 485 592 1563 592 485 592 485 593 1562 593 485 593 485 592 1563 592 1563 592 485 592 1562 593 1563 592 484 593 1563 592 1563 592 1563 592 1563 592 1563 592 1563 592 485 592 1563 592 485 592 485 592 485 592 485 592 485 593 485 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 485 592 485 592 485 592 1563 591 485 592 486 591 485 592 485 593 1563 591 1563 593 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4467 4390 571 1583 572 505 595 1560 572 1583 572 505 572 505 596 1559 596 482 596 481 597 1559 626 451 655 422 625 1529 596 1559 596 481 572 1582 573 1583 571 505 572 1583 595 1560 594 1561 592 1562 594 1561 593 1562 593 484 593 1563 592 485 592 485 592 485 592 485 593 484 593 485 592 485 592 1562 593 485 592 1563 592 1562 593 1562 594 483 593 485 593 1562 592 485 593 1561 593 484 593 484 593 484 593 1562 593 1562 592 5163 4462 4370 592 1563 593 484 592 1563 592 1563 592 485 592 485 593 1562 593 484 593 485 592 1562 593 484 593 485 592 1562 593 1562 593 485 592 1563 592 1563 592 485 592 1563 592 1562 593 1562 593 1563 592 1563 592 1562 592 485 593 1562 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 1563 592 1563 593 485 592 485 592 1563 592 485 592 1563 591 485 593 485 592 485 592 1563 592 1563 591 +# +# Model: Mitsubishi MSZ-AP25VGK +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3531 1667 500 1225 499 1225 499 376 499 377 498 377 498 1224 500 377 498 377 498 1224 500 1225 499 377 527 1195 557 318 556 318 555 1167 530 1194 529 374 499 1224 499 1225 497 377 497 378 497 1228 496 379 496 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 380 495 9028 3526 1672 495 1229 495 1229 495 380 495 380 495 380 495 1230 494 380 495 380 495 1229 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 1229 495 380 495 380 495 1229 495 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 380 495 381 494 381 494 381 494 381 494 381 494 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 1231 493 382 493 1231 493 382 493 383 492 382 493 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3561 1666 500 1195 529 1193 531 374 501 375 500 375 500 1196 529 374 501 375 500 1223 501 1223 501 375 529 1194 558 318 557 318 555 1168 530 1193 530 345 529 1194 529 1196 527 348 526 350 525 1200 524 352 522 353 522 1228 496 379 496 379 496 379 496 379 496 379 496 379 497 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 9028 3529 1670 496 1228 496 1228 496 379 496 379 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 1228 496 379 496 379 496 1228 496 379 496 379 496 1228 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 380 495 379 496 379 496 379 496 379 496 380 495 379 496 380 495 379 496 1229 495 380 495 380 495 379 496 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 381 494 380 495 380 495 380 495 380 495 1230 494 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3534 1637 530 1192 533 1195 529 375 500 375 500 375 500 1195 530 375 501 375 500 1224 500 1224 501 376 528 1195 557 319 556 318 555 1168 530 1193 530 345 530 1194 529 1196 527 348 526 350 525 1201 523 353 522 378 497 1228 496 379 497 379 496 379 497 379 496 379 497 379 497 379 497 379 496 379 496 379 496 379 496 379 497 379 497 379 496 379 496 9030 3530 1671 496 1229 496 1229 495 379 496 379 496 379 497 1229 496 379 496 379 497 1229 496 1229 495 380 496 1229 495 379 497 379 496 1229 496 1229 495 379 497 1229 495 1228 497 379 496 380 496 1229 496 379 497 379 496 1229 496 379 496 380 496 380 495 380 496 379 496 380 496 380 495 380 496 380 496 380 496 379 496 380 496 380 495 380 496 380 495 380 496 380 495 380 496 380 495 380 495 1229 496 380 495 380 495 380 495 380 496 380 495 1229 496 1229 496 380 495 380 496 380 495 380 496 380 496 380 495 380 495 380 496 380 496 380 495 380 496 380 495 1229 495 1230 495 380 495 1229 496 1229 496 380 495 381 495 380 495 380 495 380 496 380 496 380 495 380 496 1229 496 380 495 1230 495 380 495 380 495 1230 495 380 495 1230 495 1230 495 380 495 380 496 380 495 380 495 380 495 380 496 380 496 380 495 380 496 380 495 380 495 380 496 380 495 381 495 380 495 380 495 380 495 381 495 380 495 381 495 380 495 381 494 381 495 381 495 380 495 1230 495 381 495 380 495 381 494 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 494 381 495 381 494 381 494 381 494 381 495 1230 495 381 495 1230 495 1230 495 381 494 1230 494 381 495 381 494 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3534 1667 500 1224 501 1224 501 376 500 375 500 376 500 1224 501 376 500 376 499 1224 501 1225 500 376 500 1225 556 320 556 318 555 1167 530 1194 530 345 530 1195 529 1196 528 348 527 377 498 1227 498 378 497 379 496 1229 496 379 496 379 497 379 497 379 497 379 497 380 496 379 497 379 496 379 497 380 496 380 496 380 496 380 496 379 497 379 497 9033 3530 1672 496 1229 496 1229 496 380 496 380 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1230 495 1229 496 380 495 380 496 1229 496 380 496 380 496 1229 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 495 380 496 380 496 380 496 380 496 380 496 380 496 380 496 1230 495 380 496 380 495 380 496 381 495 380 495 1230 495 1230 495 380 496 380 496 380 496 1230 495 1230 495 1230 495 380 496 380 496 380 496 380 496 380 496 381 495 1230 495 1230 495 381 495 1230 495 1230 495 380 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 1230 495 381 495 1230 495 381 495 380 496 1230 495 381 495 1230 495 1230 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 1231 494 381 495 381 495 381 495 381 495 381 495 381 494 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 382 494 382 494 1231 494 381 495 1231 494 1231 494 381 495 382 494 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3539 1670 501 1226 502 1226 501 376 501 377 500 376 501 1226 501 376 499 378 500 1226 501 1226 501 377 528 1199 557 320 557 318 529 1197 531 1195 531 346 530 1196 530 1198 528 349 527 352 524 1229 498 379 498 379 498 1230 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9045 3536 1674 496 1231 497 1231 497 380 497 380 497 380 497 1231 496 380 497 380 497 1231 497 1231 496 380 497 1231 496 380 497 380 497 1231 496 1231 497 380 497 1231 496 1231 496 380 497 380 497 1231 496 381 496 380 497 1231 497 381 496 380 497 380 497 380 497 380 497 381 496 381 496 381 496 380 497 380 497 381 496 381 496 381 496 381 496 380 497 381 496 381 496 381 496 381 496 380 497 1231 496 381 496 381 496 380 497 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 1232 495 1232 495 1232 496 1232 495 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 1232 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 381 496 1232 495 381 496 1232 495 381 496 1232 495 1232 495 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1232 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1233 495 1233 494 1233 495 382 495 382 495 1233 495 1233 494 382 495 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3539 1637 533 1225 502 1193 534 375 501 376 501 376 500 1226 501 376 501 376 500 1226 500 1227 501 376 529 1197 558 320 557 319 555 1169 531 1195 531 346 529 1196 530 1198 528 349 526 352 524 1229 497 379 497 379 497 1230 497 380 497 380 497 379 498 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9042 3535 1674 496 1230 497 1230 497 380 497 380 497 380 497 1230 497 380 497 380 496 1230 497 1230 497 380 497 1230 497 380 497 380 497 1231 496 1230 497 380 497 1231 496 1231 496 380 496 380 497 1231 496 380 497 380 496 1231 496 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 496 380 497 380 497 380 497 380 497 380 496 381 496 380 497 380 497 380 497 381 496 1231 496 381 496 380 497 380 497 381 496 381 496 1231 496 381 496 381 496 380 497 381 495 1231 496 1231 496 1231 496 380 497 381 496 381 496 380 496 381 496 381 496 381 496 381 496 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1231 496 381 496 381 496 1232 495 381 495 1232 495 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 495 381 496 381 496 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 381 495 381 496 381 495 382 495 381 496 382 495 381 495 382 495 381 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 381 495 1232 495 1232 495 1232 495 1232 495 1232 495 382 495 382 495 382 495 +# +# Model: Hitachi RAK-50PEB +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30683 50966 3411 1600 493 1186 493 347 492 348 491 348 491 349 490 349 490 350 489 351 488 351 488 352 487 352 488 351 488 1192 487 352 487 351 488 352 487 352 487 352 488 352 488 351 488 1192 487 1191 488 352 487 352 487 352 487 352 487 352 487 352 487 352 487 352 487 1192 487 352 487 1192 487 1192 487 1192 487 1192 488 1192 487 1192 488 352 487 1192 487 1192 487 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 352 487 352 488 352 487 1192 487 352 487 352 487 352 487 352 487 1192 487 352 487 353 486 1192 487 353 486 353 486 353 486 353 486 1193 486 353 487 353 486 353 486 353 486 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 353 487 353 486 353 486 353 486 1193 486 353 486 353 486 1193 486 353 487 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 354 485 353 486 354 485 353 486 354 486 353 486 353 486 354 486 353 486 353 487 1193 486 1194 485 353 487 353 486 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 355 484 355 484 355 484 354 485 354 485 355 484 355 484 355 484 355 484 354 486 355 484 378 461 356 484 378 461 355 485 355 484 355 484 355 485 355 484 378 461 378 461 355 484 356 483 355 484 378 461 378 462 355 485 378 461 378 462 378 461 379 461 356 483 378 461 1195 484 378 461 379 461 356 484 378 461 379 460 379 461 378 461 378 462 378 461 379 461 378 461 378 461 379 460 379 460 379 461 378 461 378 461 378 461 378 461 378 461 379 460 379 460 379 460 379 461 379 460 1219 460 1219 460 379 461 1219 460 379 461 1219 460 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30684 50965 3412 1599 494 1185 494 346 493 346 493 347 492 348 491 349 490 349 490 350 489 351 489 350 489 350 489 351 489 1191 488 351 488 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 351 488 351 488 351 488 351 488 351 488 351 489 351 488 351 488 1191 488 351 489 1191 488 1191 488 1191 488 1191 488 1191 488 1191 488 351 488 1191 488 1192 487 351 488 352 487 351 488 351 488 352 487 352 487 352 487 352 488 1192 487 1192 487 1216 463 1192 487 1192 488 1192 487 1193 486 1192 488 352 487 352 487 352 487 1193 486 376 463 376 463 376 464 352 487 1216 463 376 463 376 463 1216 464 376 463 376 463 376 463 1216 463 376 463 376 463 353 486 353 487 376 463 376 463 376 463 1216 463 376 463 1216 463 376 464 376 463 376 463 376 464 376 463 376 463 376 463 376 463 1216 463 376 463 1216 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 464 376 463 1216 463 1217 463 376 463 377 462 377 462 377 463 376 463 376 463 377 462 376 463 377 462 377 462 377 463 376 463 377 462 377 462 1217 462 1216 463 377 463 377 462 377 462 377 462 377 463 376 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 1217 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 1217 462 377 462 377 462 377 462 377 462 378 461 377 462 377 462 378 462 377 462 377 462 377 463 377 462 378 461 378 462 377 462 378 461 377 462 378 461 378 461 378 461 378 462 377 462 378 462 1217 462 1218 461 1218 461 378 461 378 461 1218 462 377 462 378 462 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30747 50897 3484 1554 543 1137 542 310 529 309 530 309 530 309 530 310 529 309 530 310 529 309 530 309 530 309 530 309 530 1138 541 309 531 309 530 309 530 309 530 309 530 309 530 309 530 1138 541 1138 541 310 529 309 530 309 531 309 530 309 530 309 530 309 530 310 529 1139 541 309 530 1138 541 1138 541 1138 541 1138 542 1138 541 1138 541 310 529 1138 541 1138 541 309 530 309 530 309 531 310 529 309 530 309 530 309 530 309 530 1139 541 1139 540 1139 541 1138 541 1139 540 1139 540 1138 541 1139 540 310 529 310 529 309 530 1139 541 310 529 309 530 309 530 309 530 1139 540 309 530 309 530 1139 541 309 530 309 530 309 530 1139 540 309 531 309 530 1139 541 309 530 309 530 309 530 309 530 309 530 309 530 1139 540 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 1139 540 310 529 309 530 309 530 309 530 309 530 309 531 310 529 310 529 309 531 310 529 1140 540 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 310 529 1140 539 1140 539 309 530 309 530 309 530 309 530 310 529 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 1140 540 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 530 310 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 530 309 530 1141 538 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 530 309 530 310 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 1142 537 309 530 1142 538 309 530 1141 538 309 530 309 530 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30694 50951 3483 1555 542 1137 542 308 531 309 530 308 531 308 531 308 531 308 531 308 531 308 531 308 532 308 531 308 532 1139 541 308 531 308 531 308 531 308 531 308 531 309 531 308 531 1139 541 1139 540 308 531 308 531 308 531 308 531 309 530 308 531 308 531 308 532 1139 541 308 532 1139 540 1139 541 1139 540 1139 540 1139 540 1139 541 309 530 1139 540 1139 540 308 531 308 531 308 531 308 532 308 531 308 531 308 531 308 531 1140 540 1139 541 1139 540 1139 540 1139 540 1139 541 1139 540 1139 540 308 531 308 531 308 531 1140 540 309 530 308 531 308 532 308 531 1140 540 308 531 308 532 1140 539 308 531 308 531 308 531 308 532 308 531 308 531 1140 540 308 531 308 531 308 531 308 531 308 532 308 531 1140 540 308 531 308 531 308 531 308 531 308 531 308 531 1140 540 1140 540 1140 539 308 531 1140 539 308 531 308 531 308 532 308 531 306 533 308 531 306 533 308 531 307 533 308 531 1141 539 308 532 308 531 308 531 306 534 306 533 306 534 306 533 306 533 306 533 306 533 306 534 307 532 307 533 306 533 308 532 1141 539 1141 539 308 531 308 532 308 531 307 533 307 481 352 538 307 533 307 532 307 533 307 481 352 539 307 532 307 533 306 482 352 487 352 537 306 534 307 482 352 538 307 482 352 487 1192 539 309 530 307 531 306 483 352 487 352 538 307 482 352 538 306 483 352 487 352 487 352 487 353 486 352 488 353 486 352 487 352 487 353 487 353 486 353 486 353 487 352 487 353 486 353 486 353 486 353 486 353 487 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 487 353 486 353 487 352 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 487 353 486 353 487 353 486 1193 537 306 483 353 486 353 487 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 486 353 486 353 486 1194 485 353 487 1193 538 1142 486 1193 486 353 486 353 486 353 568 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30703 50953 3432 1606 490 1189 490 349 490 349 490 349 490 350 489 350 489 350 489 350 490 350 489 350 489 350 490 350 489 1190 490 350 489 350 489 350 489 350 489 350 490 350 489 350 490 1190 490 1190 489 350 489 350 489 350 489 350 490 350 490 350 489 350 490 350 490 1190 489 350 490 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 350 490 1190 489 1190 489 350 490 350 489 350 490 350 489 350 489 350 490 350 489 350 490 1190 489 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 1191 489 350 489 350 489 350 490 1190 490 350 489 350 490 350 489 350 490 1190 489 350 490 350 489 1190 490 350 489 350 490 350 490 350 489 350 489 350 489 1191 489 350 489 350 490 350 489 350 489 1191 489 1190 489 350 489 350 490 350 489 350 490 350 489 351 489 350 489 350 489 350 489 350 490 350 489 350 489 1191 489 350 489 351 489 351 488 351 489 351 488 351 489 350 489 351 489 351 489 1191 488 351 488 351 488 351 489 350 489 351 488 350 490 350 489 351 488 351 488 351 489 350 489 350 489 351 489 351 489 350 489 1191 488 1191 489 350 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 489 351 489 350 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 488 351 489 351 489 1191 488 351 489 351 488 351 489 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 489 351 489 351 488 351 489 351 488 351 488 351 489 351 488 1192 488 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1191 488 352 488 351 488 352 488 351 489 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30675 50953 3432 1606 490 1190 489 350 489 350 489 350 489 350 489 351 488 350 489 351 489 351 488 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 351 488 351 489 351 488 1191 489 1191 489 351 488 351 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 1191 489 1191 488 1191 488 1191 488 1191 488 1191 488 351 489 1191 488 1191 489 351 488 351 489 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 1191 488 1191 489 1191 488 1191 488 1191 489 1191 488 351 488 351 489 351 488 1191 488 351 489 351 488 351 488 351 489 1191 488 351 489 351 488 1191 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 1191 488 1191 488 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1192 488 1191 488 351 489 1191 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 488 351 488 351 488 1192 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 352 487 352 488 351 488 352 488 351 488 351 488 351 488 1192 488 1192 487 352 488 352 487 352 487 352 488 352 487 352 488 351 488 352 488 352 488 352 487 352 488 351 488 351 488 352 488 352 487 352 488 352 487 352 488 352 488 352 488 352 487 1192 487 352 487 352 488 352 488 352 488 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 1193 486 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 488 352 487 352 488 352 487 353 486 353 487 352 487 352 488 352 488 352 487 352 487 352 487 352 487 353 487 352 488 352 488 352 487 1193 486 1193 486 1193 486 1193 487 353 487 352 487 353 486 +# +# Model: LG PC07SQR +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3169 9836 535 1553 511 551 491 543 491 544 490 1586 490 532 510 530 511 543 491 1579 489 1577 490 543 490 543 491 543 491 544 490 544 489 544 490 543 491 544 490 550 492 544 490 543 491 1586 489 542 492 1583 492 552 490 532 510 537 512 1585 491 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3385 9888 512 1544 512 544 489 544 489 544 490 1586 489 545 489 544 489 545 489 531 511 541 508 533 508 542 507 544 489 546 487 544 490 1587 489 1573 510 543 490 543 491 1578 490 545 489 1574 509 545 488 1587 489 1573 510 1586 490 1579 489 1568 507 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3288 9816 598 1504 574 472 569 463 571 463 571 1515 568 472 569 461 573 471 571 459 590 462 571 463 571 447 594 462 572 462 571 464 570 459 590 463 570 464 570 1496 571 1497 570 463 571 1514 569 464 570 1504 572 1514 569 471 571 473 568 462 572 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3205 9865 616 1473 536 518 516 517 517 517 517 1575 517 527 568 464 515 517 571 463 570 462 572 462 572 469 573 461 573 463 570 462 572 469 573 1504 572 451 590 451 591 472 569 463 571 1494 573 461 573 1497 570 1505 570 1503 572 471 571 1491 592 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3363 9846 596 1495 514 518 515 517 517 515 518 1552 516 502 539 517 517 516 518 516 518 517 517 517 571 462 517 518 516 1554 568 461 589 462 572 1505 571 1507 568 1514 514 1568 570 461 573 1504 572 472 570 1507 592 1490 593 460 574 462 572 447 594 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3204 9889 537 1587 491 529 512 544 489 545 489 1573 510 530 511 543 491 552 490 538 511 543 491 543 491 532 509 543 491 1587 489 537 512 543 491 1577 490 543 491 543 491 544 489 543 491 1586 489 544 490 1587 489 539 510 543 491 543 491 1586 490 diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index b45171cb1425..c4b6f0c42b2c 100644 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1675,3 +1675,45 @@ type: parsed protocol: NEC address: 04 00 00 00 command: 03 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3462 1592 490 332 513 1200 489 331 514 1201 489 355 490 1201 489 356 512 1178 489 356 512 1178 512 334 487 1202 488 1202 488 357 512 1178 512 334 486 1203 487 1202 488 1203 487 1204 486 383 461 1228 488 357 488 357 487 357 487 1203 486 1204 486 359 485 1205 485 360 485 361 484 360 485 360 485 361 484 361 484 361 484 1206 484 360 484 361 484 361 484 1206 484 361 484 361 484 361 484 1206 484 361 484 1206 484 361 484 71543 3434 1620 486 359 485 1205 485 360 485 1206 484 360 485 1206 484 360 485 1206 484 360 485 1206 484 360 485 1205 485 1206 484 360 485 1206 484 360 485 1206 484 1206 484 1206 484 1206 484 361 484 1206 484 360 485 360 485 361 484 1206 484 1206 484 360 484 1206 484 360 485 361 484 361 484 360 485 361 484 361 484 361 484 1206 484 361 484 361 484 361 484 1206 484 361 484 361 484 361 484 1207 483 361 484 1206 484 361 484 71543 3435 1619 486 358 486 1204 486 359 486 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 484 1205 485 1205 485 360 485 1205 485 360 485 1205 485 1205 485 1205 485 1206 484 360 485 1205 485 360 485 360 485 360 485 1205 485 1206 484 360 485 1206 484 360 485 360 485 360 485 360 485 360 485 360 485 360 485 1206 484 360 485 360 485 360 485 1206 484 360 485 360 485 360 485 1205 485 360 485 1206 484 360 485 71542 3436 1619 486 358 487 1204 486 359 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 1205 485 360 485 1205 485 360 485 1206 484 1206 484 1206 484 1206 484 360 485 1206 484 360 485 360 485 361 484 1206 484 1206 484 361 484 1206 484 361 484 361 484 361 484 361 484 361 484 361 484 360 485 1206 484 361 484 361 484 361 484 1206 484 361 484 361 484 360 485 1206 484 361 484 1206 484 361 484 71542 3437 1618 487 358 486 1204 486 359 486 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 1206 484 360 485 1205 485 360 485 1206 484 1205 485 1206 484 1205 485 360 485 1205 485 360 485 360 485 360 485 1205 485 1205 485 360 485 1205 485 360 485 360 485 360 485 360 485 360 485 360 485 360 485 1205 485 360 485 360 485 360 485 1205 485 360 485 360 485 360 485 1205 485 360 485 1205 485 360 485 +# Model: TCL +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3977 3993 494 1994 495 1995 496 1997 494 1996 495 1004 496 1004 496 1995 496 1004 496 1997 494 1005 495 1995 495 1007 493 1006 494 1006 494 1005 495 1007 493 1997 494 1995 496 1004 496 1995 496 1005 495 1995 496 1003 497 1995 496 8467 3980 3993 494 1994 495 1996 495 1997 494 1995 496 1004 496 1006 494 1995 496 1004 496 1996 495 1004 496 1996 495 1005 495 1005 495 1004 496 1005 495 1005 495 1996 495 1995 496 1005 495 1996 495 1004 496 1995 496 1006 569 1920 571 8393 3980 3993 571 1918 572 1922 569 1920 571 1920 571 929 572 929 571 1920 571 929 571 1920 571 929 571 1921 570 930 570 929 571 929 571 929 571 928 572 1920 571 1921 570 930 571 1920 572 930 571 1921 571 929 571 1923 569 8396 3980 3994 570 1921 569 1923 569 1921 571 1920 572 929 572 930 571 1921 570 930 571 1922 570 930 571 1921 570 930 570 930 571 929 571 929 571 929 572 1922 570 1921 570 931 570 1922 570 930 571 1922 569 931 570 1921 570 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3979 3995 493 1994 496 1997 495 1999 492 1999 492 1006 495 1008 493 1998 493 1006 494 1997 495 1999 493 1997 494 1998 493 1006 495 1007 493 1005 495 1006 495 2025 467 1998 494 1006 494 1996 495 1006 494 1005 495 1006 494 1007 493 8468 3979 3995 492 1995 495 1999 492 1997 494 1997 494 1007 493 1006 494 1997 494 1006 494 1997 494 1996 571 1923 569 1921 570 930 570 929 571 931 569 931 575 1916 570 1920 571 930 570 1922 570 930 571 930 570 930 576 924 576 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3951 3994 494 1997 493 1998 494 1998 493 1998 493 1005 496 1005 496 1996 495 1005 495 1997 495 1996 495 1996 495 1006 494 1005 495 1006 494 1005 495 1006 494 1996 495 1998 493 1005 495 1997 494 1008 492 1006 494 1006 494 1997 494 8471 3977 3996 493 1996 493 1997 494 1998 493 1997 494 1006 494 1007 493 1997 494 1009 492 1996 495 1996 495 1997 494 1006 494 1006 494 1006 494 1006 494 1006 494 1997 493 1997 494 1006 494 1996 494 1005 495 1004 495 1006 494 1996 494 +# +name: Ch_next +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3978 3994 494 1995 495 1996 495 1996 495 1996 495 1006 494 1004 497 1997 494 1005 495 1997 520 1970 577 923 578 1914 577 924 576 924 576 925 575 924 576 1914 577 1915 576 924 576 1914 577 924 576 923 577 1915 576 926 574 8388 3978 3993 576 1913 576 1915 576 1915 576 1917 574 923 577 923 577 1943 548 925 575 1916 576 1915 575 924 576 1915 576 925 575 927 573 925 575 926 574 1916 574 1918 573 927 573 1918 573 928 572 927 573 1918 573 926 574 8389 4006 3966 572 1918 571 1919 572 1918 573 1920 570 929 571 929 571 1922 569 928 571 1920 571 1921 570 928 572 1919 572 929 571 929 571 929 571 929 571 1921 570 1921 521 980 569 1921 521 981 519 979 521 1971 520 979 521 +# +name: Ch_prev +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3979 3994 494 1995 495 1997 494 1996 495 1998 493 1005 495 1006 494 1997 494 1005 495 1996 495 1995 496 1005 495 1005 495 1006 494 1005 495 1004 496 1005 495 1997 494 1997 494 1004 496 1996 495 1005 495 1005 495 1997 494 1996 495 8467 3976 3991 496 1995 495 1996 494 1994 496 1996 494 1005 495 1005 495 1996 495 1005 495 1995 495 1995 496 1006 494 1005 495 1006 494 1005 495 1004 496 1006 494 1994 496 1996 494 1005 495 1995 495 1004 496 1004 496 1995 495 1996 494 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3981 3992 495 1994 495 1995 496 1996 494 1996 495 1005 495 1006 494 1995 495 1997 494 1996 495 1996 494 1997 494 1996 495 1006 494 1005 495 1004 496 1005 495 1995 496 1994 496 1005 495 1004 496 1005 495 1006 494 1004 496 1006 494 8466 3978 3991 495 1994 495 1997 493 1994 496 1995 495 1004 496 1004 496 1996 494 1997 493 1996 494 1995 495 1995 495 1997 493 1004 495 1004 495 1006 494 1005 494 1998 491 1996 494 1006 494 1004 496 1006 494 1006 493 1005 495 1005 571 diff --git a/assets/unit_tests/infrared/test_rca.irtest b/assets/unit_tests/infrared/test_rca.irtest new file mode 100644 index 000000000000..bfe34e8e41ea --- /dev/null +++ b/assets/unit_tests/infrared/test_rca.irtest @@ -0,0 +1,105 @@ +Filetype: IR tests file +Version: 1 +# +name: decoder_input1 +type: raw +data: 1000000 3994 3969 552 1945 551 1945 552 1945 551 1945 552 946 551 947 550 1947 548 951 546 1953 542 979 518 1979 517 981 492 1006 491 1006 492 1006 492 1006 492 2005 492 2005 492 1006 492 2005 492 1006 492 2005 492 1006 492 2006 491 +# +name: decoder_expected1 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: 54 00 00 00 +repeat: false +# +name: decoder_input2 +type: raw +data: 1000000 4055 3941 605 1891 551 1947 550 1946 551 1946 551 947 551 947 550 1947 549 949 548 1951 545 1977 519 1978 519 1979 518 980 518 980 518 980 518 980 518 1979 518 1979 518 981 517 1979 518 980 518 980 518 980 518 980 518 +# +name: decoder_expected2 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: F4 00 00 00 +repeat: false +# +name: decoder_input3 +type: raw +data: 1000000 4027 3970 551 1946 550 1946 551 1946 551 1946 551 946 551 947 550 1947 549 949 547 1951 545 1978 518 1979 492 1006 492 1007 491 1006 492 1006 492 1006 492 2006 491 2006 491 1006 492 2006 491 1007 491 1007 491 1006 492 2006 491 +# +name: decoder_expected3 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: 74 00 00 00 +repeat: false +# +name: decoder_input4 +type: raw +data: 1000000 4021 3941 551 1946 550 1946 551 1946 551 1945 552 946 551 947 550 1947 549 950 547 1952 544 1977 519 979 519 1979 518 980 518 980 518 980 518 980 518 1979 518 1979 518 980 518 1979 518 980 518 980 518 1979 518 980 518 +# +name: decoder_expected4 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: B4 00 00 00 +repeat: false +# +name: decoder_input5 +type: raw +data: 1000000 4022 3941 551 1946 551 1946 577 1919 578 1919 578 920 552 946 551 1946 550 947 550 1949 547 1952 544 978 520 979 519 980 518 980 518 980 518 980 518 1979 518 1979 518 980 518 1979 518 980 518 980 518 1979 518 1980 517 +# +name: decoder_expected5 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: 34 00 00 00 +repeat: false +# +name: decoder_input6 +type: raw +data: 1000000 3995 3968 552 1944 552 1946 550 1946 550 1946 551 947 550 948 549 1947 549 1949 547 1952 544 1978 518 1979 492 2005 492 1006 492 1006 492 1006 492 1006 492 2005 492 2005 492 1006 492 1006 492 1006 492 1006 492 1006 492 1006 492 +# +name: decoder_expected6 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: FC 00 00 00 +repeat: false +# +name: encoder_decoder_input1 +type: parsed_array +count: 4 +# +protocol: RCA +address: 0F 00 00 00 +command: 74 00 00 00 +repeat: false +# +protocol: RCA +address: 0F 00 00 00 +command: B4 00 00 00 +repeat: false +# +protocol: RCA +address: 0F 00 00 00 +command: 34 00 00 00 +repeat: false +# +protocol: RCA +address: 0F 00 00 00 +command: FC 00 00 00 +repeat: false +# diff --git a/assets/unit_tests/nfc/Ntag213_locked.nfc b/assets/unit_tests/nfc/Ntag213_locked.nfc new file mode 100644 index 000000000000..32f7771165f6 --- /dev/null +++ b/assets/unit_tests/nfc/Ntag213_locked.nfc @@ -0,0 +1,66 @@ +Filetype: Flipper NFC device +Version: 3 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic +Device type: NTAG213 +# UID, ATQA and SAK are common for all formats +UID: 04 AC 6B 72 BA 6C 80 +ATQA: 00 44 +SAK: 00 +# Mifare Ultralight specific data +Data format version: 1 +Signature: 2D AE BC AF 84 B8 85 87 C2 FB FE 76 13 58 86 72 8E 1D 3C B5 DA 24 23 44 E5 63 4D 4C 82 FB D7 18 +Mifare version: 00 04 04 02 01 00 0F 03 +Counter 0: 0 +Tearing 0: 00 +Counter 1: 0 +Tearing 1: 00 +Counter 2: 0 +Tearing 2: 00 +Pages total: 45 +Pages read: 45 +Page 0: 04 AC 6B 4B +Page 1: 72 BA 6C 80 +Page 2: 24 48 00 00 +Page 3: E1 10 12 00 +Page 4: 00 00 41 50 +Page 5: 00 00 31 31 +Page 6: 00 20 09 28 +Page 7: 00 03 31 59 +Page 8: 91 DF D3 00 +Page 9: 00 00 00 00 +Page 10: 00 00 00 00 +Page 11: 00 00 00 00 +Page 12: 00 00 00 00 +Page 13: 00 00 00 00 +Page 14: 00 00 00 00 +Page 15: 00 00 00 00 +Page 16: 00 00 00 00 +Page 17: 00 00 00 00 +Page 18: 00 00 00 00 +Page 19: 00 00 00 00 +Page 20: 00 00 00 00 +Page 21: 00 00 00 00 +Page 22: 00 00 00 00 +Page 23: 00 00 00 00 +Page 24: 00 00 00 00 +Page 25: 00 00 00 00 +Page 26: 00 00 00 00 +Page 27: 00 00 00 00 +Page 28: 00 00 00 00 +Page 29: 00 00 00 00 +Page 30: 00 00 00 00 +Page 31: 00 00 00 00 +Page 32: 00 00 00 00 +Page 33: 00 00 00 00 +Page 34: 00 00 00 00 +Page 35: 00 00 00 00 +Page 36: 00 00 00 00 +Page 37: 00 00 00 00 +Page 38: 00 00 00 00 +Page 39: 00 00 00 00 +Page 40: 00 00 00 BD +Page 41: 04 00 00 04 +Page 42: C0 05 00 00 +Page 43: 95 3F 52 FF +Page 44: 00 00 00 00 +Failed authentication attempts: 0 diff --git a/assets/unit_tests/nfc/Ntag215.nfc b/assets/unit_tests/nfc/Ntag215.nfc new file mode 100644 index 000000000000..420af08a66d2 --- /dev/null +++ b/assets/unit_tests/nfc/Ntag215.nfc @@ -0,0 +1,156 @@ +Filetype: Flipper NFC device +Version: 3 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic +Device type: NTAG215 +# UID, ATQA and SAK are common for all formats +UID: 04 51 5C FA 6F 73 81 +ATQA: 00 44 +SAK: 00 +# Mifare Ultralight specific data +Data format version: 1 +Signature: 42 21 E4 6C 79 6A 81 5E EA 0D 93 6D 85 EE 4B 0C 2A 00 D5 77 F1 C5 67 F3 63 75 F8 EB 86 48 5E 6B +Mifare version: 00 04 04 02 01 00 11 03 +Counter 0: 0 +Tearing 0: 00 +Counter 1: 0 +Tearing 1: 00 +Counter 2: 00 +Tearing 2: 00 +Pages total: 135 +Pages read: 135 +Page 0: 04 51 5C 81 +Page 1: FA 6F 73 81 +Page 2: 67 48 0F E0 +Page 3: F1 10 FF EE +Page 4: A5 00 00 00 +Page 5: 90 42 74 71 +Page 6: FD 8F 50 61 +Page 7: C5 65 1B 54 +Page 8: EF 68 D0 8E +Page 9: 3D 35 DB 83 +Page 10: D3 00 29 F6 +Page 11: 42 2A A5 5C +Page 12: F1 69 0A FC +Page 13: B6 44 E9 6B +Page 14: 77 41 88 81 +Page 15: 86 31 CB AD +Page 16: B1 DE F1 AB +Page 17: DF 96 C2 C5 +Page 18: C1 26 99 96 +Page 19: 85 AF 9F 0E +Page 20: 58 FE ED DC +Page 21: 0A 0A 00 01 +Page 22: 03 C1 05 02 +Page 23: 38 39 34 33 +Page 24: 49 2D 4E 5C +Page 25: 5B 21 0F 44 +Page 26: 3F 3F 76 69 +Page 27: B4 72 D8 38 +Page 28: A0 35 53 51 +Page 29: 53 EB A6 7C +Page 30: 3E 8B 97 C0 +Page 31: 00 7A 45 13 +Page 32: 3A 8B D4 0F +Page 33: 31 C2 32 CC +Page 34: B4 24 A6 1B +Page 35: D3 F5 4A 1F +Page 36: CD 8F 1D 64 +Page 37: 01 F4 DF C2 +Page 38: 11 16 C2 C5 +Page 39: 30 6D 49 AF +Page 40: 10 D4 7C 3C +Page 41: 6E 36 4E 08 +Page 42: 95 76 BC 84 +Page 43: 35 50 DD F0 +Page 44: 21 0F EE D9 +Page 45: 85 19 54 5F +Page 46: 3E A9 04 20 +Page 47: 1B 97 E4 39 +Page 48: FF 0A 45 F6 +Page 49: 13 D4 3E DD +Page 50: 97 42 FC 67 +Page 51: 6A AC 78 96 +Page 52: D1 DA 25 23 +Page 53: BF 4D B3 76 +Page 54: F1 21 ED 15 +Page 55: BD 55 11 C4 +Page 56: 4E 8C E9 23 +Page 57: C0 C4 6D 5A +Page 58: 58 25 FF 95 +Page 59: 3C 2B 7A 57 +Page 60: 66 BE A0 61 +Page 61: BC FC 4A 31 +Page 62: 4D AC EE 81 +Page 63: BE 1A 86 04 +Page 64: F6 D7 5E B3 +Page 65: E7 A8 A2 86 +Page 66: E9 40 AB 47 +Page 67: C8 36 E4 3E +Page 68: A7 4D D3 EA +Page 69: 83 9A 64 F7 +Page 70: 96 6B 5D BF +Page 71: 4E A2 A6 0F +Page 72: BD 3D BE 7C +Page 73: 22 0C 68 51 +Page 74: 0F 9A B8 AE +Page 75: 38 2C C4 CD +Page 76: 53 D8 DD 18 +Page 77: A6 5D 35 87 +Page 78: C9 6D 99 59 +Page 79: 61 9F B6 DC +Page 80: E6 22 0F 99 +Page 81: 39 82 79 60 +Page 82: 58 2E BE F7 +Page 83: EF F7 95 62 +Page 84: D5 06 1B 58 +Page 85: 65 05 A9 08 +Page 86: 75 ED 5D 90 +Page 87: 5A E1 7E C9 +Page 88: 35 D6 29 BB +Page 89: D0 67 6C F9 +Page 90: A0 FF 0B 93 +Page 91: 22 EA A3 3F +Page 92: E2 BD BD 58 +Page 93: BE 93 D9 94 +Page 94: 41 CC 7E 40 +Page 95: E6 8C 5A 43 +Page 96: 65 C1 24 94 +Page 97: B9 97 61 13 +Page 98: AD 74 FF 21 +Page 99: 0F EC F6 03 +Page 100: 89 5D 89 E5 +Page 101: 8D 11 F8 D7 +Page 102: 33 43 79 2E +Page 103: 23 E5 29 B5 +Page 104: 53 98 13 FF +Page 105: E8 79 8B 33 +Page 106: 45 6C 34 38 +Page 107: 3B 69 28 D7 +Page 108: D2 80 B0 2F +Page 109: D0 18 D5 DD +Page 110: 6C 2D D9 97 +Page 111: CA 78 B4 A2 +Page 112: B7 3E B8 79 +Page 113: A2 BE 54 E4 +Page 114: C8 28 0C 4A +Page 115: 81 E7 EC 1C +Page 116: 39 93 6F 70 +Page 117: 75 77 5C FC +Page 118: 66 58 0C 1C +Page 119: 9F 70 2E C8 +Page 120: 52 4A 52 BD +Page 121: 56 D5 6A 15 +Page 122: 54 1B 33 90 +Page 123: 44 11 C1 07 +Page 124: 11 5C BA 80 +Page 125: 10 14 20 9A +Page 126: 4A D8 E6 36 +Page 127: DA B8 59 E5 +Page 128: 5E 48 95 DA +Page 129: 96 6A 26 85 +Page 130: 01 00 0F BD +Page 131: 00 00 00 04 +Page 132: 5F 00 00 00 +Page 133: 00 00 00 00 +Page 134: 00 00 00 00 +Failed authentication attempts: 0 diff --git a/assets/unit_tests/nfc/Ntag216.nfc b/assets/unit_tests/nfc/Ntag216.nfc new file mode 100644 index 000000000000..debe43858fd7 --- /dev/null +++ b/assets/unit_tests/nfc/Ntag216.nfc @@ -0,0 +1,252 @@ +Filetype: Flipper NFC device +Version: 2 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card +Device type: NTAG216 +# UID, ATQA and SAK are common for all formats +UID: 04 D9 65 0A 32 5E 80 +ATQA: 44 00 +SAK: 00 +# Mifare Ultralight specific data +Data format version: 1 +Signature: 48 2A F2 01 0F F2 F5 A7 9A D5 79 6E CB 14 54 48 98 D1 57 5D 8A 23 A9 B0 E8 20 02 3E CD C8 16 DB +Mifare version: 00 04 04 02 01 00 13 03 +Counter 0: 0 +Tearing 0: 00 +Counter 1: 0 +Tearing 1: 00 +Counter 2: 0 +Tearing 2: 00 +Pages total: 231 +Pages read: 231 +Page 0: 04 D9 65 30 +Page 1: 0A 32 5E 80 +Page 2: E6 48 00 00 +Page 3: E1 10 6D 00 +Page 4: 03 37 D1 01 +Page 5: 33 55 04 6D +Page 6: 2E 79 6F 75 +Page 7: 74 75 62 65 +Page 8: 2E 63 6F 6D +Page 9: 2F 77 61 74 +Page 10: 63 68 3F 76 +Page 11: 3D 62 78 71 +Page 12: 4C 73 72 6C +Page 13: 61 6B 4B 38 +Page 14: 26 66 65 61 +Page 15: 74 75 72 65 +Page 16: 3D 79 6F 75 +Page 17: 74 75 2E 62 +Page 18: 65 FE 00 00 +Page 19: 00 00 00 00 +Page 20: 00 00 00 00 +Page 21: 00 00 00 00 +Page 22: 00 00 00 00 +Page 23: 00 00 00 00 +Page 24: 00 00 00 00 +Page 25: 00 00 00 00 +Page 26: 00 00 00 00 +Page 27: 00 00 00 00 +Page 28: 00 00 00 00 +Page 29: 00 00 00 00 +Page 30: 00 00 00 00 +Page 31: 00 00 00 00 +Page 32: 00 00 00 00 +Page 33: 00 00 00 00 +Page 34: 00 00 00 00 +Page 35: 00 00 00 00 +Page 36: 00 00 00 00 +Page 37: 00 00 00 00 +Page 38: 00 00 00 00 +Page 39: 00 00 00 00 +Page 40: 00 00 00 00 +Page 41: 00 00 00 00 +Page 42: 00 00 00 00 +Page 43: 00 00 00 00 +Page 44: 00 00 00 00 +Page 45: 00 00 00 00 +Page 46: 00 00 00 00 +Page 47: 00 00 00 00 +Page 48: 00 00 00 00 +Page 49: 00 00 00 00 +Page 50: 00 00 00 00 +Page 51: 00 00 00 00 +Page 52: 00 00 00 00 +Page 53: 00 00 00 00 +Page 54: 00 00 00 00 +Page 55: 00 00 00 00 +Page 56: 00 00 00 00 +Page 57: 00 00 00 00 +Page 58: 00 00 00 00 +Page 59: 00 00 00 00 +Page 60: 00 00 00 00 +Page 61: 00 00 00 00 +Page 62: 00 00 00 00 +Page 63: 00 00 00 00 +Page 64: 00 00 00 00 +Page 65: 00 00 00 00 +Page 66: 00 00 00 00 +Page 67: 00 00 00 00 +Page 68: 00 00 00 00 +Page 69: 00 00 00 00 +Page 70: 00 00 00 00 +Page 71: 00 00 00 00 +Page 72: 00 00 00 00 +Page 73: 00 00 00 00 +Page 74: 00 00 00 00 +Page 75: 00 00 00 00 +Page 76: 00 00 00 00 +Page 77: 00 00 00 00 +Page 78: 00 00 00 00 +Page 79: 00 00 00 00 +Page 80: 00 00 00 00 +Page 81: 00 00 00 00 +Page 82: 00 00 00 00 +Page 83: 00 00 00 00 +Page 84: 00 00 00 00 +Page 85: 00 00 00 00 +Page 86: 00 00 00 00 +Page 87: 00 00 00 00 +Page 88: 00 00 00 00 +Page 89: 00 00 00 00 +Page 90: 00 00 00 00 +Page 91: 00 00 00 00 +Page 92: 00 00 00 00 +Page 93: 00 00 00 00 +Page 94: 00 00 00 00 +Page 95: 00 00 00 00 +Page 96: 00 00 00 00 +Page 97: 00 00 00 00 +Page 98: 00 00 00 00 +Page 99: 00 00 00 00 +Page 100: 00 00 00 00 +Page 101: 00 00 00 00 +Page 102: 00 00 00 00 +Page 103: 00 00 00 00 +Page 104: 00 00 00 00 +Page 105: 00 00 00 00 +Page 106: 00 00 00 00 +Page 107: 00 00 00 00 +Page 108: 00 00 00 00 +Page 109: 00 00 00 00 +Page 110: 00 00 00 00 +Page 111: 00 00 00 00 +Page 112: 00 00 00 00 +Page 113: 00 00 00 00 +Page 114: 00 00 00 00 +Page 115: 00 00 00 00 +Page 116: 00 00 00 00 +Page 117: 00 00 00 00 +Page 118: 00 00 00 00 +Page 119: 00 00 00 00 +Page 120: 00 00 00 00 +Page 121: 00 00 00 00 +Page 122: 00 00 00 00 +Page 123: 00 00 00 00 +Page 124: 00 00 00 00 +Page 125: 00 00 00 00 +Page 126: 00 00 00 00 +Page 127: 00 00 00 00 +Page 128: 00 00 00 00 +Page 129: 00 00 00 00 +Page 130: 00 00 00 00 +Page 131: 00 00 00 00 +Page 132: 00 00 00 00 +Page 133: 00 00 00 00 +Page 134: 00 00 00 00 +Page 135: 00 00 00 00 +Page 136: 00 00 00 00 +Page 137: 00 00 00 00 +Page 138: 00 00 00 00 +Page 139: 00 00 00 00 +Page 140: 00 00 00 00 +Page 141: 00 00 00 00 +Page 142: 00 00 00 00 +Page 143: 00 00 00 00 +Page 144: 00 00 00 00 +Page 145: 00 00 00 00 +Page 146: 00 00 00 00 +Page 147: 00 00 00 00 +Page 148: 00 00 00 00 +Page 149: 00 00 00 00 +Page 150: 00 00 00 00 +Page 151: 00 00 00 00 +Page 152: 00 00 00 00 +Page 153: 00 00 00 00 +Page 154: 00 00 00 00 +Page 155: 00 00 00 00 +Page 156: 00 00 00 00 +Page 157: 00 00 00 00 +Page 158: 00 00 00 00 +Page 159: 00 00 00 00 +Page 160: 00 00 00 00 +Page 161: 00 00 00 00 +Page 162: 00 00 00 00 +Page 163: 00 00 00 00 +Page 164: 00 00 00 00 +Page 165: 00 00 00 00 +Page 166: 00 00 00 00 +Page 167: 00 00 00 00 +Page 168: 00 00 00 00 +Page 169: 00 00 00 00 +Page 170: 00 00 00 00 +Page 171: 00 00 00 00 +Page 172: 00 00 00 00 +Page 173: 00 00 00 00 +Page 174: 00 00 00 00 +Page 175: 00 00 00 00 +Page 176: 00 00 00 00 +Page 177: 00 00 00 00 +Page 178: 00 00 00 00 +Page 179: 00 00 00 00 +Page 180: 00 00 00 00 +Page 181: 00 00 00 00 +Page 182: 00 00 00 00 +Page 183: 00 00 00 00 +Page 184: 00 00 00 00 +Page 185: 00 00 00 00 +Page 186: 00 00 00 00 +Page 187: 00 00 00 00 +Page 188: 00 00 00 00 +Page 189: 00 00 00 00 +Page 190: 00 00 00 00 +Page 191: 00 00 00 00 +Page 192: 00 00 00 00 +Page 193: 00 00 00 00 +Page 194: 00 00 00 00 +Page 195: 00 00 00 00 +Page 196: 00 00 00 00 +Page 197: 00 00 00 00 +Page 198: 00 00 00 00 +Page 199: 00 00 00 00 +Page 200: 00 00 00 00 +Page 201: 00 00 00 00 +Page 202: 00 00 00 00 +Page 203: 00 00 00 00 +Page 204: 00 00 00 00 +Page 205: 00 00 00 00 +Page 206: 00 00 00 00 +Page 207: 00 00 00 00 +Page 208: 00 00 00 00 +Page 209: 00 00 00 00 +Page 210: 00 00 00 00 +Page 211: 00 00 00 00 +Page 212: 00 00 00 00 +Page 213: 00 00 00 00 +Page 214: 00 00 00 00 +Page 215: 00 00 00 00 +Page 216: 00 00 00 00 +Page 217: 00 00 00 00 +Page 218: 00 00 00 00 +Page 219: 00 00 00 00 +Page 220: 00 00 00 00 +Page 221: 00 00 00 00 +Page 222: 00 00 00 00 +Page 223: 00 00 00 00 +Page 224: 00 00 00 00 +Page 225: 00 00 00 00 +Page 226: 00 00 00 BD +Page 227: 04 00 00 FF +Page 228: 00 05 00 00 +Page 229: 00 00 00 00 +Page 230: 00 00 00 00 +Failed authentication attempts: 0 diff --git a/assets/unit_tests/nfc/Ultralight_11.nfc b/assets/unit_tests/nfc/Ultralight_11.nfc new file mode 100644 index 000000000000..22441289dd08 --- /dev/null +++ b/assets/unit_tests/nfc/Ultralight_11.nfc @@ -0,0 +1,41 @@ +Filetype: Flipper NFC device +Version: 3 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic +Device type: Mifare Ultralight 11 +# UID, ATQA and SAK are common for all formats +UID: 04 15 74 F2 B0 5E 81 +ATQA: 00 44 +SAK: 00 +# Mifare Ultralight specific data +Data format version: 1 +Signature: A4 37 7D E5 8C 2F 88 D8 04 60 41 6E 3A C8 CD DB 19 94 26 12 C5 D0 12 B0 EB 88 05 72 89 F2 A5 61 +Mifare version: 00 04 03 01 01 00 0B 03 +Counter 0: 0 +Tearing 0: BD +Counter 1: 0 +Tearing 1: BD +Counter 2: 0 +Tearing 2: BD +Pages total: 20 +Pages read: 20 +Page 0: 04 15 74 ED +Page 1: F2 B0 5E 81 +Page 2: 9D 48 F8 FF +Page 3: C1 31 3E 3F +Page 4: B0 00 F0 02 +Page 5: 2F B3 45 A0 +Page 6: D4 9C 02 F2 +Page 7: 4A B1 ED FF +Page 8: C8 01 00 02 +Page 9: 4F B3 46 70 +Page 10: EE F6 60 B0 +Page 11: B6 C6 12 1B +Page 12: B9 1E 49 C3 +Page 13: 49 DF 7A 57 +Page 14: 08 52 2A 11 +Page 15: 28 0A 28 59 +Page 16: 00 00 00 FF +Page 17: 00 05 00 00 +Page 18: FF FF FF FF +Page 19: 00 00 00 00 +Failed authentication attempts: 0 diff --git a/assets/unit_tests/nfc/Ultralight_21.nfc b/assets/unit_tests/nfc/Ultralight_21.nfc new file mode 100644 index 000000000000..dc01e93a6027 --- /dev/null +++ b/assets/unit_tests/nfc/Ultralight_21.nfc @@ -0,0 +1,62 @@ +Filetype: Flipper NFC device +Version: 3 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic +Device type: Mifare Ultralight 21 +# UID, ATQA and SAK are common for all formats +UID: 34 BF AB B1 AE 73 D6 +ATQA: 00 44 +SAK: 00 +# Mifare Ultralight specific data +Data format version: 1 +Signature: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Mifare version: 00 34 21 01 01 00 0E 03 +Counter 0: 0 +Tearing 0: 00 +Counter 1: 0 +Tearing 1: 00 +Counter 2: 0 +Tearing 2: 00 +Pages total: 41 +Pages read: 41 +Page 0: 34 BF AB A8 +Page 1: B1 AE 73 D6 +Page 2: BA 00 70 08 +Page 3: FF FF FF FC +Page 4: 45 D9 BB A0 +Page 5: 5D 9D FA 00 +Page 6: 80 70 38 40 +Page 7: 12 30 02 00 +Page 8: 00 00 00 00 +Page 9: 00 00 00 00 +Page 10: AC A1 0D E4 +Page 11: 80 70 38 40 +Page 12: 00 57 A0 01 +Page 13: 00 08 C1 40 +Page 14: 00 00 00 00 +Page 15: AC A1 0D E4 +Page 16: 00 00 00 00 +Page 17: 00 00 00 00 +Page 18: 00 00 00 00 +Page 19: 00 00 00 00 +Page 20: 00 00 00 00 +Page 21: 00 00 00 00 +Page 22: 00 00 00 00 +Page 23: 00 00 00 00 +Page 24: 00 00 00 00 +Page 25: 00 00 00 00 +Page 26: 00 00 00 00 +Page 27: 00 00 00 00 +Page 28: 00 00 00 00 +Page 29: 00 00 00 00 +Page 30: 00 00 00 00 +Page 31: 00 00 00 00 +Page 32: 00 00 00 00 +Page 33: 00 00 00 00 +Page 34: 00 00 00 00 +Page 35: 00 00 00 00 +Page 36: 00 00 00 BD +Page 37: 00 00 00 FF +Page 38: 00 05 00 00 +Page 39: FF FF FF FF +Page 40: 00 00 00 00 +Failed authentication attempts: 0 diff --git a/documentation/AppManifests.md b/documentation/AppManifests.md index 99f6386b2c9a..b48a6b8eddc5 100644 --- a/documentation/AppManifests.md +++ b/documentation/AppManifests.md @@ -47,7 +47,7 @@ Only two parameters are mandatory: **_appid_** and **_apptype_**. Others are opt The following parameters are used only for [FAPs](./AppsOnSDCard.md): - **sources**: list of strings, file name masks used for gathering sources within the app folder. The default value of `["*.c*"]` includes C and C++ source files. Applications cannot use the `"lib"` folder for their own source code, as it is reserved for **fap_private_libs**. -- **fap_version**: tuple, 2 numbers in the form of (x,y): application version to be embedded within .fap file. The default value is (0,1), meaning version "0.1". +- **fap_version**: string, application version. The default value is "0.1". You can also use a tuple of 2 numbers in the form of (x,y) to specify the version. It is also possible to add more dot-separated parts to the version, like patch number, but only major and minor version numbers are stored in the built .fap. - **fap_icon**: name of a `.png` file, 1-bit color depth, 10x10px, to be embedded within `.fap` file. - **fap_libs**: list of extra libraries to link the application against. Provides access to extra functions that are not exported as a part of main firmware at the expense of increased `.fap` file size and RAM consumption. - **fap_category**: string, may be empty. App subcategory, also determines the path of the FAP within the apps folder in the file system. diff --git a/documentation/AppsOnSDCard.md b/documentation/AppsOnSDCard.md index 212df5b1bf0e..051fbb84f5b7 100644 --- a/documentation/AppsOnSDCard.md +++ b/documentation/AppsOnSDCard.md @@ -13,7 +13,8 @@ FAPs are created and developed the same way as internal applications that are pa To build your application as a FAP, create a folder with your app's source code in `applications_user`, then write its code the way you'd do when creating a regular built-in application. Then configure its `application.fam` manifest, and set its _apptype_ to FlipperAppType.EXTERNAL. See [Application Manifests](./AppManifests.md#application-definition) for more details. - To build your application, run `./fbt fap_{APPID}`, where APPID is your application's ID in its manifest. -- To build your app and upload it over USB to run on Flipper, use `./fbt launch_app APPSRC=applications_user/path/to/app`. This command is configured in the default [VS Code profile](../.vscode/ReadMe.md) as a "Launch App on Flipper" build action (Ctrl+Shift+B menu). +- To build your app and upload it over USB to run on Flipper, use `./fbt launch APPSRC=applications_user/path/to/app`. This command is configured in the default [VS Code profile](../.vscode/ReadMe.md) as a "Launch App on Flipper" build action (Ctrl+Shift+B menu). +- To build an app without uploading it to Flipper, use `./fbt build APPSRC=applications_user/path/to/app`. This command is also availabe in VSCode configuration as "Build App". - To build all FAPs, run `./fbt faps` or `./fbt fap_dist`. ## FAP assets diff --git a/documentation/FuriHalBus.md b/documentation/FuriHalBus.md new file mode 100644 index 000000000000..230a98050fd7 --- /dev/null +++ b/documentation/FuriHalBus.md @@ -0,0 +1,113 @@ +# Using FuriHalBus API + +## Basic info + +On system startup, most of the peripheral devices are under reset and not clocked by default. This is done to reduce power consumption and to guarantee that the device will always be in the same state before use. +Some crucial peripherals are enabled right away by the system, others must be explicitly enabled by the user code. + +**NOTE:** Here and afterwards the word *"system"* refers to any code belonging to the operating system, hardware drivers or built-in applications. + +To **ENABLE** a peripheral, call `furi_hal_bus_enable()`. At the time of the call, the peripheral in question MUST be disabled, otherwise a crash will occur to indicate improper use. This means that any given peripheral cannot be enabled twice or more without disabling it first. + +To **DISABLE** a peripheral, call `furi_hal_bus_disable()`. Likewise, the peripheral in question MUST be enabled, otherwise a crash will occur. + +To **RESET** a peripheral, call `furi_hal_bus_reset()`. The peripheral in question MUST be enabled, otherwise a crash will occur. This method is used whenever it is necessary to reset all the peripheral's registers to their initial states without disabling it. + +## Peripherals + +Built-in peripherals are divided into three categories: +- Enabled by the system on startup, never disabled; +- Enabled and disabled by the system on demand; +- Enabled and disabled by the user code. + +### Always-on peripherals + +Below is the list of peripherals that are enabled by the system. The user code must NEVER attempt to disable them. If a corresponding API is provided, the user code must employ it in order to access the peripheral. + +*Table 1* - Peripherals enabled by the system + +| Peripheral | Enabled at | +| :-----------: | :-----------------------: | +| DMA1 | `furi_hal_dma.c` | +| DMA2 | -- | +| DMAMUX | -- | +| GPIOA | `furi_hal_resources.c` | +| GPIOB | -- | +| GPIOC | -- | +| GPIOD | -- | +| GPIOE | -- | +| GPIOH | -- | +| PKA | `furi_hal_bt.c` | +| AES2 | -- | +| HSEM | -- | +| IPCC | -- | +| FLASH | enabled by hardware | + +### On-demand system peripherals + +Below is the list of peripherals that are enabled and disabled by the system. The user code must avoid using them directly, preferring the respective APIs instead. + +When not using the API, these peripherals MUST be enabled by the user code and then disabled when not needed anymore. + +*Table 2* - Peripherals enabled and disabled by the system + +| Peripheral | API header file | +| :-----------: | :-------------------: | +| RNG | `furi_hal_random.h` | +| SPI1 | `furi_hal_spi.h` | +| SPI2 | -- | +| I2C1 | `furi_hal_i2c.h` | +| I2C3 | -- | +| USART1 | `furi_hal_uart.h` | +| LPUART1 | -- | +| USB | `furi_hal_usb.h` | + +### On-demand shared peripherals + +Below is the list of peripherals that are not enabled by default and MUST be enabled by the user code each time it accesses them. + +Note that some of these peripherals may also be used by the system to implement its certain features. +The system will take over any given peripheral only when the respective feature is in use. + +*Table 3* - Peripherals enabled and disabled by user + +| Peripheral | System | Purpose | +| :-----------: | :-------: | ------------------------------------- | +| CRC | | | +| TSC | | | +| ADC | | | +| QUADSPI | | | +| TIM1 | yes | subghz, lfrfid, nfc, infrared, etc... | +| TIM2 | yes | subghz, infrared, etc... | +| TIM16 | yes | speaker | +| TIM17 | yes | cc1101_ext | +| LPTIM1 | yes | tickless idle timer | +| LPTIM2 | yes | pwm | +| SAI1 | | | +| LCD | | | + + +## DMA + +The DMA1,2 peripherals are a special case in that they have multiple independent channels. Some of the channels may be in use by the system. + +Below is the list of DMA channels and their usage by the system. + +*Table 4* - DMA channels + +| DMA | Channel | System | Purpose | +| :---: | :-------: | :-------: | ------------------------- | +| DMA1 | 1 | yes | digital signal | +| -- | 2 | yes | -- | +| -- | 3 | | | +| -- | 4 | yes | pulse reader | +| -- | 5 | | | +| -- | 6 | | | +| -- | 7 | | | +| DMA2 | 1 | yes | infrared, lfrfid, subghz, | +| -- | 2 | yes | -- | +| -- | 3 | yes | cc1101_ext | +| -- | 4 | yes | cc1101_ext | +| -- | 5 | yes | cc1101_ext | +| -- | 6 | yes | SPI | +| -- | 7 | yes | SPI | diff --git a/documentation/FuriHalDebuging.md b/documentation/FuriHalDebuging.md index 8ff770163b23..e7f2d8f2abee 100644 --- a/documentation/FuriHalDebuging.md +++ b/documentation/FuriHalDebuging.md @@ -1,6 +1,6 @@ # Furi HAL Debugging -Some Furi subsystem got additional debugging features that can be enabled by adding additional defines to firmware compilation. +Some Furi subsystems got additional debugging features that can be enabled by adding additional defines to firmware compilation. Usually they are used for low level tracing and profiling or signal redirection/duplication. @@ -23,4 +23,4 @@ There are 3 signals that will be exposed to external GPIO pins: There are 2 signals that will be exposed to external GPIO pins: - `WFI` - `PB2` - Light sleep (wait for interrupt) used. Basically this is lightest and most non-breaking things power save mode. All function and debug should work correctly in this mode. -- `STOP` - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible. \ No newline at end of file +- `STOP` - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible. diff --git a/documentation/OTA.md b/documentation/OTA.md index 799548f4d523..ed75560cfe6e 100644 --- a/documentation/OTA.md +++ b/documentation/OTA.md @@ -87,9 +87,12 @@ Even if something goes wrong, updater allows you to retry failed operations and | Uninstalling radio FW | **4** | **0** | SHCI Delete command error | | | | **80** | Error awaiting command status | | Writing radio FW | **5** | **0-100** | Block read/write error | -| Installing radio FW | **6** | **0** | SHCI Install command error | +| Installing radio FW | **6** | **10** | SHCI Install command error | | | | **80** | Error awaiting command status | -| Radio is updating | **7** | **10** | Error waiting for operation completion | +| Core2 is busy | **7** | **10** | Couldn't start C2 | +| | | **20** | Failed to switch C2 to FUS mode | +| | | **30** | Error in FUS operation | +| | | **50** | Failed to switch C2 to stack mode | | Validating opt. bytes | **8** | **yy** | Option byte code | | Checking DFU file | **9** | **0** | Error opening DFU file | | | | **1-98** | Error reading DFU file | diff --git a/documentation/fbt.md b/documentation/fbt.md index d9eb8f4aabbf..c19780ef5939 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -114,6 +114,8 @@ To run cleanup (think of `make clean`) for specified targets, add the `-c` optio Default configuration variables are set in the configuration file: `fbt_options.py`. Values set in the command line have higher precedence over the configuration file. +You can also create a file called `fbt_options_local.py` that will be evaluated when loading default options file, enabling persisent overriding of default options without modifying default configuration. + You can find out available options with `./fbt -h`. ### Firmware application set diff --git a/documentation/file_formats/InfraredFileFormats.md b/documentation/file_formats/InfraredFileFormats.md index 3c0acdcb7385..c9b6a9536553 100644 --- a/documentation/file_formats/InfraredFileFormats.md +++ b/documentation/file_formats/InfraredFileFormats.md @@ -1,5 +1,22 @@ # Infrared Flipper File Formats + +## Supported protocols list for `type: parsed` +``` + NEC + NECext + NEC42 + NEC42ext + Samsung32 + RC6 + RC5 + RC5X + SIRC + SIRC15 + SIRC20 + Kaseikyo + RCA +``` ## Infrared Remote File Format ### Example diff --git a/documentation/file_formats/NfcFileFormats.md b/documentation/file_formats/NfcFileFormats.md index 78c6420ee037..f752cdb901b8 100644 --- a/documentation/file_formats/NfcFileFormats.md +++ b/documentation/file_formats/NfcFileFormats.md @@ -1,44 +1,109 @@ # NFC Flipper File Formats -## NFC-A (UID) + Header +## UID + Header (General format) ### Example Filetype: Flipper NFC device - Version: 3 - # Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card - Device type: UID - # UID, ATQA and SAK are common for all formats - UID: 04 85 92 8A A0 61 81 - ATQA: 00 44 - SAK: 00 + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: ISO14443-4A + # UID is common for all formats + UID: 04 48 6A 32 33 58 80 + ------------------------- + (Device-specific data) ### Description -This file format is used to store the UID, SAK and ATQA of a NFC-A device. It does not store any internal data, so it can be used for multiple different card types. Also used as a header for other formats. +This file format is used to store the device type and the UID of an NFC device. It does not store any internal data, so it is only used as a header for other formats. Version differences: 1. Initial version, deprecated 2. LSB ATQA (e.g. 4400 instead of 0044) 3. MSB ATQA (current version) +4. Replace UID device type with ISO14443-3A -UID can be either 4 or 7 bytes long. ATQA is 2 bytes long. SAK is 1 byte long. +## ISO14443-3A -## Mifare Ultralight/NTAG + Filetype: Flipper NFC device + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: ISO14443-3A + # UID is common for all formats + UID: 34 19 6D 41 14 56 E6 + # ISO14443-3A specific data + ATQA: 00 44 + SAK: 00 + +### Description + +This file format is used to store the UID, SAK and ATQA of a ISO14443-3A device. +UID must be either 4 or 7 bytes long. ATQA is 2 bytes long. SAK is 1 byte long. + +Version differences: +None, there are no versions yet. + +## ISO14443-3B + + Filetype: Flipper NFC device + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: ISO14443-3B + # UID is common for all formats + UID: 30 1D B3 28 + # ISO14443-3B specific data + Application data: 00 12 34 FF + Protocol info: 11 81 E1 + +### Description + +This file format is used to store the UID, Application data and Protocol info of a ISO14443-3B device. +UID must be 4 bytes long. Application data is 4 bytes long. Protocol info is 3 bytes long. + +Version differences: +None, there are no versions yet. + +## ISO14443-4A + +### Example + + Filetype: Flipper NFC device + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: ISO14443-4A + # UID is common for all formats + UID: 04 48 6A 32 33 58 80 + # ISO14443-3A specific data + ATQA: 03 44 + SAK: 20 + # ISO14443-4A specific data + ATS: 06 75 77 81 02 80 + +### Description + +This file format is used to store the UID, SAK and ATQA of a ISO14443-4A device. It also stores the Answer to Select (ATS) data of the card. +ATS must be no less than 5 bytes long. + +Version differences: +None, there are no versions yet. + +## NTAG/Ultralight ### Example Filetype: Flipper NFC device - Version: 3 - # Nfc device type can be UID, Mifare Ultralight, Mifare Classic - Device type: NTAG216 - # UID, ATQA and SAK are common for all formats + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: NTAG/Ultralight + # UID is common for all formats UID: 04 85 90 54 12 98 23 + # ISO14443-3A specific data ATQA: 00 44 SAK: 00 - # Mifare Ultralight specific data - Data format version: 1 + # NTAG/Ultralight specific data + Data format version: 2 + NTAG/Ultralight type: NTAG216 Signature: 1B 84 EB 70 BD 4C BD 1B 1D E4 98 0B 18 58 BD 7C 72 85 B4 E4 7B 38 8E 96 CF 88 6B EE A3 43 AD 90 Mifare version: 00 04 04 02 01 00 13 03 Counter 0: 0 @@ -66,6 +131,8 @@ UID can be either 4 or 7 bytes long. ATQA is 2 bytes long. SAK is 1 byte long. This file format is used to store the UID, SAK and ATQA of a Mifare Ultralight/NTAG device. It also stores the internal data of the card, the signature, the version, and the counters. The data is stored in pages, just like on the card itself. +The "NTAG/Ultralight type" field contains the concrete device type. It must be one of: Mifare Ultralight, Mifare Ultralight 11, Mifare Ultralight 21, NTAG203, NTAG213, NTAG215, NTAG216, NTAG I2C 1K, NTAG I2C 2K, NTAG I2C Plus 1K, NTAG I2C Plus 2K. + The "Signature" field contains the reply of the tag to the READ_SIG command. More on that can be found here: (page 31) The "Mifare version" field is not related to the file format version but to the Mifare Ultralight version. It contains the response of the tag to the GET_VERSION command. More on that can be found here: (page 21) @@ -74,18 +141,20 @@ Other fields are the direct representation of the card's internal state. Learn m Version differences: -1. Current version +1. Mifare Ultralight type is stored directly in Device type field +2. Current version, Mifare Ultralight type is stored in the same-named field ## Mifare Classic ### Example Filetype: Flipper NFC device - Version: 3 - # Nfc device type can be UID, Mifare Ultralight, Mifare Classic + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire Device type: Mifare Classic - # UID, ATQA and SAK are common for all formats + # UID is common for all formats UID: BA E2 7C 9D + # ISO14443-3A specific data ATQA: 00 02 SAK: 18 # Mifare Classic specific data @@ -145,13 +214,16 @@ Example: ### Example Filetype: Flipper NFC device - Version: 3 - # Nfc device type can be UID, Mifare Ultralight, Mifare Classic + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire Device type: Mifare DESFire - # UID, ATQA and SAK are common for all formats + # UID is common for all formats UID: 04 2F 19 0A CD 66 80 + # ISO14443-3A specific data ATQA: 03 44 SAK: 20 + # ISO14443-4A specific data + ATS: 06 75 77 81 02 80 # Mifare DESFire specific data PICC Version: 04 01 01 12 00 1A 05 04 01 01 02 01 1A 05 04 2F 19 0A CD 66 80 CE ED D4 51 80 31 19 PICC Free Memory: 7520 diff --git a/fbt_options.py b/fbt_options.py index d05b882a0cad..b6fcc70f2337 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -1,7 +1,9 @@ +from pathlib import Path import posixpath # For more details on these options, run 'fbt -h' +FIRMWARE_ORIGIN = "Official" # Default hardware target TARGET_HW = 7 @@ -75,3 +77,8 @@ } FIRMWARE_APP_SET = "default" + +custom_options_fn = "fbt_options_local.py" + +if Path(custom_options_fn).exists(): + exec(compile(Path(custom_options_fn).read_text(), custom_options_fn, "exec")) diff --git a/firmware.scons b/firmware.scons index c46996899135..5e4398427513 100644 --- a/firmware.scons +++ b/firmware.scons @@ -18,6 +18,7 @@ env = ENV.Clone( "fbt_apps", "pvsstudio", "fbt_hwtarget", + "fbt_envhooks", ], COMPILATIONDB_USE_ABSPATH=False, BUILD_DIR=fw_build_meta["build_dir"], @@ -67,11 +68,22 @@ env = ENV.Clone( "FURI_DEBUG" if ENV["DEBUG"] else "FURI_NDEBUG", ], }, + "nfc": { + "CCFLAGS": [ + "-Og", + ], + "CPPDEFINES": [ + "NDEBUG", + "FURI_DEBUG", + ], + }, }, FW_API_TABLE=None, _APP_ICONS=None, ) +env.PreConfigureFwEnvionment() + if env["IS_BASE_FIRMWARE"]: env.Append( FIRMWARE_BUILD_CFG="firmware", @@ -100,6 +112,13 @@ lib_targets = env.BuildModules( ], ) +# Configure firmware origin definitions +env.Append( + CPPDEFINES=[ + env.subst("FW_ORIGIN_${FIRMWARE_ORIGIN}"), + ] +) + # Now, env is fully set up with everything to build apps fwenv = env.Clone(FW_ARTIFACTS=[]) @@ -271,5 +290,6 @@ if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS): Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_all", fw_artifacts) +env.PostConfigureFwEnvionment() Return("fwenv") diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 828c771662c8..f26b424f8e54 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,26.2,, +Version,+,34.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -36,12 +36,13 @@ Header,+,applications/services/notification/notification_messages.h,, Header,+,applications/services/power/power_service/power.h,, Header,+,applications/services/rpc/rpc_app.h,, Header,+,applications/services/storage/storage.h,, -Header,-,firmware/targets/f18/furi_hal/furi_hal_power_calibration.h,, Header,+,firmware/targets/f18/furi_hal/furi_hal_resources.h,, Header,+,firmware/targets/f18/furi_hal/furi_hal_spi_config.h,, Header,+,firmware/targets/f18/furi_hal/furi_hal_target_hw.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_bus.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_clock.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_console.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_dma.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_flash.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_gpio.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_i2c_config.h,, @@ -54,6 +55,7 @@ Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_types.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_uart.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_usb_cdc.h,, Header,+,firmware/targets/f7/platform_specific/intrinsic_export.h,, +Header,+,firmware/targets/f7/platform_specific/math_wrapper.h,, Header,+,firmware/targets/furi_hal_include/furi_hal.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.h,, @@ -78,6 +80,8 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_vibro.h,, +Header,+,lib/digital_signal/digital_signal.h,, +Header,+,lib/drivers/cc1101_regs.h,, Header,+,lib/flipper_application/api_hashtable/api_hashtable.h,, Header,+,lib/flipper_application/api_hashtable/compilesort.hpp,, Header,+,lib/flipper_application/flipper_application.h,, @@ -125,10 +129,12 @@ Header,+,lib/mlib/m-list.h,, Header,+,lib/mlib/m-rbtree.h,, Header,+,lib/mlib/m-tuple.h,, Header,+,lib/mlib/m-variant.h,, +Header,+,lib/music_worker/music_worker.h,, Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_slave.h,, Header,+,lib/print/wrappers.h,, +Header,+,lib/pulse_reader/pulse_reader.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_adc.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_bus.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_comp.h,, @@ -156,7 +162,7 @@ Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_tim.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_usart.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,, -Header,+,lib/pulse_reader/pulse_reader.h,, +Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, @@ -278,15 +284,24 @@ Function,-,LL_mDelay,void,uint32_t Function,-,SystemCoreClockUpdate,void, Function,-,SystemInit,void, Function,-,_Exit,void,int +Function,+,__aeabi_uldivmod,void*,"uint64_t, uint64_t" Function,-,__assert,void,"const char*, int, const char*" Function,+,__assert_func,void,"const char*, int, const char*, const char*" Function,+,__clear_cache,void,"void*, void*" Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" Function,+,__errno,int*, +Function,-,__fpclassifyd,int,double +Function,-,__fpclassifyf,int,float Function,+,__furi_crash,void, +Function,+,__furi_critical_enter,__FuriCriticalInfo, +Function,+,__furi_critical_exit,void,__FuriCriticalInfo Function,+,__furi_halt,void, Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" Function,-,__getline,ssize_t,"char**, size_t*, FILE*" +Function,-,__isinfd,int,double +Function,-,__isinff,int,float +Function,-,__isnand,int,double +Function,-,__isnanf,int,float Function,-,__itoa,char*,"int, char*, int" Function,-,__locale_mb_cur_max,int, Function,+,__retarget_lock_acquire,void,_LOCK_T @@ -299,6 +314,9 @@ Function,+,__retarget_lock_release,void,_LOCK_T Function,+,__retarget_lock_release_recursive,void,_LOCK_T Function,-,__retarget_lock_try_acquire,int,_LOCK_T Function,-,__retarget_lock_try_acquire_recursive,int,_LOCK_T +Function,-,__signbitd,int,double +Function,-,__signbitf,int,float +Function,-,__signgam,int*, Function,-,__srget_r,int,"_reent*, FILE*" Function,-,__swbuf_r,int,"_reent*, int, FILE*" Function,-,__utoa,char*,"unsigned, char*, int" @@ -451,6 +469,12 @@ Function,-,_wctomb_r,int,"_reent*, char*, wchar_t, _mbstate_t*" Function,-,a64l,long,const char* Function,+,abort,void, Function,-,abs,int,int +Function,-,acos,double,double +Function,-,acosf,float,float +Function,-,acosh,double,double +Function,-,acoshf,float,float +Function,-,acoshl,long double,long double +Function,-,acosl,long double,long double Function,-,aligned_alloc,void*,"size_t, size_t" Function,+,aligned_free,void,void* Function,+,aligned_malloc,void*,"size_t, size_t" @@ -466,11 +490,26 @@ Function,+,args_read_probably_quoted_string_and_trim,_Bool,"FuriString*, FuriStr Function,+,args_read_string_and_trim,_Bool,"FuriString*, FuriString*" Function,-,asctime,char*,const tm* Function,-,asctime_r,char*,"const tm*, char*" +Function,-,asin,double,double +Function,-,asinf,float,float +Function,-,asinh,double,double +Function,-,asinhf,float,float +Function,-,asinhl,long double,long double +Function,-,asinl,long double,long double Function,-,asiprintf,int,"char**, const char*, ..." Function,-,asniprintf,char*,"char*, size_t*, const char*, ..." Function,-,asnprintf,char*,"char*, size_t*, const char*, ..." Function,-,asprintf,int,"char**, const char*, ..." Function,-,at_quick_exit,int,void (*)() +Function,-,atan,double,double +Function,-,atan2,double,"double, double" +Function,-,atan2f,float,"float, float" +Function,-,atan2l,long double,"long double, long double" +Function,-,atanf,float,float +Function,-,atanh,double,double +Function,-,atanhf,float,float +Function,-,atanhl,long double,long double +Function,-,atanl,long double,long double Function,-,atexit,int,void (*)() Function,-,atof,double,const char* Function,-,atoff,float,const char* @@ -563,6 +602,12 @@ Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" Function,+,canvas_width,uint8_t,const Canvas* +Function,-,cbrt,double,double +Function,-,cbrtf,float,float +Function,-,cbrtl,long double,long double +Function,-,ceil,double,double +Function,-,ceilf,float,float +Function,-,ceill,long double,long double Function,-,cfree,void,void* Function,-,clearerr,void,FILE* Function,-,clearerr_unlocked,void,FILE* @@ -583,6 +628,15 @@ Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiI Function,+,composite_api_resolver_alloc,CompositeApiResolver*, Function,+,composite_api_resolver_free,void,CompositeApiResolver* Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* +Function,-,copysign,double,"double, double" +Function,-,copysignf,float,"float, float" +Function,-,copysignl,long double,"long double, long double" +Function,-,cos,double,double +Function,-,cosf,float,float +Function,-,cosh,double,double +Function,-,coshf,float,float +Function,-,coshl,long double,long double +Function,-,cosl,long double,long double Function,+,crc32_calc_buffer,uint32_t,"uint32_t, const void*, size_t" Function,+,crc32_calc_file,uint32_t,"File*, const FileCrcProgressCb, void*" Function,-,ctermid,char*,char* @@ -614,6 +668,24 @@ Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, u Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*" Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" Function,-,difftime,double,"time_t, time_t" +Function,-,digital_sequence_add,void,"DigitalSequence*, uint8_t" +Function,-,digital_sequence_alloc,DigitalSequence*,"uint32_t, const GpioPin*" +Function,-,digital_sequence_clear,void,DigitalSequence* +Function,-,digital_sequence_free,void,DigitalSequence* +Function,-,digital_sequence_send,_Bool,DigitalSequence* +Function,-,digital_sequence_set_sendtime,void,"DigitalSequence*, uint32_t" +Function,-,digital_sequence_set_signal,void,"DigitalSequence*, uint8_t, DigitalSignal*" +Function,-,digital_sequence_timebase_correction,void,"DigitalSequence*, float" +Function,-,digital_signal_add,void,"DigitalSignal*, uint32_t" +Function,-,digital_signal_add_pulse,void,"DigitalSignal*, uint32_t, _Bool" +Function,-,digital_signal_alloc,DigitalSignal*,uint32_t +Function,-,digital_signal_append,_Bool,"DigitalSignal*, DigitalSignal*" +Function,-,digital_signal_free,void,DigitalSignal* +Function,-,digital_signal_get_edge,uint32_t,"DigitalSignal*, uint32_t" +Function,-,digital_signal_get_edges_cnt,uint32_t,DigitalSignal* +Function,-,digital_signal_get_start_level,_Bool,DigitalSignal* +Function,-,digital_signal_prepare_arr,void,DigitalSignal* +Function,-,digital_signal_send,void,"DigitalSignal*, const GpioPin*" Function,-,diprintf,int,"int, const char*, ..." Function,+,dir_walk_alloc,DirWalk*,Storage* Function,+,dir_walk_close,void,DirWalk* @@ -624,7 +696,7 @@ Function,+,dir_walk_read,DirWalkResult,"DirWalk*, FuriString*, FileInfo*" Function,+,dir_walk_set_filter_cb,void,"DirWalk*, DirWalkFilterCb, void*" Function,+,dir_walk_set_recursive,void,"DirWalk*, _Bool" Function,-,div,div_t,"int, int" -Function,+,dolphin_deed,void,"Dolphin*, DolphinDeed" +Function,+,dolphin_deed,void,DolphinDeed Function,+,dolphin_deed_get_app,DolphinApp,DolphinDeed Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed @@ -634,6 +706,8 @@ Function,+,dolphin_stats,DolphinStats,Dolphin* Function,+,dolphin_upgrade_level,void,Dolphin* Function,-,dprintf,int,"int, const char*, ..." Function,-,drand48,double, +Function,-,drem,double,"double, double" +Function,-,dremf,float,"float, float" Function,-,eTaskConfirmSleepModeStatus,eSleepModeStatus, Function,-,eTaskGetState,eTaskState,TaskHandle_t Function,+,elements_bold_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" @@ -655,15 +729,39 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" -Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*" +Function,+,elf_symbolname_hash,uint32_t,const char* Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* Function,-,erand48,double,unsigned short[3] +Function,-,erf,double,double +Function,-,erfc,double,double +Function,-,erfcf,float,float +Function,-,erfcl,long double,long double +Function,-,erff,float,float +Function,-,erfl,long double,long double Function,-,exit,void,int +Function,-,exp,double,double +Function,-,exp10,double,double +Function,-,exp10f,float,float +Function,-,exp2,double,double +Function,-,exp2f,float,float +Function,-,exp2l,long double,long double +Function,-,expf,float,float +Function,-,expl,long double,long double Function,-,explicit_bzero,void,"void*, size_t" +Function,-,expm1,double,double +Function,-,expm1f,float,float +Function,-,expm1l,long double,long double +Function,-,fabs,double,double +Function,-,fabsf,float,float +Function,-,fabsl,long double,long double Function,-,fclose,int,FILE* Function,-,fcloseall,int, +Function,-,fdim,double,"double, double" +Function,-,fdimf,float,"float, float" +Function,-,fdiml,long double,"long double, long double" Function,-,fdopen,FILE*,"int, const char*" Function,-,feof,int,FILE* Function,-,feof_unlocked,int,FILE* @@ -708,12 +806,17 @@ Function,+,file_stream_open,_Bool,"Stream*, const char*, FS_AccessMode, FS_OpenM Function,-,fileno,int,FILE* Function,-,fileno_unlocked,int,FILE* Function,+,filesystem_api_error_get_desc,const char*,FS_Error +Function,-,finite,int,double +Function,-,finitef,int,float +Function,-,finitel,int,long double Function,-,fiprintf,int,"FILE*, const char*, ..." Function,-,fiscanf,int,"FILE*, const char*, ..." Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" +Function,+,flipper_application_alloc_thread,FuriThread*,"FlipperApplication*, const char*" Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* +Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*" Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* @@ -723,7 +826,6 @@ Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescr Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus -Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* Function,+,flipper_format_buffered_file_open_always,_Bool,"FlipperFormat*, const char*" @@ -784,10 +886,25 @@ Function,+,flipper_format_write_string_cstr,_Bool,"FlipperFormat*, const char*, Function,+,flipper_format_write_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" Function,+,float_is_equal,_Bool,"float, float" Function,-,flockfile,void,FILE* +Function,-,floor,double,double +Function,-,floorf,float,float +Function,-,floorl,long double,long double Function,-,fls,int,int Function,-,flsl,int,long Function,-,flsll,int,long long +Function,-,fma,double,"double, double, double" +Function,-,fmaf,float,"float, float, float" +Function,-,fmal,long double,"long double, long double, long double" +Function,-,fmax,double,"double, double" +Function,-,fmaxf,float,"float, float" +Function,-,fmaxl,long double,"long double, long double" Function,-,fmemopen,FILE*,"void*, size_t, const char*" +Function,-,fmin,double,"double, double" +Function,-,fminf,float,"float, float" +Function,-,fminl,long double,"long double, long double" +Function,-,fmod,double,"double, double" +Function,-,fmodf,float,"float, float" +Function,-,fmodl,long double,"long double, long double" Function,-,fopen,FILE*,"const char*, const char*" Function,-,fopencookie,FILE*,"void*, const char*, cookie_io_functions_t" Function,-,fprintf,int,"FILE*, const char*, ..." @@ -800,6 +917,9 @@ Function,-,fread,size_t,"void*, size_t, size_t, FILE*" Function,-,fread_unlocked,size_t,"void*, size_t, size_t, FILE*" Function,+,free,void,void* Function,-,freopen,FILE*,"const char*, const char*, FILE*" +Function,-,frexp,double,"double, int*" +Function,-,frexpf,float,"float, int*" +Function,-,frexpl,long double,"long double, int*" Function,-,fscanf,int,"FILE*, const char*, ..." Function,-,fseek,int,"FILE*, long, int" Function,-,fseeko,int,"FILE*, off_t, int" @@ -872,6 +992,12 @@ Function,+,furi_hal_bt_stop_tone_tx,void, Function,+,furi_hal_bt_unlock_core2,void, Function,+,furi_hal_bt_update_battery_level,void,uint8_t Function,+,furi_hal_bt_update_power_state,void, +Function,+,furi_hal_bus_deinit_early,void, +Function,+,furi_hal_bus_disable,void,FuriHalBus +Function,+,furi_hal_bus_enable,void,FuriHalBus +Function,+,furi_hal_bus_init_early,void, +Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus +Function,+,furi_hal_bus_reset,void,FuriHalBus Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" @@ -914,6 +1040,8 @@ Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,-,furi_hal_deinit_early,void, +Function,+,furi_hal_dma_deinit_early,void, +Function,+,furi_hal_dma_init_early,void, Function,-,furi_hal_flash_erase,void,uint8_t Function,-,furi_hal_flash_get_base,size_t, Function,-,furi_hal_flash_get_cycles_count,size_t, @@ -992,14 +1120,16 @@ Function,-,furi_hal_mpu_init,void, Function,+,furi_hal_mpu_protect_disable,void,FuriHalMpuRegion Function,+,furi_hal_mpu_protect_no_access,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_mpu_protect_read_only,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" +Function,-,f_hal_nfc_iso14443a_listener_set_col_res_data,FHalNfcError,"uint8_t*, uint8_t, uint8_t*, uint8_t" Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, +Function,+,furi_hal_power_check_otg_fault,_Bool, Function,+,furi_hal_power_check_otg_status,void, Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_otg,void, Function,+,furi_hal_power_enable_external_3_3v,void, -Function,+,furi_hal_power_enable_otg,void, +Function,+,furi_hal_power_enable_otg,_Bool, Function,+,furi_hal_power_gauge_is_ok,_Bool, Function,+,furi_hal_power_get_bat_health_pct,uint8_t, Function,+,furi_hal_power_get_battery_charge_voltage_limit,float, @@ -1019,6 +1149,7 @@ Function,-,furi_hal_power_insomnia_level,uint16_t, Function,+,furi_hal_power_is_charging,_Bool, Function,+,furi_hal_power_is_charging_done,_Bool, Function,+,furi_hal_power_is_otg_enabled,_Bool, +Function,+,furi_hal_power_is_shutdown_requested,_Bool, Function,+,furi_hal_power_off,void, Function,+,furi_hal_power_reset,void, Function,+,furi_hal_power_set_battery_charge_voltage_limit,void,float @@ -1027,11 +1158,13 @@ Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, Function,+,furi_hal_power_suppress_charge_enter,void, Function,+,furi_hal_power_suppress_charge_exit,void, +Function,+,furi_hal_pwm_is_running,_Bool,FuriHalPwmOutputId Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId Function,+,furi_hal_random_fill_buf,void,"uint8_t*, uint32_t" Function,+,furi_hal_random_get,uint32_t, +Function,+,furi_hal_random_init,void, Function,+,furi_hal_region_get,const FuriHalRegion*, Function,+,furi_hal_region_get_band,const FuriHalRegionBand*,uint32_t Function,+,furi_hal_region_get_name,const char*, @@ -1047,6 +1180,8 @@ Function,+,furi_hal_rtc_datetime_to_timestamp,uint32_t,FuriHalRtcDateTime* Function,-,furi_hal_rtc_deinit_early,void, Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode, Function,+,furi_hal_rtc_get_datetime,void,FuriHalRtcDateTime* +Function,+,furi_hal_rtc_get_days_per_month,uint8_t,"_Bool, uint8_t" +Function,+,furi_hal_rtc_get_days_per_year,uint16_t,uint16_t Function,+,furi_hal_rtc_get_fault_data,uint32_t, Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, @@ -1059,6 +1194,7 @@ Function,+,furi_hal_rtc_get_timestamp,uint32_t, Function,-,furi_hal_rtc_init,void, Function,-,furi_hal_rtc_init_early,void, Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag +Function,+,furi_hal_rtc_is_leap_year,_Bool,uint16_t Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* @@ -1129,6 +1265,7 @@ Function,+,furi_hal_version_get_hw_target,uint8_t, Function,+,furi_hal_version_get_hw_timestamp,uint32_t, Function,+,furi_hal_version_get_hw_version,uint8_t, Function,+,furi_hal_version_get_ic_id,const char*, +Function,+,furi_hal_version_get_mic_id,const char*, Function,+,furi_hal_version_get_model_code,const char*, Function,+,furi_hal_version_get_model_name,const char*, Function,+,furi_hal_version_get_name_ptr,const char*, @@ -1146,6 +1283,8 @@ Function,+,furi_kernel_restore_lock,int32_t,int32_t Function,+,furi_kernel_unlock,int32_t, Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, +Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*" +Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**" Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." Function,+,furi_log_set_level,void,FuriLogLevel @@ -1296,6 +1435,10 @@ Function,+,furi_timer_start,FuriStatus,"FuriTimer*, uint32_t" Function,+,furi_timer_stop,FuriStatus,FuriTimer* Function,-,fwrite,size_t,"const void*, size_t, size_t, FILE*" Function,-,fwrite_unlocked,size_t,"const void*, size_t, size_t, FILE*" +Function,-,gamma,double,double +Function,-,gamma_r,double,"double, int*" +Function,-,gammaf,float,float +Function,-,gammaf_r,float,"float, int*" Function,-,gap_get_state,GapState, Function,-,gap_init,_Bool,"GapConfig*, GapEventCallback, void*" Function,-,gap_start_advertising,void, @@ -1328,6 +1471,9 @@ Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*" Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*" Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*" Function,+,hex_chars_to_uint8,_Bool,"const char*, uint8_t*" +Function,-,hypot,double,"double, double" +Function,-,hypotf,float,"float, float" +Function,-,hypotl,long double,"long double, long double" Function,+,icon_animation_alloc,IconAnimation*,const Icon* Function,+,icon_animation_free,void,IconAnimation* Function,+,icon_animation_get_height,uint8_t,const IconAnimation* @@ -1339,7 +1485,12 @@ Function,+,icon_animation_stop,void,IconAnimation* Function,+,icon_get_data,const uint8_t*,const Icon* Function,+,icon_get_height,uint8_t,const Icon* Function,+,icon_get_width,uint8_t,const Icon* +Function,-,ilogb,int,double +Function,-,ilogbf,int,float +Function,-,ilogbl,int,long double Function,-,index,char*,"const char*, int" +Function,-,infinity,double, +Function,-,infinityf,float, Function,-,initstate,char*,"unsigned, char*, size_t" Function,+,input_get_key_name,const char*,InputKey Function,+,input_get_type_name,const char*,InputType @@ -1359,8 +1510,12 @@ Function,-,isdigit,int,int Function,-,isdigit_l,int,"int, locale_t" Function,-,isgraph,int,int Function,-,isgraph_l,int,"int, locale_t" +Function,-,isinf,int,double +Function,-,isinff,int,float Function,-,islower,int,int Function,-,islower_l,int,"int, locale_t" +Function,-,isnan,int,double +Function,-,isnanf,int,float Function,-,isprint,int,int Function,-,isprint_l,int,"int, locale_t" Function,-,ispunct,int,int @@ -1372,18 +1527,39 @@ Function,-,isupper_l,int,"int, locale_t" Function,-,isxdigit,int,int Function,-,isxdigit_l,int,"int, locale_t" Function,-,itoa,char*,"int, char*, int" +Function,-,j0,double,double +Function,-,j0f,float,float +Function,-,j1,double,double +Function,-,j1f,float,float +Function,-,jn,double,"int, double" +Function,-,jnf,float,"int, float" Function,-,jrand48,long,unsigned short[3] Function,-,l64a,char*,long Function,-,labs,long,long Function,-,lcong48,void,unsigned short[7] +Function,-,ldexp,double,"double, int" +Function,-,ldexpf,float,"float, int" +Function,-,ldexpl,long double,"long double, int" Function,-,ldiv,ldiv_t,"long, long" +Function,-,lgamma,double,double +Function,-,lgamma_r,double,"double, int*" +Function,-,lgammaf,float,float +Function,-,lgammaf_r,float,"float, int*" +Function,-,lgammal,long double,long double Function,-,llabs,long long,long long Function,-,lldiv,lldiv_t,"long long, long long" +Function,-,llrint,long long int,double +Function,-,llrintf,long long int,float +Function,-,llrintl,long long int,long double +Function,-,llround,long long int,double +Function,-,llroundf,long long int,float +Function,-,llroundl,long long int,long double Function,+,loader_get_pubsub,FuriPubSub*,Loader* Function,+,loader_is_locked,_Bool,Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void,Loader* -Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" +Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*" +Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_unlock,void,Loader* Function,+,loading_alloc,Loading*, Function,+,loading_free,void,Loading* @@ -1400,7 +1576,28 @@ Function,+,locale_set_measurement_unit,void,LocaleMeasurementUnits Function,+,locale_set_time_format,void,LocaleTimeFormat Function,-,localtime,tm*,const time_t* Function,-,localtime_r,tm*,"const time_t*, tm*" +Function,-,log,double,double +Function,-,log10,double,double +Function,-,log10f,float,float +Function,-,log10l,long double,long double +Function,-,log1p,double,double +Function,-,log1pf,float,float +Function,-,log1pl,long double,long double +Function,-,log2,double,double +Function,-,log2f,float,float +Function,-,log2l,long double,long double +Function,-,logb,double,double +Function,-,logbf,float,float +Function,-,logbl,long double,long double +Function,-,logf,float,float +Function,-,logl,long double,long double Function,-,lrand48,long, +Function,-,lrint,long int,double +Function,-,lrintf,long int,float +Function,-,lrintl,long int,long double +Function,-,lround,long int,double +Function,-,lroundf,long int,float +Function,-,lroundl,long,long double Function,+,malloc,void*,size_t Function,+,manchester_advance,_Bool,"ManchesterState, ManchesterEvent, ManchesterState*, _Bool*" Function,+,manchester_encoder_advance,_Bool,"ManchesterEncoderState*, const _Bool, ManchesterEncoderResult*" @@ -1478,7 +1675,34 @@ Function,-,mkstemp,int,char* Function,-,mkstemps,int,"char*, int" Function,-,mktemp,char*,char* Function,-,mktime,time_t,tm* +Function,-,modf,double,"double, double*" +Function,-,modff,float,"float, float*" +Function,-,modfl,long double,"long double, long double*" Function,-,mrand48,long, +Function,-,music_worker_alloc,MusicWorker*, +Function,-,music_worker_clear,void,MusicWorker* +Function,-,music_worker_free,void,MusicWorker* +Function,-,music_worker_is_playing,_Bool,MusicWorker* +Function,-,music_worker_load,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_fmf_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_string,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_set_callback,void,"MusicWorker*, MusicWorkerCallback, void*" +Function,-,music_worker_set_volume,void,"MusicWorker*, float" +Function,-,music_worker_start,void,MusicWorker* +Function,-,music_worker_stop,void,MusicWorker* +Function,-,nan,double,const char* +Function,-,nanf,float,const char* +Function,-,nanl,long double,const char* +Function,-,nearbyint,double,double +Function,-,nearbyintf,float,float +Function,-,nearbyintl,long double,long double +Function,-,nextafter,double,"double, double" +Function,-,nextafterf,float,"float, float" +Function,-,nextafterl,long double,"long double, long double" +Function,-,nexttoward,double,"double, long double" +Function,-,nexttowardf,float,"float, long double" +Function,-,nexttowardl,long double,"long double, long double" Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" @@ -1521,6 +1745,35 @@ Function,+,path_extract_dirname,void,"const char*, FuriString*" Function,+,path_extract_extension,void,"FuriString*, char*, size_t" Function,+,path_extract_filename,void,"FuriString*, FuriString*, _Bool" Function,+,path_extract_filename_no_ext,void,"const char*, FuriString*" +Function,+,pb_close_string_substream,_Bool,"pb_istream_t*, pb_istream_t*" +Function,+,pb_decode,_Bool,"pb_istream_t*, const pb_msgdesc_t*, void*" +Function,+,pb_decode_bool,_Bool,"pb_istream_t*, _Bool*" +Function,+,pb_decode_ex,_Bool,"pb_istream_t*, const pb_msgdesc_t*, void*, unsigned int" +Function,+,pb_decode_fixed32,_Bool,"pb_istream_t*, void*" +Function,+,pb_decode_fixed64,_Bool,"pb_istream_t*, void*" +Function,+,pb_decode_svarint,_Bool,"pb_istream_t*, int64_t*" +Function,+,pb_decode_tag,_Bool,"pb_istream_t*, pb_wire_type_t*, uint32_t*, _Bool*" +Function,+,pb_decode_varint,_Bool,"pb_istream_t*, uint64_t*" +Function,+,pb_decode_varint32,_Bool,"pb_istream_t*, uint32_t*" +Function,+,pb_default_field_callback,_Bool,"pb_istream_t*, pb_ostream_t*, const pb_field_t*" +Function,+,pb_encode,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_encode_ex,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*, unsigned int" +Function,+,pb_encode_fixed32,_Bool,"pb_ostream_t*, const void*" +Function,+,pb_encode_fixed64,_Bool,"pb_ostream_t*, const void*" +Function,+,pb_encode_string,_Bool,"pb_ostream_t*, const pb_byte_t*, size_t" +Function,+,pb_encode_submessage,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_encode_svarint,_Bool,"pb_ostream_t*, int64_t" +Function,+,pb_encode_tag,_Bool,"pb_ostream_t*, pb_wire_type_t, uint32_t" +Function,+,pb_encode_tag_for_field,_Bool,"pb_ostream_t*, const pb_field_iter_t*" +Function,+,pb_encode_varint,_Bool,"pb_ostream_t*, uint64_t" +Function,+,pb_get_encoded_size,_Bool,"size_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_istream_from_buffer,pb_istream_t,"const pb_byte_t*, size_t" +Function,+,pb_make_string_substream,_Bool,"pb_istream_t*, pb_istream_t*" +Function,+,pb_ostream_from_buffer,pb_ostream_t,"pb_byte_t*, size_t" +Function,+,pb_read,_Bool,"pb_istream_t*, pb_byte_t*, size_t" +Function,+,pb_release,void,"const pb_msgdesc_t*, void*" +Function,+,pb_skip_field,_Bool,"pb_istream_t*, pb_wire_type_t" +Function,+,pb_write,_Bool,"pb_ostream_t*, const pb_byte_t*, size_t" Function,-,pcTaskGetName,char*,TaskHandle_t Function,-,pcTimerGetName,const char*,TimerHandle_t Function,-,pclose,int,FILE* @@ -1546,12 +1799,17 @@ Function,+,popup_set_icon,void,"Popup*, uint8_t, uint8_t, const Icon*" Function,+,popup_set_text,void,"Popup*, const char*, uint8_t, uint8_t, Align, Align" Function,+,popup_set_timeout,void,"Popup*, uint32_t" Function,-,posix_memalign,int,"void**, size_t, size_t" +Function,-,pow,double,"double, double" +Function,-,pow10,double,double +Function,-,pow10f,float,float Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool" Function,+,power_get_info,void,"Power*, PowerInfo*" Function,+,power_get_pubsub,FuriPubSub*,Power* Function,+,power_is_battery_healthy,_Bool,Power* Function,+,power_off,void,Power* Function,+,power_reboot,void,PowerBootMode +Function,+,powf,float,"float, float" +Function,-,powl,long double,"long double, long double" Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t" Function,-,printf,int,"const char*, ..." Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." @@ -1609,11 +1867,23 @@ Function,+,realloc,void*,"void*, size_t" Function,-,reallocarray,void*,"void*, size_t, size_t" Function,-,reallocf,void*,"void*, size_t" Function,-,realpath,char*,"const char*, char*" +Function,-,remainder,double,"double, double" +Function,-,remainderf,float,"float, float" +Function,-,remainderl,long double,"long double, long double" Function,-,remove,int,const char* +Function,-,remquo,double,"double, double, int*" +Function,-,remquof,float,"float, float, int*" +Function,-,remquol,long double,"long double, long double, int*" Function,-,rename,int,"const char*, const char*" Function,-,renameat,int,"int, const char*, int, const char*" Function,-,rewind,void,FILE* Function,-,rindex,char*,"const char*, int" +Function,-,rint,double,double +Function,-,rintf,float,float +Function,-,rintl,long double,long double +Function,-,round,double,double +Function,+,roundf,float,float +Function,-,roundl,long double,long double Function,+,rpc_session_close,void,RpcSession* Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, TickType_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* @@ -1638,6 +1908,12 @@ Function,-,rpmatch,int,const char* Function,+,saved_struct_get_payload_size,_Bool,"const char*, uint8_t, uint8_t, size_t*" Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" +Function,-,scalbln,double,"double, long int" +Function,-,scalblnf,float,"float, long int" +Function,-,scalblnl,long double,"long double, long" +Function,-,scalbn,double,"double, int" +Function,+,scalbnf,float,"float, int" +Function,-,scalbnl,long double,"long double, int" Function,-,scanf,int,"const char*, ..." Function,+,scene_manager_alloc,SceneManager*,"const SceneManagerHandlers*, void*" Function,+,scene_manager_free,void,SceneManager* @@ -1677,11 +1953,22 @@ Function,+,sha256_finish,void,"sha256_context*, unsigned char[32]" Function,+,sha256_process,void,sha256_context* Function,+,sha256_start,void,sha256_context* Function,+,sha256_update,void,"sha256_context*, const unsigned char*, unsigned int" +Function,-,sin,double,double +Function,-,sincos,void,"double, double*, double*" +Function,-,sincosf,void,"float, float*, float*" +Function,-,sinf,float,float +Function,-,sinh,double,double +Function,-,sinhf,float,float +Function,-,sinhl,long double,long double +Function,-,sinl,long double,long double Function,-,siprintf,int,"char*, const char*, ..." Function,-,siscanf,int,"const char*, const char*, ..." Function,-,sniprintf,int,"char*, size_t, const char*, ..." Function,+,snprintf,int,"char*, size_t, const char*, ..." Function,-,sprintf,int,"char*, const char*, ..." +Function,-,sqrt,double,double +Function,-,sqrtf,float,float +Function,-,sqrtl,long double,long double Function,+,srand,void,unsigned Function,-,srand48,void,long Function,-,srandom,void,unsigned @@ -1836,6 +2123,12 @@ Function,+,submenu_reset,void,Submenu* Function,+,submenu_set_header,void,"Submenu*, const char*" Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" Function,-,system,int,const char* +Function,-,tan,double,double +Function,-,tanf,float,float +Function,-,tanh,double,double +Function,-,tanhf,float,float +Function,-,tanhl,long double,long double +Function,-,tanl,long double,long double Function,+,tar_archive_add_dir,_Bool,"TarArchive*, const char*, const char*" Function,+,tar_archive_add_file,_Bool,"TarArchive*, const char*, const char*, const int32_t" Function,+,tar_archive_alloc,TarArchive*,Storage* @@ -1868,6 +2161,9 @@ Function,+,text_input_reset,void,TextInput* Function,+,text_input_set_header_text,void,"TextInput*, const char*" Function,+,text_input_set_result_callback,void,"TextInput*, TextInputCallback, void*, char*, size_t, _Bool" Function,+,text_input_set_validator,void,"TextInput*, TextInputValidatorCallback, void*" +Function,-,tgamma,double,double +Function,-,tgammaf,float,float +Function,-,tgammal,long double,long double Function,-,time,time_t,time_t* Function,-,timingsafe_bcmp,int,"const void*, const void*, size_t" Function,-,timingsafe_memcmp,int,"const void*, const void*, size_t" @@ -1879,6 +2175,9 @@ Function,-,tolower,int,int Function,-,tolower_l,int,"int, locale_t" Function,-,toupper,int,int Function,-,toupper_l,int,"int, locale_t" +Function,-,trunc,double,double +Function,-,truncf,float,float +Function,-,truncl,long double,long double Function,-,tzset,void, Function,-,uECC_compress,void,"const uint8_t*, uint8_t*, uECC_Curve" Function,+,uECC_compute_public_key,int,"const uint8_t*, uint8_t*, uECC_Curve" @@ -1965,6 +2264,7 @@ Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* @@ -1988,6 +2288,8 @@ Function,-,vdprintf,int,"int, const char*, __gnuc_va_list" Function,+,version_get,const Version*, Function,+,version_get_builddate,const char*,const Version* Function,+,version_get_dirty_flag,_Bool,const Version* +Function,+,version_get_firmware_origin,const char*,const Version* +Function,+,version_get_git_origin,const char*,const Version* Function,+,version_get_gitbranch,const char*,const Version* Function,+,version_get_gitbranchnum,const char*,const Version* Function,+,version_get_githash,const char*,const Version* @@ -2108,6 +2410,12 @@ Function,-,xTimerGetTimerDaemonTaskHandle,TaskHandle_t, Function,-,xTimerIsTimerActive,BaseType_t,TimerHandle_t Function,-,xTimerPendFunctionCall,BaseType_t,"PendedFunction_t, void*, uint32_t, TickType_t" Function,-,xTimerPendFunctionCallFromISR,BaseType_t,"PendedFunction_t, void*, uint32_t, BaseType_t*" +Function,-,y0,double,double +Function,-,y0f,float,float +Function,-,y1,double,double +Function,-,y1f,float,float +Function,-,yn,double,"int, double" +Function,-,ynf,float,"int, float" Variable,-,AHBPrescTable,const uint32_t[16], Variable,-,APBPrescTable,const uint32_t[8], Variable,-,ITM_RxBuffer,volatile int32_t, @@ -2223,6 +2531,7 @@ Variable,+,message_force_vibro_setting_off,const NotificationMessage, Variable,+,message_force_vibro_setting_on,const NotificationMessage, Variable,+,message_green_0,const NotificationMessage, Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_lcd_contrast_update,const NotificationMessage, Variable,+,message_note_a0,const NotificationMessage, Variable,+,message_note_a1,const NotificationMessage, Variable,+,message_note_a2,const NotificationMessage, @@ -2366,6 +2675,7 @@ Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, Variable,+,sequence_display_backlight_on,const NotificationSequence, Variable,+,sequence_double_vibro,const NotificationSequence, Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_lcd_contrast_update,const NotificationSequence, Variable,+,sequence_not_charging,const NotificationSequence, Variable,+,sequence_reset_blue,const NotificationSequence, Variable,+,sequence_reset_display,const NotificationSequence, diff --git a/firmware/targets/f18/furi_hal/furi_hal.c b/firmware/targets/f18/furi_hal/furi_hal.c index 4064dd64726a..5f4e6165dca4 100644 --- a/firmware/targets/f18/furi_hal/furi_hal.c +++ b/firmware/targets/f18/furi_hal/furi_hal.c @@ -9,6 +9,8 @@ void furi_hal_init_early() { furi_hal_cortex_init_early(); furi_hal_clock_init_early(); + furi_hal_bus_init_early(); + furi_hal_dma_init_early(); furi_hal_resources_init_early(); furi_hal_os_init(); furi_hal_spi_config_init_early(); @@ -22,12 +24,15 @@ void furi_hal_deinit_early() { furi_hal_i2c_deinit_early(); furi_hal_spi_config_deinit_early(); furi_hal_resources_deinit_early(); + furi_hal_dma_deinit_early(); + furi_hal_bus_deinit_early(); furi_hal_clock_deinit_early(); } void furi_hal_init() { furi_hal_mpu_init(); furi_hal_clock_init(); + furi_hal_random_init(); furi_hal_console_init(); furi_hal_rtc_init(); furi_hal_interrupt_init(); diff --git a/firmware/targets/f18/furi_hal/furi_hal_power_calibration.h b/firmware/targets/f18/furi_hal/furi_hal_power_calibration.h deleted file mode 100644 index e97e1657dba2..000000000000 --- a/firmware/targets/f18/furi_hal/furi_hal_power_calibration.h +++ /dev/null @@ -1,37 +0,0 @@ -const ParamCEDV cedv = { - .cedv_conf.gauge_conf = - { - .CCT = 1, - .CSYNC = 0, - .EDV_CMP = 0, - .SC = 1, - .FIXED_EDV0 = 1, - .FCC_LIM = 1, - .FC_FOR_VDQ = 1, - .IGNORE_SD = 1, - .SME0 = 0, - }, - .full_charge_cap = 1300, - .design_cap = 1300, - .EDV0 = 3300, - .EDV1 = 3321, - .EDV2 = 3355, - .EMF = 3679, - .C0 = 430, - .C1 = 0, - .R1 = 408, - .R0 = 334, - .T0 = 4626, - .TC = 11, - .DOD0 = 4044, - .DOD10 = 3905, - .DOD20 = 3807, - .DOD30 = 3718, - .DOD40 = 3642, - .DOD50 = 3585, - .DOD60 = 3546, - .DOD70 = 3514, - .DOD80 = 3477, - .DOD90 = 3411, - .DOD100 = 3299, -}; diff --git a/firmware/targets/f18/furi_hal/furi_hal_power_config.c b/firmware/targets/f18/furi_hal/furi_hal_power_config.c new file mode 100644 index 000000000000..50efceb7de38 --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_power_config.c @@ -0,0 +1,149 @@ +#include + +const BQ27220DMGaugingConfig furi_hal_power_gauge_data_memory_gauging_config = { + .CCT = 1, + .CSYNC = 0, + .EDV_CMP = 0, + .SC = 1, + .FIXED_EDV0 = 1, + .FCC_LIM = 1, + .FC_FOR_VDQ = 1, + .IGNORE_SD = 1, + .SME0 = 0, +}; + +const BQ27220DMData furi_hal_power_gauge_data_memory[] = { + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1GaugingConfig, + .type = BQ27220DMTypePtr16, + .value.u32 = (uint32_t)&furi_hal_power_gauge_data_memory_gauging_config, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1FullChargeCapacity, + .type = BQ27220DMTypeU16, + .value.u16 = 1300, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1DesignCapacity, + .type = BQ27220DMTypeU16, + .value.u16 = 1300, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EMF, + .type = BQ27220DMTypeU16, + .value.u16 = 3679, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1C0, + .type = BQ27220DMTypeU16, + .value.u16 = 430, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1R0, + .type = BQ27220DMTypeU16, + .value.u16 = 334, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1T0, + .type = BQ27220DMTypeU16, + .value.u16 = 4626, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1R1, + .type = BQ27220DMTypeU16, + .value.u16 = 408, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1TC, + .type = BQ27220DMTypeU8, + .value.u8 = 11, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1C1, + .type = BQ27220DMTypeU8, + .value.u8 = 0, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD0, + .type = BQ27220DMTypeU16, + .value.u16 = 4044, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD10, + .type = BQ27220DMTypeU16, + .value.u16 = 3905, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD20, + .type = BQ27220DMTypeU16, + .value.u16 = 3807, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD30, + .type = BQ27220DMTypeU16, + .value.u16 = 3718, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD40, + .type = BQ27220DMTypeU16, + .value.u16 = 3642, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD50, + .type = BQ27220DMTypeU16, + .value.u16 = 3585, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD60, + .type = BQ27220DMTypeU16, + .value.u16 = 3546, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD70, + .type = BQ27220DMTypeU16, + .value.u16 = 3514, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD80, + .type = BQ27220DMTypeU16, + .value.u16 = 3477, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD90, + .type = BQ27220DMTypeU16, + .value.u16 = 3411, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD100, + .type = BQ27220DMTypeU16, + .value.u16 = 3299, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV0, + .type = BQ27220DMTypeU16, + .value.u16 = 3300, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV1, + .type = BQ27220DMTypeU16, + .value.u16 = 3321, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV2, + .type = BQ27220DMTypeU16, + .value.u16 = 3355, + }, + { + .address = BQ27220DMAddressCalibrationCurrentDeadband, + .type = BQ27220DMTypeU8, + .value.u8 = 1, + }, + { + .address = BQ27220DMAddressConfigurationPowerSleepCurrent, + .type = BQ27220DMTypeI16, + .value.i16 = 1, + }, + { + .type = BQ27220DMTypeEnd, + }, +}; diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index 6db483dbcbb8..32c9b619c4cf 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -118,6 +119,13 @@ static void furi_hal_resources_init_input_pins(GpioMode mode) { } void furi_hal_resources_init_early() { + furi_hal_bus_enable(FuriHalBusGPIOA); + furi_hal_bus_enable(FuriHalBusGPIOB); + furi_hal_bus_enable(FuriHalBusGPIOC); + furi_hal_bus_enable(FuriHalBusGPIOD); + furi_hal_bus_enable(FuriHalBusGPIOE); + furi_hal_bus_enable(FuriHalBusGPIOH); + furi_hal_resources_init_input_pins(GpioModeInput); // SD Card stepdown control @@ -162,6 +170,12 @@ void furi_hal_resources_init_early() { void furi_hal_resources_deinit_early() { furi_hal_resources_init_input_pins(GpioModeAnalog); + furi_hal_bus_disable(FuriHalBusGPIOA); + furi_hal_bus_disable(FuriHalBusGPIOB); + furi_hal_bus_disable(FuriHalBusGPIOC); + furi_hal_bus_disable(FuriHalBusGPIOD); + furi_hal_bus_disable(FuriHalBusGPIOE); + furi_hal_bus_disable(FuriHalBusGPIOH); } void furi_hal_resources_init() { diff --git a/firmware/targets/f18/furi_hal/furi_hal_spi_config.c b/firmware/targets/f18/furi_hal/furi_hal_spi_config.c index 0fbe55e2ac96..5ac84906f82c 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_spi_config.c +++ b/firmware/targets/f18/furi_hal/furi_hal_spi_config.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -96,28 +97,17 @@ void furi_hal_spi_config_init() { static void furi_hal_spi_bus_r_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { if(event == FuriHalSpiBusEventInit) { furi_hal_spi_bus_r_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); bus->current_handle = NULL; } else if(event == FuriHalSpiBusEventDeinit) { furi_mutex_free(furi_hal_spi_bus_r_mutex); - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); - LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); } else if(event == FuriHalSpiBusEventLock) { furi_check(furi_mutex_acquire(furi_hal_spi_bus_r_mutex, FuriWaitForever) == FuriStatusOk); } else if(event == FuriHalSpiBusEventUnlock) { furi_check(furi_mutex_release(furi_hal_spi_bus_r_mutex) == FuriStatusOk); } else if(event == FuriHalSpiBusEventActivate) { - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); + furi_hal_bus_enable(FuriHalBusSPI1); } else if(event == FuriHalSpiBusEventDeactivate) { - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(FuriHalBusSPI1); } } @@ -131,28 +121,17 @@ FuriMutex* furi_hal_spi_bus_d_mutex = NULL; static void furi_hal_spi_bus_d_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { if(event == FuriHalSpiBusEventInit) { furi_hal_spi_bus_d_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); bus->current_handle = NULL; } else if(event == FuriHalSpiBusEventDeinit) { furi_mutex_free(furi_hal_spi_bus_d_mutex); - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); } else if(event == FuriHalSpiBusEventLock) { furi_check(furi_mutex_acquire(furi_hal_spi_bus_d_mutex, FuriWaitForever) == FuriStatusOk); } else if(event == FuriHalSpiBusEventUnlock) { furi_check(furi_mutex_release(furi_hal_spi_bus_d_mutex) == FuriStatusOk); } else if(event == FuriHalSpiBusEventActivate) { - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); + furi_hal_bus_enable(FuriHalBusSPI2); } else if(event == FuriHalSpiBusEventDeactivate) { - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(FuriHalBusSPI2); } } diff --git a/firmware/targets/f18/furi_hal/furi_hal_version_device.c b/firmware/targets/f18/furi_hal/furi_hal_version_device.c index 1b5090b930a8..e038b98d7376 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_version_device.c +++ b/firmware/targets/f18/furi_hal/furi_hal_version_device.c @@ -5,17 +5,21 @@ bool furi_hal_version_do_i_belong_here() { } const char* furi_hal_version_get_model_name() { - return "Komi"; + return "Flipper Nano"; } const char* furi_hal_version_get_model_code() { - return "N/A"; + return "FN.1"; } const char* furi_hal_version_get_fcc_id() { - return "N/A"; + return "Pending"; } const char* furi_hal_version_get_ic_id() { - return "N/A"; + return "Pending"; +} + +const char* furi_hal_version_get_mic_id() { + return "Pending"; } diff --git a/firmware/targets/f18/target.json b/firmware/targets/f18/target.json index 14d395d22276..4b8fac047547 100644 --- a/firmware/targets/f18/target.json +++ b/firmware/targets/f18/target.json @@ -25,6 +25,7 @@ "appframe", "assets", "one_wire", + "music_worker", "misc", "flipper_application", "flipperformat", @@ -34,12 +35,18 @@ "excluded_sources": [ "furi_hal_infrared.c", "furi_hal_nfc.c", + "f_hal_nfc.c", + "f_hal_nfca.c", + "f_hal_nfc_event.c", + "f_hal_nfc_irq.c", + "f_hal_nfc_timer.c", "furi_hal_rfid.c", "furi_hal_subghz.c" ], "excluded_headers": [ "furi_hal_infrared.h", "furi_hal_nfc.h", + "f_hal_nfc.h", "furi_hal_rfid.h", "furi_hal_subghz.h", "furi_hal_ibutton.h", @@ -53,4 +60,4 @@ "infrared", "st25rfal002" ] -} \ No newline at end of file +} diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d73a1c7bc05e..e7fa9038024a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,26.2,, +Version,+,41.8,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -36,8 +36,10 @@ Header,+,applications/services/notification/notification_messages.h,, Header,+,applications/services/power/power_service/power.h,, Header,+,applications/services/rpc/rpc_app.h,, Header,+,applications/services/storage/storage.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_bus.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_clock.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_console.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_dma.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_flash.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_gpio.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_i2c_config.h,, @@ -47,18 +49,18 @@ Header,+,firmware/targets/f7/furi_hal/furi_hal_idle_timer.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_interrupt.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_nfc.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_os.h,, -Header,-,firmware/targets/f7/furi_hal/furi_hal_power_calibration.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_pwm.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_resources.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_rfid.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_config.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_types.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_subghz.h,, -Header,+,firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_target_hw.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_uart.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_usb_cdc.h,, Header,+,firmware/targets/f7/platform_specific/intrinsic_export.h,, +Header,+,firmware/targets/f7/platform_specific/math_wrapper.h,, +Header,+,firmware/targets/furi_hal_include/f_hal_nfc.h,, Header,+,firmware/targets/furi_hal_include/furi_hal.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.h,, @@ -84,6 +86,8 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_vibro.h,, +Header,+,lib/digital_signal/digital_signal.h,, +Header,+,lib/drivers/cc1101_regs.h,, Header,+,lib/flipper_application/api_hashtable/api_hashtable.h,, Header,+,lib/flipper_application/api_hashtable/compilesort.hpp,, Header,+,lib/flipper_application/flipper_application.h,, @@ -143,11 +147,42 @@ Header,+,lib/mlib/m-list.h,, Header,+,lib/mlib/m-rbtree.h,, Header,+,lib/mlib/m-tuple.h,, Header,+,lib/mlib/m-variant.h,, +Header,+,lib/music_worker/music_worker.h,, +Header,+,lib/nanopb/pb.h,, +Header,+,lib/nanopb/pb_decode.h,, +Header,+,lib/nanopb/pb_encode.h,, +Header,+,lib/nfc/helpers/iso14443_crc.h,, +Header,+,lib/nfc/helpers/nfc_util.h,, +Header,+,lib/nfc/nfc.h,, Header,+,lib/nfc/nfc_device.h,, +Header,+,lib/nfc/nfc_listener.h,, +Header,+,lib/nfc/nfc_poller.h,, +Header,+,lib/nfc/nfc_scanner.h,, +Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a.h,, +Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h,, +Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h,, +Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync_api.h,, +Header,+,lib/nfc/protocols/iso14443_3b/iso14443_3b.h,, +Header,+,lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h,, +Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a.h,, +Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h,, +Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h,, +Header,+,lib/nfc/protocols/mf_classic/mf_classic.h,, +Header,+,lib/nfc/protocols/mf_classic/mf_classic_listener.h,, +Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller.h,, +Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.h,, +Header,+,lib/nfc/protocols/mf_desfire/mf_desfire.h,, +Header,+,lib/nfc/protocols/mf_desfire/mf_desfire_poller.h,, +Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight.h,, +Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h,, +Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h,, +Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync_api.h,, Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_slave.h,, Header,+,lib/print/wrappers.h,, +Header,+,lib/pulse_reader/pulse_reader.h,, +Header,+,lib/signal_reader/signal_reader.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_adc.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_bus.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_comp.h,, @@ -175,20 +210,23 @@ Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_tim.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_usart.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,, -Header,+,lib/pulse_reader/pulse_reader.h,, Header,+,lib/subghz/blocks/const.h,, Header,+,lib/subghz/blocks/decoder.h,, Header,+,lib/subghz/blocks/encoder.h,, Header,+,lib/subghz/blocks/generic.h,, Header,+,lib/subghz/blocks/math.h,, +Header,+,lib/subghz/devices/cc1101_configs.h,, +Header,+,lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h,, Header,+,lib/subghz/environment.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, Header,+,lib/subghz/registry.h,, +Header,+,lib/subghz/subghz_protocol_registry.h,, Header,+,lib/subghz/subghz_setting.h,, Header,+,lib/subghz/subghz_tx_rx_worker.h,, Header,+,lib/subghz/subghz_worker.h,, Header,+,lib/subghz/transmitter.h,, +Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, @@ -203,6 +241,7 @@ Header,+,lib/toolbox/protocols/protocol_dict.h,, Header,+,lib/toolbox/random_name.h,, Header,+,lib/toolbox/saved_struct.h,, Header,+,lib/toolbox/sha256.h,, +Header,+,lib/toolbox/simple_array.h,, Header,+,lib/toolbox/stream/buffered_file_stream.h,, Header,+,lib/toolbox/stream/file_stream.h,, Header,+,lib/toolbox/stream/stream.h,, @@ -310,6 +349,7 @@ Function,-,LL_mDelay,void,uint32_t Function,-,SystemCoreClockUpdate,void, Function,-,SystemInit,void, Function,-,_Exit,void,int +Function,+,__aeabi_uldivmod,void*,"uint64_t, uint64_t" Function,-,__assert,void,"const char*, int, const char*" Function,+,__assert_func,void,"const char*, int, const char*, const char*" Function,+,__clear_cache,void,"void*, void*" @@ -318,6 +358,8 @@ Function,+,__errno,int*, Function,-,__fpclassifyd,int,double Function,-,__fpclassifyf,int,float Function,+,__furi_crash,void, +Function,+,__furi_critical_enter,__FuriCriticalInfo, +Function,+,__furi_critical_exit,void,__FuriCriticalInfo Function,+,__furi_halt,void, Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" Function,-,__getline,ssize_t,"char**, size_t*, FILE*" @@ -542,6 +584,36 @@ Function,-,atoll,long long,const char* Function,-,basename,char*,const char* Function,-,bcmp,int,"const void*, const void*, size_t" Function,-,bcopy,void,"const void*, void*, size_t" +Function,+,bit_buffer_alloc,BitBuffer*,size_t +Function,+,bit_buffer_append,void,"BitBuffer*, const BitBuffer*" +Function,+,bit_buffer_append_bit,void,"BitBuffer*, _Bool" +Function,+,bit_buffer_append_byte,void,"BitBuffer*, uint8_t" +Function,+,bit_buffer_append_bytes,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_append_right,void,"BitBuffer*, const BitBuffer*, size_t" +Function,+,bit_buffer_copy,void,"BitBuffer*, const BitBuffer*" +Function,+,bit_buffer_copy_bits,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_copy_bytes,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_copy_bytes_with_parity,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_copy_left,void,"BitBuffer*, const BitBuffer*, size_t" +Function,+,bit_buffer_copy_right,void,"BitBuffer*, const BitBuffer*, size_t" +Function,+,bit_buffer_free,void,BitBuffer* +Function,+,bit_buffer_get_byte,uint8_t,"const BitBuffer*, size_t" +Function,+,bit_buffer_get_byte_from_bit,uint8_t,"const BitBuffer*, size_t" +Function,+,bit_buffer_get_capacity_bytes,size_t,const BitBuffer* +Function,+,bit_buffer_get_data,const uint8_t*,const BitBuffer* +Function,+,bit_buffer_get_parity,const uint8_t*,const BitBuffer* +Function,+,bit_buffer_get_size,size_t,const BitBuffer* +Function,+,bit_buffer_get_size_bytes,size_t,const BitBuffer* +Function,+,bit_buffer_has_partial_byte,_Bool,const BitBuffer* +Function,+,bit_buffer_reset,void,BitBuffer* +Function,+,bit_buffer_set_byte,void,"BitBuffer*, size_t, uint8_t" +Function,+,bit_buffer_set_byte_with_parity,void,"BitBuffer*, size_t, uint8_t, _Bool" +Function,+,bit_buffer_set_size,void,"BitBuffer*, size_t" +Function,+,bit_buffer_set_size_bytes,void,"BitBuffer*, size_t" +Function,+,bit_buffer_starts_with_byte,_Bool,"const BitBuffer*, uint8_t" +Function,+,bit_buffer_write_bytes,void,"const BitBuffer*, void*, size_t" +Function,+,bit_buffer_write_bytes_mid,void,"const BitBuffer*, void*, size_t, size_t" +Function,+,bit_buffer_write_bytes_with_parity,void,"const BitBuffer*, void*, size_t, size_t*" Function,+,bit_lib_add_parity,size_t,"const uint8_t*, size_t, uint8_t*, size_t, uint8_t, uint8_t, BitLibParity" Function,+,bit_lib_copy_bits,void,"uint8_t*, size_t, size_t, const uint8_t*, size_t" Function,+,bit_lib_crc16,uint16_t,"const uint8_t*, size_t, uint16_t, uint16_t, _Bool, _Bool, uint16_t" @@ -648,26 +720,6 @@ Function,+,canvas_width,uint8_t,const Canvas* Function,-,cbrt,double,double Function,-,cbrtf,float,float Function,-,cbrtl,long double,long double -Function,+,cc1101_calibrate,void,FuriHalSpiBusHandle* -Function,+,cc1101_flush_rx,void,FuriHalSpiBusHandle* -Function,+,cc1101_flush_tx,void,FuriHalSpiBusHandle* -Function,-,cc1101_get_partnumber,uint8_t,FuriHalSpiBusHandle* -Function,+,cc1101_get_rssi,uint8_t,FuriHalSpiBusHandle* -Function,+,cc1101_get_status,CC1101Status,FuriHalSpiBusHandle* -Function,-,cc1101_get_version,uint8_t,FuriHalSpiBusHandle* -Function,+,cc1101_read_fifo,uint8_t,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*" -Function,+,cc1101_read_reg,CC1101Status,"FuriHalSpiBusHandle*, uint8_t, uint8_t*" -Function,+,cc1101_reset,void,FuriHalSpiBusHandle* -Function,+,cc1101_set_frequency,uint32_t,"FuriHalSpiBusHandle*, uint32_t" -Function,-,cc1101_set_intermediate_frequency,uint32_t,"FuriHalSpiBusHandle*, uint32_t" -Function,+,cc1101_set_pa_table,void,"FuriHalSpiBusHandle*, const uint8_t[8]" -Function,+,cc1101_shutdown,void,FuriHalSpiBusHandle* -Function,+,cc1101_strobe,CC1101Status,"FuriHalSpiBusHandle*, uint8_t" -Function,+,cc1101_switch_to_idle,void,FuriHalSpiBusHandle* -Function,+,cc1101_switch_to_rx,void,FuriHalSpiBusHandle* -Function,+,cc1101_switch_to_tx,void,FuriHalSpiBusHandle* -Function,+,cc1101_write_fifo,uint8_t,"FuriHalSpiBusHandle*, const uint8_t*, uint8_t" -Function,+,cc1101_write_reg,CC1101Status,"FuriHalSpiBusHandle*, uint8_t, uint8_t" Function,-,ceil,double,double Function,-,ceilf,float,float Function,-,ceill,long double,long double @@ -702,14 +754,6 @@ Function,-,coshl,long double,long double Function,-,cosl,long double,long double Function,+,crc32_calc_buffer,uint32_t,"uint32_t, const void*, size_t" Function,+,crc32_calc_file,uint32_t,"File*, const FileCrcProgressCb, void*" -Function,-,crypto1_bit,uint8_t,"Crypto1*, uint8_t, int" -Function,-,crypto1_byte,uint8_t,"Crypto1*, uint8_t, int" -Function,-,crypto1_decrypt,void,"Crypto1*, uint8_t*, uint16_t, uint8_t*" -Function,-,crypto1_encrypt,void,"Crypto1*, uint8_t*, uint8_t*, uint16_t, uint8_t*, uint8_t*" -Function,-,crypto1_filter,uint32_t,uint32_t -Function,-,crypto1_init,void,"Crypto1*, uint64_t" -Function,-,crypto1_reset,void,Crypto1* -Function,-,crypto1_word,uint32_t,"Crypto1*, uint32_t, int" Function,-,ctermid,char*,char* Function,-,ctime,char*,const time_t* Function,-,ctime_r,char*,"const time_t*, char*" @@ -767,7 +811,7 @@ Function,+,dir_walk_read,DirWalkResult,"DirWalk*, FuriString*, FileInfo*" Function,+,dir_walk_set_filter_cb,void,"DirWalk*, DirWalkFilterCb, void*" Function,+,dir_walk_set_recursive,void,"DirWalk*, _Bool" Function,-,div,div_t,"int, int" -Function,+,dolphin_deed,void,"Dolphin*, DolphinDeed" +Function,+,dolphin_deed,void,DolphinDeed Function,+,dolphin_deed_get_app,DolphinApp,DolphinDeed Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed @@ -800,12 +844,11 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" -Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*" +Function,+,elf_symbolname_hash,uint32_t,const char* Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* -Function,-,emv_card_emulation,_Bool,FuriHalNfcTxRxContext* -Function,-,emv_read_bank_card,_Bool,"FuriHalNfcTxRxContext*, EmvApplication*" Function,-,erand48,double,unsigned short[3] Function,-,erf,double,double Function,-,erfc,double,double @@ -826,6 +869,39 @@ Function,-,explicit_bzero,void,"void*, size_t" Function,-,expm1,double,double Function,-,expm1f,float,float Function,-,expm1l,long double,long double +Function,-,f_hal_nfc_abort,FHalNfcError, +Function,-,f_hal_nfc_acquire,FHalNfcError, +Function,+,f_hal_nfc_event_start,FHalNfcError, +Function,-,f_hal_nfc_init,FHalNfcError, +Function,-,f_hal_nfc_is_hal_ready,FHalNfcError, +Function,-,f_hal_nfc_iso14443a_listener_set_col_res_data,FHalNfcError,"uint8_t*, uint8_t, uint8_t*, uint8_t" +Function,-,f_hal_nfc_iso14443a_listener_tx_custom_parity,FHalNfcError,"const uint8_t*, const uint8_t*, size_t" +Function,-,f_hal_nfc_iso14443a_poller_trx_short_frame,FHalNfcError,FHalNfcaShortFrame +Function,-,f_hal_nfc_iso14443a_poller_tx_custom_parity,FHalNfcError,"const uint8_t*, size_t" +Function,-,f_hal_nfc_iso14443a_rx_sdd_frame,FHalNfcError,"uint8_t*, size_t, size_t*" +Function,-,f_hal_nfc_iso14443a_tx_sdd_frame,FHalNfcError,"const uint8_t*, size_t" +Function,-,f_hal_nfc_listener_enable_rx,FHalNfcError, +Function,-,f_hal_nfc_listener_idle,FHalNfcError, +Function,+,f_hal_nfc_listener_rx,FHalNfcError,"uint8_t*, size_t, size_t*" +Function,-,f_hal_nfc_listener_sleep,FHalNfcError, +Function,-,f_hal_nfc_listener_tx,FHalNfcError,"const uint8_t*, size_t" +Function,-,f_hal_nfc_listener_wait_event,FHalNfcEvent,uint32_t +Function,-,f_hal_nfc_low_power_mode_start,FHalNfcError, +Function,-,f_hal_nfc_low_power_mode_stop,FHalNfcError, +Function,-,f_hal_nfc_poller_field_on,FHalNfcError, +Function,-,f_hal_nfc_poller_rx,FHalNfcError,"uint8_t*, size_t, size_t*" +Function,-,f_hal_nfc_poller_tx,FHalNfcError,"const uint8_t*, size_t" +Function,-,f_hal_nfc_poller_wait_event,FHalNfcEvent,uint32_t +Function,-,f_hal_nfc_release,FHalNfcError, +Function,-,f_hal_nfc_reset_mode,FHalNfcError, +Function,+,f_hal_nfc_set_mode,FHalNfcError,"FHalNfcMode, FHalNfcTech" +Function,-,f_hal_nfc_timer_block_tx_is_running,_Bool, +Function,-,f_hal_nfc_timer_block_tx_start,void,uint32_t +Function,-,f_hal_nfc_timer_block_tx_start_us,void,uint32_t +Function,-,f_hal_nfc_timer_block_tx_stop,void, +Function,-,f_hal_nfc_timer_fwt_start,void,uint32_t +Function,-,f_hal_nfc_timer_fwt_stop,void, +Function,-,f_hal_nfc_trx_reset,FHalNfcError, Function,-,fabs,double,double Function,-,fabsf,float,float Function,-,fabsl,long double,long double @@ -884,9 +960,11 @@ Function,-,finitel,int,long double Function,-,fiprintf,int,"FILE*, const char*, ..." Function,-,fiscanf,int,"FILE*, const char*, ..." Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" +Function,+,flipper_application_alloc_thread,FuriThread*,"FlipperApplication*, const char*" Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* +Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*" Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* @@ -896,7 +974,6 @@ Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescr Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus -Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* Function,+,flipper_format_buffered_file_open_always,_Bool,"FlipperFormat*, const char*" @@ -1063,6 +1140,12 @@ Function,+,furi_hal_bt_stop_tone_tx,void, Function,+,furi_hal_bt_unlock_core2,void, Function,+,furi_hal_bt_update_battery_level,void,uint8_t Function,+,furi_hal_bt_update_power_state,void, +Function,+,furi_hal_bus_deinit_early,void, +Function,+,furi_hal_bus_disable,void,FuriHalBus +Function,+,furi_hal_bus_enable,void,FuriHalBus +Function,+,furi_hal_bus_init_early,void, +Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus +Function,+,furi_hal_bus_reset,void,FuriHalBus Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" @@ -1105,6 +1188,8 @@ Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,-,furi_hal_deinit_early,void, +Function,+,furi_hal_dma_deinit_early,void, +Function,+,furi_hal_dma_init_early,void, Function,-,furi_hal_flash_erase,void,uint8_t Function,-,furi_hal_flash_get_base,size_t, Function,-,furi_hal_flash_get_cycles_count,size_t, @@ -1206,6 +1291,8 @@ Function,-,furi_hal_nfc_deinit,void, Function,+,furi_hal_nfc_detect,_Bool,"FuriHalNfcDevData*, uint32_t" Function,+,furi_hal_nfc_emulate_nfca,_Bool,"uint8_t*, uint8_t, uint8_t*, uint8_t, FuriHalNfcEmulateCallback, void*, uint32_t" Function,+,furi_hal_nfc_exit_sleep,void, +Function,+,furi_hal_nfc_field_detect_start,void, +Function,+,furi_hal_nfc_field_is_present,_Bool, Function,+,furi_hal_nfc_field_off,void, Function,+,furi_hal_nfc_field_on,void, Function,-,furi_hal_nfc_init,void, @@ -1233,12 +1320,13 @@ Function,+,furi_hal_nfc_tx_rx,_Bool,"FuriHalNfcTxRxContext*, uint16_t" Function,+,furi_hal_nfc_tx_rx_full,_Bool,FuriHalNfcTxRxContext* Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, +Function,+,furi_hal_power_check_otg_fault,_Bool, Function,+,furi_hal_power_check_otg_status,void, Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_otg,void, Function,+,furi_hal_power_enable_external_3_3v,void, -Function,+,furi_hal_power_enable_otg,void, +Function,+,furi_hal_power_enable_otg,_Bool, Function,+,furi_hal_power_gauge_is_ok,_Bool, Function,+,furi_hal_power_get_bat_health_pct,uint8_t, Function,+,furi_hal_power_get_battery_charge_voltage_limit,float, @@ -1258,6 +1346,7 @@ Function,-,furi_hal_power_insomnia_level,uint16_t, Function,+,furi_hal_power_is_charging,_Bool, Function,+,furi_hal_power_is_charging_done,_Bool, Function,+,furi_hal_power_is_otg_enabled,_Bool, +Function,+,furi_hal_power_is_shutdown_requested,_Bool, Function,+,furi_hal_power_off,void, Function,+,furi_hal_power_reset,void, Function,+,furi_hal_power_set_battery_charge_voltage_limit,void,float @@ -1266,11 +1355,13 @@ Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, Function,+,furi_hal_power_suppress_charge_enter,void, Function,+,furi_hal_power_suppress_charge_exit,void, +Function,+,furi_hal_pwm_is_running,_Bool,FuriHalPwmOutputId Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId Function,+,furi_hal_random_fill_buf,void,"uint8_t*, uint32_t" Function,+,furi_hal_random_get,uint32_t, +Function,+,furi_hal_random_init,void, Function,+,furi_hal_region_get,const FuriHalRegion*, Function,+,furi_hal_region_get_band,const FuriHalRegionBand*,uint32_t Function,+,furi_hal_region_get_name,const char*, @@ -1282,35 +1373,32 @@ Function,-,furi_hal_resources_deinit_early,void, Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,-,furi_hal_resources_init,void, Function,-,furi_hal_resources_init_early,void, -Function,+,furi_hal_rfid_change_read_config,void,"float, float" Function,+,furi_hal_rfid_comp_set_callback,void,"FuriHalRfidCompCallback, void*" Function,+,furi_hal_rfid_comp_start,void, Function,+,furi_hal_rfid_comp_stop,void, +Function,+,furi_hal_rfid_field_detect_start,void, +Function,+,furi_hal_rfid_field_detect_stop,void, +Function,+,furi_hal_rfid_field_is_present,_Bool,uint32_t* Function,-,furi_hal_rfid_init,void, Function,+,furi_hal_rfid_pin_pull_pulldown,void, Function,+,furi_hal_rfid_pin_pull_release,void, -Function,+,furi_hal_rfid_pins_emulate,void, -Function,+,furi_hal_rfid_pins_read,void, Function,+,furi_hal_rfid_pins_reset,void, -Function,+,furi_hal_rfid_set_emulate_period,void,uint32_t -Function,+,furi_hal_rfid_set_emulate_pulse,void,uint32_t Function,+,furi_hal_rfid_set_read_period,void,uint32_t Function,+,furi_hal_rfid_set_read_pulse,void,uint32_t -Function,+,furi_hal_rfid_tim_emulate,void,float Function,+,furi_hal_rfid_tim_emulate_dma_start,void,"uint32_t*, uint32_t*, size_t, FuriHalRfidDMACallback, void*" Function,+,furi_hal_rfid_tim_emulate_dma_stop,void, -Function,+,furi_hal_rfid_tim_emulate_start,void,"FuriHalRfidEmulateCallback, void*" -Function,+,furi_hal_rfid_tim_emulate_stop,void, -Function,+,furi_hal_rfid_tim_read,void,"float, float" Function,+,furi_hal_rfid_tim_read_capture_start,void,"FuriHalRfidReadCaptureCallback, void*" Function,+,furi_hal_rfid_tim_read_capture_stop,void, -Function,+,furi_hal_rfid_tim_read_start,void, +Function,+,furi_hal_rfid_tim_read_continue,void, +Function,+,furi_hal_rfid_tim_read_pause,void, +Function,+,furi_hal_rfid_tim_read_start,void,"float, float" Function,+,furi_hal_rfid_tim_read_stop,void, -Function,+,furi_hal_rfid_tim_reset,void, Function,+,furi_hal_rtc_datetime_to_timestamp,uint32_t,FuriHalRtcDateTime* Function,-,furi_hal_rtc_deinit_early,void, Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode, Function,+,furi_hal_rtc_get_datetime,void,FuriHalRtcDateTime* +Function,+,furi_hal_rtc_get_days_per_month,uint8_t,"_Bool, uint8_t" +Function,+,furi_hal_rtc_get_days_per_year,uint16_t,uint16_t Function,+,furi_hal_rtc_get_fault_data,uint32_t, Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, @@ -1323,6 +1411,7 @@ Function,+,furi_hal_rtc_get_timestamp,uint32_t, Function,-,furi_hal_rtc_init,void, Function,-,furi_hal_rtc_init_early,void, Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag +Function,+,furi_hal_rtc_is_leap_year,_Bool,uint16_t Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* @@ -1362,6 +1451,7 @@ Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,-,furi_hal_subghz_dump_state,void, Function,+,furi_hal_subghz_flush_rx,void, Function,+,furi_hal_subghz_flush_tx,void, +Function,+,furi_hal_subghz_get_data_gpio,const GpioPin*, Function,+,furi_hal_subghz_get_lqi,uint8_t, Function,+,furi_hal_subghz_get_rssi,float, Function,+,furi_hal_subghz_idle,void, @@ -1369,10 +1459,9 @@ Function,-,furi_hal_subghz_init,void, Function,+,furi_hal_subghz_is_async_tx_complete,_Bool, Function,+,furi_hal_subghz_is_frequency_valid,_Bool,uint32_t Function,+,furi_hal_subghz_is_rx_data_crc_valid,_Bool, -Function,+,furi_hal_subghz_load_custom_preset,void,uint8_t* +Function,+,furi_hal_subghz_load_custom_preset,void,const uint8_t* Function,+,furi_hal_subghz_load_patable,void,const uint8_t[8] -Function,+,furi_hal_subghz_load_preset,void,FuriHalSubGhzPreset -Function,+,furi_hal_subghz_load_registers,void,uint8_t* +Function,+,furi_hal_subghz_load_registers,void,const uint8_t* Function,+,furi_hal_subghz_read_packet,void,"uint8_t*, uint8_t*" Function,+,furi_hal_subghz_reset,void, Function,+,furi_hal_subghz_rx,void, @@ -1381,7 +1470,7 @@ Function,+,furi_hal_subghz_set_async_mirror_pin,void,const GpioPin* Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath -Function,-,furi_hal_subghz_shutdown,void, +Function,+,furi_hal_subghz_shutdown,void, Function,+,furi_hal_subghz_sleep,void, Function,+,furi_hal_subghz_start_async_rx,void,"FuriHalSubGhzCaptureCallback, void*" Function,+,furi_hal_subghz_start_async_tx,_Bool,"FuriHalSubGhzAsyncTxCallback, void*" @@ -1423,6 +1512,7 @@ Function,+,furi_hal_version_get_hw_target,uint8_t, Function,+,furi_hal_version_get_hw_timestamp,uint32_t, Function,+,furi_hal_version_get_hw_version,uint8_t, Function,+,furi_hal_version_get_ic_id,const char*, +Function,+,furi_hal_version_get_mic_id,const char*, Function,+,furi_hal_version_get_model_code,const char*, Function,+,furi_hal_version_get_model_name,const char*, Function,+,furi_hal_version_get_name_ptr,const char*, @@ -1440,6 +1530,8 @@ Function,+,furi_kernel_restore_lock,int32_t,int32_t Function,+,furi_kernel_unlock,int32_t, Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, +Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*" +Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**" Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." Function,+,furi_log_set_level,void,FuriLogLevel @@ -1714,7 +1806,7 @@ Function,+,infrared_worker_rx_set_received_signal_callback,void,"InfraredWorker* Function,+,infrared_worker_rx_start,void,InfraredWorker* Function,+,infrared_worker_rx_stop,void,InfraredWorker* Function,+,infrared_worker_set_decoded_signal,void,"InfraredWorker*, const InfraredMessage*" -Function,+,infrared_worker_set_raw_signal,void,"InfraredWorker*, const uint32_t*, size_t" +Function,+,infrared_worker_set_raw_signal,void,"InfraredWorker*, const uint32_t*, size_t, uint32_t, float" Function,+,infrared_worker_signal_is_decoded,_Bool,const InfraredWorkerSignal* Function,+,infrared_worker_tx_get_signal_steady_callback,InfraredWorkerGetSignalResponse,"void*, InfraredWorker*" Function,+,infrared_worker_tx_set_get_signal_callback,void,"InfraredWorker*, InfraredWorkerGetSignalCallback, void*" @@ -1746,6 +1838,48 @@ Function,-,islower,int,int Function,-,islower_l,int,"int, locale_t" Function,-,isnan,int,double Function,-,isnanf,int,float +Function,+,iso14443_3a_alloc,Iso14443_3aData*, +Function,+,iso14443_3a_copy,void,"Iso14443_3aData*, const Iso14443_3aData*" +Function,+,iso14443_3a_free,void,Iso14443_3aData* +Function,+,iso14443_3a_get_base_data,Iso14443_3aData*,const Iso14443_3aData* +Function,+,iso14443_3a_get_cuid,uint32_t,const Iso14443_3aData* +Function,+,iso14443_3a_get_device_name,const char*,"const Iso14443_3aData*, NfcDeviceNameType" +Function,+,iso14443_3a_get_uid,const uint8_t*,"const Iso14443_3aData*, size_t*" +Function,+,iso14443_3a_is_equal,_Bool,"const Iso14443_3aData*, const Iso14443_3aData*" +Function,+,iso14443_3a_load,_Bool,"Iso14443_3aData*, FlipperFormat*, uint32_t" +Function,+,iso14443_3a_poller_read,Iso14443_3aError,"Nfc*, Iso14443_3aData*" +Function,+,iso14443_3a_reset,void,Iso14443_3aData* +Function,+,iso14443_3a_save,_Bool,"const Iso14443_3aData*, FlipperFormat*" +Function,+,iso14443_3a_set_uid,_Bool,"Iso14443_3aData*, const uint8_t*, size_t" +Function,+,iso14443_3a_verify,_Bool,"Iso14443_3aData*, const FuriString*" +Function,+,iso14443_3b_alloc,Iso14443_3bData*, +Function,+,iso14443_3b_copy,void,"Iso14443_3bData*, const Iso14443_3bData*" +Function,+,iso14443_3b_free,void,Iso14443_3bData* +Function,+,iso14443_3b_get_base_data,Iso14443_3bData*,const Iso14443_3bData* +Function,+,iso14443_3b_get_device_name,const char*,"const Iso14443_3bData*, NfcDeviceNameType" +Function,+,iso14443_3b_get_uid,const uint8_t*,"const Iso14443_3bData*, size_t*" +Function,+,iso14443_3b_is_equal,_Bool,"const Iso14443_3bData*, const Iso14443_3bData*" +Function,+,iso14443_3b_load,_Bool,"Iso14443_3bData*, FlipperFormat*, uint32_t" +Function,+,iso14443_3b_reset,void,Iso14443_3bData* +Function,+,iso14443_3b_save,_Bool,"const Iso14443_3bData*, FlipperFormat*" +Function,+,iso14443_3b_set_uid,_Bool,"Iso14443_3bData*, const uint8_t*, size_t" +Function,+,iso14443_3b_verify,_Bool,"Iso14443_3bData*, const FuriString*" +Function,+,iso14443_4a_alloc,Iso14443_4aData*, +Function,+,iso14443_4a_copy,void,"Iso14443_4aData*, const Iso14443_4aData*" +Function,+,iso14443_4a_free,void,Iso14443_4aData* +Function,+,iso14443_4a_get_base_data,Iso14443_3aData*,const Iso14443_4aData* +Function,+,iso14443_4a_get_device_name,const char*,"const Iso14443_4aData*, NfcDeviceNameType" +Function,+,iso14443_4a_get_uid,const uint8_t*,"const Iso14443_4aData*, size_t*" +Function,+,iso14443_4a_is_ats_supported,_Bool,const Iso14443_4aData* +Function,+,iso14443_4a_is_equal,_Bool,"const Iso14443_4aData*, const Iso14443_4aData*" +Function,+,iso14443_4a_load,_Bool,"Iso14443_4aData*, FlipperFormat*, uint32_t" +Function,+,iso14443_4a_reset,void,Iso14443_4aData* +Function,+,iso14443_4a_save,_Bool,"const Iso14443_4aData*, FlipperFormat*" +Function,+,iso14443_4a_set_uid,_Bool,"Iso14443_4aData*, const uint8_t*, size_t" +Function,+,iso14443_4a_verify,_Bool,"Iso14443_4aData*, const FuriString*" +Function,+,iso14443_crc_append,void,"Iso14443CrcType, BitBuffer*" +Function,+,iso14443_crc_check,_Bool,"Iso14443CrcType, const BitBuffer*" +Function,+,iso14443_crc_trim,void,BitBuffer* Function,-,isprint,int,int Function,-,isprint_l,int,"int, locale_t" Function,-,ispunct,int,int @@ -1813,7 +1947,8 @@ Function,+,loader_get_pubsub,FuriPubSub*,Loader* Function,+,loader_is_locked,_Bool,Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void,Loader* -Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" +Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*" +Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_unlock,void,Loader* Function,+,loading_alloc,Loading*, Function,+,loading_free,void,Loading* @@ -1922,112 +2057,90 @@ Function,+,menu_free,void,Menu* Function,+,menu_get_view,View*,Menu* Function,+,menu_reset,void,Menu* Function,+,menu_set_selected_item,void,"Menu*, uint32_t" -Function,-,mf_classic_auth_attempt,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, MfClassicAuthContext*, uint64_t" -Function,-,mf_classic_auth_init_context,void,"MfClassicAuthContext*, uint8_t" -Function,-,mf_classic_auth_write_block,_Bool,"FuriHalNfcTxRxContext*, MfClassicBlock*, uint8_t, MfClassicKey, uint64_t" -Function,-,mf_classic_authenticate,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint64_t, MfClassicKey" -Function,-,mf_classic_authenticate_skip_activate,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint64_t, MfClassicKey, _Bool, uint32_t" -Function,-,mf_classic_block_to_value,_Bool,"const uint8_t*, int32_t*, uint8_t*" -Function,-,mf_classic_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" -Function,-,mf_classic_dict_add_key,_Bool,"MfClassicDict*, uint8_t*" -Function,-,mf_classic_dict_add_key_str,_Bool,"MfClassicDict*, FuriString*" -Function,-,mf_classic_dict_alloc,MfClassicDict*,MfClassicDictType -Function,-,mf_classic_dict_check_presence,_Bool,MfClassicDictType -Function,-,mf_classic_dict_delete_index,_Bool,"MfClassicDict*, uint32_t" -Function,-,mf_classic_dict_find_index,_Bool,"MfClassicDict*, uint8_t*, uint32_t*" -Function,-,mf_classic_dict_find_index_str,_Bool,"MfClassicDict*, FuriString*, uint32_t*" -Function,-,mf_classic_dict_free,void,MfClassicDict* -Function,-,mf_classic_dict_get_key_at_index,_Bool,"MfClassicDict*, uint64_t*, uint32_t" -Function,-,mf_classic_dict_get_key_at_index_str,_Bool,"MfClassicDict*, FuriString*, uint32_t" -Function,-,mf_classic_dict_get_next_key,_Bool,"MfClassicDict*, uint64_t*" -Function,-,mf_classic_dict_get_next_key_str,_Bool,"MfClassicDict*, FuriString*" -Function,-,mf_classic_dict_get_total_keys,uint32_t,MfClassicDict* -Function,-,mf_classic_dict_is_key_present,_Bool,"MfClassicDict*, uint8_t*" -Function,-,mf_classic_dict_is_key_present_str,_Bool,"MfClassicDict*, FuriString*" -Function,-,mf_classic_dict_rewind,_Bool,MfClassicDict* -Function,-,mf_classic_emulator,_Bool,"MfClassicEmulator*, FuriHalNfcTxRxContext*" -Function,-,mf_classic_get_classic_type,MfClassicType,"uint8_t, uint8_t, uint8_t" -Function,-,mf_classic_get_read_sectors_and_keys,void,"MfClassicData*, uint8_t*, uint8_t*" -Function,-,mf_classic_get_sector_by_block,uint8_t,uint8_t -Function,-,mf_classic_get_sector_trailer_block_num_by_sector,uint8_t,uint8_t -Function,-,mf_classic_get_sector_trailer_by_sector,MfClassicSectorTrailer*,"MfClassicData*, uint8_t" +Function,+,mf_classic_alloc,MfClassicData*, +Function,+,mf_classic_block_to_value,_Bool,"const MfClassicBlock*, int32_t*, uint8_t*" +Function,+,mf_classic_copy,void,"MfClassicData*, const MfClassicData*" +Function,+,mf_classic_detect_protocol,_Bool,"Iso14443_3aData*, MfClassicType*" +Function,+,mf_classic_free,void,MfClassicData* +Function,+,mf_classic_get_base_data,Iso14443_3aData*,const MfClassicData* +Function,+,mf_classic_get_blocks_num_in_sector,uint8_t,uint8_t +Function,+,mf_classic_get_device_name,const char*,"const MfClassicData*, NfcDeviceNameType" +Function,+,mf_classic_get_first_block_num_of_sector,uint8_t,uint8_t +Function,+,mf_classic_get_read_sectors_and_keys,void,"const MfClassicData*, uint8_t*, uint8_t*" +Function,+,mf_classic_get_sector_by_block,uint8_t,uint8_t +Function,+,mf_classic_get_sector_trailer_by_sector,MfClassicSectorTrailer*,"const MfClassicData*, uint8_t" +Function,+,mf_classic_get_sector_trailer_num_by_block,uint8_t,uint8_t +Function,+,mf_classic_get_sector_trailer_num_by_sector,uint8_t,uint8_t Function,-,mf_classic_get_total_block_num,uint16_t,MfClassicType -Function,-,mf_classic_get_total_sectors_num,uint8_t,MfClassicType -Function,-,mf_classic_get_type_str,const char*,MfClassicType -Function,-,mf_classic_halt,void,"FuriHalNfcTxRxContext*, Crypto1*" -Function,-,mf_classic_is_allowed_access_data_block,_Bool,"MfClassicData*, uint8_t, MfClassicKey, MfClassicAction" -Function,-,mf_classic_is_allowed_access_sector_trailer,_Bool,"MfClassicData*, uint8_t, MfClassicKey, MfClassicAction" -Function,-,mf_classic_is_block_read,_Bool,"MfClassicData*, uint8_t" -Function,-,mf_classic_is_card_read,_Bool,MfClassicData* -Function,-,mf_classic_is_key_found,_Bool,"MfClassicData*, uint8_t, MfClassicKey" -Function,-,mf_classic_is_sector_data_read,_Bool,"MfClassicData*, uint8_t" -Function,-,mf_classic_is_sector_read,_Bool,"MfClassicData*, uint8_t" -Function,-,mf_classic_is_sector_trailer,_Bool,uint8_t +Function,+,mf_classic_get_total_sectors_num,uint8_t,MfClassicType +Function,+,mf_classic_get_uid,const uint8_t*,"const MfClassicData*, size_t*" +Function,+,mf_classic_is_allowed_access,_Bool,"MfClassicData*, uint8_t, MfClassicKeyType, MfClassicAction" +Function,+,mf_classic_is_allowed_access_data_block,_Bool,"MfClassicSectorTrailer*, uint8_t, MfClassicKeyType, MfClassicAction" +Function,+,mf_classic_is_block_read,_Bool,"const MfClassicData*, uint8_t" +Function,+,mf_classic_is_card_read,_Bool,const MfClassicData* +Function,+,mf_classic_is_equal,_Bool,"const MfClassicData*, const MfClassicData*" +Function,+,mf_classic_is_key_found,_Bool,"const MfClassicData*, uint8_t, MfClassicKeyType" +Function,+,mf_classic_is_sector_read,_Bool,"const MfClassicData*, uint8_t" +Function,+,mf_classic_is_sector_trailer,_Bool,uint8_t Function,-,mf_classic_is_value_block,_Bool,"MfClassicData*, uint8_t" -Function,-,mf_classic_read_block,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t, MfClassicBlock*" -Function,-,mf_classic_read_card,uint8_t,"FuriHalNfcTxRxContext*, MfClassicReader*, MfClassicData*" -Function,-,mf_classic_read_sector,void,"FuriHalNfcTxRxContext*, MfClassicData*, uint8_t" -Function,-,mf_classic_reader_add_sector,void,"MfClassicReader*, uint8_t, uint64_t, uint64_t" -Function,-,mf_classic_set_block_read,void,"MfClassicData*, uint8_t, MfClassicBlock*" -Function,-,mf_classic_set_key_found,void,"MfClassicData*, uint8_t, MfClassicKey, uint64_t" -Function,-,mf_classic_set_key_not_found,void,"MfClassicData*, uint8_t, MfClassicKey" -Function,-,mf_classic_set_sector_data_not_read,void,MfClassicData* -Function,-,mf_classic_transfer,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t" -Function,-,mf_classic_update_card,uint8_t,"FuriHalNfcTxRxContext*, MfClassicData*" -Function,-,mf_classic_value_cmd,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t, uint8_t, int32_t" -Function,-,mf_classic_value_cmd_full,_Bool,"FuriHalNfcTxRxContext*, MfClassicBlock*, uint8_t, MfClassicKey, uint64_t, int32_t" -Function,-,mf_classic_value_to_block,void,"int32_t, uint8_t, uint8_t*" -Function,-,mf_classic_write_block,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t, MfClassicBlock*" -Function,-,mf_classic_write_sector,_Bool,"FuriHalNfcTxRxContext*, MfClassicData*, MfClassicData*, uint8_t" -Function,-,mf_df_cat_application,void,"MifareDesfireApplication*, FuriString*" -Function,-,mf_df_cat_application_info,void,"MifareDesfireApplication*, FuriString*" -Function,-,mf_df_cat_card_info,void,"MifareDesfireData*, FuriString*" -Function,-,mf_df_cat_data,void,"MifareDesfireData*, FuriString*" -Function,-,mf_df_cat_file,void,"MifareDesfireFile*, FuriString*" -Function,-,mf_df_cat_free_mem,void,"MifareDesfireFreeMemory*, FuriString*" -Function,-,mf_df_cat_key_settings,void,"MifareDesfireKeySettings*, FuriString*" -Function,-,mf_df_cat_version,void,"MifareDesfireVersion*, FuriString*" -Function,-,mf_df_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" -Function,-,mf_df_clear,void,MifareDesfireData* -Function,-,mf_df_parse_get_application_ids_response,_Bool,"uint8_t*, uint16_t, MifareDesfireApplication**" -Function,-,mf_df_parse_get_file_ids_response,_Bool,"uint8_t*, uint16_t, MifareDesfireFile**" -Function,-,mf_df_parse_get_file_settings_response,_Bool,"uint8_t*, uint16_t, MifareDesfireFile*" -Function,-,mf_df_parse_get_free_memory_response,_Bool,"uint8_t*, uint16_t, MifareDesfireFreeMemory*" -Function,-,mf_df_parse_get_key_settings_response,_Bool,"uint8_t*, uint16_t, MifareDesfireKeySettings*" -Function,-,mf_df_parse_get_key_version_response,_Bool,"uint8_t*, uint16_t, MifareDesfireKeyVersion*" -Function,-,mf_df_parse_get_version_response,_Bool,"uint8_t*, uint16_t, MifareDesfireVersion*" -Function,-,mf_df_parse_read_data_response,_Bool,"uint8_t*, uint16_t, MifareDesfireFile*" -Function,-,mf_df_parse_select_application_response,_Bool,"uint8_t*, uint16_t" -Function,-,mf_df_prepare_get_application_ids,uint16_t,uint8_t* -Function,-,mf_df_prepare_get_file_ids,uint16_t,uint8_t* -Function,-,mf_df_prepare_get_file_settings,uint16_t,"uint8_t*, uint8_t" -Function,-,mf_df_prepare_get_free_memory,uint16_t,uint8_t* -Function,-,mf_df_prepare_get_key_settings,uint16_t,uint8_t* -Function,-,mf_df_prepare_get_key_version,uint16_t,"uint8_t*, uint8_t" -Function,-,mf_df_prepare_get_value,uint16_t,"uint8_t*, uint8_t" -Function,-,mf_df_prepare_get_version,uint16_t,uint8_t* -Function,-,mf_df_prepare_read_data,uint16_t,"uint8_t*, uint8_t, uint32_t, uint32_t" -Function,-,mf_df_prepare_read_records,uint16_t,"uint8_t*, uint8_t, uint32_t, uint32_t" -Function,-,mf_df_prepare_select_application,uint16_t,"uint8_t*, uint8_t[3]" -Function,-,mf_df_read_card,_Bool,"FuriHalNfcTxRxContext*, MifareDesfireData*" -Function,-,mf_ul_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" -Function,-,mf_ul_is_full_capture,_Bool,MfUltralightData* -Function,-,mf_ul_prepare_emulation,void,"MfUltralightEmulator*, MfUltralightData*" -Function,-,mf_ul_prepare_emulation_response,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*, uint32_t*, void*" -Function,-,mf_ul_pwdgen_amiibo,uint32_t,FuriHalNfcDevData* -Function,-,mf_ul_pwdgen_xiaomi,uint32_t,FuriHalNfcDevData* -Function,-,mf_ul_read_card,_Bool,"FuriHalNfcTxRxContext*, MfUltralightReader*, MfUltralightData*" -Function,-,mf_ul_reset,void,MfUltralightData* -Function,-,mf_ul_reset_emulation,void,"MfUltralightEmulator*, _Bool" -Function,-,mf_ultralight_authenticate,_Bool,"FuriHalNfcTxRxContext*, uint32_t, uint16_t*" -Function,-,mf_ultralight_fast_read_pages,_Bool,"FuriHalNfcTxRxContext*, MfUltralightReader*, MfUltralightData*" -Function,-,mf_ultralight_get_config_pages,MfUltralightConfigPages*,MfUltralightData* -Function,-,mf_ultralight_read_counters,_Bool,"FuriHalNfcTxRxContext*, MfUltralightData*" -Function,-,mf_ultralight_read_pages,_Bool,"FuriHalNfcTxRxContext*, MfUltralightReader*, MfUltralightData*" -Function,-,mf_ultralight_read_pages_direct,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint8_t*" -Function,-,mf_ultralight_read_signature,_Bool,"FuriHalNfcTxRxContext*, MfUltralightData*" -Function,-,mf_ultralight_read_tearing_flags,_Bool,"FuriHalNfcTxRxContext*, MfUltralightData*" -Function,-,mf_ultralight_read_version,_Bool,"FuriHalNfcTxRxContext*, MfUltralightReader*, MfUltralightData*" +Function,+,mf_classic_load,_Bool,"MfClassicData*, FlipperFormat*, uint32_t" +Function,+,mf_classic_poller_auth,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*" +Function,+,mf_classic_poller_change_value,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, int32_t, int32_t*" +Function,+,mf_classic_poller_read,MfClassicError,"Nfc*, const MfClassicDeviceKeys*, MfClassicData*" +Function,+,mf_classic_poller_read_block,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicBlock*" +Function,+,mf_classic_poller_read_value,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, int32_t*" +Function,+,mf_classic_poller_write_block,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicBlock*" +Function,+,mf_classic_reset,void,MfClassicData* +Function,+,mf_classic_save,_Bool,"const MfClassicData*, FlipperFormat*" +Function,+,mf_classic_set_block_read,void,"MfClassicData*, uint8_t, MfClassicBlock*" +Function,+,mf_classic_set_key_found,void,"MfClassicData*, uint8_t, MfClassicKeyType, uint64_t" +Function,+,mf_classic_set_key_not_found,void,"MfClassicData*, uint8_t, MfClassicKeyType" +Function,+,mf_classic_set_uid,_Bool,"MfClassicData*, const uint8_t*, size_t" +Function,+,mf_classic_value_to_block,void,"int32_t, uint8_t, MfClassicBlock*" +Function,+,mf_classic_verify,_Bool,"MfClassicData*, const FuriString*" +Function,+,mf_desfire_alloc,MfDesfireData*, +Function,+,mf_desfire_copy,void,"MfDesfireData*, const MfDesfireData*" +Function,+,mf_desfire_free,void,MfDesfireData* +Function,+,mf_desfire_get_application,const MfDesfireApplication*,"const MfDesfireData*, const MfDesfireApplicationId*" +Function,+,mf_desfire_get_base_data,Iso14443_4aData*,const MfDesfireData* +Function,+,mf_desfire_get_device_name,const char*,"const MfDesfireData*, NfcDeviceNameType" +Function,+,mf_desfire_get_file_data,const MfDesfireFileData*,"const MfDesfireApplication*, const MfDesfireFileId*" +Function,+,mf_desfire_get_file_settings,const MfDesfireFileSettings*,"const MfDesfireApplication*, const MfDesfireFileId*" +Function,+,mf_desfire_get_uid,const uint8_t*,"const MfDesfireData*, size_t*" +Function,+,mf_desfire_is_equal,_Bool,"const MfDesfireData*, const MfDesfireData*" +Function,+,mf_desfire_load,_Bool,"MfDesfireData*, FlipperFormat*, uint32_t" +Function,+,mf_desfire_reset,void,MfDesfireData* +Function,+,mf_desfire_save,_Bool,"const MfDesfireData*, FlipperFormat*" +Function,+,mf_desfire_set_uid,_Bool,"MfDesfireData*, const uint8_t*, size_t" +Function,+,mf_desfire_verify,_Bool,"MfDesfireData*, const FuriString*" +Function,+,mf_ultralight_alloc,MfUltralightData*, +Function,+,mf_ultralight_copy,void,"MfUltralightData*, const MfUltralightData*" +Function,+,mf_ultralight_detect_protocol,_Bool,const Iso14443_3aData* +Function,+,mf_ultralight_free,void,MfUltralightData* +Function,+,mf_ultralight_get_base_data,Iso14443_3aData*,const MfUltralightData* +Function,+,mf_ultralight_get_config_page,_Bool,"const MfUltralightData*, MfUltralightConfigPages**" +Function,+,mf_ultralight_get_config_page_num,uint16_t,MfUltralightType +Function,+,mf_ultralight_get_device_name,const char*,"const MfUltralightData*, NfcDeviceNameType" +Function,+,mf_ultralight_get_feature_support_set,uint32_t,MfUltralightType +Function,+,mf_ultralight_get_pages_total,uint16_t,MfUltralightType +Function,+,mf_ultralight_get_type_by_version,MfUltralightType,MfUltralightVersion* +Function,+,mf_ultralight_get_uid,const uint8_t*,"const MfUltralightData*, size_t*" +Function,+,mf_ultralight_is_all_data_read,_Bool,const MfUltralightData* +Function,+,mf_ultralight_is_counter_configured,_Bool,const MfUltralightData* +Function,+,mf_ultralight_is_equal,_Bool,"const MfUltralightData*, const MfUltralightData*" +Function,+,mf_ultralight_load,_Bool,"MfUltralightData*, FlipperFormat*, uint32_t" +Function,+,mf_ultralight_poller_read_card,MfUltralightError,"Nfc*, MfUltralightData*" +Function,+,mf_ultralight_poller_read_counter,MfUltralightError,"Nfc*, uint8_t, MfUltralightCounter*" +Function,+,mf_ultralight_poller_read_page,MfUltralightError,"Nfc*, uint16_t, MfUltralightPage*" +Function,+,mf_ultralight_poller_read_signature,MfUltralightError,"Nfc*, MfUltralightSignature*" +Function,+,mf_ultralight_poller_read_tearing_flag,MfUltralightError,"Nfc*, uint8_t, MfUltralightTearingFlag*" +Function,+,mf_ultralight_poller_read_version,MfUltralightError,"Nfc*, MfUltralightVersion*" +Function,+,mf_ultralight_poller_write_page,MfUltralightError,"Nfc*, uint16_t, MfUltralightPage*" +Function,+,mf_ultralight_reset,void,MfUltralightData* +Function,+,mf_ultralight_save,_Bool,"const MfUltralightData*, FlipperFormat*" +Function,+,mf_ultralight_set_uid,_Bool,"MfUltralightData*, const uint8_t*, size_t" +Function,+,mf_ultralight_support_feature,_Bool,"const uint32_t, const uint32_t" +Function,+,mf_ultralight_verify,_Bool,"MfUltralightData*, const FuriString*" Function,-,mkdtemp,char*,char* Function,-,mkostemp,int,"char*, int" Function,-,mkostemps,int,"char*, int, int" @@ -2039,6 +2152,18 @@ Function,-,modf,double,"double, double*" Function,-,modff,float,"float, float*" Function,-,modfl,long double,"long double, long double*" Function,-,mrand48,long, +Function,-,music_worker_alloc,MusicWorker*, +Function,-,music_worker_clear,void,MusicWorker* +Function,-,music_worker_free,void,MusicWorker* +Function,-,music_worker_is_playing,_Bool,MusicWorker* +Function,-,music_worker_load,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_fmf_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_string,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_set_callback,void,"MusicWorker*, MusicWorkerCallback, void*" +Function,-,music_worker_set_volume,void,"MusicWorker*, float" +Function,-,music_worker_start,void,MusicWorker* +Function,-,music_worker_stop,void,MusicWorker* Function,-,nan,double,const char* Function,-,nanf,float,const char* Function,-,nanl,long double,const char* @@ -2051,19 +2176,61 @@ Function,-,nextafterl,long double,"long double, long double" Function,-,nexttoward,double,"double, long double" Function,-,nexttowardf,float,"float, long double" Function,-,nexttowardl,long double,"long double, long double" +Function,+,nfc_alloc,Nfc*, +Function,+,nfc_config,void,"Nfc*, NfcMode, NfcTech" Function,+,nfc_device_alloc,NfcDevice*, Function,+,nfc_device_clear,void,NfcDevice* -Function,+,nfc_device_data_clear,void,NfcDeviceData* -Function,+,nfc_device_delete,_Bool,"NfcDevice*, _Bool" +Function,+,nfc_device_copy_data,void,"const NfcDevice*, NfcProtocol, NfcDeviceData*" Function,+,nfc_device_free,void,NfcDevice* -Function,+,nfc_device_load,_Bool,"NfcDevice*, const char*, _Bool" -Function,+,nfc_device_load_key_cache,_Bool,NfcDevice* -Function,+,nfc_device_restore,_Bool,"NfcDevice*, _Bool" +Function,+,nfc_device_get_data,const NfcDeviceData*,"const NfcDevice*, NfcProtocol" +Function,+,nfc_device_get_name,const char*,"const NfcDevice*, NfcDeviceNameType" +Function,+,nfc_device_get_protocol,NfcProtocol,const NfcDevice* +Function,+,nfc_device_get_protocol_name,const char*,NfcProtocol +Function,+,nfc_device_get_uid,const uint8_t*,"const NfcDevice*, size_t*" +Function,+,nfc_device_is_equal,_Bool,"const NfcDevice*, const NfcDevice*" +Function,+,nfc_device_load,_Bool,"NfcDevice*, const char*" +Function,+,nfc_device_reset,void,NfcDevice* Function,+,nfc_device_save,_Bool,"NfcDevice*, const char*" -Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" +Function,+,nfc_device_set_data,void,"NfcDevice*, NfcProtocol, const NfcDeviceData*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" -Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" -Function,+,nfc_file_select,_Bool,NfcDevice* +Function,+,nfc_device_set_uid,_Bool,"NfcDevice*, const uint8_t*, size_t" +Function,-,nfc_free,void,Nfc* +Function,-,nfc_iso14443a_listener_set_col_res_data,NfcError,"Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t" +Function,-,nfc_iso14443a_listener_tx_custom_parity,NfcError,"Nfc*, const BitBuffer*" +Function,-,nfc_iso14443a_poller_trx_custom_parity,NfcError,"Nfc*, const BitBuffer*, BitBuffer*, uint32_t" +Function,-,nfc_iso14443a_poller_trx_sdd_frame,NfcError,"Nfc*, const BitBuffer*, BitBuffer*, uint32_t" +Function,-,nfc_iso14443a_poller_trx_short_frame,NfcError,"Nfc*, NfcIso14443aShortFrame, BitBuffer*, uint32_t" +Function,-,nfc_listener_alloc,NfcListener*,"Nfc*, NfcProtocol, const NfcDeviceData*" +Function,-,nfc_listener_free,void,NfcListener* +Function,-,nfc_listener_get_data,const NfcDeviceData*,"NfcListener*, NfcProtocol" +Function,-,nfc_listener_start,void,"NfcListener*, NfcGenericCallback, void*" +Function,-,nfc_listener_stop,void,NfcListener* +Function,-,nfc_listener_tx,NfcError,"Nfc*, const BitBuffer*" +Function,+,nfc_poller_alloc,NfcPoller*,"Nfc*, NfcProtocol" +Function,+,nfc_poller_detect,_Bool,NfcPoller* +Function,+,nfc_poller_free,void,NfcPoller* +Function,+,nfc_poller_get_data,const NfcDeviceData*,NfcPoller* +Function,+,nfc_poller_start,void,"NfcPoller*, NfcGenericCallback, void*" +Function,+,nfc_poller_stop,void,NfcPoller* +Function,+,nfc_poller_trx,NfcError,"Nfc*, const BitBuffer*, BitBuffer*, uint32_t" +Function,+,nfc_protocol_get_parent,NfcProtocol,NfcProtocol +Function,+,nfc_protocol_has_parent,_Bool,"NfcProtocol, NfcProtocol" +Function,+,nfc_scanner_alloc,NfcScanner*,Nfc* +Function,+,nfc_scanner_free,void,NfcScanner* +Function,+,nfc_scanner_start,void,"NfcScanner*, NfcScannerCallback, void*" +Function,+,nfc_scanner_stop,void,NfcScanner* +Function,-,nfc_set_fdt_listen_fc,void,"Nfc*, uint32_t" +Function,-,nfc_set_fdt_poll_fc,void,"Nfc*, uint32_t" +Function,-,nfc_set_fdt_poll_poll_us,void,"Nfc*, uint32_t" +Function,-,nfc_set_guard_time_us,void,"Nfc*, uint32_t" +Function,-,nfc_set_mask_receive_time_fc,void,"Nfc*, uint32_t" +Function,+,nfc_start,void,"Nfc*, NfcEventCallback, void*" +Function,-,nfc_stop,void,Nfc* +Function,+,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" +Function,+,nfc_util_even_parity32,uint8_t,uint32_t +Function,+,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" +Function,+,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" +Function,+,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" @@ -2112,6 +2279,35 @@ Function,+,path_extract_dirname,void,"const char*, FuriString*" Function,+,path_extract_extension,void,"FuriString*, char*, size_t" Function,+,path_extract_filename,void,"FuriString*, FuriString*, _Bool" Function,+,path_extract_filename_no_ext,void,"const char*, FuriString*" +Function,+,pb_close_string_substream,_Bool,"pb_istream_t*, pb_istream_t*" +Function,+,pb_decode,_Bool,"pb_istream_t*, const pb_msgdesc_t*, void*" +Function,+,pb_decode_bool,_Bool,"pb_istream_t*, _Bool*" +Function,+,pb_decode_ex,_Bool,"pb_istream_t*, const pb_msgdesc_t*, void*, unsigned int" +Function,+,pb_decode_fixed32,_Bool,"pb_istream_t*, void*" +Function,+,pb_decode_fixed64,_Bool,"pb_istream_t*, void*" +Function,+,pb_decode_svarint,_Bool,"pb_istream_t*, int64_t*" +Function,+,pb_decode_tag,_Bool,"pb_istream_t*, pb_wire_type_t*, uint32_t*, _Bool*" +Function,+,pb_decode_varint,_Bool,"pb_istream_t*, uint64_t*" +Function,+,pb_decode_varint32,_Bool,"pb_istream_t*, uint32_t*" +Function,+,pb_default_field_callback,_Bool,"pb_istream_t*, pb_ostream_t*, const pb_field_t*" +Function,+,pb_encode,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_encode_ex,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*, unsigned int" +Function,+,pb_encode_fixed32,_Bool,"pb_ostream_t*, const void*" +Function,+,pb_encode_fixed64,_Bool,"pb_ostream_t*, const void*" +Function,+,pb_encode_string,_Bool,"pb_ostream_t*, const pb_byte_t*, size_t" +Function,+,pb_encode_submessage,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_encode_svarint,_Bool,"pb_ostream_t*, int64_t" +Function,+,pb_encode_tag,_Bool,"pb_ostream_t*, pb_wire_type_t, uint32_t" +Function,+,pb_encode_tag_for_field,_Bool,"pb_ostream_t*, const pb_field_iter_t*" +Function,+,pb_encode_varint,_Bool,"pb_ostream_t*, uint64_t" +Function,+,pb_get_encoded_size,_Bool,"size_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_istream_from_buffer,pb_istream_t,"const pb_byte_t*, size_t" +Function,+,pb_make_string_substream,_Bool,"pb_istream_t*, pb_istream_t*" +Function,+,pb_ostream_from_buffer,pb_ostream_t,"pb_byte_t*, size_t" +Function,+,pb_read,_Bool,"pb_istream_t*, pb_byte_t*, size_t" +Function,+,pb_release,void,"const pb_msgdesc_t*, void*" +Function,+,pb_skip_field,_Bool,"pb_istream_t*, pb_wire_type_t" +Function,+,pb_write,_Bool,"pb_ostream_t*, const pb_byte_t*, size_t" Function,-,pcTaskGetName,char*,TaskHandle_t Function,-,pcTimerGetName,const char*,TimerHandle_t Function,-,pclose,int,FILE* @@ -2156,7 +2352,6 @@ Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t" Function,-,printf,int,"const char*, ..." -Function,-,prng_successor,uint32_t,"uint32_t, uint32_t" Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t" @@ -2470,6 +2665,25 @@ Function,+,sha256_finish,void,"sha256_context*, unsigned char[32]" Function,+,sha256_process,void,sha256_context* Function,+,sha256_start,void,sha256_context* Function,+,sha256_update,void,"sha256_context*, const unsigned char*, unsigned int" +Function,+,signal_reader_alloc,SignalReader*,"const GpioPin*, uint32_t" +Function,+,signal_reader_free,void,SignalReader* +Function,+,signal_reader_set_polarity,void,"SignalReader*, SignalReaderPolarity" +Function,+,signal_reader_set_pull,void,"SignalReader*, GpioPull" +Function,+,signal_reader_set_sample_rate,void,"SignalReader*, SignalReaderTimeUnit, uint32_t" +Function,+,signal_reader_set_trigger,void,"SignalReader*, SignalReaderTrigger" +Function,+,signal_reader_start,void,"SignalReader*, SignalReaderCallback, void*" +Function,+,signal_reader_stop,void,SignalReader* +Function,+,simple_array_alloc,SimpleArray*,const SimpleArrayConfig* +Function,+,simple_array_cget,const SimpleArrayElement*,"const SimpleArray*, uint32_t" +Function,+,simple_array_cget_data,const SimpleArrayData*,const SimpleArray* +Function,+,simple_array_copy,void,"SimpleArray*, const SimpleArray*" +Function,+,simple_array_free,void,SimpleArray* +Function,+,simple_array_get,SimpleArrayElement*,"SimpleArray*, uint32_t" +Function,+,simple_array_get_count,uint32_t,const SimpleArray* +Function,+,simple_array_get_data,SimpleArrayData*,SimpleArray* +Function,+,simple_array_init,void,"SimpleArray*, uint32_t" +Function,+,simple_array_is_equal,_Bool,"const SimpleArray*, const SimpleArray*" +Function,+,simple_array_reset,void,SimpleArray* Function,-,sin,double,double Function,-,sincos,void,"double, double*, double*" Function,-,sincosf,void,"float, float*, float*" @@ -2636,6 +2850,36 @@ Function,+,subghz_block_generic_deserialize,SubGhzProtocolStatus,"SubGhzBlockGen Function,+,subghz_block_generic_deserialize_check_count_bit,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*, uint16_t" Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" Function,+,subghz_block_generic_serialize,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_devices_begin,_Bool,const SubGhzDevice* +Function,+,subghz_devices_deinit,void, +Function,+,subghz_devices_end,void,const SubGhzDevice* +Function,+,subghz_devices_flush_rx,void,const SubGhzDevice* +Function,+,subghz_devices_flush_tx,void,const SubGhzDevice* +Function,+,subghz_devices_get_by_name,const SubGhzDevice*,const char* +Function,+,subghz_devices_get_data_gpio,const GpioPin*,const SubGhzDevice* +Function,+,subghz_devices_get_lqi,uint8_t,const SubGhzDevice* +Function,+,subghz_devices_get_name,const char*,const SubGhzDevice* +Function,+,subghz_devices_get_rssi,float,const SubGhzDevice* +Function,+,subghz_devices_idle,void,const SubGhzDevice* +Function,+,subghz_devices_init,void, +Function,+,subghz_devices_is_async_complete_tx,_Bool,const SubGhzDevice* +Function,+,subghz_devices_is_connect,_Bool,const SubGhzDevice* +Function,+,subghz_devices_is_frequency_valid,_Bool,"const SubGhzDevice*, uint32_t" +Function,+,subghz_devices_is_rx_data_crc_valid,_Bool,const SubGhzDevice* +Function,+,subghz_devices_load_preset,void,"const SubGhzDevice*, FuriHalSubGhzPreset, uint8_t*" +Function,+,subghz_devices_read_packet,void,"const SubGhzDevice*, uint8_t*, uint8_t*" +Function,+,subghz_devices_reset,void,const SubGhzDevice* +Function,+,subghz_devices_rx_pipe_not_empty,_Bool,const SubGhzDevice* +Function,+,subghz_devices_set_async_mirror_pin,void,"const SubGhzDevice*, const GpioPin*" +Function,+,subghz_devices_set_frequency,uint32_t,"const SubGhzDevice*, uint32_t" +Function,+,subghz_devices_set_rx,void,const SubGhzDevice* +Function,+,subghz_devices_set_tx,_Bool,const SubGhzDevice* +Function,+,subghz_devices_sleep,void,const SubGhzDevice* +Function,+,subghz_devices_start_async_rx,void,"const SubGhzDevice*, void*, void*" +Function,+,subghz_devices_start_async_tx,_Bool,"const SubGhzDevice*, void*, void*" +Function,+,subghz_devices_stop_async_rx,void,const SubGhzDevice* +Function,+,subghz_devices_stop_async_tx,void,const SubGhzDevice* +Function,+,subghz_devices_write_packet,void,"const SubGhzDevice*, const uint8_t*, uint8_t" Function,+,subghz_environment_alloc,SubGhzEnvironment*, Function,+,subghz_environment_free,void,SubGhzEnvironment* Function,+,subghz_environment_get_alutech_at_4n_rainbow_table_file_name,const char*,SubGhzEnvironment* @@ -2643,12 +2887,12 @@ Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char* Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" -Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment* +Function,+,subghz_environment_get_protocol_registry,const SubGhzProtocolRegistry*,SubGhzEnvironment* Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_alutech_at_4n_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" -Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*" +Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, const SubGhzProtocolRegistry*" Function,-,subghz_keystore_alloc,SubGhzKeystore*, Function,-,subghz_keystore_free,void,SubGhzKeystore* Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* @@ -2682,6 +2926,7 @@ Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDeco Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" Function,+,subghz_protocol_decoder_base_serialize,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" +Function,+,subghz_protocol_decoder_bin_raw_data_input_rssi,void,"SubGhzProtocolDecoderBinRAW*, float" Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_decoder_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,+,subghz_protocol_decoder_raw_feed,void,"void*, _Bool, uint32_t" @@ -2693,8 +2938,9 @@ Function,+,subghz_protocol_encoder_raw_deserialize,SubGhzProtocolStatus,"void*, Function,+,subghz_protocol_encoder_raw_free,void,void* Function,+,subghz_protocol_encoder_raw_stop,void,void* Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* +Function,+,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" -Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" +Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzRadioPreset*" Function,+,subghz_protocol_raw_save_to_file_pause,void,"SubGhzProtocolDecoderRAW*, _Bool" @@ -2702,6 +2948,8 @@ Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry* Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t" Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" +Function,+,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t +Function,+,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" Function,+,subghz_receiver_free,void,SubGhzReceiver* @@ -2738,7 +2986,7 @@ Function,+,subghz_tx_rx_worker_free,void,SubGhzTxRxWorker* Function,+,subghz_tx_rx_worker_is_running,_Bool,SubGhzTxRxWorker* Function,+,subghz_tx_rx_worker_read,size_t,"SubGhzTxRxWorker*, uint8_t*, size_t" Function,+,subghz_tx_rx_worker_set_callback_have_read,void,"SubGhzTxRxWorker*, SubGhzTxRxWorkerCallbackHaveRead, void*" -Function,+,subghz_tx_rx_worker_start,_Bool,"SubGhzTxRxWorker*, uint32_t" +Function,+,subghz_tx_rx_worker_start,_Bool,"SubGhzTxRxWorker*, const SubGhzDevice*, uint32_t" Function,+,subghz_tx_rx_worker_stop,void,SubGhzTxRxWorker* Function,+,subghz_tx_rx_worker_write,_Bool,"SubGhzTxRxWorker*, uint8_t*, size_t" Function,+,subghz_worker_alloc,SubGhzWorker*, @@ -2906,6 +3154,7 @@ Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* @@ -2929,6 +3178,8 @@ Function,-,vdprintf,int,"int, const char*, __gnuc_va_list" Function,+,version_get,const Version*, Function,+,version_get_builddate,const char*,const Version* Function,+,version_get_dirty_flag,_Bool,const Version* +Function,+,version_get_firmware_origin,const char*,const Version* +Function,+,version_get_git_origin,const char*,const Version* Function,+,version_get_gitbranch,const char*,const Version* Function,+,version_get_gitbranchnum,const char*,const Version* Function,+,version_get_githash,const char*,const Version* @@ -3173,6 +3424,7 @@ Variable,+,message_force_vibro_setting_off,const NotificationMessage, Variable,+,message_force_vibro_setting_on,const NotificationMessage, Variable,+,message_green_0,const NotificationMessage, Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_lcd_contrast_update,const NotificationMessage, Variable,+,message_note_a0,const NotificationMessage, Variable,+,message_note_a1,const NotificationMessage, Variable,+,message_note_a2,const NotificationMessage, @@ -3286,6 +3538,12 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, +Variable,-,nfc_device_iso14443_3a,const NfcDeviceBase, +Variable,+,nfc_device_iso14443_3b,const NfcDeviceBase, +Variable,-,nfc_device_iso14443_4a,const NfcDeviceBase, +Variable,-,nfc_device_mf_classic,const NfcDeviceBase, +Variable,-,nfc_device_mf_desfire,const NfcDeviceBase, +Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -3316,6 +3574,7 @@ Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, Variable,+,sequence_display_backlight_on,const NotificationSequence, Variable,+,sequence_double_vibro,const NotificationSequence, Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_lcd_contrast_update,const NotificationSequence, Variable,+,sequence_not_charging,const NotificationSequence, Variable,+,sequence_reset_blue,const NotificationSequence, Variable,+,sequence_reset_display,const NotificationSequence, @@ -3334,9 +3593,18 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, +Variable,+,simple_array_config_uint8_t,const SimpleArrayConfig, +Variable,-,subghz_device_cc1101_int,const SubGhzDevice, +Variable,+,subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_gfsk_9_99kb_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_msk_99_97kb_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_ook_270khz_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_ook_650khz_async_regs,const uint8_t[], Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, +Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry, Variable,-,suboptarg,char*, Variable,+,usb_cdc_dual,FuriHalUsbInterface, Variable,+,usb_cdc_single,FuriHalUsbInterface, diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index b443bee21f02..d28852822308 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -196,14 +196,14 @@ static void APPD_SetCPU2GpioConfig(void) { gpio_config.Pin = gpiob_pin_list; LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOB); LL_GPIO_Init(GPIOB, &gpio_config); - LL_GPIO_ResetOutputPin(GPIOB, gpioa_pin_list); + LL_GPIO_ResetOutputPin(GPIOB, gpiob_pin_list); } if(gpioc_pin_list != 0) { gpio_config.Pin = gpioc_pin_list; LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOC); LL_GPIO_Init(GPIOC, &gpio_config); - LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); + LL_GPIO_ResetOutputPin(GPIOC, gpioc_pin_list); } } diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 37d8f7cd04bd..c0418d9fe807 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -33,6 +33,51 @@ static int32_t ble_app_hci_thread(void* context); static void ble_app_hci_event_handler(void* pPayload); static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); +static const HCI_TL_HciInitConf_t hci_tl_config = { + .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, + .StatusNotCallBack = ble_app_hci_status_not_handler, +}; + +static const SHCI_C2_CONFIG_Cmd_Param_t config_param = { + .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, + .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, + .BleNvmRamAddress = (uint32_t)ble_app_nvm, + .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, +}; + +static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { + .Header = {{0, 0, 0}}, // Header unused + .Param = { + .pBleBufferAddress = 0, // pBleBufferAddress not used + .BleBufferSize = 0, // BleBufferSize not used + .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, + .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, + .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, + .NumOfLinks = CFG_BLE_NUM_LINK, + .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, + .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, + .MblockCount = CFG_BLE_MBLOCK_COUNT, + .AttMtu = CFG_BLE_MAX_ATT_MTU, + .SlaveSca = CFG_BLE_SLAVE_SCA, + .MasterSca = CFG_BLE_MASTER_SCA, + .LsSource = CFG_BLE_LSE_SOURCE, + .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, + .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, + .ViterbiEnable = CFG_BLE_VITERBI_MODE, + .Options = CFG_BLE_OPTIONS, + .HwVersion = 0, + .max_coc_initiator_nbr = 32, + .min_tx_power = 0, + .max_tx_power = 0, + .rx_model_config = 1, + /* New stack (13.3->15.0) */ + .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB + .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB + .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) + }}; + bool ble_app_init() { SHCI_CmdStatus_t status; ble_app = malloc(sizeof(BleApp)); @@ -44,58 +89,16 @@ bool ble_app_init() { furi_thread_start(ble_app->thread); // Initialize Ble Transport Layer - HCI_TL_HciInitConf_t hci_tl_config = { - .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, - .StatusNotCallBack = ble_app_hci_status_not_handler, - }; hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config); // Configure NVM store for pairing data - SHCI_C2_CONFIG_Cmd_Param_t config_param = { - .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, - .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, - .BleNvmRamAddress = (uint32_t)ble_app_nvm, - .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, - }; - status = SHCI_C2_Config(&config_param); + status = SHCI_C2_Config((SHCI_C2_CONFIG_Cmd_Param_t*)&config_param); if(status) { FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); } // Start ble stack on 2nd core - SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { - .Header = {{0, 0, 0}}, // Header unused - .Param = { - .pBleBufferAddress = 0, // pBleBufferAddress not used - .BleBufferSize = 0, // BleBufferSize not used - .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, - .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, - .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, - .NumOfLinks = CFG_BLE_NUM_LINK, - .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, - .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, - .MblockCount = CFG_BLE_MBLOCK_COUNT, - .AttMtu = CFG_BLE_MAX_ATT_MTU, - .SlaveSca = CFG_BLE_SLAVE_SCA, - .MasterSca = CFG_BLE_MASTER_SCA, - .LsSource = CFG_BLE_LSE_SOURCE, - .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, - .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, - .ViterbiEnable = CFG_BLE_VITERBI_MODE, - .Options = CFG_BLE_OPTIONS, - .HwVersion = 0, - .max_coc_initiator_nbr = 32, - .min_tx_power = 0, - .max_tx_power = 0, - .rx_model_config = 1, - /* New stack (13.3->15.0) */ - .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set - .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set - .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB - .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB - .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) - }}; - status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); + status = SHCI_C2_BLE_Init((SHCI_C2_Ble_Init_Cmd_Packet_t*)&ble_init_cmd_packet); if(status) { FURI_LOG_E(TAG, "Failed to start ble stack: %d", status); } diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c deleted file mode 100644 index 8bdb2eea84c7..000000000000 --- a/firmware/targets/f7/ble_glue/dev_info_service.c +++ /dev/null @@ -1,220 +0,0 @@ -#include "dev_info_service.h" -#include "app_common.h" -#include - -#include -#include -#include - -#define TAG "BtDevInfoSvc" - -typedef struct { - uint16_t service_handle; - uint16_t man_name_char_handle; - uint16_t serial_num_char_handle; - uint16_t firmware_rev_char_handle; - uint16_t software_rev_char_handle; - uint16_t rpc_version_char_handle; - FuriString* version_string; - char hardware_revision[4]; -} DevInfoSvc; - -static DevInfoSvc* dev_info_svc = NULL; - -static const char dev_info_man_name[] = "Flipper Devices Inc."; -static const char dev_info_serial_num[] = "1.0"; -static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); - -static const uint8_t dev_info_rpc_version_uuid[] = - {0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03}; - -void dev_info_svc_start() { - dev_info_svc = malloc(sizeof(DevInfoSvc)); - dev_info_svc->version_string = furi_string_alloc_printf( - "%s %s %s %s", - version_get_githash(NULL), - version_get_gitbranch(NULL), - version_get_gitbranchnum(NULL), - version_get_builddate(NULL)); - snprintf( - dev_info_svc->hardware_revision, - sizeof(dev_info_svc->hardware_revision), - "%d", - version_get_target(NULL)); - tBleStatus status; - - // Add Device Information Service - uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; - status = aci_gatt_add_service( - UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 11, &dev_info_svc->service_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); - } - - // Add characteristics - uuid = MANUFACTURER_NAME_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - strlen(dev_info_man_name), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->man_name_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add manufacturer name char: %d", status); - } - uuid = SERIAL_NUMBER_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - strlen(dev_info_serial_num), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->serial_num_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add serial number char: %d", status); - } - uuid = FIRMWARE_REVISION_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - strlen(dev_info_svc->hardware_revision), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->firmware_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add firmware revision char: %d", status); - } - uuid = SOFTWARE_REVISION_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - furi_string_size(dev_info_svc->version_string), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->software_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add software revision char: %d", status); - } - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_128, - (const Char_UUID_t*)dev_info_rpc_version_uuid, - strlen(dev_info_rpc_version), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->rpc_version_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add rpc version characteristic: %d", status); - } - - // Update characteristics - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->man_name_char_handle, - 0, - strlen(dev_info_man_name), - (uint8_t*)dev_info_man_name); - if(status) { - FURI_LOG_E(TAG, "Failed to update manufacturer name char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->serial_num_char_handle, - 0, - strlen(dev_info_serial_num), - (uint8_t*)dev_info_serial_num); - if(status) { - FURI_LOG_E(TAG, "Failed to update serial number char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->firmware_rev_char_handle, - 0, - strlen(dev_info_svc->hardware_revision), - (uint8_t*)dev_info_svc->hardware_revision); - if(status) { - FURI_LOG_E(TAG, "Failed to update firmware revision char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->software_rev_char_handle, - 0, - furi_string_size(dev_info_svc->version_string), - (uint8_t*)furi_string_get_cstr(dev_info_svc->version_string)); - if(status) { - FURI_LOG_E(TAG, "Failed to update software revision char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->rpc_version_char_handle, - 0, - strlen(dev_info_rpc_version), - (uint8_t*)dev_info_rpc_version); - if(status) { - FURI_LOG_E(TAG, "Failed to update rpc version char: %d", status); - } -} - -void dev_info_svc_stop() { - tBleStatus status; - if(dev_info_svc) { - furi_string_free(dev_info_svc->version_string); - // Delete service characteristics - status = - aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete manufacturer name char: %d", status); - } - status = - aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete serial number char: %d", status); - } - status = aci_gatt_del_char( - dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete firmware revision char: %d", status); - } - status = aci_gatt_del_char( - dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete software revision char: %d", status); - } - status = - aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->rpc_version_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete rpc version char: %d", status); - } - // Delete service - status = aci_gatt_del_service(dev_info_svc->service_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); - } - free(dev_info_svc); - dev_info_svc = NULL; - } -} - -bool dev_info_svc_is_started() { - return dev_info_svc != NULL; -} diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index f0a9ced3cb72..360c1f6b6844 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -330,6 +330,7 @@ static void gap_init_svc(Gap* gap) { if(status) { FURI_LOG_E(TAG, "Failed updating name characteristic: %d", status); } + uint8_t gap_appearence_char_uuid[2] = { gap->config->appearance_char & 0xff, gap->config->appearance_char >> 8}; status = aci_gatt_update_char_value( diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c deleted file mode 100644 index 47d242d4dff8..000000000000 --- a/firmware/targets/f7/ble_glue/hid_service.c +++ /dev/null @@ -1,332 +0,0 @@ -#include "hid_service.h" -#include "app_common.h" -#include - -#include - -#define TAG "BtHid" - -typedef struct { - uint16_t svc_handle; - uint16_t protocol_mode_char_handle; - uint16_t report_char_handle[HID_SVC_REPORT_COUNT]; - uint16_t report_ref_desc_handle[HID_SVC_REPORT_COUNT]; - uint16_t report_map_char_handle; - uint16_t info_char_handle; - uint16_t ctrl_point_char_handle; -} HIDSvc; - -static HIDSvc* hid_svc = NULL; - -static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { - SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; - hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); - evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; - // aci_gatt_attribute_modified_event_rp0* attribute_modified; - if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { - if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { - // Process modification events - ret = SVCCTL_EvtAckFlowEnable; - } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { - // Process notification confirmation - ret = SVCCTL_EvtAckFlowEnable; - } - } - return ret; -} - -void hid_svc_start() { - tBleStatus status; - hid_svc = malloc(sizeof(HIDSvc)); - Service_UUID_t svc_uuid = {}; - Char_Desc_Uuid_t desc_uuid = {}; - Char_UUID_t char_uuid = {}; - - // Register event handler - SVCCTL_RegisterSvcHandler(hid_svc_event_handler); - // Add service - svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; - /** - * Add Human Interface Device Service - */ - status = aci_gatt_add_service( - UUID_TYPE_16, - &svc_uuid, - PRIMARY_SERVICE, - 2 + /* protocol mode */ - (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + - (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + - 2, /* Service + Report Map + HID Information + HID Control Point */ - &hid_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add HID service: %d", status); - } - // Add Protocol mode characteristics - char_uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - 1, - CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, - ATTR_PERMISSION_NONE, - GATT_NOTIFY_ATTRIBUTE_WRITE, - 10, - CHAR_VALUE_LEN_CONSTANT, - &hid_svc->protocol_mode_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add protocol mode characteristic: %d", status); - } - // Update Protocol mode characteristic - uint8_t protocol_mode = 1; - status = aci_gatt_update_char_value( - hid_svc->svc_handle, hid_svc->protocol_mode_char_handle, 0, 1, &protocol_mode); - if(status) { - FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status); - } - -#if(HID_SVC_REPORT_COUNT != 0) - for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { - if(i < HID_SVC_INPUT_REPORT_COUNT) { //-V547 - uint8_t buf[2] = {i + 1, 1}; // 1 input - char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAX_LEN, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &(hid_svc->report_char_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); - } - - desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; - status = aci_gatt_add_char_desc( - hid_svc->svc_handle, - hid_svc->report_char_handle[i], - UUID_TYPE_16, - &desc_uuid, - HID_SVC_REPORT_REF_LEN, - HID_SVC_REPORT_REF_LEN, - buf, - ATTR_PERMISSION_NONE, - ATTR_ACCESS_READ_WRITE, - GATT_DONT_NOTIFY_EVENTS, - MIN_ENCRY_KEY_SIZE, - CHAR_VALUE_LEN_CONSTANT, - &(hid_svc->report_ref_desc_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); - } - } else if((i - HID_SVC_INPUT_REPORT_COUNT) < HID_SVC_OUTPUT_REPORT_COUNT) { - uint8_t buf[2] = {i + 1, 2}; // 2 output - char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAX_LEN, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &(hid_svc->report_char_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); - } - - desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; - status = aci_gatt_add_char_desc( - hid_svc->svc_handle, - hid_svc->report_char_handle[i], - UUID_TYPE_16, - &desc_uuid, - HID_SVC_REPORT_REF_LEN, - HID_SVC_REPORT_REF_LEN, - buf, - ATTR_PERMISSION_NONE, - ATTR_ACCESS_READ_WRITE, - GATT_DONT_NOTIFY_EVENTS, - MIN_ENCRY_KEY_SIZE, - CHAR_VALUE_LEN_CONSTANT, - &(hid_svc->report_ref_desc_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); - } - } else { - uint8_t buf[2] = {i + 1, 3}; // 3 feature - char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAX_LEN, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &(hid_svc->report_char_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); - } - - desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; - status = aci_gatt_add_char_desc( - hid_svc->svc_handle, - hid_svc->report_char_handle[i], - UUID_TYPE_16, - &desc_uuid, - HID_SVC_REPORT_REF_LEN, - HID_SVC_REPORT_REF_LEN, - buf, - ATTR_PERMISSION_NONE, - ATTR_ACCESS_READ_WRITE, - GATT_DONT_NOTIFY_EVENTS, - MIN_ENCRY_KEY_SIZE, - CHAR_VALUE_LEN_CONSTANT, - &(hid_svc->report_ref_desc_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); - } - } - } -#endif - // Add Report Map characteristic - char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAP_MAX_LEN, - CHAR_PROP_READ, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &hid_svc->report_map_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status); - } - - // Add Information characteristic - char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_INFO_LEN, - CHAR_PROP_READ, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &hid_svc->info_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add information characteristic: %d", status); - } - // Add Control Point characteristic - char_uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_CONTROL_POINT_LEN, - CHAR_PROP_WRITE_WITHOUT_RESP, - ATTR_PERMISSION_NONE, - GATT_NOTIFY_ATTRIBUTE_WRITE, - 10, - CHAR_VALUE_LEN_CONSTANT, - &hid_svc->ctrl_point_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status); - } -} - -bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - - tBleStatus status = aci_gatt_update_char_value( - hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data); - if(status) { - FURI_LOG_E(TAG, "Failed updating report map characteristic: %d", status); - return false; - } - return true; -} - -bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - - tBleStatus status = aci_gatt_update_char_value( - hid_svc->svc_handle, hid_svc->report_char_handle[input_report_num], 0, len, data); - if(status) { - FURI_LOG_E(TAG, "Failed updating report characteristic: %d", status); - return false; - } - return true; -} - -bool hid_svc_update_info(uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - - tBleStatus status = - aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data); - if(status) { - FURI_LOG_E(TAG, "Failed updating info characteristic: %d", status); - return false; - } - return true; -} - -bool hid_svc_is_started() { - return hid_svc != NULL; -} - -void hid_svc_stop() { - tBleStatus status; - if(hid_svc) { - // Delete characteristics - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_map_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status); - } -#if(HID_SVC_INPUT_REPORT_COUNT != 0) - for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle[i]); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status); - } - } -#endif - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status); - } - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status); - } - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->ctrl_point_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status); - } - // Delete service - status = aci_gatt_del_service(hid_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); - } - // Delete buffer size mutex - free(hid_svc); - hid_svc = NULL; - } -} diff --git a/firmware/targets/f7/ble_glue/battery_service.c b/firmware/targets/f7/ble_glue/services/battery_service.c similarity index 53% rename from firmware/targets/f7/ble_glue/battery_service.c rename to firmware/targets/f7/ble_glue/services/battery_service.c index 8c371efadba0..63f736b3b7ab 100644 --- a/firmware/targets/f7/ble_glue/battery_service.c +++ b/firmware/targets/f7/ble_glue/services/battery_service.c @@ -1,5 +1,7 @@ #include "battery_service.h" #include "app_common.h" +#include "gatt_char.h" + #include #include @@ -7,12 +9,6 @@ #define TAG "BtBatterySvc" -typedef struct { - uint16_t svc_handle; - uint16_t battery_level_char_handle; - uint16_t power_state_char_handle; -} BatterySvc; - enum { // Common states BatterySvcPowerStateUnknown = 0b00, @@ -40,13 +36,44 @@ typedef struct { _Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size"); -static BatterySvc* battery_svc = NULL; - #define BATTERY_POWER_STATE (0x2A1A) static const uint16_t service_uuid = BATTERY_SERVICE_UUID; -static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID; -static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE; + +typedef enum { + BatterySvcGattCharacteristicBatteryLevel = 0, + BatterySvcGattCharacteristicPowerState, + BatterySvcGattCharacteristicCount, +} BatterySvcGattCharacteristicId; + +static const FlipperGattCharacteristicParams battery_svc_chars[BatterySvcGattCharacteristicCount] = + {[BatterySvcGattCharacteristicBatteryLevel] = + {.name = "Battery Level", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = BATTERY_LEVEL_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [BatterySvcGattCharacteristicPowerState] = { + .name = "Power State", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = BATTERY_POWER_STATE, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}}; + +typedef struct { + uint16_t svc_handle; + FlipperGattCharacteristicInstance chars[BatterySvcGattCharacteristicCount]; +} BatterySvc; + +static BatterySvc* battery_svc = NULL; void battery_svc_start() { battery_svc = malloc(sizeof(BatterySvc)); @@ -58,53 +85,19 @@ void battery_svc_start() { if(status) { FURI_LOG_E(TAG, "Failed to add Battery service: %d", status); } - // Add Battery level characteristic - status = aci_gatt_add_char( - battery_svc->svc_handle, - UUID_TYPE_16, - (Char_UUID_t*)&battery_level_char_uuid, - 1, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &battery_svc->battery_level_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); + for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + battery_svc->svc_handle, &battery_svc_chars[i], &battery_svc->chars[i]); } - // Add Power state characteristic - status = aci_gatt_add_char( - battery_svc->svc_handle, - UUID_TYPE_16, - (Char_UUID_t*)&power_state_char_uuid, - 1, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &battery_svc->power_state_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); - } - // Update power state charachteristic + battery_svc_update_power_state(); } void battery_svc_stop() { tBleStatus status; if(battery_svc) { - // Delete Battery level characteristic - status = - aci_gatt_del_char(battery_svc->svc_handle, battery_svc->battery_level_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); - } - // Delete Power state characteristic - status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->power_state_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); + for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]); } // Delete Battery service status = aci_gatt_del_service(battery_svc->svc_handle); @@ -126,13 +119,10 @@ bool battery_svc_update_level(uint8_t battery_charge) { return false; } // Update battery level characteristic - FURI_LOG_D(TAG, "Updating battery level characteristic"); - tBleStatus result = aci_gatt_update_char_value( - battery_svc->svc_handle, battery_svc->battery_level_char_handle, 0, 1, &battery_charge); - if(result) { - FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result); - } - return result != BLE_STATUS_SUCCESS; + return flipper_gatt_characteristic_update( + battery_svc->svc_handle, + &battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel], + &battery_charge); } bool battery_svc_update_power_state() { @@ -152,15 +142,9 @@ bool battery_svc_update_power_state() { power_state.charging = BatterySvcPowerStateNotCharging; power_state.discharging = BatterySvcPowerStateDischarging; } - FURI_LOG_D(TAG, "Updating power state characteristic"); - tBleStatus result = aci_gatt_update_char_value( + + return flipper_gatt_characteristic_update( battery_svc->svc_handle, - battery_svc->power_state_char_handle, - 0, - 1, - (uint8_t*)&power_state); - if(result) { - FURI_LOG_E(TAG, "Failed updating Power state characteristic: %d", result); - } - return result != BLE_STATUS_SUCCESS; + &battery_svc->chars[BatterySvcGattCharacteristicPowerState], + &power_state); } diff --git a/firmware/targets/f7/ble_glue/battery_service.h b/firmware/targets/f7/ble_glue/services/battery_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/battery_service.h rename to firmware/targets/f7/ble_glue/services/battery_service.h diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.c b/firmware/targets/f7/ble_glue/services/dev_info_service.c new file mode 100644 index 000000000000..cc95bb2fc1c8 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/dev_info_service.c @@ -0,0 +1,176 @@ +#include "dev_info_service.h" +#include "app_common.h" +#include "gatt_char.h" +#include + +#include +#include +#include + +#include "dev_info_service_uuid.inc" + +#define TAG "BtDevInfoSvc" + +typedef enum { + DevInfoSvcGattCharacteristicMfgName = 0, + DevInfoSvcGattCharacteristicSerial, + DevInfoSvcGattCharacteristicFirmwareRev, + DevInfoSvcGattCharacteristicSoftwareRev, + DevInfoSvcGattCharacteristicRpcVersion, + DevInfoSvcGattCharacteristicCount, +} DevInfoSvcGattCharacteristicId; + +#define DEVICE_INFO_HARDWARE_REV_SIZE 4 +typedef struct { + uint16_t service_handle; + FlipperGattCharacteristicInstance characteristics[DevInfoSvcGattCharacteristicCount]; + FuriString* version_string; + char hardware_revision[DEVICE_INFO_HARDWARE_REV_SIZE]; +} DevInfoSvc; + +static DevInfoSvc* dev_info_svc = NULL; + +static const char dev_info_man_name[] = "Flipper Devices Inc."; +static const char dev_info_serial_num[] = "1.0"; +static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); + +static bool dev_info_char_firmware_rev_callback( + const void* context, + const uint8_t** data, + uint16_t* data_len) { + const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; + *data_len = sizeof(dev_info_svc->hardware_revision); + if(data) { + *data = (const uint8_t*)&dev_info_svc->hardware_revision; + } + return false; +} + +static bool dev_info_char_software_rev_callback( + const void* context, + const uint8_t** data, + uint16_t* data_len) { + const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; + *data_len = furi_string_size(dev_info_svc->version_string); + if(data) { + *data = (const uint8_t*)furi_string_get_cstr(dev_info_svc->version_string); + } + return false; +} + +static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCharacteristicCount] = + {[DevInfoSvcGattCharacteristicMfgName] = + {.name = "Manufacturer Name", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(dev_info_man_name) - 1, + .data.fixed.ptr = (const uint8_t*)&dev_info_man_name, + .uuid.Char_UUID_16 = MANUFACTURER_NAME_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicSerial] = + {.name = "Serial Number", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(dev_info_serial_num) - 1, + .data.fixed.ptr = (const uint8_t*)&dev_info_serial_num, + .uuid.Char_UUID_16 = SERIAL_NUMBER_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicFirmwareRev] = + {.name = "Firmware Revision", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.context = &dev_info_svc, + .data.callback.fn = dev_info_char_firmware_rev_callback, + .uuid.Char_UUID_16 = FIRMWARE_REVISION_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicSoftwareRev] = + {.name = "Software Revision", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.context = &dev_info_svc, + .data.callback.fn = dev_info_char_software_rev_callback, + .uuid.Char_UUID_16 = SOFTWARE_REVISION_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicRpcVersion] = { + .name = "RPC Version", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(dev_info_rpc_version) - 1, + .data.fixed.ptr = (const uint8_t*)&dev_info_rpc_version, + .uuid.Char_UUID_128 = DEV_INVO_RPC_VERSION_UID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}}; + +void dev_info_svc_start() { + dev_info_svc = malloc(sizeof(DevInfoSvc)); + dev_info_svc->version_string = furi_string_alloc_printf( + "%s %s %s %s", + version_get_githash(NULL), + version_get_gitbranch(NULL), + version_get_gitbranchnum(NULL), + version_get_builddate(NULL)); + snprintf( + dev_info_svc->hardware_revision, + sizeof(dev_info_svc->hardware_revision), + "%d", + version_get_target(NULL)); + tBleStatus status; + + // Add Device Information Service + uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; + status = aci_gatt_add_service( + UUID_TYPE_16, + (Service_UUID_t*)&uuid, + PRIMARY_SERVICE, + 1 + 2 * DevInfoSvcGattCharacteristicCount, + &dev_info_svc->service_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); + } + + for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + dev_info_svc->service_handle, + &dev_info_svc_chars[i], + &dev_info_svc->characteristics[i]); + flipper_gatt_characteristic_update( + dev_info_svc->service_handle, &dev_info_svc->characteristics[i], NULL); + } +} + +void dev_info_svc_stop() { + tBleStatus status; + if(dev_info_svc) { + furi_string_free(dev_info_svc->version_string); + // Delete service characteristics + for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete( + dev_info_svc->service_handle, &dev_info_svc->characteristics[i]); + } + // Delete service + status = aci_gatt_del_service(dev_info_svc->service_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); + } + free(dev_info_svc); + dev_info_svc = NULL; + } +} + +bool dev_info_svc_is_started() { + return dev_info_svc != NULL; +} diff --git a/firmware/targets/f7/ble_glue/dev_info_service.h b/firmware/targets/f7/ble_glue/services/dev_info_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/dev_info_service.h rename to firmware/targets/f7/ble_glue/services/dev_info_service.h diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc b/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc new file mode 100644 index 000000000000..ad520f62e5e0 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc @@ -0,0 +1,3 @@ +#define DEV_INVO_RPC_VERSION_UID \ + { 0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03 } + diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.c b/firmware/targets/f7/ble_glue/services/gatt_char.c new file mode 100644 index 000000000000..9b6a44f61b43 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/gatt_char.c @@ -0,0 +1,123 @@ +#include "gatt_char.h" + +#include + +#define TAG "GattChar" + +#define GATT_MIN_READ_KEY_SIZE (10) + +void flipper_gatt_characteristic_init( + uint16_t svc_handle, + const FlipperGattCharacteristicParams* char_descriptor, + FlipperGattCharacteristicInstance* char_instance) { + furi_assert(char_descriptor); + furi_assert(char_instance); + + // Copy the descriptor to the instance, since it may point to stack memory + // TODO: only copy if really comes from stack + char_instance->characteristic = malloc(sizeof(FlipperGattCharacteristicParams)); + memcpy( + (void*)char_instance->characteristic, + char_descriptor, + sizeof(FlipperGattCharacteristicParams)); + + uint16_t char_data_size = 0; + if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { + char_data_size = char_descriptor->data.fixed.length; + } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { + char_descriptor->data.callback.fn( + char_descriptor->data.callback.context, NULL, &char_data_size); + } + + tBleStatus status = aci_gatt_add_char( + svc_handle, + char_descriptor->uuid_type, + &char_descriptor->uuid, + char_data_size, + char_descriptor->char_properties, + char_descriptor->security_permissions, + char_descriptor->gatt_evt_mask, + GATT_MIN_READ_KEY_SIZE, + char_descriptor->is_variable, + &char_instance->handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add %s char: %d", char_descriptor->name, status); + } + + char_instance->descriptor_handle = 0; + if((status == 0) && char_descriptor->descriptor_params) { + uint8_t const* char_data = NULL; + const FlipperGattCharacteristicDescriptorParams* char_data_descriptor = + char_descriptor->descriptor_params; + bool release_data = char_data_descriptor->data_callback.fn( + char_data_descriptor->data_callback.context, &char_data, &char_data_size); + + status = aci_gatt_add_char_desc( + svc_handle, + char_instance->handle, + char_data_descriptor->uuid_type, + &char_data_descriptor->uuid, + char_data_descriptor->max_length, + char_data_size, + char_data, + char_data_descriptor->security_permissions, + char_data_descriptor->access_permissions, + char_data_descriptor->gatt_evt_mask, + GATT_MIN_READ_KEY_SIZE, + char_data_descriptor->is_variable, + &char_instance->descriptor_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add %s char descriptor: %d", char_descriptor->name, status); + } + if(release_data) { + free((void*)char_data); + } + } +} + +void flipper_gatt_characteristic_delete( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance) { + tBleStatus status = aci_gatt_del_char(svc_handle, char_instance->handle); + if(status) { + FURI_LOG_E( + TAG, "Failed to delete %s char: %d", char_instance->characteristic->name, status); + } + free((void*)char_instance->characteristic); +} + +bool flipper_gatt_characteristic_update( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance, + const void* source) { + furi_assert(char_instance); + const FlipperGattCharacteristicParams* char_descriptor = char_instance->characteristic; + FURI_LOG_D(TAG, "Updating %s char", char_descriptor->name); + + const uint8_t* char_data = NULL; + uint16_t char_data_size = 0; + bool release_data = false; + if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { + char_data = char_descriptor->data.fixed.ptr; + if(source) { + char_data = (uint8_t*)source; + } + char_data_size = char_descriptor->data.fixed.length; + } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { + const void* context = char_descriptor->data.callback.context; + if(source) { + context = source; + } + release_data = char_descriptor->data.callback.fn(context, &char_data, &char_data_size); + } + + tBleStatus result = aci_gatt_update_char_value( + svc_handle, char_instance->handle, 0, char_data_size, char_data); + if(result) { + FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result); + } + if(release_data) { + free((void*)char_data); + } + return result != BLE_STATUS_SUCCESS; +} \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.h b/firmware/targets/f7/ble_glue/services/gatt_char.h new file mode 100644 index 000000000000..959ab67a49f7 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/gatt_char.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Callback signature for getting characteristic data +// Is called when characteristic is created to get max data length. Data ptr is NULL in this case +// The result is passed to aci_gatt_add_char as "Char_Value_Length" +// For updates, called with a context - see flipper_gatt_characteristic_update +// Returns true if *data ownership is transferred to the caller and will be freed +typedef bool (*cbFlipperGattCharacteristicData)( + const void* context, + const uint8_t** data, + uint16_t* data_len); + +typedef enum { + FlipperGattCharacteristicDataFixed, + FlipperGattCharacteristicDataCallback, +} FlipperGattCharacteristicDataType; + +typedef struct { + Char_Desc_Uuid_t uuid; + struct { + cbFlipperGattCharacteristicData fn; + const void* context; + } data_callback; + uint8_t uuid_type; + uint8_t max_length; + uint8_t security_permissions; + uint8_t access_permissions; + uint8_t gatt_evt_mask; + uint8_t is_variable; +} FlipperGattCharacteristicDescriptorParams; + +typedef struct { + const char* name; + FlipperGattCharacteristicDescriptorParams* descriptor_params; + union { + struct { + const uint8_t* ptr; + uint16_t length; + } fixed; + struct { + cbFlipperGattCharacteristicData fn; + const void* context; + } callback; + } data; + Char_UUID_t uuid; + // Some packed bitfields to save space + FlipperGattCharacteristicDataType data_prop_type : 2; + uint8_t is_variable : 2; + uint8_t uuid_type : 2; + uint8_t char_properties; + uint8_t security_permissions; + uint8_t gatt_evt_mask; +} FlipperGattCharacteristicParams; + +_Static_assert( + sizeof(FlipperGattCharacteristicParams) == 36, + "FlipperGattCharacteristicParams size must be 36 bytes"); + +typedef struct { + const FlipperGattCharacteristicParams* characteristic; + uint16_t handle; + uint16_t descriptor_handle; +} FlipperGattCharacteristicInstance; + +// Initialize a characteristic instance; copies the characteristic descriptor into the instance +void flipper_gatt_characteristic_init( + uint16_t svc_handle, + const FlipperGattCharacteristicParams* char_descriptor, + FlipperGattCharacteristicInstance* char_instance); + +// Delete a characteristic instance; frees the copied characteristic descriptor from the instance +void flipper_gatt_characteristic_delete( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance); + +// Update a characteristic instance; if source==NULL, uses the data from the characteristic +// - For fixed data, fixed.ptr is used as the source if source==NULL +// - For callback-based data, collback.context is passed as the context if source==NULL +bool flipper_gatt_characteristic_update( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance, + const void* source); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c new file mode 100644 index 000000000000..cf2aca24e3f4 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/hid_service.c @@ -0,0 +1,302 @@ +#include "hid_service.h" +#include "app_common.h" +#include +#include "gatt_char.h" + +#include + +#define TAG "BtHid" + +typedef enum { + HidSvcGattCharacteristicProtocolMode = 0, + HidSvcGattCharacteristicReportMap, + HidSvcGattCharacteristicInfo, + HidSvcGattCharacteristicCtrlPoint, + HidSvcGattCharacteristicCount, +} HidSvcGattCharacteristicId; + +typedef struct { + uint8_t report_idx; + uint8_t report_type; +} HidSvcReportId; + +static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); + +static const Service_UUID_t hid_svc_uuid = { + .Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, +}; + +static bool + hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { + const HidSvcReportId* report_id = context; + *data_len = sizeof(HidSvcReportId); + if(data) { + *data = (const uint8_t*)report_id; + } + return false; +} + +typedef struct { + const void* data_ptr; + uint16_t data_len; +} HidSvcDataWrapper; + +static bool + hid_svc_report_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { + const HidSvcDataWrapper* report_data = context; + if(data) { + *data = report_data->data_ptr; + *data_len = report_data->data_len; + } else { + *data_len = HID_SVC_REPORT_MAP_MAX_LEN; + } + return false; +} + +static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { + [HidSvcGattCharacteristicProtocolMode] = + {.name = "Protocol Mode", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicReportMap] = + {.name = "Report Map", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.fn = hid_svc_report_data_callback, + .data.callback.context = NULL, + .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [HidSvcGattCharacteristicInfo] = + {.name = "HID Information", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = HID_SVC_INFO_LEN, + .data.fixed.ptr = NULL, + .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicCtrlPoint] = + {.name = "HID Control Point", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = HID_SVC_CONTROL_POINT_LEN, + .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, +}; + +static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { + .uuid_type = UUID_TYPE_16, + .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, + .max_length = HID_SVC_REPORT_REF_LEN, + .data_callback.fn = hid_svc_char_desc_data_callback, + .security_permissions = ATTR_PERMISSION_NONE, + .access_permissions = ATTR_ACCESS_READ_WRITE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT, +}; + +static const FlipperGattCharacteristicParams hid_svc_report_template = { + .name = "Report", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.fn = hid_svc_report_data_callback, + .data.callback.context = NULL, + .uuid.Char_UUID_16 = REPORT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE, +}; + +typedef struct { + uint16_t svc_handle; + FlipperGattCharacteristicInstance chars[HidSvcGattCharacteristicCount]; + FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; + FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; + FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; +} HIDSvc; + +static HIDSvc* hid_svc = NULL; + +static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { + SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; + hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); + evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; + // aci_gatt_attribute_modified_event_rp0* attribute_modified; + if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { + if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { + // Process modification events + ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { + // Process notification confirmation + ret = SVCCTL_EvtAckFlowEnable; + } + } + return ret; +} + +void hid_svc_start() { + tBleStatus status; + hid_svc = malloc(sizeof(HIDSvc)); + + // Register event handler + SVCCTL_RegisterSvcHandler(hid_svc_event_handler); + /** + * Add Human Interface Device Service + */ + status = aci_gatt_add_service( + UUID_TYPE_16, + &hid_svc_uuid, + PRIMARY_SERVICE, + 2 + /* protocol mode */ + (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + + (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + + 2, /* Service + Report Map + HID Information + HID Control Point */ + &hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add HID service: %d", status); + } + + // Maintain previously defined characteristic order + flipper_gatt_characteristic_init( + hid_svc->svc_handle, + &hid_svc_chars[HidSvcGattCharacteristicProtocolMode], + &hid_svc->chars[HidSvcGattCharacteristicProtocolMode]); + + uint8_t protocol_mode = 1; + flipper_gatt_characteristic_update( + hid_svc->svc_handle, + &hid_svc->chars[HidSvcGattCharacteristicProtocolMode], + &protocol_mode); + + // reports + FlipperGattCharacteristicDescriptorParams hid_svc_char_descr; + FlipperGattCharacteristicParams report_char; + HidSvcReportId report_id; + + memcpy(&hid_svc_char_descr, &hid_svc_char_descr_template, sizeof(hid_svc_char_descr)); + memcpy(&report_char, &hid_svc_report_template, sizeof(report_char)); + + hid_svc_char_descr.data_callback.context = &report_id; + report_char.descriptor_params = &hid_svc_char_descr; + + typedef struct { + uint8_t report_type; + uint8_t report_count; + FlipperGattCharacteristicInstance* chars; + } HidSvcReportCharProps; + + HidSvcReportCharProps hid_report_chars[] = { + {0x01, HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, + {0x02, HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, + {0x03, HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, + }; + + for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); + report_type_idx++) { + report_id.report_type = hid_report_chars[report_type_idx].report_type; + for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; + report_idx++) { + report_id.report_idx = report_idx + 1; + flipper_gatt_characteristic_init( + hid_svc->svc_handle, + &report_char, + &hid_report_chars[report_type_idx].chars[report_idx]); + } + } + + // Setup remaining characteristics + for(size_t i = HidSvcGattCharacteristicReportMap; i < HidSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); + } +} + +bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + + HidSvcDataWrapper report_data = { + .data_ptr = data, + .data_len = len, + }; + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data); +} + +bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + furi_assert(input_report_num < HID_SVC_INPUT_REPORT_COUNT); + + HidSvcDataWrapper report_data = { + .data_ptr = data, + .data_len = len, + }; + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); +} + +bool hid_svc_update_info(uint8_t* data) { + furi_assert(data); + furi_assert(hid_svc); + + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); +} + +bool hid_svc_is_started() { + return hid_svc != NULL; +} + +void hid_svc_stop() { + tBleStatus status; + if(hid_svc) { + // Delete characteristics + for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]); + } + + typedef struct { + uint8_t report_count; + FlipperGattCharacteristicInstance* chars; + } HidSvcReportCharProps; + + HidSvcReportCharProps hid_report_chars[] = { + {HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, + {HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, + {HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, + }; + + for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); + report_type_idx++) { + for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; + report_idx++) { + flipper_gatt_characteristic_delete( + hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]); + } + } + + // Delete service + status = aci_gatt_del_service(hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); + } + free(hid_svc); + hid_svc = NULL; + } +} diff --git a/firmware/targets/f7/ble_glue/hid_service.h b/firmware/targets/f7/ble_glue/services/hid_service.h similarity index 87% rename from firmware/targets/f7/ble_glue/hid_service.h rename to firmware/targets/f7/ble_glue/services/hid_service.h index 723460d496ac..211adcd6c440 100644 --- a/firmware/targets/f7/ble_glue/hid_service.h +++ b/firmware/targets/f7/ble_glue/services/hid_service.h @@ -25,4 +25,5 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len); bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); -bool hid_svc_update_info(uint8_t* data, uint16_t len); +// Expects data to be of length HID_SVC_INFO_LEN (4 bytes) +bool hid_svc_update_info(uint8_t* data); diff --git a/firmware/targets/f7/ble_glue/serial_service.c b/firmware/targets/f7/ble_glue/services/serial_service.c similarity index 57% rename from firmware/targets/f7/ble_glue/serial_service.c rename to firmware/targets/f7/ble_glue/services/serial_service.c index c6421dc28fee..0db25b3d3af5 100644 --- a/firmware/targets/f7/ble_glue/serial_service.c +++ b/firmware/targets/f7/ble_glue/services/serial_service.c @@ -1,17 +1,67 @@ #include "serial_service.h" #include "app_common.h" #include +#include "gatt_char.h" #include +#include "serial_service_uuid.inc" + #define TAG "BtSerialSvc" +typedef enum { + SerialSvcGattCharacteristicRx = 0, + SerialSvcGattCharacteristicTx, + SerialSvcGattCharacteristicFlowCtrl, + SerialSvcGattCharacteristicStatus, + SerialSvcGattCharacteristicCount, +} SerialSvcGattCharacteristicId; + +static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = { + [SerialSvcGattCharacteristicRx] = + {.name = "RX", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, + .uuid.Char_UUID_128 = SERIAL_SVC_RX_CHAR_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [SerialSvcGattCharacteristicTx] = + {.name = "TX", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, + .uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [SerialSvcGattCharacteristicFlowCtrl] = + {.name = "Flow control", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(uint32_t), + .uuid.Char_UUID_128 = SERIAL_SVC_FLOW_CONTROL_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [SerialSvcGattCharacteristicStatus] = { + .name = "RPC status", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(SerialServiceRpcStatus), + .uuid.Char_UUID_128 = SERIAL_SVC_RPC_STATUS_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}}; + typedef struct { uint16_t svc_handle; - uint16_t rx_char_handle; - uint16_t tx_char_handle; - uint16_t flow_ctrl_char_handle; - uint16_t rpc_status_char_handle; + FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount]; FuriMutex* buff_size_mtx; uint32_t buff_size; uint16_t bytes_ready_to_receive; @@ -21,17 +71,6 @@ typedef struct { static SerialSvc* serial_svc = NULL; -static const uint8_t service_uuid[] = - {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; -static const uint8_t char_tx_uuid[] = - {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -static const uint8_t char_rx_uuid[] = - {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -static const uint8_t flow_ctrl_uuid[] = - {0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -static const uint8_t rpc_status_uuid[] = - {0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; - static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); @@ -40,11 +79,14 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; - if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 2) { + if(attribute_modified->Attr_Handle == + serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 2) { // Descriptor handle ret = SVCCTL_EvtAckFlowEnable; FURI_LOG_D(TAG, "RX descriptor event"); - } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) { + } else if( + attribute_modified->Attr_Handle == + serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 1) { FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); if(serial_svc->callback) { furi_check( @@ -70,7 +112,9 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); } ret = SVCCTL_EvtAckFlowEnable; - } else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) { + } else if( + attribute_modified->Attr_Handle == + serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) { SerialServiceRpcStatus* rpc_status = (SerialServiceRpcStatus*)attribute_modified->Attr_Data; if(*rpc_status == SerialServiceRpcStatusNotActive) { @@ -97,18 +141,12 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { } static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) { - tBleStatus ble_status = aci_gatt_update_char_value( - serial_svc->svc_handle, - serial_svc->rpc_status_char_handle, - 0, - sizeof(SerialServiceRpcStatus), - (uint8_t*)&status); - if(ble_status) { - FURI_LOG_E(TAG, "Failed to update RPC status char: %d", ble_status); - } + flipper_gatt_characteristic_update( + serial_svc->svc_handle, &serial_svc->chars[SerialSvcGattCharacteristicStatus], &status); } void serial_svc_start() { + UNUSED(serial_svc_chars); tBleStatus status; serial_svc = malloc(sizeof(SerialSvc)); // Register event handler @@ -116,72 +154,17 @@ void serial_svc_start() { // Add service status = aci_gatt_add_service( - UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); + UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); if(status) { FURI_LOG_E(TAG, "Failed to add Serial service: %d", status); } - // Add RX characteristics - status = aci_gatt_add_char( - serial_svc->svc_handle, - UUID_TYPE_128, - (const Char_UUID_t*)char_rx_uuid, - SERIAL_SVC_DATA_LEN_MAX, - CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, - GATT_NOTIFY_ATTRIBUTE_WRITE, - 10, - CHAR_VALUE_LEN_VARIABLE, - &serial_svc->rx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add RX characteristic: %d", status); + // Add characteristics + for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + serial_svc->svc_handle, &serial_svc_chars[i], &serial_svc->chars[i]); } - // Add TX characteristic - status = aci_gatt_add_char( - serial_svc->svc_handle, - UUID_TYPE_128, - (const Char_UUID_t*)char_tx_uuid, - SERIAL_SVC_DATA_LEN_MAX, - CHAR_PROP_READ | CHAR_PROP_INDICATE, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &serial_svc->tx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add TX characteristic: %d", status); - } - // Add Flow Control characteristic - status = aci_gatt_add_char( - serial_svc->svc_handle, - UUID_TYPE_128, - (const Char_UUID_t*)flow_ctrl_uuid, - sizeof(uint32_t), - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &serial_svc->flow_ctrl_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status); - } - // Add RPC status characteristic - status = aci_gatt_add_char( - serial_svc->svc_handle, - UUID_TYPE_128, - (const Char_UUID_t*)rpc_status_uuid, - sizeof(SerialServiceRpcStatus), - CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, - GATT_NOTIFY_ATTRIBUTE_WRITE, - 10, - CHAR_VALUE_LEN_CONSTANT, - &serial_svc->rpc_status_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add RPC status characteristic: %d", status); - } serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive); // Allocate buffer size mutex serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal); @@ -196,13 +179,12 @@ void serial_svc_set_callbacks( serial_svc->context = context; serial_svc->buff_size = buff_size; serial_svc->bytes_ready_to_receive = buff_size; + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); - aci_gatt_update_char_value( + flipper_gatt_characteristic_update( serial_svc->svc_handle, - serial_svc->flow_ctrl_char_handle, - 0, - sizeof(uint32_t), - (uint8_t*)&buff_size_reversed); + &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], + &buff_size_reversed); } void serial_svc_notify_buffer_is_empty() { @@ -213,13 +195,12 @@ void serial_svc_notify_buffer_is_empty() { if(serial_svc->bytes_ready_to_receive == 0) { FURI_LOG_D(TAG, "Buffer is empty. Notifying client"); serial_svc->bytes_ready_to_receive = serial_svc->buff_size; + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); - aci_gatt_update_char_value( + flipper_gatt_characteristic_update( serial_svc->svc_handle, - serial_svc->flow_ctrl_char_handle, - 0, - sizeof(uint32_t), - (uint8_t*)&buff_size_reversed); + &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], + &buff_size_reversed); } furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); } @@ -227,22 +208,8 @@ void serial_svc_notify_buffer_is_empty() { void serial_svc_stop() { tBleStatus status; if(serial_svc) { - // Delete characteristics - status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete TX characteristic: %d", status); - } - status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete RX characteristic: %d", status); - } - status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status); - } - status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rpc_status_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete RPC Status characteristic: %d", status); + for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]); } // Delete service status = aci_gatt_del_service(serial_svc->svc_handle); @@ -273,7 +240,7 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) { tBleStatus result = aci_gatt_update_char_value_ext( 0, serial_svc->svc_handle, - serial_svc->tx_char_handle, + serial_svc->chars[SerialSvcGattCharacteristicTx].handle, remained ? 0x00 : 0x02, data_len, value_offset, diff --git a/firmware/targets/f7/ble_glue/serial_service.h b/firmware/targets/f7/ble_glue/services/serial_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/serial_service.h rename to firmware/targets/f7/ble_glue/services/serial_service.h diff --git a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc b/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc new file mode 100644 index 000000000000..a297d9ad604a --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc @@ -0,0 +1,12 @@ + +static const Service_UUID_t service_uuid = { .Service_UUID_128 = \ + { 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f }}; + +#define SERIAL_SVC_TX_CHAR_UUID \ + { 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +#define SERIAL_SVC_RX_CHAR_UUID \ + { 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +#define SERIAL_SVC_FLOW_CONTROL_UUID \ + { 0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +#define SERIAL_SVC_RPC_STATUS_UUID \ + { 0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc.c b/firmware/targets/f7/furi_hal/f_hal_nfc.c new file mode 100644 index 000000000000..999fc7268f4d --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc.c @@ -0,0 +1,564 @@ +#include "f_hal_nfc_i.h" + +#include + +#include +#include + +#define TAG "FHalNfc" + +static const FHalNfcTechBase* f_hal_nfc_tech[FHalNfcTechNum] = { + [FHalNfcTechIso14443a] = &f_hal_nfc_iso14443a, + [FHalNfcTechIso14443b] = &f_hal_nfc_iso14443b, + [FHalNfcTechIso15693] = &f_hal_nfc_iso15693, + [FHalNfcTechFelica] = &f_hal_nfc_felica, +}; + +FHalNfc f_hal_nfc; + +static FHalNfcError f_hal_nfc_turn_on_osc(FuriHalSpiBusHandle* handle) { + FHalNfcError error = FHalNfcErrorNone; + + if(!st25r3916_check_reg( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en, + ST25R3916_REG_OP_CONTROL_en)) { + st25r3916_mask_irq(handle, ~ST25R3916_IRQ_MASK_OSC); + st25r3916_set_reg_bits(handle, ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_en); + f_hal_nfc_event_wait_for_specific_irq(handle, ST25R3916_IRQ_MASK_OSC, 10); + } + // Disable IRQs + st25r3916_mask_irq(handle, ST25R3916_IRQ_MASK_ALL); + + bool osc_on = st25r3916_check_reg( + handle, + ST25R3916_REG_AUX_DISPLAY, + ST25R3916_REG_AUX_DISPLAY_osc_ok, + ST25R3916_REG_AUX_DISPLAY_osc_ok); + if(!osc_on) { + error = FHalNfcErrorOscillator; + } + + return error; +} + +FHalNfcError f_hal_nfc_is_hal_ready() { + FHalNfcError error = FHalNfcErrorNone; + + do { + error = f_hal_nfc_acquire(); + if(error != FHalNfcErrorNone) break; + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + uint8_t chip_id = 0; + st25r3916_read_reg(handle, ST25R3916_REG_IC_IDENTITY, &chip_id); + if((chip_id & ST25R3916_REG_IC_IDENTITY_ic_type_mask) != + ST25R3916_REG_IC_IDENTITY_ic_type_st25r3916) { + FURI_LOG_E(TAG, "Wrong chip id"); + error = FHalNfcErrorCommunication; + } + + f_hal_nfc_release(); + } while(false); + + return error; +} + +FHalNfcError f_hal_nfc_init() { + furi_assert(f_hal_nfc.mutex == NULL); + + f_hal_nfc.mutex = furi_mutex_alloc(FuriMutexTypeNormal); + FHalNfcError error = FHalNfcErrorNone; + + f_hal_nfc_event_init(); + f_hal_nfc_event_start(); + + do { + error = f_hal_nfc_acquire(); + if(error != FHalNfcErrorNone) break; + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + // Set default state + st25r3916_direct_cmd(handle, ST25R3916_CMD_SET_DEFAULT); + // Increase IO driver strength of MISO and IRQ + st25r3916_write_reg(handle, ST25R3916_REG_IO_CONF2, ST25R3916_REG_IO_CONF2_io_drv_lvl); + // Check chip ID + uint8_t chip_id = 0; + st25r3916_read_reg(handle, ST25R3916_REG_IC_IDENTITY, &chip_id); + if((chip_id & ST25R3916_REG_IC_IDENTITY_ic_type_mask) != + ST25R3916_REG_IC_IDENTITY_ic_type_st25r3916) { + FURI_LOG_E(TAG, "Wrong chip id"); + error = FHalNfcErrorCommunication; + } + // Clear interrupts + st25r3916_get_irq(handle); + // Mask all interrupts + st25r3916_mask_irq(handle, ST25R3916_IRQ_MASK_ALL); + // Enable interrupts + f_hal_nfc_init_gpio_isr(); + // Disable internal overheat protection + st25r3916_change_test_reg_bits(handle, 0x04, 0x10, 0x10); + + error = f_hal_nfc_turn_on_osc(handle); + if(error != FHalNfcErrorNone) break; + + // Measure voltage + // Set measure power supply voltage source + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_REGULATOR_CONTROL, + ST25R3916_REG_REGULATOR_CONTROL_mpsv_mask, + ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd); + // Enable timer and interrupt register + st25r3916_mask_irq(handle, ~ST25R3916_IRQ_MASK_DCT); + st25r3916_direct_cmd(handle, ST25R3916_CMD_MEASURE_VDD); + f_hal_nfc_event_wait_for_specific_irq(handle, ST25R3916_IRQ_MASK_DCT, 100); + st25r3916_mask_irq(handle, ST25R3916_IRQ_MASK_ALL); + uint8_t ad_res = 0; + st25r3916_read_reg(handle, ST25R3916_REG_AD_RESULT, &ad_res); + uint16_t mV = ((uint16_t)ad_res) * 23U; + mV += (((((uint16_t)ad_res) * 4U) + 5U) / 10U); + + if(mV < 3600) { + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_IO_CONF2, + ST25R3916_REG_IO_CONF2_sup3V, + ST25R3916_REG_IO_CONF2_sup3V_3V); + } else { + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_IO_CONF2, + ST25R3916_REG_IO_CONF2_sup3V, + ST25R3916_REG_IO_CONF2_sup3V_5V); + } + + // Disable MCU CLK + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_IO_CONF1, + ST25R3916_REG_IO_CONF1_out_cl_mask | ST25R3916_REG_IO_CONF1_lf_clk_off, + 0x07); + // Disable MISO pull-down + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_IO_CONF2, + ST25R3916_REG_IO_CONF2_miso_pd1 | ST25R3916_REG_IO_CONF2_miso_pd2, + 0x00); + // Set tx driver resistance to 1 Om + st25r3916_change_reg_bits( + handle, ST25R3916_REG_TX_DRIVER, ST25R3916_REG_TX_DRIVER_d_res_mask, 0x00); + // Use minimum non-overlap + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_RES_AM_MOD, + ST25R3916_REG_RES_AM_MOD_fa3_f, + ST25R3916_REG_RES_AM_MOD_fa3_f); + + // Set activation threashold + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_FIELD_THRESHOLD_ACTV, + ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask, + ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_105mV); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_FIELD_THRESHOLD_ACTV, + ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask, + ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_105mV); + // Set deactivation threashold + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_mask, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_75mV); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_mask, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_75mV); + // Enable external load modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_lm_ext, + ST25R3916_REG_AUX_MOD_lm_ext); + // Enable internal load modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_lm_dri, + ST25R3916_REG_AUX_MOD_lm_dri); + // Adjust FDT + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_PASSIVE_TARGET, + ST25R3916_REG_PASSIVE_TARGET_fdel_mask, + (5U << ST25R3916_REG_PASSIVE_TARGET_fdel_shift)); + // Reduce RFO resistance in Modulated state + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_PT_MOD, + ST25R3916_REG_PT_MOD_ptm_res_mask | ST25R3916_REG_PT_MOD_pt_res_mask, + 0x0f); + // Enable RX start on first 4 bits + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_EMD_SUP_CONF, + ST25R3916_REG_EMD_SUP_CONF_rx_start_emv, + ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_on); + // Set antena tunning + st25r3916_change_reg_bits(handle, ST25R3916_REG_ANT_TUNE_A, 0xff, 0x82); + st25r3916_change_reg_bits(handle, ST25R3916_REG_ANT_TUNE_B, 0xff, 0x82); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en_fd_mask, + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + + // Perform calibration + if(st25r3916_check_reg( + handle, + ST25R3916_REG_REGULATOR_CONTROL, + ST25R3916_REG_REGULATOR_CONTROL_reg_s, + 0x00)) { + FURI_LOG_I(TAG, "Adjusting regulators"); + // Reset logic + st25r3916_set_reg_bits( + handle, ST25R3916_REG_REGULATOR_CONTROL, ST25R3916_REG_REGULATOR_CONTROL_reg_s); + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_REGULATOR_CONTROL, ST25R3916_REG_REGULATOR_CONTROL_reg_s); + st25r3916_direct_cmd(handle, ST25R3916_CMD_ADJUST_REGULATORS); + furi_delay_ms(6); + } + + f_hal_nfc_low_power_mode_start(); + f_hal_nfc_release(); + } while(false); + + return error; +} + +static bool f_hal_nfc_is_mine() { + return (furi_mutex_get_owner(f_hal_nfc.mutex) == furi_thread_get_current_id()); +} + +FHalNfcError f_hal_nfc_acquire() { + furi_check(f_hal_nfc.mutex); + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_nfc); + + FHalNfcError error = FHalNfcErrorNone; + if(furi_mutex_acquire(f_hal_nfc.mutex, 100) != FuriStatusOk) { + furi_hal_spi_release(&furi_hal_spi_bus_handle_nfc); + error = FHalNfcErrorBusy; + } + + return error; +} + +FHalNfcError f_hal_nfc_release() { + furi_check(f_hal_nfc.mutex); + furi_check(f_hal_nfc_is_mine()); + furi_check(furi_mutex_release(f_hal_nfc.mutex) == FuriStatusOk); + + furi_hal_spi_release(&furi_hal_spi_bus_handle_nfc); + + return FHalNfcErrorNone; +} + +FHalNfcError f_hal_nfc_low_power_mode_start() { + FHalNfcError error = FHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + (ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_wu | ST25R3916_REG_OP_CONTROL_tx_en | + ST25R3916_REG_OP_CONTROL_en_fd_mask)); + f_hal_nfc_deinit_gpio_isr(); + f_hal_nfc_timers_deinit(); + + return error; +} + +FHalNfcError f_hal_nfc_low_power_mode_stop() { + FHalNfcError error = FHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + do { + f_hal_nfc_init_gpio_isr(); + f_hal_nfc_timers_init(); + error = f_hal_nfc_turn_on_osc(handle); + if(error != FHalNfcErrorNone) break; + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en_fd_mask, + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + + } while(false); + + return error; +} + +static FHalNfcError f_hal_nfc_poller_init_common(FuriHalSpiBusHandle* handle) { + // Disable wake up + st25r3916_clear_reg_bits(handle, ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); + // Enable correlator + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_AUX, + ST25R3916_REG_AUX_dis_corr, + ST25R3916_REG_AUX_dis_corr_correlator); + + st25r3916_change_reg_bits(handle, ST25R3916_REG_ANT_TUNE_A, 0xff, 0x82); + st25r3916_change_reg_bits(handle, ST25R3916_REG_ANT_TUNE_B, 0xFF, 0x82); + + st25r3916_write_reg(handle, ST25R3916_REG_OVERSHOOT_CONF1, 0x00); + st25r3916_write_reg(handle, ST25R3916_REG_OVERSHOOT_CONF2, 0x00); + st25r3916_write_reg(handle, ST25R3916_REG_UNDERSHOOT_CONF1, 0x00); + st25r3916_write_reg(handle, ST25R3916_REG_UNDERSHOOT_CONF2, 0x00); + + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_listener_init_common(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + // TODO: Common listener configuration + return FHalNfcErrorNone; +} + +FHalNfcError f_hal_nfc_set_mode(FHalNfcMode mode, FHalNfcTech tech) { + furi_assert(mode < FHalNfcModeNum); + furi_assert(tech < FHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + FHalNfcError error = FHalNfcErrorNone; + + if(mode == FHalNfcModePoller) { + do { + error = f_hal_nfc_poller_init_common(handle); + if(error != FHalNfcErrorNone) break; + error = f_hal_nfc_tech[tech]->poller.init(handle); + } while(false); + + } else if(mode == FHalNfcModeListener) { + do { + error = f_hal_nfc_listener_init_common(handle); + if(error != FHalNfcErrorNone) break; + error = f_hal_nfc_tech[tech]->listener.init(handle); + } while(false); + } + + f_hal_nfc.mode = mode; + f_hal_nfc.tech = tech; + return error; +} + +FHalNfcError f_hal_nfc_reset_mode() { + FHalNfcError error = FHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + + const FHalNfcMode mode = f_hal_nfc.mode; + const FHalNfcTech tech = f_hal_nfc.tech; + if(mode == FHalNfcModePoller) { + error = f_hal_nfc_tech[tech]->poller.deinit(handle); + } else if(mode == FHalNfcModeListener) { + error = f_hal_nfc_tech[tech]->listener.deinit(handle); + } + // Set default value in mode register + st25r3916_write_reg(handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_om0); + st25r3916_write_reg(handle, ST25R3916_REG_STREAM_MODE, 0); + st25r3916_clear_reg_bits(handle, ST25R3916_REG_AUX, ST25R3916_REG_AUX_no_crc_rx); + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_BIT_RATE, + ST25R3916_REG_BIT_RATE_txrate_mask | ST25R3916_REG_BIT_RATE_rxrate_mask); + + // Write default values + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, 0); + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_sqm_dyn | ST25R3916_REG_RX_CONF2_agc_en | + ST25R3916_REG_RX_CONF2_agc_m); + + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s7 | ST25R3916_REG_CORR_CONF1_corr_s4 | + ST25R3916_REG_CORR_CONF1_corr_s1 | ST25R3916_REG_CORR_CONF1_corr_s0); + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0); + + return error; +} + +FHalNfcError f_hal_nfc_poller_field_on() { + FHalNfcError error = FHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + if(!st25r3916_check_reg( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_tx_en, + ST25R3916_REG_OP_CONTROL_tx_en)) { + // Set min guard time + st25r3916_write_reg(handle, ST25R3916_REG_FIELD_ON_GT, 0); + // Enable tx rx + st25r3916_set_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + (ST25R3916_REG_OP_CONTROL_rx_en | ST25R3916_REG_OP_CONTROL_tx_en)); + } + + return error; +} + +FHalNfcError f_hal_nfc_poller_tx_common( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + furi_assert(tx_data); + + FHalNfcError err = FHalNfcErrorNone; + + // Prepare tx + st25r3916_direct_cmd(handle, ST25R3916_CMD_CLEAR_FIFO); + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_TIMER_EMV_CONTROL, ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443A_NFC, + (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par), + (ST25R3916_REG_ISO14443A_NFC_no_tx_par_off | ST25R3916_REG_ISO14443A_NFC_no_rx_par_off)); + uint32_t interrupts = + (ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS | + ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | + ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE); + // Clear interrupts + st25r3916_get_irq(handle); + // Enable interrupts + st25r3916_mask_irq(handle, ~interrupts); + + st25r3916_write_fifo(handle, tx_data, tx_bits); + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_WITHOUT_CRC); + + return err; +} + +FHalNfcError + f_hal_nfc_common_fifo_tx(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits) { + FHalNfcError err = FHalNfcErrorNone; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_CLEAR_FIFO); + st25r3916_write_fifo(handle, tx_data, tx_bits); + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_WITHOUT_CRC); + + return err; +} + +FHalNfcError f_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits) { + furi_assert(f_hal_nfc.mode == FHalNfcModePoller); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + return f_hal_nfc_tech[f_hal_nfc.tech]->poller.tx(handle, tx_data, tx_bits); +} + +FHalNfcError f_hal_nfc_poller_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { + furi_assert(f_hal_nfc.mode == FHalNfcModePoller); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + return f_hal_nfc_tech[f_hal_nfc.tech]->poller.rx(handle, rx_data, rx_data_size, rx_bits); +} + +FHalNfcEvent f_hal_nfc_poller_wait_event(uint32_t timeout_ms) { + furi_assert(f_hal_nfc.mode == FHalNfcModePoller); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + + return f_hal_nfc_tech[f_hal_nfc.tech]->poller.wait_event(timeout_ms); +} + +FHalNfcEvent f_hal_nfc_listener_wait_event(uint32_t timeout_ms) { + furi_assert(f_hal_nfc.mode == FHalNfcModeListener); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + + return f_hal_nfc_tech[f_hal_nfc.tech]->listener.wait_event(timeout_ms); +} + +FHalNfcError f_hal_nfc_listener_tx(const uint8_t* tx_data, size_t tx_bits) { + furi_assert(tx_data); + + furi_assert(f_hal_nfc.mode == FHalNfcModeListener); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + return f_hal_nfc_tech[f_hal_nfc.tech]->listener.tx(handle, tx_data, tx_bits); +} + +FHalNfcError f_hal_nfc_common_fifo_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits) { + FHalNfcError error = FHalNfcErrorNone; + + if(!st25r3916_read_fifo(handle, rx_data, rx_data_size, rx_bits)) { + error = FHalNfcErrorBufferOverflow; + } + + return error; +} + +FHalNfcError f_hal_nfc_listener_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { + furi_assert(rx_data); + furi_assert(rx_bits); + + furi_assert(f_hal_nfc.mode == FHalNfcModeListener); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + return f_hal_nfc_tech[f_hal_nfc.tech]->listener.rx(handle, rx_data, rx_data_size, rx_bits); +} + +FHalNfcError f_hal_nfc_trx_reset() { + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + + return FHalNfcErrorNone; +} + +FHalNfcError f_hal_nfc_listener_sleep() { + furi_assert(f_hal_nfc.mode == FHalNfcModeListener); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + return f_hal_nfc_tech[f_hal_nfc.tech]->listener.sleep(handle); +} + +FHalNfcError f_hal_nfc_listener_idle() { + furi_assert(f_hal_nfc.mode == FHalNfcModeListener); + furi_assert(f_hal_nfc.tech < FHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + return f_hal_nfc_tech[f_hal_nfc.tech]->listener.idle(handle); +} + +FHalNfcError f_hal_nfc_listener_enable_rx() { + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA); + + return FHalNfcErrorNone; +} diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_event.c b/firmware/targets/f7/furi_hal/f_hal_nfc_event.c new file mode 100644 index 000000000000..d4876f126ff0 --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_event.c @@ -0,0 +1,108 @@ +#include + +FHalNfcEventInternal* f_hal_nfc_event = NULL; + +void f_hal_nfc_event_init() { + f_hal_nfc_event = malloc(sizeof(FHalNfcEventInternal)); +} + +FHalNfcError f_hal_nfc_event_start() { + furi_assert(f_hal_nfc_event); + + f_hal_nfc_event->thread = furi_thread_get_current_id(); + furi_thread_flags_clear(F_HAL_NFC_EVENT_INTERNAL_ALL); + + return FHalNfcErrorNone; +} + +void f_hal_nfc_event_set(FHalNfcEventInternalType event) { + furi_assert(f_hal_nfc_event); + furi_assert(f_hal_nfc_event->thread); + + furi_thread_flags_set(f_hal_nfc_event->thread, event); +} + +FHalNfcError f_hal_nfc_abort() { + f_hal_nfc_event_set(FHalNfcEventInternalTypeAbort); + return FHalNfcErrorNone; +} + +FHalNfcEvent f_hal_nfc_wait_event_common(uint32_t timeout_ms) { + furi_assert(f_hal_nfc_event); + furi_assert(f_hal_nfc_event->thread); + + FHalNfcEvent event = 0; + uint32_t event_timeout = timeout_ms == F_HAL_NFC_EVENT_WAIT_FOREVER ? FuriWaitForever : + timeout_ms; + uint32_t event_flag = + furi_thread_flags_wait(F_HAL_NFC_EVENT_INTERNAL_ALL, FuriFlagWaitAny, event_timeout); + if(event_flag != FuriFlagErrorTimeout) { + if(event_flag & FHalNfcEventInternalTypeIrq) { + furi_thread_flags_clear(FHalNfcEventInternalTypeIrq); + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + uint32_t irq = f_hal_nfc_get_irq(handle); + if(irq & ST25R3916_IRQ_MASK_OSC) { + event |= FHalNfcEventOscOn; + } + if(irq & ST25R3916_IRQ_MASK_TXE) { + event |= FHalNfcEventTxEnd; + } + if(irq & ST25R3916_IRQ_MASK_RXS) { + event |= FHalNfcEventRxStart; + } + if(irq & ST25R3916_IRQ_MASK_RXE) { + event |= FHalNfcEventRxEnd; + } + if(irq & ST25R3916_IRQ_MASK_COL) { + event |= FHalNfcEventCollision; + } + if(irq & ST25R3916_IRQ_MASK_EON) { + event |= FHalNfcEventFieldOn; + } + if(irq & ST25R3916_IRQ_MASK_EOF) { + event |= FHalNfcEventFieldOff; + } + if(irq & ST25R3916_IRQ_MASK_WU_A) { + event |= FHalNfcEventListenerActive; + } + if(irq & ST25R3916_IRQ_MASK_WU_A_X) { + event |= FHalNfcEventListenerActive; + } + } + if(event_flag & FHalNfcEventInternalTypeTimerFwtExpired) { + event |= FHalNfcEventTimerFwtExpired; + furi_thread_flags_clear(FHalNfcEventInternalTypeTimerFwtExpired); + } + if(event_flag & FHalNfcEventInternalTypeTimerBlockTxExpired) { + event |= FHalNfcEventTimerBlockTxExpired; + furi_thread_flags_clear(FHalNfcEventInternalTypeTimerBlockTxExpired); + } + if(event_flag & FHalNfcEventInternalTypeAbort) { + event |= FHalNfcEventAbortRequest; + furi_thread_flags_clear(FHalNfcEventInternalTypeAbort); + } + } else { + event = FHalNfcEventTimeout; + } + + return event; +} + +bool f_hal_nfc_event_wait_for_specific_irq( + FuriHalSpiBusHandle* handle, + uint32_t mask, + uint32_t timeout_ms) { + furi_assert(f_hal_nfc_event); + furi_assert(f_hal_nfc_event->thread); + + bool irq_received = false; + uint32_t event_flag = + furi_thread_flags_wait(FHalNfcEventInternalTypeIrq, FuriFlagWaitAny, timeout_ms); + if(event_flag == FHalNfcEventInternalTypeIrq) { + uint32_t irq = f_hal_nfc_get_irq(handle); + irq_received = ((irq & mask) == mask); + furi_thread_flags_clear(FHalNfcEventInternalTypeIrq); + } + + return irq_received; +} diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_felica.c b/firmware/targets/f7/furi_hal/f_hal_nfc_felica.c new file mode 100644 index 000000000000..683da8534ee4 --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_felica.c @@ -0,0 +1,63 @@ +#include "f_hal_nfc_i.h" + +static FHalNfcError f_hal_nfc_felica_poller_init(FuriHalSpiBusHandle* handle) { + // Enable Felica mode, AM modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_felica | ST25R3916_REG_MODE_tr_am_am); + + // 10% ASK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_TX_DRIVER, + ST25R3916_REG_TX_DRIVER_am_mod_mask, + ST25R3916_REG_TX_DRIVER_am_mod_10percent); + + // Use regulator AM, resistive AM disabled + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); + + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_BIT_RATE, + ST25R3916_REG_BIT_RATE_txrate_mask | ST25R3916_REG_BIT_RATE_rxrate_mask, + ST25R3916_REG_BIT_RATE_txrate_212 | ST25R3916_REG_BIT_RATE_rxrate_212); + + // Receive configuration + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF1, + ST25R3916_REG_RX_CONF1_lp0 | ST25R3916_REG_RX_CONF1_hz_12_80khz); + + // Correlator setup + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s6 | ST25R3916_REG_CORR_CONF1_corr_s4 | + ST25R3916_REG_CORR_CONF1_corr_s3); + + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_felica_poller_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + + return FHalNfcErrorNone; +} + +const FHalNfcTechBase f_hal_nfc_felica = { + .poller = + { + .init = f_hal_nfc_felica_poller_init, + .deinit = f_hal_nfc_felica_poller_deinit, + .wait_event = f_hal_nfc_wait_event_common, + .tx = f_hal_nfc_poller_tx_common, + .rx = f_hal_nfc_common_fifo_rx, + }, + + .listener = {0}, +}; diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_i.h b/firmware/targets/f7/furi_hal/f_hal_nfc_i.h new file mode 100644 index 000000000000..d61fff1327dd --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_i.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FHalNfcEventInternalTypeAbort = (1U << 0), + FHalNfcEventInternalTypeIrq = (1U << 1), + FHalNfcEventInternalTypeTimerFwtExpired = (1U << 2), + FHalNfcEventInternalTypeTimerBlockTxExpired = (1U << 3), + FHalNfcEventInternalTypeTransparentDataReceived = (1U << 4), +} FHalNfcEventInternalType; + +#define F_HAL_NFC_EVENT_INTERNAL_ALL \ + ((FHalNfcEventInternalTypeAbort | FHalNfcEventInternalTypeIrq | \ + FHalNfcEventInternalTypeTimerFwtExpired | FHalNfcEventInternalTypeTimerBlockTxExpired | \ + FHalNfcEventInternalTypeTransparentDataReceived)) + +typedef struct { + FuriThreadId thread; + void* context; +} FHalNfcEventInternal; + +typedef struct { + FuriMutex* mutex; + FHalNfcMode mode; + FHalNfcTech tech; +} FHalNfc; + +extern FHalNfc f_hal_nfc; + +void f_hal_nfc_event_init(); + +void f_hal_nfc_event_set(FHalNfcEventInternalType event); + +void f_hal_nfc_init_gpio_isr(); + +void f_hal_nfc_deinit_gpio_isr(); + +void f_hal_nfc_timers_init(); + +void f_hal_nfc_timers_deinit(); + +uint32_t f_hal_nfc_get_irq(FuriHalSpiBusHandle* handle); + +bool f_hal_nfc_event_wait_for_specific_irq( + FuriHalSpiBusHandle* handle, + uint32_t mask, + uint32_t timeout_ms); + +// Common technology methods +FHalNfcEvent f_hal_nfc_wait_event_common(uint32_t timeout_ms); +FHalNfcError f_hal_nfc_common_listener_rx_start(FuriHalSpiBusHandle* handle); +FHalNfcError + f_hal_nfc_common_fifo_tx(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); +FHalNfcError f_hal_nfc_common_fifo_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits); + +FHalNfcError + f_hal_nfc_poller_tx_common(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); + +// Technology specific API +typedef FHalNfcError (*FHalNfcChipConfig)(FuriHalSpiBusHandle* handle); +typedef FHalNfcError ( + *FHalNfcTx)(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); +typedef FHalNfcError (*FHalNfcRx)( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits); +typedef FHalNfcEvent (*FHalNfcWaitEvent)(uint32_t timeout_ms); +typedef FHalNfcError (*FHalNfcSleep)(FuriHalSpiBusHandle* handle); +typedef FHalNfcError (*FHalNfcIdle)(FuriHalSpiBusHandle* handle); + +typedef struct { + FHalNfcChipConfig init; + FHalNfcChipConfig deinit; + FHalNfcWaitEvent wait_event; + FHalNfcTx tx; + FHalNfcRx rx; +} FHalNfcTechPollerBase; + +typedef struct { + FHalNfcChipConfig init; + FHalNfcChipConfig deinit; + FHalNfcWaitEvent wait_event; + FHalNfcTx tx; + FHalNfcRx rx; + FHalNfcSleep sleep; + FHalNfcIdle idle; +} FHalNfcTechListenerBase; + +typedef struct { + FHalNfcTechPollerBase poller; + FHalNfcTechListenerBase listener; +} FHalNfcTechBase; + +extern const FHalNfcTechBase f_hal_nfc_iso14443a; +extern const FHalNfcTechBase f_hal_nfc_iso14443b; +extern const FHalNfcTechBase f_hal_nfc_iso15693; +extern const FHalNfcTechBase f_hal_nfc_felica; + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_irq.c b/firmware/targets/f7/furi_hal/f_hal_nfc_irq.c new file mode 100644 index 000000000000..95f6c4129280 --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_irq.c @@ -0,0 +1,28 @@ +#include "f_hal_nfc_i.h" + +#include +#include + +static void f_hal_nfc_int_callback() { + f_hal_nfc_event_set(FHalNfcEventInternalTypeIrq); +} + +uint32_t f_hal_nfc_get_irq(FuriHalSpiBusHandle* handle) { + uint32_t irq = 0; + while(furi_hal_gpio_read_port_pin(gpio_nfc_irq_rfid_pull.port, gpio_nfc_irq_rfid_pull.pin)) { + irq |= st25r3916_get_irq(handle); + } + return irq; +} + +void f_hal_nfc_init_gpio_isr() { + furi_hal_gpio_init( + &gpio_nfc_irq_rfid_pull, GpioModeInterruptRise, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, f_hal_nfc_int_callback, NULL); + furi_hal_gpio_enable_int_callback(&gpio_nfc_irq_rfid_pull); +} + +void f_hal_nfc_deinit_gpio_isr() { + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_remove_int_callback(&gpio_nfc_irq_rfid_pull); +} diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_iso14443a.c b/firmware/targets/f7/furi_hal/f_hal_nfc_iso14443a.c new file mode 100644 index 000000000000..f04f05ae0171 --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_iso14443a.c @@ -0,0 +1,345 @@ +#include + +#include +#include + +#include + +#define TAG "FuriHalIso14443a" + +static Iso14443_3aSignal* iso14443_3a_signal = NULL; + +static FHalNfcError f_hal_nfc_iso14443a_common_init(FuriHalSpiBusHandle* handle) { + // Common NFC-A settings, 106 kbps + + // 1st stage zero = 600kHz, 3rd stage zero = 200 kHz + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, ST25R3916_REG_RX_CONF1_z600k); + // AGC enabled, ratio 3:1, squelch after TX + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m | + ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn); + // HF operation, full gain on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); + // No gain reduction on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); + // Correlator config + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s4 | + ST25R3916_REG_CORR_CONF1_corr_s6); + // Sleep mode disable, 424kHz mode off + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0x00); + + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_iso14443a_poller_init(FuriHalSpiBusHandle* handle) { + // Enable ISO14443A mode, OOK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_iso14443a | ST25R3916_REG_MODE_tr_am_ook); + + // Overshoot protection - is this necessary here? + st25r3916_change_reg_bits(handle, ST25R3916_REG_OVERSHOOT_CONF1, 0xff, 0x40); + st25r3916_change_reg_bits(handle, ST25R3916_REG_OVERSHOOT_CONF2, 0xff, 0x03); + st25r3916_change_reg_bits(handle, ST25R3916_REG_UNDERSHOOT_CONF1, 0xff, 0x40); + st25r3916_change_reg_bits(handle, ST25R3916_REG_UNDERSHOOT_CONF2, 0xff, 0x03); + + return f_hal_nfc_iso14443a_common_init(handle); +} + +static FHalNfcError f_hal_nfc_iso14443a_poller_deinit(FuriHalSpiBusHandle* handle) { + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443A_NFC, + (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par), + (ST25R3916_REG_ISO14443A_NFC_no_tx_par_off | ST25R3916_REG_ISO14443A_NFC_no_rx_par_off)); + + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_iso14443a_listener_init(FuriHalSpiBusHandle* handle) { + furi_check(iso14443_3a_signal == NULL); + iso14443_3a_signal = iso14443_3a_signal_alloc(&gpio_spi_r_mosi); + + st25r3916_write_reg( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + st25r3916_write_reg( + handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om0); + st25r3916_write_reg( + handle, + ST25R3916_REG_PASSIVE_TARGET, + ST25R3916_REG_PASSIVE_TARGET_fdel_2 | ST25R3916_REG_PASSIVE_TARGET_fdel_0 | + ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p | ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r); + + st25r3916_write_reg(handle, ST25R3916_REG_MASK_RX_TIMER, 0x02); + + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + uint32_t interrupts = + (ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS | + ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | + ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE | + ST25R3916_IRQ_MASK_EON | ST25R3916_IRQ_MASK_EOF | ST25R3916_IRQ_MASK_WU_A_X | + ST25R3916_IRQ_MASK_WU_A); + // Clear interrupts + st25r3916_get_irq(handle); + // Enable interrupts + st25r3916_mask_irq(handle, ~interrupts); + // Enable auto collision resolution + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a); + st25r3916_direct_cmd(handle, ST25R3916_CMD_GOTO_SENSE); + + return f_hal_nfc_iso14443a_common_init(handle); +} + +static FHalNfcError f_hal_nfc_iso14443a_listener_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + + if(iso14443_3a_signal) { + iso14443_3a_signal_free(iso14443_3a_signal); + iso14443_3a_signal = NULL; + } + + return FHalNfcErrorNone; +} + +static FHalNfcEvent f_hal_nfc_iso14443_3a_listener_wait_event(uint32_t timeout_ms) { + FHalNfcEvent event = f_hal_nfc_wait_event_common(timeout_ms); + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + if(event & FHalNfcEventListenerActive) { + st25r3916_set_reg_bits( + handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a); + } + + return event; +} + +FHalNfcError f_hal_nfc_iso14443a_poller_trx_short_frame(FHalNfcaShortFrame frame) { + FHalNfcError error = FHalNfcErrorNone; + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + // Disable crc check + st25r3916_set_reg_bits(handle, ST25R3916_REG_AUX, ST25R3916_REG_AUX_no_crc_rx); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443A_NFC, + (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par), + (ST25R3916_REG_ISO14443A_NFC_no_tx_par_off | ST25R3916_REG_ISO14443A_NFC_no_rx_par_off)); + + st25r3916_write_reg(handle, ST25R3916_REG_NUM_TX_BYTES2, 0); + uint32_t interrupts = + (ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS | + ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | + ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE); + // Clear interrupts + st25r3916_get_irq(handle); + // Enable interrupts + st25r3916_mask_irq(handle, ~interrupts); + if(frame == FHalNfcaShortFrameAllReq) { + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_REQA); + } else { + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_WUPA); + } + + return error; +} + +FHalNfcError f_hal_nfc_iso14443a_tx_sdd_frame(const uint8_t* tx_data, size_t tx_bits) { + FHalNfcError error = FHalNfcErrorNone; + // TODO Set anticollision parameters + error = f_hal_nfc_poller_tx(tx_data, tx_bits); + + return error; +} + +FHalNfcError + f_hal_nfc_iso14443a_rx_sdd_frame(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { + FHalNfcError error = FHalNfcErrorNone; + UNUSED(rx_data); + UNUSED(rx_bits); + UNUSED(rx_data_size); + + error = f_hal_nfc_poller_rx(rx_data, rx_data_size, rx_bits); + // TODO reset anticollision parameters here + + return error; +} + +FHalNfcError f_hal_nfc_iso14443a_poller_tx_custom_parity(const uint8_t* tx_data, size_t tx_bits) { + furi_assert(tx_data); + + // TODO common code for f_hal_nfc_poller_tx + + FHalNfcError err = FHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + // Prepare tx + st25r3916_direct_cmd(handle, ST25R3916_CMD_CLEAR_FIFO); + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_TIMER_EMV_CONTROL, ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443A_NFC, + (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par), + (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par)); + uint32_t interrupts = + (ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS | + ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | + ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE); + // Clear interrupts + st25r3916_get_irq(handle); + // Enable interrupts + st25r3916_mask_irq(handle, ~interrupts); + + st25r3916_write_fifo(handle, tx_data, tx_bits); + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_WITHOUT_CRC); + return err; +} + +FHalNfcError f_hal_nfc_iso14443a_listener_set_col_res_data( + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak) { + furi_assert(uid); + furi_assert(atqa); + UNUSED(uid_len); + UNUSED(sak); + FHalNfcError error = FHalNfcErrorNone; + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + // Set 4 or 7 bytes UID + if(uid_len == 4) { + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_AUX, + ST25R3916_REG_AUX_nfc_id_mask, + ST25R3916_REG_AUX_nfc_id_4bytes); + } else { + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_AUX, + ST25R3916_REG_AUX_nfc_id_mask, + ST25R3916_REG_AUX_nfc_id_7bytes); + } + // Write PT Memory + uint8_t pt_memory[15] = {}; + memcpy(pt_memory, uid, uid_len); + pt_memory[10] = atqa[0]; + pt_memory[11] = atqa[1]; + if(uid_len == 4) { + pt_memory[12] = sak & ~0x04; + } else { + pt_memory[12] = 0x04; + } + pt_memory[13] = sak & ~0x04; + pt_memory[14] = sak & ~0x04; + + st25r3916_write_pta_mem(handle, pt_memory, sizeof(pt_memory)); + + return error; +} + +FHalNfcError f_hal_iso4443a_listener_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + FHalNfcError error = FHalNfcErrorNone; + + do { + error = f_hal_nfc_common_fifo_tx(handle, tx_data, tx_bits); + if(error != FHalNfcErrorNone) break; + + bool tx_end = f_hal_nfc_event_wait_for_specific_irq(handle, ST25R3916_IRQ_MASK_TXE, 10); + if(!tx_end) { + error = FHalNfcErrorCommunicationTimeout; + break; + } + + } while(false); + + return error; +} + +FHalNfcError f_hal_nfc_iso14443a_listener_tx_custom_parity( + const uint8_t* tx_data, + const uint8_t* tx_parity, + size_t tx_bits) { + furi_assert(tx_data); + furi_assert(tx_parity); + + furi_assert(iso14443_3a_signal); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSPARENT_MODE); + // Reconfigure gpio for Transparent mode + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + + // Send signal + iso14443_3a_signal_tx(iso14443_3a_signal, tx_data, tx_parity, tx_bits); + + // Exit transparent mode + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + + // Configure gpio back to SPI and exit transparent + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA); + + // TODO handle field off + return FHalNfcErrorNone; +} + +FHalNfcError f_hal_iso14443_3a_listener_sleep(FuriHalSpiBusHandle* handle) { + // Enable auto collision resolution + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a); + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + st25r3916_direct_cmd(handle, ST25R3916_CMD_GOTO_SLEEP); + + return FHalNfcErrorNone; +} + +FHalNfcError f_hal_iso14443_3a_listener_idle(FuriHalSpiBusHandle* handle) { + // Enable auto collision resolution + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a); + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + st25r3916_direct_cmd(handle, ST25R3916_CMD_GOTO_SENSE); + + return FHalNfcErrorNone; +} + +const FHalNfcTechBase f_hal_nfc_iso14443a = { + .poller = + { + .init = f_hal_nfc_iso14443a_poller_init, + .deinit = f_hal_nfc_iso14443a_poller_deinit, + .wait_event = f_hal_nfc_wait_event_common, + .tx = f_hal_nfc_poller_tx_common, + .rx = f_hal_nfc_common_fifo_rx, + }, + + .listener = + { + .init = f_hal_nfc_iso14443a_listener_init, + .deinit = f_hal_nfc_iso14443a_listener_deinit, + .wait_event = f_hal_nfc_iso14443_3a_listener_wait_event, + .tx = f_hal_iso4443a_listener_tx, + .rx = f_hal_nfc_common_fifo_rx, + .sleep = f_hal_iso14443_3a_listener_sleep, + .idle = f_hal_iso14443_3a_listener_idle, + }, +}; diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_iso14443b.c b/firmware/targets/f7/furi_hal/f_hal_nfc_iso14443b.c new file mode 100644 index 000000000000..a66efbfa8144 --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_iso14443b.c @@ -0,0 +1,102 @@ +#include "f_hal_nfc_i.h" + +static FHalNfcError f_hal_nfc_iso14443b_common_init(FuriHalSpiBusHandle* handle) { + // Common NFC-B settings, 106kbps + + // 1st stage zero = 60kHz, 3rd stage zero = 200 kHz + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, ST25R3916_REG_RX_CONF1_h200); + + // Enable AGC + // AGC Ratio 6 + // AGC algorithm with RESET (recommended for ISO14443-B) + // AGC operation during complete receive period + // Squelch ratio 6/3 (recommended for ISO14443-B) + // Squelch automatic activation on TX end + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_alg | + ST25R3916_REG_RX_CONF2_agc_m | ST25R3916_REG_RX_CONF2_agc_en | + ST25R3916_REG_RX_CONF2_pulz_61 | ST25R3916_REG_RX_CONF2_sqm_dyn); + + // HF operation, full gain on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); + // No gain reduction on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); + + // Subcarrier end detector enabled + // Subcarrier end detection level = 66% + // BPSK start 33 pilot pulses + // AM & PM summation before digitizing on + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s1 | + ST25R3916_REG_CORR_CONF1_corr_s3 | ST25R3916_REG_CORR_CONF1_corr_s4); + // Sleep mode disable, 424kHz mode off + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0x00); + + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_iso14443b_poller_init(FuriHalSpiBusHandle* handle) { + // Enable ISO14443B mode, AM modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_iso14443b | ST25R3916_REG_MODE_tr_am_am); + + // 10% ASK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_TX_DRIVER, + ST25R3916_REG_TX_DRIVER_am_mod_mask, + ST25R3916_REG_TX_DRIVER_am_mod_10percent); + + // Use regulator AM, resistive AM disabled + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); + + // EGT = 0 etu + // SOF = 10 etu LOW + 2 etu HIGH + // EOF = 10 etu + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443B_1, + ST25R3916_REG_ISO14443B_1_egt_mask | ST25R3916_REG_ISO14443B_1_sof_mask | + ST25R3916_REG_ISO14443B_1_eof, + (0U << ST25R3916_REG_ISO14443B_1_egt_shift) | ST25R3916_REG_ISO14443B_1_sof_0_10etu | + ST25R3916_REG_ISO14443B_1_sof_1_2etu | ST25R3916_REG_ISO14443B_1_eof_10etu); + + // TR1 = 80 / fs + // B' mode off (no_sof & no_eof = 0) + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443B_2, + ST25R3916_REG_ISO14443B_2_tr1_mask | ST25R3916_REG_ISO14443B_2_no_sof | + ST25R3916_REG_ISO14443B_2_no_eof, + ST25R3916_REG_ISO14443B_2_tr1_80fs80fs); + + return f_hal_nfc_iso14443b_common_init(handle); +} + +static FHalNfcError f_hal_nfc_iso14443b_poller_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + return FHalNfcErrorNone; +} + +const FHalNfcTechBase f_hal_nfc_iso14443b = { + .poller = + { + .init = f_hal_nfc_iso14443b_poller_init, + .deinit = f_hal_nfc_iso14443b_poller_deinit, + .wait_event = f_hal_nfc_wait_event_common, + .tx = f_hal_nfc_poller_tx_common, + .rx = f_hal_nfc_common_fifo_rx, + }, + + .listener = {0}, +}; diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_iso15693.c b/firmware/targets/f7/furi_hal/f_hal_nfc_iso15693.c new file mode 100644 index 000000000000..d8e7739ffb4f --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_iso15693.c @@ -0,0 +1,435 @@ +#include "f_hal_nfc_i.h" + +#include +#include + +#include + +#define F_HAL_NFC_ISO15693_MAX_FRAME_SIZE (1024U) +#define F_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE (64) + +#define F_HAL_NFC_ISO15693_RESP_SOF_SIZE (5) +#define F_HAL_NFC_ISO15693_RESP_EOF_SIZE (5) +#define F_HAL_NFC_ISO15693_RESP_SOF_MASK (0x1FU) +#define F_HAL_NFC_ISO15693_RESP_SOF_PATTERN (0x17U) +#define F_HAL_NFC_ISO15693_RESP_EOF_PATTERN (0x1DU) + +#define F_HAL_NFC_ISO15693_RESP_PATTERN_MASK (0x03U) +#define F_HAL_NFC_ISO15693_RESP_PATTERN_0 (0x01U) +#define F_HAL_NFC_ISO15693_RESP_PATTERN_1 (0x02U) + +#define BITS_IN_BYTE (8U) + +#define TAG "FuriHalIso15693" + +typedef struct { + Iso15693Signal* signal; + Iso15693Parser* parser; +} FHalNfcIso15693Listener; + +typedef struct { + // 4 bits per data bit on transmit + uint8_t fifo_buf[F_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE * 4]; + size_t fifo_buf_bits; + uint8_t frame_buf[F_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE * 2]; + size_t frame_buf_bits; +} FHalNfcIso15693Poller; + +static FHalNfcIso15693Listener* f_hal_nfc_iso15693_listener = NULL; +static FHalNfcIso15693Poller* f_hal_nfc_iso15693_poller = NULL; + +static FHalNfcIso15693Listener* f_hal_nfc_iso15693_listener_alloc() { + FHalNfcIso15693Listener* instance = malloc(sizeof(FHalNfcIso15693Listener)); + + instance->signal = iso15693_signal_alloc(&gpio_spi_r_mosi); + instance->parser = iso15693_parser_alloc(&gpio_spi_r_miso, F_HAL_NFC_ISO15693_MAX_FRAME_SIZE); + + return instance; +} + +static void f_hal_nfc_iso15693_listener_free(FHalNfcIso15693Listener* instance) { + furi_assert(instance); + + iso15693_signal_free(instance->signal); + iso15693_parser_free(instance->parser); + + free(instance); +} + +static FHalNfcIso15693Poller* f_hal_nfc_iso15693_poller_alloc() { + FHalNfcIso15693Poller* instance = malloc(sizeof(FHalNfcIso15693Poller)); + + return instance; +} + +static void f_hal_nfc_iso15693_poller_free(FHalNfcIso15693Poller* instance) { + furi_assert(instance); + + free(instance); +} + +static FHalNfcError f_hal_nfc_iso15693_common_init(FuriHalSpiBusHandle* handle) { + // Common NFC-V settings, 26.48 kbps + + // 1st stage zero = 12 kHz, 3rd stage zero = 80 kHz, low-pass = 600 kHz + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF1, + ST25R3916_REG_RX_CONF1_z12k | ST25R3916_REG_RX_CONF1_h80 | + ST25R3916_REG_RX_CONF1_lp_600khz); + + // Enable AGC + // AGC Ratio 6 + // AGC algorithm with RESET (recommended for ISO15693) + // AGC operation during complete receive period + // Squelch automatic activation on TX end + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m | + ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn); + + // HF operation, full gain on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); + // No gain reduction on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); + + // Collision detection level 53% + // AM & PM summation before digitizing on + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s1 | + ST25R3916_REG_CORR_CONF1_corr_s4); + // 424 kHz subcarrier stream mode on + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, ST25R3916_REG_CORR_CONF2_corr_s8); + return FHalNfcErrorNone; +} + +static FHalNfcError f_hal_nfc_iso15693_poller_init(FuriHalSpiBusHandle* handle) { + furi_assert(f_hal_nfc_iso15693_poller == NULL); + + f_hal_nfc_iso15693_poller = f_hal_nfc_iso15693_poller_alloc(); + + // Enable Subcarrier Stream mode, OOK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_subcarrier_stream | ST25R3916_REG_MODE_tr_am_ook); + + // Subcarrier 424 kHz mode + // 8 sub-carrier pulses in report period + st25r3916_write_reg( + handle, + ST25R3916_REG_STREAM_MODE, + ST25R3916_REG_STREAM_MODE_scf_sc424 | ST25R3916_REG_STREAM_MODE_stx_106 | + ST25R3916_REG_STREAM_MODE_scp_8pulses); + + // Use regulator AM, resistive AM disabled + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); + + return f_hal_nfc_iso15693_common_init(handle); +} + +static FHalNfcError f_hal_nfc_iso15693_poller_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + furi_assert(f_hal_nfc_iso15693_poller); + + f_hal_nfc_iso15693_poller_free(f_hal_nfc_iso15693_poller); + f_hal_nfc_iso15693_poller = NULL; + + return FHalNfcErrorNone; +} + +static void iso15693_3_poller_encode_frame( + const uint8_t* tx_data, + size_t tx_bits, + uint8_t* frame_buf, + size_t frame_buf_size, + size_t* frame_buf_bits) { + static const uint8_t bit_patterns_1_out_of_4[] = {0x02, 0x08, 0x20, 0x80}; + size_t frame_buf_size_calc = (tx_bits / 2) + 2; + furi_assert(frame_buf_size >= frame_buf_size_calc); + + // Add SOF 1 out of 4 + frame_buf[0] = 0x21; + + size_t byte_pos = 1; + for(size_t i = 0; i < tx_bits / BITS_IN_BYTE; ++i) { + for(size_t j = 0; j < BITS_IN_BYTE; j += (BITS_IN_BYTE) / 4) { + const uint8_t bit_pair = (tx_data[i] >> j) & 0x03; + frame_buf[byte_pos++] = bit_patterns_1_out_of_4[bit_pair]; + } + } + // Add EOF + frame_buf[byte_pos++] = 0x04; + *frame_buf_bits = byte_pos * BITS_IN_BYTE; +} + +static bool iso15693_3_poller_decode_frame( + const uint8_t* buf, + size_t buf_bits, + uint8_t* buf_decoded, + size_t buf_decoded_size, + size_t* buf_decoded_bits) { + bool decoded = false; + size_t bit_pos = 0; + memset(buf_decoded, 0, buf_decoded_size); + + do { + if(buf_bits == 0) break; + // Check SOF + if((buf[0] & F_HAL_NFC_ISO15693_RESP_SOF_MASK) != F_HAL_NFC_ISO15693_RESP_SOF_PATTERN) + break; + + // 2 response bits = 1 data bit + for(uint32_t i = F_HAL_NFC_ISO15693_RESP_SOF_SIZE; + i < buf_bits - F_HAL_NFC_ISO15693_RESP_SOF_SIZE; + i += BITS_IN_BYTE / 4) { + const size_t byte_index = i / BITS_IN_BYTE; + const size_t bit_offset = i % BITS_IN_BYTE; + const uint8_t resp_byte = (buf[byte_index] >> bit_offset) | + (buf[byte_index + 1] << (BITS_IN_BYTE - bit_offset)); + + // Check EOF + if(resp_byte == F_HAL_NFC_ISO15693_RESP_EOF_PATTERN) { + decoded = true; + break; + } + + const uint8_t bit_pattern = resp_byte & F_HAL_NFC_ISO15693_RESP_PATTERN_MASK; + + if(bit_pattern == F_HAL_NFC_ISO15693_RESP_PATTERN_0) { + bit_pos++; + } else if(bit_pattern == F_HAL_NFC_ISO15693_RESP_PATTERN_1) { + buf_decoded[bit_pos / BITS_IN_BYTE] |= 1 << (bit_pos % BITS_IN_BYTE); + bit_pos++; + } else { + break; + } + if(bit_pos / BITS_IN_BYTE > buf_decoded_size) { + break; + } + } + + } while(false); + + if(decoded) { + *buf_decoded_bits = bit_pos; + } + + return decoded; +} + +static FHalNfcError f_hal_nfc_iso15693_poller_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + FHalNfcIso15693Poller* instance = f_hal_nfc_iso15693_poller; + iso15693_3_poller_encode_frame( + tx_data, + tx_bits, + instance->frame_buf, + sizeof(instance->frame_buf), + &instance->frame_buf_bits); + return f_hal_nfc_poller_tx_common(handle, instance->frame_buf, instance->frame_buf_bits); +} + +static FHalNfcError f_hal_nfc_iso15693_poller_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits) { + FHalNfcError error = FHalNfcErrorNone; + FHalNfcIso15693Poller* instance = f_hal_nfc_iso15693_poller; + + do { + error = f_hal_nfc_common_fifo_rx( + handle, instance->fifo_buf, sizeof(instance->fifo_buf), &instance->fifo_buf_bits); + if(error != FHalNfcErrorNone) break; + + if(!iso15693_3_poller_decode_frame( + instance->fifo_buf, + instance->fifo_buf_bits, + instance->frame_buf, + sizeof(instance->frame_buf), + &instance->frame_buf_bits)) { + error = FHalNfcErrorDataFormat; + break; + } + if(rx_data_size < instance->frame_buf_bits / BITS_IN_BYTE) { + error = FHalNfcErrorBufferOverflow; + break; + } + + memcpy(rx_data, instance->frame_buf, instance->frame_buf_bits / BITS_IN_BYTE); + *rx_bits = instance->frame_buf_bits; + } while(false); + + return error; +} + +static void f_hal_nfc_iso15693_listener_transparent_mode_enter(FuriHalSpiBusHandle* handle) { + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(handle); + f_hal_nfc_deinit_gpio_isr(); +} + +static void f_hal_nfc_iso15693_listener_transparent_mode_exit(FuriHalSpiBusHandle* handle) { + // Configure gpio back to SPI and exit transparent mode + f_hal_nfc_init_gpio_isr(); + furi_hal_spi_bus_handle_init(handle); + + st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA); +} + +static FHalNfcError f_hal_nfc_iso15693_listener_init(FuriHalSpiBusHandle* handle) { + furi_assert(f_hal_nfc_iso15693_listener == NULL); + + f_hal_nfc_iso15693_listener = f_hal_nfc_iso15693_listener_alloc(); + + // Set default operation mode + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_targ_nfca | ST25R3916_REG_MODE_tr_am_ook); + + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_rx_en, + ST25R3916_REG_OP_CONTROL_rx_en); + + // Enable passive target mode + st25r3916_change_reg_bits( + handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ, ST25R3916_REG_MODE_targ_targ); + + FHalNfcError error = f_hal_nfc_iso15693_common_init(handle); + + f_hal_nfc_iso15693_listener_transparent_mode_enter(handle); + + return error; +} + +static FHalNfcError f_hal_nfc_iso15693_listener_deinit(FuriHalSpiBusHandle* handle) { + furi_assert(f_hal_nfc_iso15693_listener); + + f_hal_nfc_iso15693_listener_transparent_mode_exit(handle); + + f_hal_nfc_iso15693_listener_free(f_hal_nfc_iso15693_listener); + f_hal_nfc_iso15693_listener = NULL; + + return FHalNfcErrorNone; +} + +static FHalNfcError + f_hal_nfc_iso15693_listener_tx_transparent(const uint8_t* data, size_t data_size) { + iso15693_signal_tx( + f_hal_nfc_iso15693_listener->signal, Iso15693SignalDataRateHi, data, data_size); + + return FHalNfcErrorNone; +} + +static void f_hal_nfc_iso15693_parser_callback(Iso15693ParserEvent event, void* context) { + furi_assert(context); + + if(event == Iso15693ParserEventDataReceived) { + FuriThreadId thread_id = context; + furi_thread_flags_set(thread_id, FHalNfcEventInternalTypeTransparentDataReceived); + } +} + +static FHalNfcEvent f_hal_nfc_iso15693_wait_event(uint32_t timeout_ms) { + FHalNfcEvent event = 0; + + FuriThreadId thread_id = furi_thread_get_current_id(); + iso15693_parser_start( + f_hal_nfc_iso15693_listener->parser, f_hal_nfc_iso15693_parser_callback, thread_id); + + while(true) { + uint32_t flag = furi_thread_flags_wait( + FHalNfcEventInternalTypeAbort | FHalNfcEventInternalTypeTransparentDataReceived, + FuriFlagWaitAny, + timeout_ms); + furi_thread_flags_clear(flag); + + if(flag & FHalNfcEventInternalTypeAbort) { + event = FHalNfcEventAbortRequest; + break; + } + if(flag & FHalNfcEventInternalTypeTransparentDataReceived) { + if(iso15693_parser_run(f_hal_nfc_iso15693_listener->parser)) { + event = FHalNfcEventRxEnd; + break; + } + } + } + iso15693_parser_stop(f_hal_nfc_iso15693_listener->parser); + + return event; +} + +static FHalNfcError f_hal_nfc_iso15693_listener_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + UNUSED(handle); + furi_assert(f_hal_nfc_iso15693_listener); + + FHalNfcError error = FHalNfcErrorNone; + + error = f_hal_nfc_iso15693_listener_tx_transparent(tx_data, tx_bits / BITS_IN_BYTE); + + return error; +} + +static FHalNfcError f_hal_nfc_iso15693_listener_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits) { + furi_assert(f_hal_nfc_iso15693_listener); + UNUSED(handle); + + if(rx_data_size < iso15693_parser_get_data_size_bytes(f_hal_nfc_iso15693_listener->parser)) { + return FHalNfcErrorBufferOverflow; + } + + iso15693_parser_get_data(f_hal_nfc_iso15693_listener->parser, rx_data, rx_data_size, rx_bits); + + return FHalNfcErrorNone; +} + +FHalNfcError f_hal_iso15693_listener_sleep(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + + return FHalNfcErrorNone; +} + +const FHalNfcTechBase f_hal_nfc_iso15693 = { + .poller = + { + .init = f_hal_nfc_iso15693_poller_init, + .deinit = f_hal_nfc_iso15693_poller_deinit, + .wait_event = f_hal_nfc_wait_event_common, + .tx = f_hal_nfc_iso15693_poller_tx, + .rx = f_hal_nfc_iso15693_poller_rx, + }, + + .listener = + { + .init = f_hal_nfc_iso15693_listener_init, + .deinit = f_hal_nfc_iso15693_listener_deinit, + .wait_event = f_hal_nfc_iso15693_wait_event, + .tx = f_hal_nfc_iso15693_listener_tx, + .rx = f_hal_nfc_iso15693_listener_rx, + .sleep = f_hal_iso15693_listener_sleep, + .idle = f_hal_iso15693_listener_sleep, + }, +}; diff --git a/firmware/targets/f7/furi_hal/f_hal_nfc_timer.c b/firmware/targets/f7/furi_hal/f_hal_nfc_timer.c new file mode 100644 index 000000000000..2339f1dc0b7e --- /dev/null +++ b/firmware/targets/f7/furi_hal/f_hal_nfc_timer.c @@ -0,0 +1,167 @@ +#include "f_hal_nfc_i.h" + +#include +#include +#include +#include +#include + +#define F_HAL_NFC_FREQ_KHZ (13560U) + +typedef enum { + FHalNfcTimerFwt, + FHalNfcTimerBlockTx, + FHalNfcTimerCount, +} FHalNfcTimer; + +typedef struct { + TIM_TypeDef* timer; + FuriHalBus bus; + uint32_t prescaler; + uint32_t freq_khz; + FHalNfcEventInternalType event; + FuriHalInterruptId irq_id; + IRQn_Type irq_type; + bool is_configured; + const GpioPin* pin; +} FHalNfcTimerConfig; + +static FHalNfcTimerConfig f_hal_nfc_timers[FHalNfcTimerCount] = { + [FHalNfcTimerFwt] = + { + .pin = &gpio_ext_pa7, + .timer = TIM1, + .bus = FuriHalBusTIM1, + .prescaler = 15, + .freq_khz = 4000U, + .event = FHalNfcEventInternalTypeTimerFwtExpired, + .irq_id = FuriHalInterruptIdTim1UpTim16, + .irq_type = TIM1_UP_TIM16_IRQn, + .is_configured = false, + }, + [FHalNfcTimerBlockTx] = + { + .pin = &gpio_ext_pa6, + .timer = TIM17, + .bus = FuriHalBusTIM17, + .prescaler = 31, + .freq_khz = 2000U, + .event = FHalNfcEventInternalTypeTimerBlockTxExpired, + .irq_id = FuriHalInterruptIdTim1TrgComTim17, + .irq_type = TIM1_TRG_COM_TIM17_IRQn, + .is_configured = false, + }, +}; + +static void f_hal_nfc_timer_irq_callback(void* context) { + FHalNfcTimerConfig* timer = context; + if(LL_TIM_IsActiveFlag_UPDATE(timer->timer)) { + LL_TIM_ClearFlag_UPDATE(timer->timer); + f_hal_nfc_event_set(timer->event); + furi_hal_gpio_write(timer->pin, false); + } +} + +static void f_hal_nfc_timer_init(FHalNfcTimer timer) { + furi_hal_bus_enable(f_hal_nfc_timers[timer].bus); + + LL_TIM_SetPrescaler(f_hal_nfc_timers[timer].timer, f_hal_nfc_timers[timer].prescaler); + LL_TIM_SetOnePulseMode(f_hal_nfc_timers[timer].timer, LL_TIM_ONEPULSEMODE_SINGLE); + LL_TIM_EnableUpdateEvent(f_hal_nfc_timers[timer].timer); + LL_TIM_SetCounterMode(f_hal_nfc_timers[timer].timer, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockSource(f_hal_nfc_timers[timer].timer, LL_TIM_CLOCKSOURCE_INTERNAL); + + LL_TIM_GenerateEvent_UPDATE(f_hal_nfc_timers[timer].timer); + + LL_TIM_EnableIT_UPDATE(f_hal_nfc_timers[timer].timer); + + furi_hal_interrupt_set_isr( + f_hal_nfc_timers[timer].irq_id, f_hal_nfc_timer_irq_callback, &f_hal_nfc_timers[timer]); + NVIC_SetPriority( + f_hal_nfc_timers[timer].irq_type, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(f_hal_nfc_timers[timer].irq_type); + f_hal_nfc_timers[timer].is_configured = true; + furi_hal_gpio_init( + f_hal_nfc_timers[timer].pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(f_hal_nfc_timers[timer].pin, false); +} + +static void f_hal_nfc_timer_deinit(FHalNfcTimer timer) { + LL_TIM_ClearFlag_UPDATE(f_hal_nfc_timers[timer].timer); + furi_hal_interrupt_set_isr(f_hal_nfc_timers[timer].irq_id, NULL, NULL); + NVIC_DisableIRQ(f_hal_nfc_timers[timer].irq_type); + f_hal_nfc_timers[timer].is_configured = false; + + if(furi_hal_bus_is_enabled(f_hal_nfc_timers[timer].bus)) { + furi_hal_bus_disable(f_hal_nfc_timers[timer].bus); + } +} + +static void f_hal_nfc_timer_start(FHalNfcTimer timer, uint32_t time_fc) { + uint32_t arr_reg = f_hal_nfc_timers[timer].freq_khz * time_fc / F_HAL_NFC_FREQ_KHZ; + furi_check(arr_reg < UINT16_MAX); + + LL_TIM_SetAutoReload(f_hal_nfc_timers[timer].timer, arr_reg); + LL_TIM_EnableCounter(f_hal_nfc_timers[timer].timer); + furi_hal_gpio_write(f_hal_nfc_timers[timer].pin, true); +} + +static void f_hal_nfc_timer_stop(FHalNfcTimer timer) { + LL_TIM_DisableCounter(f_hal_nfc_timers[timer].timer); + LL_TIM_SetCounter(f_hal_nfc_timers[timer].timer, 0); + LL_TIM_SetAutoReload(f_hal_nfc_timers[timer].timer, 0); + if(LL_TIM_IsActiveFlag_UPDATE(f_hal_nfc_timers[timer].timer)) { + LL_TIM_ClearFlag_UPDATE(f_hal_nfc_timers[timer].timer); + } + furi_hal_gpio_write(f_hal_nfc_timers[timer].pin, false); +} + +void f_hal_nfc_timers_init() { + for(size_t i = 0; i < FHalNfcTimerCount; i++) { + f_hal_nfc_timer_init(i); + } +} + +void f_hal_nfc_timers_deinit() { + for(size_t i = 0; i < FHalNfcTimerCount; i++) { + f_hal_nfc_timer_deinit(i); + } +} + +void f_hal_nfc_timer_fwt_start(uint32_t time_fc) { + furi_check(f_hal_nfc_timers[FHalNfcTimerFwt].is_configured); + f_hal_nfc_timer_start(FHalNfcTimerFwt, time_fc); +} + +void f_hal_nfc_timer_fwt_stop() { + furi_check(f_hal_nfc_timers[FHalNfcTimerFwt].is_configured); + f_hal_nfc_timer_stop(FHalNfcTimerFwt); +} + +void f_hal_nfc_timer_block_tx_start(uint32_t time_fc) { + furi_check(f_hal_nfc_timers[FHalNfcTimerBlockTx].is_configured); + furi_check(!f_hal_nfc_timer_block_tx_is_running()); + + f_hal_nfc_timer_start(FHalNfcTimerBlockTx, time_fc); +} + +void f_hal_nfc_timer_block_tx_start_us(uint32_t time_us) { + furi_check(f_hal_nfc_timers[FHalNfcTimerBlockTx].is_configured); + furi_check(!f_hal_nfc_timer_block_tx_is_running()); + + uint32_t arr_reg = f_hal_nfc_timers[FHalNfcTimerBlockTx].freq_khz / 1000 * time_us; + furi_check(arr_reg < UINT16_MAX); + + LL_TIM_SetAutoReload(f_hal_nfc_timers[FHalNfcTimerBlockTx].timer, arr_reg); + LL_TIM_EnableCounter(f_hal_nfc_timers[FHalNfcTimerBlockTx].timer); + furi_hal_gpio_write(f_hal_nfc_timers[FHalNfcTimerBlockTx].pin, true); +} + +void f_hal_nfc_timer_block_tx_stop() { + furi_check(f_hal_nfc_timers[FHalNfcTimerBlockTx].is_configured); + f_hal_nfc_timer_stop(FHalNfcTimerBlockTx); +} + +bool f_hal_nfc_timer_block_tx_is_running() { + return LL_TIM_IsEnabledCounter(f_hal_nfc_timers[FHalNfcTimerBlockTx].timer) == 1; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index 1b710bb9637f..6bfc40ef7b53 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -9,6 +9,8 @@ void furi_hal_init_early() { furi_hal_cortex_init_early(); furi_hal_clock_init_early(); + furi_hal_bus_init_early(); + furi_hal_dma_init_early(); furi_hal_resources_init_early(); furi_hal_os_init(); furi_hal_spi_config_init_early(); @@ -22,12 +24,15 @@ void furi_hal_deinit_early() { furi_hal_i2c_deinit_early(); furi_hal_spi_config_deinit_early(); furi_hal_resources_deinit_early(); + furi_hal_dma_deinit_early(); + furi_hal_bus_deinit_early(); furi_hal_clock_deinit_early(); } void furi_hal_init() { furi_hal_mpu_init(); furi_hal_clock_init(); + furi_hal_random_init(); furi_hal_console_init(); furi_hal_rtc_init(); furi_hal_interrupt_init(); @@ -50,7 +55,8 @@ void furi_hal_init() { furi_hal_usb_init(); furi_hal_vibro_init(); furi_hal_subghz_init(); - furi_hal_nfc_init(); + // furi_hal_nfc_init(); + f_hal_nfc_init(); furi_hal_rfid_init(); #endif } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 048a8b3090b5..57aee0bf2a25 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -7,8 +7,8 @@ #include #include #include -#include "battery_service.h" - +#include +#include #include #define TAG "FuriHalBt" @@ -21,8 +21,17 @@ #define FURI_HAL_BT_HARDFAULT_INFO_MAGIC 0x1170FD0F -FuriMutex* furi_hal_bt_core2_mtx = NULL; -static FuriHalBtStack furi_hal_bt_stack = FuriHalBtStackUnknown; +typedef struct { + FuriMutex* core2_mtx; + FuriTimer* hardfault_check_timer; + FuriHalBtStack stack; +} FuriHalBt; + +static FuriHalBt furi_hal_bt = { + .core2_mtx = NULL, + .hardfault_check_timer = NULL, + .stack = FuriHalBtStackUnknown, +}; typedef void (*FuriHalBtProfileStart)(void); typedef void (*FuriHalBtProfileStop)(void); @@ -79,10 +88,29 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = { }; FuriHalBtProfileConfig* current_profile = NULL; +static void furi_hal_bt_hardfault_check(void* context) { + UNUSED(context); + if(furi_hal_bt_get_hardfault_info()) { + furi_crash("ST(R) Copro(R) HardFault"); + } +} + void furi_hal_bt_init() { - if(!furi_hal_bt_core2_mtx) { - furi_hal_bt_core2_mtx = furi_mutex_alloc(FuriMutexTypeNormal); - furi_assert(furi_hal_bt_core2_mtx); + furi_hal_bus_enable(FuriHalBusHSEM); + furi_hal_bus_enable(FuriHalBusIPCC); + furi_hal_bus_enable(FuriHalBusAES2); + furi_hal_bus_enable(FuriHalBusPKA); + furi_hal_bus_enable(FuriHalBusCRC); + + if(!furi_hal_bt.core2_mtx) { + furi_hal_bt.core2_mtx = furi_mutex_alloc(FuriMutexTypeNormal); + furi_assert(furi_hal_bt.core2_mtx); + } + + if(!furi_hal_bt.hardfault_check_timer) { + furi_hal_bt.hardfault_check_timer = + furi_timer_alloc(furi_hal_bt_hardfault_check, FuriTimerTypePeriodic, NULL); + furi_timer_start(furi_hal_bt.hardfault_check_timer, 5000); } // Explicitly tell that we are in charge of CLK48 domain @@ -93,13 +121,13 @@ void furi_hal_bt_init() { } void furi_hal_bt_lock_core2() { - furi_assert(furi_hal_bt_core2_mtx); - furi_check(furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever) == FuriStatusOk); + furi_assert(furi_hal_bt.core2_mtx); + furi_check(furi_mutex_acquire(furi_hal_bt.core2_mtx, FuriWaitForever) == FuriStatusOk); } void furi_hal_bt_unlock_core2() { - furi_assert(furi_hal_bt_core2_mtx); - furi_check(furi_mutex_release(furi_hal_bt_core2_mtx) == FuriStatusOk); + furi_assert(furi_hal_bt.core2_mtx); + furi_check(furi_mutex_release(furi_hal_bt.core2_mtx) == FuriStatusOk); } static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) { @@ -107,26 +135,26 @@ static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) { if(info->StackType == INFO_STACK_TYPE_BLE_LIGHT) { if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { - furi_hal_bt_stack = FuriHalBtStackLight; + furi_hal_bt.stack = FuriHalBtStackLight; supported = true; } } else if(info->StackType == INFO_STACK_TYPE_BLE_FULL) { if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { - furi_hal_bt_stack = FuriHalBtStackFull; + furi_hal_bt.stack = FuriHalBtStackFull; supported = true; } } else { - furi_hal_bt_stack = FuriHalBtStackUnknown; + furi_hal_bt.stack = FuriHalBtStackUnknown; } return supported; } bool furi_hal_bt_start_radio_stack() { bool res = false; - furi_assert(furi_hal_bt_core2_mtx); + furi_assert(furi_hal_bt.core2_mtx); - furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever); + furi_mutex_acquire(furi_hal_bt.core2_mtx, FuriWaitForever); // Explicitly tell that we are in charge of CLK48 domain furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); @@ -160,17 +188,17 @@ bool furi_hal_bt_start_radio_stack() { } res = true; } while(false); - furi_mutex_release(furi_hal_bt_core2_mtx); + furi_mutex_release(furi_hal_bt.core2_mtx); return res; } FuriHalBtStack furi_hal_bt_get_radio_stack() { - return furi_hal_bt_stack; + return furi_hal_bt.stack; } bool furi_hal_bt_is_ble_gatt_gap_supported() { - if(furi_hal_bt_stack == FuriHalBtStackLight || furi_hal_bt_stack == FuriHalBtStackFull) { + if(furi_hal_bt.stack == FuriHalBtStackLight || furi_hal_bt.stack == FuriHalBtStackFull) { return true; } else { return false; @@ -178,7 +206,7 @@ bool furi_hal_bt_is_ble_gatt_gap_supported() { } bool furi_hal_bt_is_testing_supported() { - if(furi_hal_bt_stack == FuriHalBtStackFull) { + if(furi_hal_bt.stack == FuriHalBtStackFull) { return true; } else { return false; @@ -256,6 +284,12 @@ void furi_hal_bt_reinit() { furi_delay_ms(100); ble_glue_thread_stop(); + furi_hal_bus_disable(FuriHalBusHSEM); + furi_hal_bus_disable(FuriHalBusIPCC); + furi_hal_bus_disable(FuriHalBusAES2); + furi_hal_bus_disable(FuriHalBusPKA); + furi_hal_bus_disable(FuriHalBusCRC); + FURI_LOG_I(TAG, "Start BT initialization"); furi_hal_bt_init(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 8259be2f6cd1..7ec712af4dbb 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -1,11 +1,11 @@ #include #include -#include "usb_hid.h" -#include "dev_info_service.h" -#include "battery_service.h" -#include "hid_service.h" +#include +#include +#include #include +#include #define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101) #define FURI_HAL_BT_INFO_COUNTRY_CODE (0x00) @@ -153,7 +153,7 @@ void furi_hal_bt_hid_start() { FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK | FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK, }; - hid_svc_update_info(hid_info_val, sizeof(hid_info_val)); + hid_svc_update_info(hid_info_val); } void furi_hal_bt_hid_stop() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c index 2539e6bd0e90..2927d946f980 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c @@ -1,7 +1,7 @@ #include -#include "dev_info_service.h" -#include "battery_service.h" -#include "serial_service.h" +#include +#include +#include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_bus.c b/firmware/targets/f7/furi_hal/furi_hal_bus.c new file mode 100644 index 000000000000..2c6f1f1ebc92 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_bus.c @@ -0,0 +1,302 @@ +#include +#include + +#include + +/* Bus bitmask definitions */ +#define FURI_HAL_BUS_IGNORE (0x0U) + +#define FURI_HAL_BUS_AHB1_GRP1 \ + (LL_AHB1_GRP1_PERIPH_DMA1 | LL_AHB1_GRP1_PERIPH_DMA2 | LL_AHB1_GRP1_PERIPH_DMAMUX1 | \ + LL_AHB1_GRP1_PERIPH_CRC | LL_AHB1_GRP1_PERIPH_TSC) + +#if defined(ADC_SUPPORT_5_MSPS) +#define FURI_HAL_BUS_AHB2_GRP1 \ + (LL_AHB2_GRP1_PERIPH_GPIOA | LL_AHB2_GRP1_PERIPH_GPIOB | LL_AHB2_GRP1_PERIPH_GPIOC | \ + LL_AHB2_GRP1_PERIPH_GPIOD | LL_AHB2_GRP1_PERIPH_GPIOE | LL_AHB2_GRP1_PERIPH_GPIOH | \ + LL_AHB2_GRP1_PERIPH_ADC | LL_AHB2_GRP1_PERIPH_AES1) + +#define FURI_HAL_BUS_APB2_GRP1 \ + (LL_APB2_GRP1_PERIPH_TIM1 | LL_APB2_GRP1_PERIPH_SPI1 | LL_APB2_GRP1_PERIPH_USART1 | \ + LL_APB2_GRP1_PERIPH_TIM16 | LL_APB2_GRP1_PERIPH_TIM17 | LL_APB2_GRP1_PERIPH_SAI1) +#else +#define FURI_HAL_BUS_AHB2_GRP1 \ + (LL_AHB2_GRP1_PERIPH_GPIOA | LL_AHB2_GRP1_PERIPH_GPIOB | LL_AHB2_GRP1_PERIPH_GPIOC | \ + LL_AHB2_GRP1_PERIPH_GPIOD | LL_AHB2_GRP1_PERIPH_GPIOE | LL_AHB2_GRP1_PERIPH_GPIOH | \ + LL_AHB2_GRP1_PERIPH_AES1) + +#define FURI_HAL_BUS_APB2_GRP1 \ + (LL_APB2_GRP1_PERIPH_ADC | LL_APB2_GRP1_PERIPH_TIM1 | LL_APB2_GRP1_PERIPH_SPI1 | \ + LL_APB2_GRP1_PERIPH_USART1 | LL_APB2_GRP1_PERIPH_TIM16 | LL_APB2_GRP1_PERIPH_TIM17 | \ + LL_APB2_GRP1_PERIPH_SAI1) +#endif + +#define FURI_HAL_BUS_AHB3_GRP1 \ + (LL_AHB3_GRP1_PERIPH_QUADSPI | LL_AHB3_GRP1_PERIPH_PKA | LL_AHB3_GRP1_PERIPH_AES2 | \ + LL_AHB3_GRP1_PERIPH_RNG | LL_AHB3_GRP1_PERIPH_HSEM | LL_AHB3_GRP1_PERIPH_IPCC) +// LL_AHB3_GRP1_PERIPH_FLASH enabled by default + +#define FURI_HAL_BUS_APB1_GRP1 \ + (LL_APB1_GRP1_PERIPH_TIM2 | LL_APB1_GRP1_PERIPH_LCD | LL_APB1_GRP1_PERIPH_SPI2 | \ + LL_APB1_GRP1_PERIPH_I2C1 | LL_APB1_GRP1_PERIPH_I2C3 | LL_APB1_GRP1_PERIPH_CRS | \ + LL_APB1_GRP1_PERIPH_USB | LL_APB1_GRP1_PERIPH_LPTIM1) + +#define FURI_HAL_BUS_APB1_GRP2 (LL_APB1_GRP2_PERIPH_LPUART1 | LL_APB1_GRP2_PERIPH_LPTIM2) +#define FURI_HAL_BUS_APB3_GRP1 (LL_APB3_GRP1_PERIPH_RF) + +/* Test macro definitions */ +#define FURI_HAL_BUS_IS_ALL_CLEAR(reg, value) (READ_BIT((reg), (value)) == 0UL) +#define FURI_HAL_BUS_IS_ALL_SET(reg, value) (READ_BIT((reg), (value)) == (value)) + +#define FURI_HAL_BUS_IS_CLOCK_ENABLED(bus, value, ...) \ + (FURI_HAL_BUS_IS_ALL_SET(RCC->bus##ENR##__VA_ARGS__, (value))) +#define FURI_HAL_BUS_IS_CLOCK_DISABLED(bus, value, ...) \ + (FURI_HAL_BUS_IS_ALL_CLEAR(RCC->bus##ENR##__VA_ARGS__, (value))) + +#define FURI_HAL_BUS_IS_RESET_ASSERTED(bus, value, ...) \ + (FURI_HAL_BUS_IS_ALL_SET(RCC->bus##RSTR##__VA_ARGS__, (value))) +#define FURI_HAL_BUS_IS_RESET_DEASSERTED(bus, value, ...) \ + (FURI_HAL_BUS_IS_ALL_CLEAR(RCC->bus##RSTR##__VA_ARGS__, (value))) + +#define FURI_HAL_BUS_IS_PERIPH_ENABLED(bus, value, ...) \ + (FURI_HAL_BUS_IS_RESET_DEASSERTED(bus, (value), __VA_ARGS__) && \ + FURI_HAL_BUS_IS_CLOCK_ENABLED(bus, (value), __VA_ARGS__)) + +#define FURI_HAL_BUS_IS_PERIPH_DISABLED(bus, value, ...) \ + (FURI_HAL_BUS_IS_CLOCK_DISABLED(bus, (value), __VA_ARGS__) && \ + FURI_HAL_BUS_IS_RESET_ASSERTED(bus, (value), __VA_ARGS__)) + +/* Control macro definitions */ +#define FURI_HAL_BUS_RESET_ASSERT(bus, value, grp) LL_##bus##_GRP##grp##_ForceReset(value) +#define FURI_HAL_BUS_RESET_DEASSERT(bus, value, grp) LL_##bus##_GRP##grp##_ReleaseReset(value) + +#define FURI_HAL_BUS_CLOCK_ENABLE(bus, value, grp) LL_##bus##_GRP##grp##_EnableClock(value) +#define FURI_HAL_BUS_CLOCK_DISABLE(bus, value, grp) LL_##bus##_GRP##grp##_DisableClock(value) + +#define FURI_HAL_BUS_PERIPH_ENABLE(bus, value, grp) \ + FURI_HAL_BUS_CLOCK_ENABLE(bus, value, grp); \ + FURI_HAL_BUS_RESET_DEASSERT(bus, value, grp) + +#define FURI_HAL_BUS_PERIPH_DISABLE(bus, value, grp) \ + FURI_HAL_BUS_RESET_ASSERT(bus, value, grp); \ + FURI_HAL_BUS_CLOCK_DISABLE(bus, value, grp) + +#define FURI_HAL_BUS_PERIPH_RESET(bus, value, grp) \ + FURI_HAL_BUS_RESET_ASSERT(bus, value, grp); \ + FURI_HAL_BUS_RESET_DEASSERT(bus, value, grp) + +static const uint32_t furi_hal_bus[] = { + [FuriHalBusAHB1_GRP1] = FURI_HAL_BUS_IGNORE, + [FuriHalBusDMA1] = LL_AHB1_GRP1_PERIPH_DMA1, + [FuriHalBusDMA2] = LL_AHB1_GRP1_PERIPH_DMA2, + [FuriHalBusDMAMUX1] = LL_AHB1_GRP1_PERIPH_DMAMUX1, + [FuriHalBusCRC] = LL_AHB1_GRP1_PERIPH_CRC, + [FuriHalBusTSC] = LL_AHB1_GRP1_PERIPH_TSC, + + [FuriHalBusAHB2_GRP1] = FURI_HAL_BUS_IGNORE, + [FuriHalBusGPIOA] = LL_AHB2_GRP1_PERIPH_GPIOA, + [FuriHalBusGPIOB] = LL_AHB2_GRP1_PERIPH_GPIOB, + [FuriHalBusGPIOC] = LL_AHB2_GRP1_PERIPH_GPIOC, + [FuriHalBusGPIOD] = LL_AHB2_GRP1_PERIPH_GPIOD, + [FuriHalBusGPIOE] = LL_AHB2_GRP1_PERIPH_GPIOE, + [FuriHalBusGPIOH] = LL_AHB2_GRP1_PERIPH_GPIOH, +#if defined(ADC_SUPPORT_5_MSPS) + [FuriHalBusADC] = LL_AHB2_GRP1_PERIPH_ADC, +#endif + [FuriHalBusAES1] = LL_AHB2_GRP1_PERIPH_AES1, + + [FuriHalBusAHB3_GRP1] = FURI_HAL_BUS_IGNORE, + [FuriHalBusQUADSPI] = LL_AHB3_GRP1_PERIPH_QUADSPI, + [FuriHalBusPKA] = LL_AHB3_GRP1_PERIPH_PKA, + [FuriHalBusAES2] = LL_AHB3_GRP1_PERIPH_AES2, + [FuriHalBusRNG] = LL_AHB3_GRP1_PERIPH_RNG, + [FuriHalBusHSEM] = LL_AHB3_GRP1_PERIPH_HSEM, + [FuriHalBusIPCC] = LL_AHB3_GRP1_PERIPH_IPCC, + [FuriHalBusFLASH] = LL_AHB3_GRP1_PERIPH_FLASH, + + [FuriHalBusAPB1_GRP1] = FURI_HAL_BUS_APB1_GRP1, + [FuriHalBusTIM2] = LL_APB1_GRP1_PERIPH_TIM2, + [FuriHalBusLCD] = LL_APB1_GRP1_PERIPH_LCD, + [FuriHalBusSPI2] = LL_APB1_GRP1_PERIPH_SPI2, + [FuriHalBusI2C1] = LL_APB1_GRP1_PERIPH_I2C1, + [FuriHalBusI2C3] = LL_APB1_GRP1_PERIPH_I2C3, + [FuriHalBusCRS] = LL_APB1_GRP1_PERIPH_CRS, + [FuriHalBusUSB] = LL_APB1_GRP1_PERIPH_USB, + [FuriHalBusLPTIM1] = LL_APB1_GRP1_PERIPH_LPTIM1, + + [FuriHalBusAPB1_GRP2] = FURI_HAL_BUS_APB1_GRP2, + [FuriHalBusLPUART1] = LL_APB1_GRP2_PERIPH_LPUART1, + [FuriHalBusLPTIM2] = LL_APB1_GRP2_PERIPH_LPTIM2, + + [FuriHalBusAPB2_GRP1] = FURI_HAL_BUS_APB2_GRP1, +#if defined(ADC_SUPPORT_2_5_MSPS) + [FuriHalBusADC] = LL_APB2_GRP1_PERIPH_ADC, +#endif + [FuriHalBusTIM1] = LL_APB2_GRP1_PERIPH_TIM1, + [FuriHalBusSPI1] = LL_APB2_GRP1_PERIPH_SPI1, + [FuriHalBusUSART1] = LL_APB2_GRP1_PERIPH_USART1, + [FuriHalBusTIM16] = LL_APB2_GRP1_PERIPH_TIM16, + [FuriHalBusTIM17] = LL_APB2_GRP1_PERIPH_TIM17, + [FuriHalBusSAI1] = LL_APB2_GRP1_PERIPH_SAI1, + + [FuriHalBusAPB3_GRP1] = FURI_HAL_BUS_IGNORE, // APB3_GRP1 clocking cannot be changed + [FuriHalBusRF] = LL_APB3_GRP1_PERIPH_RF, +}; + +void furi_hal_bus_init_early() { + FURI_CRITICAL_ENTER(); + + // FURI_HAL_BUS_PERIPH_DISABLE(AHB1, FURI_HAL_BUS_AHB1_GRP1, 1); + // FURI_HAL_BUS_PERIPH_DISABLE(AHB2, FURI_HAL_BUS_AHB2_GRP1, 1); + // FURI_HAL_BUS_PERIPH_DISABLE(AHB3, FURI_HAL_BUS_AHB3_GRP1, 1); + FURI_HAL_BUS_PERIPH_DISABLE(APB1, FURI_HAL_BUS_APB1_GRP1, 1); + FURI_HAL_BUS_PERIPH_DISABLE(APB1, FURI_HAL_BUS_APB1_GRP2, 2); + FURI_HAL_BUS_PERIPH_DISABLE(APB2, FURI_HAL_BUS_APB2_GRP1, 1); + + FURI_HAL_BUS_RESET_ASSERT(APB3, FURI_HAL_BUS_APB3_GRP1, 1); + + FURI_CRITICAL_EXIT(); +} + +void furi_hal_bus_deinit_early() { + FURI_CRITICAL_ENTER(); + + // FURI_HAL_BUS_PERIPH_ENABLE(AHB1, FURI_HAL_BUS_AHB1_GRP1, 1); + // FURI_HAL_BUS_PERIPH_ENABLE(AHB2, FURI_HAL_BUS_AHB2_GRP1, 1); + // FURI_HAL_BUS_PERIPH_ENABLE(AHB3, FURI_HAL_BUS_AHB3_GRP1, 1); + FURI_HAL_BUS_PERIPH_ENABLE(APB1, FURI_HAL_BUS_APB1_GRP1, 1); + FURI_HAL_BUS_PERIPH_ENABLE(APB1, FURI_HAL_BUS_APB1_GRP2, 2); + FURI_HAL_BUS_PERIPH_ENABLE(APB2, FURI_HAL_BUS_APB2_GRP1, 1); + + FURI_HAL_BUS_RESET_DEASSERT(APB3, FURI_HAL_BUS_APB3_GRP1, 1); + + FURI_CRITICAL_EXIT(); +} + +void furi_hal_bus_enable(FuriHalBus bus) { + furi_check(bus < FuriHalBusMAX); + const uint32_t value = furi_hal_bus[bus]; + if(!value) { + return; + } + + FURI_CRITICAL_ENTER(); + if(bus < FuriHalBusAHB2_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(AHB1, value)); + FURI_HAL_BUS_PERIPH_ENABLE(AHB1, value, 1); + } else if(bus < FuriHalBusAHB3_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(AHB2, value)); + FURI_HAL_BUS_PERIPH_ENABLE(AHB2, value, 1); + } else if(bus < FuriHalBusAPB1_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(AHB3, value)); + FURI_HAL_BUS_PERIPH_ENABLE(AHB3, value, 1); + } else if(bus < FuriHalBusAPB1_GRP2) { + furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(APB1, value, 1)); + FURI_HAL_BUS_PERIPH_ENABLE(APB1, value, 1); + } else if(bus < FuriHalBusAPB2_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(APB1, value, 2)); + FURI_HAL_BUS_PERIPH_ENABLE(APB1, value, 2); + } else if(bus < FuriHalBusAPB3_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(APB2, value)); + FURI_HAL_BUS_PERIPH_ENABLE(APB2, value, 1); + } else { + furi_check(FURI_HAL_BUS_IS_RESET_ASSERTED(APB3, value)); + FURI_HAL_BUS_RESET_DEASSERT(APB3, FURI_HAL_BUS_APB3_GRP1, 1); + } + FURI_CRITICAL_EXIT(); +} + +void furi_hal_bus_reset(FuriHalBus bus) { + furi_check(bus < FuriHalBusMAX); + const uint32_t value = furi_hal_bus[bus]; + if(!value) { + return; + } + + FURI_CRITICAL_ENTER(); + if(bus < FuriHalBusAHB2_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB1, value)); + FURI_HAL_BUS_PERIPH_RESET(AHB1, value, 1); + } else if(bus < FuriHalBusAHB3_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB2, value)); + FURI_HAL_BUS_PERIPH_RESET(AHB2, value, 1); + } else if(bus < FuriHalBusAPB1_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB3, value)); + FURI_HAL_BUS_PERIPH_RESET(AHB3, value, 1); + } else if(bus < FuriHalBusAPB1_GRP2) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 1)); + FURI_HAL_BUS_PERIPH_RESET(APB1, value, 1); + } else if(bus < FuriHalBusAPB2_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 2)); + FURI_HAL_BUS_PERIPH_RESET(APB1, value, 2); + } else if(bus < FuriHalBusAPB3_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB2, value)); + FURI_HAL_BUS_PERIPH_RESET(APB2, value, 1); + } else { + furi_check(FURI_HAL_BUS_IS_RESET_DEASSERTED(APB3, value)); + FURI_HAL_BUS_PERIPH_RESET(APB3, value, 1); + } + FURI_CRITICAL_EXIT(); +} + +void furi_hal_bus_disable(FuriHalBus bus) { + furi_check(bus < FuriHalBusMAX); + const uint32_t value = furi_hal_bus[bus]; + if(!value) { + return; + } + + FURI_CRITICAL_ENTER(); + if(bus < FuriHalBusAHB2_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB1, value)); + FURI_HAL_BUS_PERIPH_DISABLE(AHB1, value, 1); + } else if(bus < FuriHalBusAHB3_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB2, value)); + FURI_HAL_BUS_PERIPH_DISABLE(AHB2, value, 1); + } else if(bus < FuriHalBusAPB1_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB3, value)); + FURI_HAL_BUS_PERIPH_DISABLE(AHB3, value, 1); + } else if(bus < FuriHalBusAPB1_GRP2) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 1)); + FURI_HAL_BUS_PERIPH_DISABLE(APB1, value, 1); + } else if(bus < FuriHalBusAPB2_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 2)); + FURI_HAL_BUS_PERIPH_DISABLE(APB1, value, 2); + } else if(bus < FuriHalBusAPB3_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB2, value)); + FURI_HAL_BUS_PERIPH_DISABLE(APB2, value, 1); + } else { + furi_check(FURI_HAL_BUS_IS_RESET_DEASSERTED(APB3, value)); + FURI_HAL_BUS_RESET_ASSERT(APB3, FURI_HAL_BUS_APB3_GRP1, 1); + } + FURI_CRITICAL_EXIT(); +} + +bool furi_hal_bus_is_enabled(FuriHalBus bus) { + furi_check(bus < FuriHalBusMAX); + const uint32_t value = furi_hal_bus[bus]; + if(value == FURI_HAL_BUS_IGNORE) { + return true; + } + + bool ret = false; + FURI_CRITICAL_ENTER(); + if(bus < FuriHalBusAHB2_GRP1) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB1, value); + } else if(bus < FuriHalBusAHB3_GRP1) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB2, value); + } else if(bus < FuriHalBusAPB1_GRP1) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB3, value); + } else if(bus < FuriHalBusAPB1_GRP2) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 1); + } else if(bus < FuriHalBusAPB2_GRP1) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 2); + } else if(bus < FuriHalBusAPB3_GRP1) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(APB2, value); + } else { + ret = FURI_HAL_BUS_IS_RESET_DEASSERTED(APB3, value); + } + FURI_CRITICAL_EXIT(); + + return ret; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_bus.h b/firmware/targets/f7/furi_hal/furi_hal_bus.h new file mode 100644 index 000000000000..ad4bbec32496 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_bus.h @@ -0,0 +1,112 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stm32wbxx.h" +#include "stdbool.h" + +typedef enum { + FuriHalBusAHB1_GRP1, + FuriHalBusDMA1, + FuriHalBusDMA2, + FuriHalBusDMAMUX1, + FuriHalBusCRC, + FuriHalBusTSC, + + FuriHalBusAHB2_GRP1, + FuriHalBusGPIOA, + FuriHalBusGPIOB, + FuriHalBusGPIOC, + FuriHalBusGPIOD, + FuriHalBusGPIOE, + FuriHalBusGPIOH, +#if defined(ADC_SUPPORT_5_MSPS) + FuriHalBusADC, +#endif + FuriHalBusAES1, + + FuriHalBusAHB3_GRP1, + FuriHalBusQUADSPI, + FuriHalBusPKA, + FuriHalBusAES2, + FuriHalBusRNG, + FuriHalBusHSEM, + FuriHalBusIPCC, + FuriHalBusFLASH, + + FuriHalBusAPB1_GRP1, + FuriHalBusTIM2, + FuriHalBusLCD, + FuriHalBusSPI2, + FuriHalBusI2C1, + FuriHalBusI2C3, + FuriHalBusCRS, + FuriHalBusUSB, + FuriHalBusLPTIM1, + + FuriHalBusAPB1_GRP2, + FuriHalBusLPUART1, + FuriHalBusLPTIM2, + + FuriHalBusAPB2_GRP1, +#if defined(ADC_SUPPORT_2_5_MSPS) + FuriHalBusADC, +#endif + FuriHalBusTIM1, + FuriHalBusSPI1, + FuriHalBusUSART1, + FuriHalBusTIM16, + FuriHalBusTIM17, + FuriHalBusSAI1, + + FuriHalBusAPB3_GRP1, + FuriHalBusRF, + + FuriHalBusMAX, +} FuriHalBus; + +/** Early initialization */ +void furi_hal_bus_init_early(); + +/** Early de-initialization */ +void furi_hal_bus_deinit_early(); + +/** + * Enable a peripheral by turning the clocking on and deasserting the reset. + * @param [in] bus Peripheral to be enabled. + * @warning Peripheral must be in disabled state in order to be enabled. + */ +void furi_hal_bus_enable(FuriHalBus bus); + +/** + * Reset a peripheral by sequentially asserting and deasserting the reset. + * @param [in] bus Peripheral to be reset. + * @warning Peripheral must be in enabled state in order to be reset. + */ +void furi_hal_bus_reset(FuriHalBus bus); + +/** + * Disable a peripheral by turning the clocking off and asserting the reset. + * @param [in] bus Peripheral to be disabled. + * @warning Peripheral must be in enabled state in order to be disabled. + */ +void furi_hal_bus_disable(FuriHalBus bus); + +/** Check if peripheral is enabled + * + * @warning FuriHalBusAPB3_GRP1 is a special group of shared peripherals, for + * core1 its clock is always on and the only status we can report is + * peripheral reset status. Check code and Reference Manual for + * details. + * + * @param[in] bus The peripheral to check + * + * @return true if enabled or always enabled, false otherwise + */ +bool furi_hal_bus_is_enabled(FuriHalBus bus); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index a76fbfbca767..736ad9f7c054 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -6,7 +6,6 @@ #include #include #include -#include #define TAG "FuriHalClock" @@ -19,36 +18,9 @@ void furi_hal_clock_init_early() { LL_SetSystemCoreClock(CPU_CLOCK_HZ_EARLY); LL_Init1msTick(SystemCoreClock); - - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOD); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOE); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOH); - - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); - - LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2); - - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3); } void furi_hal_clock_deinit_early() { - LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_I2C1); - LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_I2C3); - - LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_SPI1); - LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_SPI2); - - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOA); - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOB); - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOC); - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOD); - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOE); - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOH); } void furi_hal_clock_init() { @@ -76,6 +48,10 @@ void furi_hal_clock_init() { LL_RCC_LSI1_Enable(); while(!LS_CLOCK_IS_READY()) ; + + /* RF wakeup */ + LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); + LL_EXTI_EnableIT_0_31( LL_EXTI_LINE_18); /* Why? Because that's why. See RM0434, Table 61. CPU1 vector table. */ LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_18); @@ -137,68 +113,12 @@ void furi_hal_clock_init() { SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), TICK_INT_PRIORITY, 0)); NVIC_EnableIRQ(SysTick_IRQn); - LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); - LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); - LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_PLLSAI1); - LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); - LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48); - LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1); LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_PLLSAI1); LL_RCC_HSI_EnableInStopMode(); // Ensure that MR is capable of work in STOP0 - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); + LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); LL_RCC_SetSMPSPrescaler(LL_RCC_SMPS_DIV_1); LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); - // AHB1 GRP1 - LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1); - LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA2); - LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMAMUX1); - LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_CRC); - // LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_TSC); - - // AHB2 GRP1 - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOD); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOE); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOH); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_ADC); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_AES1); - - // AHB3 GRP1 - // LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_QUADSPI); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_PKA); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_AES2); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_RNG); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_HSEM); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_IPCC); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_FLASH); - - // APB1 GRP1 - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); - // LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_LCD); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB); - // LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_WWDG); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_CRS); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USB); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_LPTIM1); - - // APB1 GRP2 - LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1); - - // APB2 - // LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC); - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1); - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1); - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1); - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM16); - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM17); - // LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SAI1); - FURI_LOG_I(TAG, "Init OK"); } @@ -208,8 +128,8 @@ void furi_hal_clock_switch_to_hsi() { while(!LL_RCC_HSI_IsReady()) ; - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); + furi_assert(LL_RCC_GetSMPSClockSource() == LL_RCC_SMPS_CLKSOURCE_HSI); while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) ; @@ -222,6 +142,10 @@ void furi_hal_clock_switch_to_hsi() { } void furi_hal_clock_switch_to_pll() { +#ifdef FURI_HAL_CLOCK_TRACK_STARTUP + uint32_t clock_start_time = DWT->CYCCNT; +#endif + LL_RCC_HSE_Enable(); LL_RCC_PLL_Enable(); LL_RCC_PLLSAI1_Enable(); @@ -240,10 +164,16 @@ void furi_hal_clock_switch_to_pll() { ; LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) ; + +#ifdef FURI_HAL_CLOCK_TRACK_STARTUP + uint32_t total = DWT->CYCCNT - clock_start_time; + if(total > (20 * 0x148)) { + furi_crash("Slow HSE/PLL startup"); + } +#endif } void furi_hal_clock_suspend_tick() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c index e0ed3ab9be26..eb5c3b782c86 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crypto.c @@ -1,8 +1,9 @@ #include #include #include +#include + #include -#include #include #include @@ -241,6 +242,8 @@ bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { furi_assert(furi_hal_crypto_mutex); furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); + furi_hal_bus_enable(FuriHalBusAES1); + if(!furi_hal_bt_is_alive()) { return false; } @@ -267,10 +270,7 @@ bool furi_hal_crypto_store_unload_key(uint8_t slot) { SHCI_CmdStatus_t shci_state = SHCI_C2_FUS_UnloadUsrKey(slot); furi_assert(shci_state == SHCI_Success); - FURI_CRITICAL_ENTER(); - LL_AHB2_GRP1_ForceReset(LL_AHB2_GRP1_PERIPH_AES1); - LL_AHB2_GRP1_ReleaseReset(LL_AHB2_GRP1_PERIPH_AES1); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(FuriHalBusAES1); furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk); return (shci_state == SHCI_Success); diff --git a/firmware/targets/f7/furi_hal/furi_hal_dma.c b/firmware/targets/f7/furi_hal/furi_hal_dma.c new file mode 100644 index 000000000000..a6a30d906dae --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_dma.c @@ -0,0 +1,14 @@ +#include +#include + +void furi_hal_dma_init_early() { + furi_hal_bus_enable(FuriHalBusDMA1); + furi_hal_bus_enable(FuriHalBusDMA2); + furi_hal_bus_enable(FuriHalBusDMAMUX1); +} + +void furi_hal_dma_deinit_early() { + furi_hal_bus_disable(FuriHalBusDMA1); + furi_hal_bus_disable(FuriHalBusDMA2); + furi_hal_bus_disable(FuriHalBusDMAMUX1); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_dma.h b/firmware/targets/f7/furi_hal/furi_hal_dma.h new file mode 100644 index 000000000000..cadcc7733e07 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_dma.h @@ -0,0 +1,15 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** Early initialization */ +void furi_hal_dma_init_early(); + +/** Early de-initialization */ +void furi_hal_dma_deinit_early(); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c index 94d269345ba7..796d20b19fa8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c @@ -11,20 +11,20 @@ #define TAG "FuriHalFlash" #define FURI_HAL_CRITICAL_MSG "Critical flash operation fail" -#define FURI_HAL_FLASH_READ_BLOCK 8 -#define FURI_HAL_FLASH_WRITE_BLOCK 8 -#define FURI_HAL_FLASH_PAGE_SIZE 4096 -#define FURI_HAL_FLASH_CYCLES_COUNT 10000 -#define FURI_HAL_FLASH_TIMEOUT 1000 -#define FURI_HAL_FLASH_KEY1 0x45670123U -#define FURI_HAL_FLASH_KEY2 0xCDEF89ABU -#define FURI_HAL_FLASH_TOTAL_PAGES 256 +#define FURI_HAL_FLASH_READ_BLOCK (8U) +#define FURI_HAL_FLASH_WRITE_BLOCK (8U) +#define FURI_HAL_FLASH_PAGE_SIZE (4096U) +#define FURI_HAL_FLASH_CYCLES_COUNT (10000U) +#define FURI_HAL_FLASH_TIMEOUT (1000U) +#define FURI_HAL_FLASH_KEY1 (0x45670123U) +#define FURI_HAL_FLASH_KEY2 (0xCDEF89ABU) +#define FURI_HAL_FLASH_TOTAL_PAGES (256U) #define FURI_HAL_FLASH_SR_ERRORS \ (FLASH_SR_OPERR | FLASH_SR_PROGERR | FLASH_SR_WRPERR | FLASH_SR_PGAERR | FLASH_SR_SIZERR | \ FLASH_SR_PGSERR | FLASH_SR_MISERR | FLASH_SR_FASTERR | FLASH_SR_RDERR | FLASH_SR_OPTVERR) -#define FURI_HAL_FLASH_OPT_KEY1 0x08192A3B -#define FURI_HAL_FLASH_OPT_KEY2 0x4C5D6E7F +#define FURI_HAL_FLASH_OPT_KEY1 (0x08192A3BU) +#define FURI_HAL_FLASH_OPT_KEY2 (0x4C5D6E7FU) #define FURI_HAL_FLASH_OB_TOTAL_WORDS (0x80 / (sizeof(uint32_t) * 2)) /* STM32CubeWB/Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_RfWithFlash/Core/Src/flash_driver.c @@ -35,7 +35,7 @@ > If for any reason this test is never passed, this means there is a failure in the system and there is no other > way to recover than applying a device reset. */ -#define FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS 3000u /* 3 seconds */ +#define FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS (3000U) /* 3 seconds */ #define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__)&0x7U) == (0x00UL)) #define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \ diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c b/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c index afc4fdf5247c..f9d88abb3763 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c +++ b/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c @@ -1,7 +1,8 @@ #include #include #include -#include +#include + #include /** Timing register value is computed with the STM32CubeMX Tool, @@ -21,17 +22,9 @@ FuriMutex* furi_hal_i2c_bus_power_mutex = NULL; static void furi_hal_i2c_bus_power_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) { if(event == FuriHalI2cBusEventInit) { furi_hal_i2c_bus_power_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_CRITICAL_ENTER(); - LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1); - FURI_CRITICAL_EXIT(); bus->current_handle = NULL; } else if(event == FuriHalI2cBusEventDeinit) { furi_mutex_free(furi_hal_i2c_bus_power_mutex); - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C1); - FURI_CRITICAL_EXIT(); } else if(event == FuriHalI2cBusEventLock) { furi_check( furi_mutex_acquire(furi_hal_i2c_bus_power_mutex, FuriWaitForever) == FuriStatusOk); @@ -39,12 +32,11 @@ static void furi_hal_i2c_bus_power_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent furi_check(furi_mutex_release(furi_hal_i2c_bus_power_mutex) == FuriStatusOk); } else if(event == FuriHalI2cBusEventActivate) { FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C1); + furi_hal_bus_enable(FuriHalBusI2C1); + LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); FURI_CRITICAL_EXIT(); } else if(event == FuriHalI2cBusEventDeactivate) { - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(FuriHalBusI2C1); } } @@ -58,17 +50,9 @@ FuriMutex* furi_hal_i2c_bus_external_mutex = NULL; static void furi_hal_i2c_bus_external_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) { if(event == FuriHalI2cBusEventInit) { furi_hal_i2c_bus_external_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_CRITICAL_ENTER(); - LL_RCC_SetI2CClockSource(LL_RCC_I2C3_CLKSOURCE_PCLK1); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3); - FURI_CRITICAL_EXIT(); bus->current_handle = NULL; } else if(event == FuriHalI2cBusEventDeinit) { furi_mutex_free(furi_hal_i2c_bus_external_mutex); - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C3); - FURI_CRITICAL_EXIT(); } else if(event == FuriHalI2cBusEventLock) { furi_check( furi_mutex_acquire(furi_hal_i2c_bus_external_mutex, FuriWaitForever) == FuriStatusOk); @@ -76,13 +60,11 @@ static void furi_hal_i2c_bus_external_event(FuriHalI2cBus* bus, FuriHalI2cBusEve furi_check(furi_mutex_release(furi_hal_i2c_bus_external_mutex) == FuriStatusOk); } else if(event == FuriHalI2cBusEventActivate) { FURI_CRITICAL_ENTER(); + furi_hal_bus_enable(FuriHalBusI2C3); LL_RCC_SetI2CClockSource(LL_RCC_I2C3_CLKSOURCE_PCLK1); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C3); FURI_CRITICAL_EXIT(); } else if(event == FuriHalI2cBusEventDeactivate) { - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(FuriHalBusI2C3); } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c index c8041c9f2cd1..f8f7e4966dc9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c @@ -1,14 +1,15 @@ #include #include #include +#include #include -#include #include #define TAG "FuriHalIbutton" #define FURI_HAL_IBUTTON_TIMER TIM1 +#define FURI_HAL_IBUTTON_TIMER_BUS FuriHalBusTIM1 #define FURI_HAL_IBUTTON_TIMER_IRQ FuriHalInterruptIdTim1UpTim16 typedef enum { @@ -49,9 +50,7 @@ void furi_hal_ibutton_emulate_start( furi_hal_ibutton->callback = callback; furi_hal_ibutton->context = context; - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(FURI_HAL_IBUTTON_TIMER); - FURI_CRITICAL_EXIT(); + furi_hal_bus_enable(FURI_HAL_IBUTTON_TIMER_BUS); furi_hal_interrupt_set_isr(FURI_HAL_IBUTTON_TIMER_IRQ, furi_hal_ibutton_emulate_isr, NULL); @@ -81,10 +80,7 @@ void furi_hal_ibutton_emulate_stop() { furi_hal_ibutton->state = FuriHalIbuttonStateIdle; LL_TIM_DisableCounter(FURI_HAL_IBUTTON_TIMER); - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(FURI_HAL_IBUTTON_TIMER); - FURI_CRITICAL_EXIT(); - + furi_hal_bus_disable(FURI_HAL_IBUTTON_TIMER_BUS); furi_hal_interrupt_set_isr(FURI_HAL_IBUTTON_TIMER_IRQ, NULL, NULL); furi_hal_ibutton->callback = NULL; diff --git a/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h b/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h index 36b45755a924..e1ffb1b25f41 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h +++ b/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h @@ -1,9 +1,10 @@ #pragma once #include -#include #include -#include +#include + +#include // Timer used for tickless idle #define FURI_HAL_IDLE_TIMER_MAX 0xFFFF @@ -11,6 +12,7 @@ #define FURI_HAL_IDLE_TIMER_IRQ LPTIM1_IRQn static inline void furi_hal_idle_timer_init() { + furi_hal_bus_enable(FuriHalBusLPTIM1); // Configure clock source LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM1_CLKSOURCE_LSE); // There is a theoretical possibility that we need it @@ -41,7 +43,7 @@ static inline void furi_hal_idle_timer_start(uint32_t count) { static inline void furi_hal_idle_timer_reset() { // Hard reset timer // THE ONLY RELIABLE WAY to stop it according to errata - LL_LPTIM_DeInit(FURI_HAL_IDLE_TIMER); + furi_hal_bus_reset(FuriHalBusLPTIM1); // Prevent IRQ handler call NVIC_ClearPendingIRQ(FURI_HAL_IDLE_TIMER_IRQ); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index 4c034ff35217..a2c9232c0580 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -23,10 +24,10 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { // Device Info version if(sep == '.') { property_value_out(&property_context, NULL, 2, "format", "major", "3"); - property_value_out(&property_context, NULL, 2, "format", "minor", "1"); + property_value_out(&property_context, NULL, 2, "format", "minor", "2"); } else { property_value_out(&property_context, NULL, 3, "device", "info", "major", "2"); - property_value_out(&property_context, NULL, 3, "device", "info", "minor", "1"); + property_value_out(&property_context, NULL, 3, "device", "info", "minor", "3"); } // Model name @@ -173,6 +174,24 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { &property_context, "%d", 3, "firmware", "api", "major", api_version_major); property_value_out( &property_context, "%d", 3, "firmware", "api", "minor", api_version_minor); + + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "origin", + "fork", + version_get_firmware_origin(firmware_version)); + + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "origin", + "git", + version_get_git_origin(firmware_version)); } if(furi_hal_bt_is_alive()) { @@ -279,6 +298,18 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { property_value_out(&property_context, NULL, 2, "radio", "alive", "false"); } + property_value_out( + &property_context, + "%u", + 2, + "system", + "debug", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)); + property_value_out( + &property_context, "%u", 3, "system", "heap", "track", furi_hal_rtc_get_heap_track_mode()); + property_value_out( + &property_context, "%u", 3, "system", "log", "level", furi_hal_rtc_get_log_level()); + property_value_out( &property_context, "%u", 3, "protobuf", "version", "major", PROTOBUF_MAJOR_VERSION); property_context.last = true; diff --git a/firmware/targets/f7/furi_hal/furi_hal_infrared.c b/firmware/targets/f7/furi_hal/furi_hal_infrared.c index 2598e5fa314a..d3e36c2b5d48 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_infrared.c +++ b/firmware/targets/f7/furi_hal/furi_hal_infrared.c @@ -1,23 +1,18 @@ #include -#include -#include "stm32wbxx_ll_dma.h" -#include "sys/_stdint.h" #include #include +#include -#include #include -#include +#include -#include #include #include -#define INFRARED_TX_DEBUG 0 +// #define INFRARED_TX_DEBUG -#if INFRARED_TX_DEBUG == 1 -#define gpio_infrared_tx gpio_infrared_tx_debug -const GpioPin gpio_infrared_tx_debug = {.port = GPIOA, .pin = GpioModeAnalog}; +#if defined INFRARED_TX_DEBUG +#define gpio_infrared_tx gpio_ext_pa7 #endif #define INFRARED_TIM_TX_DMA_BUFFER_SIZE 200 @@ -29,13 +24,23 @@ const GpioPin gpio_infrared_tx_debug = {.port = GPIOA, .pin = GpioModeAnalog}; (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE) /* Space time - force low */ /* DMA Channels definition */ -#define IR_DMA DMA2 -#define IR_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 -#define IR_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 -#define IR_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 -#define IR_DMA_CH2_IRQ FuriHalInterruptIdDma2Ch2 -#define IR_DMA_CH1_DEF IR_DMA, IR_DMA_CH1_CHANNEL -#define IR_DMA_CH2_DEF IR_DMA, IR_DMA_CH2_CHANNEL +#define INFRARED_DMA DMA2 +#define INFRARED_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define INFRARED_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define INFRARED_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define INFRARED_DMA_CH2_IRQ FuriHalInterruptIdDma2Ch2 +#define INFRARED_DMA_CH1_DEF INFRARED_DMA, INFRARED_DMA_CH1_CHANNEL +#define INFRARED_DMA_CH2_DEF INFRARED_DMA, INFRARED_DMA_CH2_CHANNEL + +/* Timers definition */ +#define INFRARED_RX_TIMER TIM2 +#define INFRARED_DMA_TIMER TIM1 +#define INFRARED_RX_TIMER_BUS FuriHalBusTIM2 +#define INFRARED_DMA_TIMER_BUS FuriHalBusTIM1 + +/* Misc */ +#define INFRARED_RX_GPIO_ALT GpioAltFn1TIM2 +#define INFRARED_RX_IRQ FuriHalInterruptIdTIM2 typedef struct { FuriHalInfraredRxCaptureCallback capture_callback; @@ -93,8 +98,8 @@ static void furi_hal_infrared_tim_rx_isr() { static uint32_t previous_captured_ch2 = 0; /* Timeout */ - if(LL_TIM_IsActiveFlag_CC3(TIM2)) { - LL_TIM_ClearFlag_CC3(TIM2); + if(LL_TIM_IsActiveFlag_CC3(INFRARED_RX_TIMER)) { + LL_TIM_ClearFlag_CC3(INFRARED_RX_TIMER); furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); /* Timers CNT register starts to counting from 0 to ARR, but it is @@ -110,13 +115,13 @@ static void furi_hal_infrared_tim_rx_isr() { } /* Rising Edge */ - if(LL_TIM_IsActiveFlag_CC1(TIM2)) { - LL_TIM_ClearFlag_CC1(TIM2); + if(LL_TIM_IsActiveFlag_CC1(INFRARED_RX_TIMER)) { + LL_TIM_ClearFlag_CC1(INFRARED_RX_TIMER); furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); - if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) { + if(READ_BIT(INFRARED_RX_TIMER->CCMR1, TIM_CCMR1_CC1S)) { /* Low pin level is a Mark state of INFRARED signal. Invert level for further processing. */ - uint32_t duration = LL_TIM_IC_GetCaptureCH1(TIM2) - previous_captured_ch2; + uint32_t duration = LL_TIM_IC_GetCaptureCH1(INFRARED_RX_TIMER) - previous_captured_ch2; if(infrared_tim_rx.capture_callback) infrared_tim_rx.capture_callback(infrared_tim_rx.capture_context, 1, duration); } else { @@ -125,13 +130,13 @@ static void furi_hal_infrared_tim_rx_isr() { } /* Falling Edge */ - if(LL_TIM_IsActiveFlag_CC2(TIM2)) { - LL_TIM_ClearFlag_CC2(TIM2); + if(LL_TIM_IsActiveFlag_CC2(INFRARED_RX_TIMER)) { + LL_TIM_ClearFlag_CC2(INFRARED_RX_TIMER); furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); - if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { + if(READ_BIT(INFRARED_RX_TIMER->CCMR1, TIM_CCMR1_CC2S)) { /* High pin level is a Space state of INFRARED signal. Invert level for further processing. */ - uint32_t duration = LL_TIM_IC_GetCaptureCH2(TIM2); + uint32_t duration = LL_TIM_IC_GetCaptureCH2(INFRARED_RX_TIMER); previous_captured_ch2 = duration; if(infrared_tim_rx.capture_callback) infrared_tim_rx.capture_callback(infrared_tim_rx.capture_context, 0, duration); @@ -145,62 +150,66 @@ void furi_hal_infrared_async_rx_start(void) { furi_assert(furi_hal_infrared_state == InfraredStateIdle); furi_hal_gpio_init_ex( - &gpio_infrared_rx, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + &gpio_infrared_rx, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedLow, + INFRARED_RX_GPIO_ALT); + + furi_hal_bus_enable(INFRARED_RX_TIMER_BUS); LL_TIM_InitTypeDef TIM_InitStruct = {0}; TIM_InitStruct.Prescaler = 64 - 1; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 0x7FFFFFFE; TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; - LL_TIM_Init(TIM2, &TIM_InitStruct); - - LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_DisableARRPreload(TIM2); - LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI1FP1); - LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET); - LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); - LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING); - LL_TIM_DisableIT_TRIG(TIM2); - LL_TIM_DisableDMAReq_TRIG(TIM2); - LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); - LL_TIM_EnableMasterSlaveMode(TIM2); - LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI); - LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); - LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI); - LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); - - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_infrared_tim_rx_isr, NULL); + LL_TIM_Init(INFRARED_RX_TIMER, &TIM_InitStruct); + + LL_TIM_SetClockSource(INFRARED_RX_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(INFRARED_RX_TIMER); + LL_TIM_SetTriggerInput(INFRARED_RX_TIMER, LL_TIM_TS_TI1FP1); + LL_TIM_SetSlaveMode(INFRARED_RX_TIMER, LL_TIM_SLAVEMODE_RESET); + LL_TIM_CC_DisableChannel(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2); + LL_TIM_IC_SetFilter(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); + LL_TIM_IC_SetPolarity(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING); + LL_TIM_DisableIT_TRIG(INFRARED_RX_TIMER); + LL_TIM_DisableDMAReq_TRIG(INFRARED_RX_TIMER); + LL_TIM_SetTriggerOutput(INFRARED_RX_TIMER, LL_TIM_TRGO_RESET); + LL_TIM_EnableMasterSlaveMode(INFRARED_RX_TIMER); + LL_TIM_IC_SetActiveInput(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI); + LL_TIM_IC_SetPrescaler(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetFilter(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); + LL_TIM_IC_SetPolarity(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetActiveInput(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI); + LL_TIM_IC_SetPrescaler(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); + + furi_hal_interrupt_set_isr(INFRARED_RX_IRQ, furi_hal_infrared_tim_rx_isr, NULL); furi_hal_infrared_state = InfraredStateAsyncRx; - LL_TIM_EnableIT_CC1(TIM2); - LL_TIM_EnableIT_CC2(TIM2); - LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); - LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); + LL_TIM_EnableIT_CC1(INFRARED_RX_TIMER); + LL_TIM_EnableIT_CC2(INFRARED_RX_TIMER); + LL_TIM_CC_EnableChannel(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH1); + LL_TIM_CC_EnableChannel(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2); - LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableCounter(TIM2); + LL_TIM_SetCounter(INFRARED_RX_TIMER, 0); + LL_TIM_EnableCounter(INFRARED_RX_TIMER); } void furi_hal_infrared_async_rx_stop(void) { furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); FURI_CRITICAL_ENTER(); - - LL_TIM_DeInit(TIM2); - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + furi_hal_bus_disable(INFRARED_RX_TIMER_BUS); + furi_hal_interrupt_set_isr(INFRARED_RX_IRQ, NULL, NULL); furi_hal_infrared_state = InfraredStateIdle; - FURI_CRITICAL_EXIT(); } void furi_hal_infrared_async_rx_set_timeout(uint32_t timeout_us) { - LL_TIM_OC_SetCompareCH3(TIM2, timeout_us); - LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE); - LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3); - LL_TIM_EnableIT_CC3(TIM2); + LL_TIM_OC_SetCompareCH3(INFRARED_RX_TIMER, timeout_us); + LL_TIM_OC_SetMode(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE); + LL_TIM_CC_EnableChannel(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH3); + LL_TIM_EnableIT_CC3(INFRARED_RX_TIMER); } bool furi_hal_infrared_is_busy(void) { @@ -222,16 +231,16 @@ void furi_hal_infrared_async_rx_set_timeout_isr_callback( } static void furi_hal_infrared_tx_dma_terminate(void) { - LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF); - LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); - LL_DMA_DisableIT_TC(IR_DMA_CH2_DEF); + LL_DMA_DisableIT_TC(INFRARED_DMA_CH1_DEF); + LL_DMA_DisableIT_HT(INFRARED_DMA_CH2_DEF); + LL_DMA_DisableIT_TC(INFRARED_DMA_CH2_DEF); furi_assert(furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress); - LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF); - LL_DMA_DisableChannel(IR_DMA_CH2_DEF); - LL_DMA_DisableChannel(IR_DMA_CH1_DEF); - LL_TIM_DisableCounter(TIM1); + LL_DMA_DisableIT_TC(INFRARED_DMA_CH1_DEF); + LL_DMA_DisableChannel(INFRARED_DMA_CH2_DEF); + LL_DMA_DisableChannel(INFRARED_DMA_CH1_DEF); + LL_TIM_DisableCounter(INFRARED_DMA_TIMER); FuriStatus status = furi_semaphore_release(infrared_tim_tx.stop_semaphore); furi_check(status == FuriStatusOk); furi_hal_infrared_state = InfraredStateAsyncTxStopped; @@ -239,7 +248,7 @@ static void furi_hal_infrared_tx_dma_terminate(void) { static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { uint8_t buf_num = 0; - uint32_t buffer_adr = LL_DMA_GetMemoryAddress(IR_DMA_CH2_DEF); + uint32_t buffer_adr = LL_DMA_GetMemoryAddress(INFRARED_DMA_CH2_DEF); if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[0].data) { buf_num = 0; } else if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[1].data) { @@ -251,13 +260,13 @@ static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { } static void furi_hal_infrared_tx_dma_polarity_isr() { -#if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 - if(LL_DMA_IsActiveFlag_TE1(IR_DMA)) { - LL_DMA_ClearFlag_TE1(IR_DMA); +#if INFRARED_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_TE1(INFRARED_DMA)) { + LL_DMA_ClearFlag_TE1(INFRARED_DMA); furi_crash(NULL); } - if(LL_DMA_IsActiveFlag_TC1(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH1_DEF)) { - LL_DMA_ClearFlag_TC1(IR_DMA); + if(LL_DMA_IsActiveFlag_TC1(INFRARED_DMA) && LL_DMA_IsEnabledIT_TC(INFRARED_DMA_CH1_DEF)) { + LL_DMA_ClearFlag_TC1(INFRARED_DMA); furi_check( (furi_hal_infrared_state == InfraredStateAsyncTx) || @@ -273,23 +282,23 @@ static void furi_hal_infrared_tx_dma_polarity_isr() { } static void furi_hal_infrared_tx_dma_isr() { -#if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 - if(LL_DMA_IsActiveFlag_TE2(IR_DMA)) { - LL_DMA_ClearFlag_TE2(IR_DMA); +#if INFRARED_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 + if(LL_DMA_IsActiveFlag_TE2(INFRARED_DMA)) { + LL_DMA_ClearFlag_TE2(INFRARED_DMA); furi_crash(NULL); } - if(LL_DMA_IsActiveFlag_HT2(IR_DMA) && LL_DMA_IsEnabledIT_HT(IR_DMA_CH2_DEF)) { - LL_DMA_ClearFlag_HT2(IR_DMA); + if(LL_DMA_IsActiveFlag_HT2(INFRARED_DMA) && LL_DMA_IsEnabledIT_HT(INFRARED_DMA_CH2_DEF)) { + LL_DMA_ClearFlag_HT2(INFRARED_DMA); uint8_t buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); uint8_t next_buf_num = !buf_num; if(infrared_tim_tx.buffer[buf_num].last_packet_end) { - LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); + LL_DMA_DisableIT_HT(INFRARED_DMA_CH2_DEF); } else if( !infrared_tim_tx.buffer[buf_num].packet_end || (furi_hal_infrared_state == InfraredStateAsyncTx)) { furi_hal_infrared_tx_fill_buffer(next_buf_num, 0); if(infrared_tim_tx.buffer[next_buf_num].last_packet_end) { - LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); + LL_DMA_DisableIT_HT(INFRARED_DMA_CH2_DEF); } } else if(furi_hal_infrared_state == InfraredStateAsyncTxStopReq) { /* fallthrough */ @@ -297,8 +306,8 @@ static void furi_hal_infrared_tx_dma_isr() { furi_crash(NULL); } } - if(LL_DMA_IsActiveFlag_TC2(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH2_DEF)) { - LL_DMA_ClearFlag_TC2(IR_DMA); + if(LL_DMA_IsActiveFlag_TC2(INFRARED_DMA) && LL_DMA_IsEnabledIT_TC(INFRARED_DMA_CH2_DEF)) { + LL_DMA_ClearFlag_TC2(INFRARED_DMA); furi_check( (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) || (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) || @@ -330,50 +339,48 @@ static void furi_hal_infrared_tx_dma_isr() { } static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cycle) { - /* LL_DBGMCU_APB2_GRP1_FreezePeriph(LL_DBGMCU_APB2_GRP1_TIM1_STOP); */ - - LL_TIM_DisableCounter(TIM1); - LL_TIM_SetRepetitionCounter(TIM1, 0); - LL_TIM_SetCounter(TIM1, 0); - LL_TIM_SetPrescaler(TIM1, 0); - LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP); - LL_TIM_EnableARRPreload(TIM1); + LL_TIM_DisableCounter(INFRARED_DMA_TIMER); + LL_TIM_SetRepetitionCounter(INFRARED_DMA_TIMER, 0); + LL_TIM_SetCounter(INFRARED_DMA_TIMER, 0); + LL_TIM_SetPrescaler(INFRARED_DMA_TIMER, 0); + LL_TIM_SetCounterMode(INFRARED_DMA_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_EnableARRPreload(INFRARED_DMA_TIMER); LL_TIM_SetAutoReload( - TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), freq)); -#if INFRARED_TX_DEBUG == 1 - LL_TIM_OC_SetCompareCH1(TIM1, ((LL_TIM_GetAutoReload(TIM1) + 1) * (1 - duty_cycle))); - LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1); + INFRARED_DMA_TIMER, + __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(INFRARED_DMA_TIMER), freq)); +#if defined INFRARED_TX_DEBUG + LL_TIM_OC_SetCompareCH1( + INFRARED_DMA_TIMER, ((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle))); + LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1); /* LL_TIM_OCMODE_PWM2 set by DMA */ - LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_FORCED_INACTIVE); - LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH); - LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1); - LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N); - LL_TIM_DisableIT_CC1(TIM1); + LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_FORCED_INACTIVE); + LL_TIM_OC_SetPolarity(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_OC_DisableFast(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1); + LL_TIM_CC_EnableChannel(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N); + LL_TIM_DisableIT_CC1(INFRARED_DMA_TIMER); #else - LL_TIM_OC_SetCompareCH3(TIM1, ((LL_TIM_GetAutoReload(TIM1) + 1) * (1 - duty_cycle))); - LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3); + LL_TIM_OC_SetCompareCH3( + INFRARED_DMA_TIMER, ((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle))); + LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3); /* LL_TIM_OCMODE_PWM2 set by DMA */ - LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_FORCED_INACTIVE); - LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH3N, LL_TIM_OCPOLARITY_HIGH); - LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH3); - LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH3N); - LL_TIM_DisableIT_CC3(TIM1); + LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_FORCED_INACTIVE); + LL_TIM_OC_SetPolarity(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3N, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_OC_DisableFast(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3); + LL_TIM_CC_EnableChannel(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3N); + LL_TIM_DisableIT_CC3(INFRARED_DMA_TIMER); #endif - LL_TIM_DisableMasterSlaveMode(TIM1); - LL_TIM_EnableAllOutputs(TIM1); - LL_TIM_DisableIT_UPDATE(TIM1); - LL_TIM_EnableDMAReq_UPDATE(TIM1); - - NVIC_SetPriority(TIM1_UP_TIM16_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); - NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); + LL_TIM_DisableMasterSlaveMode(INFRARED_DMA_TIMER); + LL_TIM_EnableAllOutputs(INFRARED_DMA_TIMER); + LL_TIM_DisableIT_UPDATE(INFRARED_DMA_TIMER); + LL_TIM_EnableDMAReq_UPDATE(INFRARED_DMA_TIMER); } static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) { LL_DMA_InitTypeDef dma_config = {0}; -#if INFRARED_TX_DEBUG == 1 - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM1->CCMR1); +#if defined INFRARED_TX_DEBUG + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (INFRARED_DMA_TIMER->CCMR1); #else - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM1->CCMR2); + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (INFRARED_DMA_TIMER->CCMR2); #endif dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL; dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; @@ -386,24 +393,25 @@ static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) { dma_config.NbData = 0; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(IR_DMA_CH1_DEF, &dma_config); + LL_DMA_Init(INFRARED_DMA_CH1_DEF, &dma_config); -#if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 - LL_DMA_ClearFlag_TE1(IR_DMA); - LL_DMA_ClearFlag_TC1(IR_DMA); +#if INFRARED_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + LL_DMA_ClearFlag_TE1(INFRARED_DMA); + LL_DMA_ClearFlag_TC1(INFRARED_DMA); #else #error Update this code. Would you kindly? #endif - LL_DMA_EnableIT_TE(IR_DMA_CH1_DEF); - LL_DMA_EnableIT_TC(IR_DMA_CH1_DEF); + LL_DMA_EnableIT_TE(INFRARED_DMA_CH1_DEF); + LL_DMA_EnableIT_TC(INFRARED_DMA_CH1_DEF); - furi_hal_interrupt_set_isr_ex(IR_DMA_CH1_IRQ, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); + furi_hal_interrupt_set_isr_ex( + INFRARED_DMA_CH1_IRQ, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); } static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { LL_DMA_InitTypeDef dma_config = {0}; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM1->RCR); + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (INFRARED_DMA_TIMER->RCR); dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL; dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; dma_config.Mode = LL_DMA_MODE_NORMAL; @@ -414,21 +422,21 @@ static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { dma_config.NbData = 0; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; - LL_DMA_Init(IR_DMA_CH2_DEF, &dma_config); + LL_DMA_Init(INFRARED_DMA_CH2_DEF, &dma_config); -#if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 - LL_DMA_ClearFlag_TC2(IR_DMA); - LL_DMA_ClearFlag_HT2(IR_DMA); - LL_DMA_ClearFlag_TE2(IR_DMA); +#if INFRARED_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 + LL_DMA_ClearFlag_TC2(INFRARED_DMA); + LL_DMA_ClearFlag_HT2(INFRARED_DMA); + LL_DMA_ClearFlag_TE2(INFRARED_DMA); #else #error Update this code. Would you kindly? #endif - LL_DMA_EnableIT_TC(IR_DMA_CH2_DEF); - LL_DMA_EnableIT_HT(IR_DMA_CH2_DEF); - LL_DMA_EnableIT_TE(IR_DMA_CH2_DEF); + LL_DMA_EnableIT_TC(INFRARED_DMA_CH2_DEF); + LL_DMA_EnableIT_HT(INFRARED_DMA_CH2_DEF); + LL_DMA_EnableIT_TE(INFRARED_DMA_CH2_DEF); - furi_hal_interrupt_set_isr_ex(IR_DMA_CH2_IRQ, 5, furi_hal_infrared_tx_dma_isr, NULL); + furi_hal_interrupt_set_isr_ex(INFRARED_DMA_CH2_IRQ, 5, furi_hal_infrared_tx_dma_isr, NULL); } static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) { @@ -530,14 +538,14 @@ static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polar furi_assert(buffer->polarity != NULL); FURI_CRITICAL_ENTER(); - bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH1_DEF); + bool channel_enabled = LL_DMA_IsEnabledChannel(INFRARED_DMA_CH1_DEF); if(channel_enabled) { - LL_DMA_DisableChannel(IR_DMA_CH1_DEF); + LL_DMA_DisableChannel(INFRARED_DMA_CH1_DEF); } - LL_DMA_SetMemoryAddress(IR_DMA_CH1_DEF, (uint32_t)buffer->polarity); - LL_DMA_SetDataLength(IR_DMA_CH1_DEF, buffer->size + polarity_shift); + LL_DMA_SetMemoryAddress(INFRARED_DMA_CH1_DEF, (uint32_t)buffer->polarity); + LL_DMA_SetDataLength(INFRARED_DMA_CH1_DEF, buffer->size + polarity_shift); if(channel_enabled) { - LL_DMA_EnableChannel(IR_DMA_CH1_DEF); + LL_DMA_EnableChannel(INFRARED_DMA_CH1_DEF); } FURI_CRITICAL_EXIT(); } @@ -550,14 +558,14 @@ static void furi_hal_infrared_tx_dma_set_buffer(uint8_t buf_num) { /* non-circular mode requires disabled channel before setup */ FURI_CRITICAL_ENTER(); - bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH2_DEF); + bool channel_enabled = LL_DMA_IsEnabledChannel(INFRARED_DMA_CH2_DEF); if(channel_enabled) { - LL_DMA_DisableChannel(IR_DMA_CH2_DEF); + LL_DMA_DisableChannel(INFRARED_DMA_CH2_DEF); } - LL_DMA_SetMemoryAddress(IR_DMA_CH2_DEF, (uint32_t)buffer->data); - LL_DMA_SetDataLength(IR_DMA_CH2_DEF, buffer->size); + LL_DMA_SetMemoryAddress(INFRARED_DMA_CH2_DEF, (uint32_t)buffer->data); + LL_DMA_SetDataLength(INFRARED_DMA_CH2_DEF, buffer->size); if(channel_enabled) { - LL_DMA_EnableChannel(IR_DMA_CH2_DEF); + LL_DMA_EnableChannel(INFRARED_DMA_CH2_DEF); } FURI_CRITICAL_EXIT(); } @@ -568,9 +576,10 @@ static void furi_hal_infrared_async_tx_free_resources(void) { (furi_hal_infrared_state == InfraredStateAsyncTxStopped)); furi_hal_gpio_init(&gpio_infrared_tx, GpioModeAnalog, GpioPullDown, GpioSpeedLow); - furi_hal_interrupt_set_isr(IR_DMA_CH1_IRQ, NULL, NULL); - furi_hal_interrupt_set_isr(IR_DMA_CH2_IRQ, NULL, NULL); - LL_TIM_DeInit(TIM1); + furi_hal_interrupt_set_isr(INFRARED_DMA_CH1_IRQ, NULL, NULL); + furi_hal_interrupt_set_isr(INFRARED_DMA_CH2_IRQ, NULL, NULL); + + furi_hal_bus_disable(INFRARED_DMA_TIMER_BUS); furi_semaphore_free(infrared_tim_tx.stop_semaphore); free(infrared_tim_tx.buffer[0].data); @@ -611,6 +620,8 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { furi_hal_infrared_tx_fill_buffer(0, INFRARED_POLARITY_SHIFT); + furi_hal_bus_enable(INFRARED_DMA_TIMER_BUS); + furi_hal_infrared_configure_tim_pwm_tx(freq, duty_cycle); furi_hal_infrared_configure_tim_cmgr2_dma_tx(); furi_hal_infrared_configure_tim_rcr_dma_tx(); @@ -619,11 +630,11 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { furi_hal_infrared_state = InfraredStateAsyncTx; - LL_TIM_ClearFlag_UPDATE(TIM1); - LL_DMA_EnableChannel(IR_DMA_CH1_DEF); - LL_DMA_EnableChannel(IR_DMA_CH2_DEF); + LL_TIM_ClearFlag_UPDATE(INFRARED_DMA_TIMER); + LL_DMA_EnableChannel(INFRARED_DMA_CH1_DEF); + LL_DMA_EnableChannel(INFRARED_DMA_CH2_DEF); furi_delay_us(5); - LL_TIM_GenerateEvent_UPDATE(TIM1); /* DMA -> TIMx_RCR */ + LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* DMA -> TIMx_RCR */ furi_delay_us(5); LL_GPIO_ResetOutputPin( gpio_infrared_tx.port, gpio_infrared_tx.pin); /* when disable it prevents false pulse */ @@ -631,8 +642,8 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { &gpio_infrared_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); FURI_CRITICAL_ENTER(); - LL_TIM_GenerateEvent_UPDATE(TIM1); /* TIMx_RCR -> Repetition counter */ - LL_TIM_EnableCounter(TIM1); + LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* TIMx_RCR -> Repetition counter */ + LL_TIM_EnableCounter(INFRARED_DMA_TIMER); FURI_CRITICAL_EXIT(); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c index b5639d2300c5..c508dac72bf4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c @@ -62,7 +62,7 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = { __attribute__((always_inline)) static inline void furi_hal_interrupt_call(FuriHalInterruptId index) { - furi_assert(furi_hal_interrupt_isr[index].isr); + furi_check(furi_hal_interrupt_isr[index].isr); furi_hal_interrupt_isr[index].isr(furi_hal_interrupt_isr[index].context); } @@ -127,16 +127,14 @@ void furi_hal_interrupt_set_isr_ex( uint16_t priority, FuriHalInterruptISR isr, void* context) { - furi_assert(index < FuriHalInterruptIdMax); - furi_assert(priority < 15); - furi_assert(furi_hal_interrupt_irqn[index]); + furi_check(index < FuriHalInterruptIdMax); + furi_check(priority <= 15); if(isr) { // Pre ISR set - furi_assert(furi_hal_interrupt_isr[index].isr == NULL); + furi_check(furi_hal_interrupt_isr[index].isr == NULL); } else { // Pre ISR clear - furi_assert(furi_hal_interrupt_isr[index].isr != NULL); furi_hal_interrupt_disable(index); furi_hal_interrupt_clear_pending(index); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_memory.c b/firmware/targets/f7/furi_hal/furi_hal_memory.c index 9716f1e5292e..7f69b90ca2bb 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_memory.c +++ b/firmware/targets/f7/furi_hal/furi_hal_memory.c @@ -4,6 +4,9 @@ #define TAG "FuriHalMemory" +// STM(TM) Copro(TM) bug(TM) workaround size +#define RAM2B_COPRO_GAP_SIZE_KB 2 + typedef enum { SRAM_A, SRAM_B, @@ -30,53 +33,47 @@ void furi_hal_memory_init() { return; } - if(!ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)) { - FURI_LOG_E(TAG, "C2 start timeout"); - return; - } - FuriHalMemory* memory = malloc(sizeof(FuriHalMemory)); - const BleGlueC2Info* c2_ver = ble_glue_get_c2_info(); + uint32_t sbrsa = (FLASH->SRRVR & FLASH_SRRVR_SBRSA_Msk) >> FLASH_SRRVR_SBRSA_Pos; + uint32_t snbrsa = (FLASH->SRRVR & FLASH_SRRVR_SNBRSA_Msk) >> FLASH_SRRVR_SNBRSA_Pos; + + uint32_t sram2a_busy_size = (uint32_t)&__sram2a_free__ - (uint32_t)&__sram2a_start__; + uint32_t sram2a_unprotected_size = (sbrsa)*1024; + uint32_t sram2b_unprotected_size = (snbrsa)*1024; - if(c2_ver->mode == BleGlueC2ModeStack) { - uint32_t sram2a_busy_size = (uint32_t)&__sram2a_free__ - (uint32_t)&__sram2a_start__; - uint32_t sram2a_unprotected_size = (32 - c2_ver->MemorySizeSram2A) * 1024; - uint32_t sram2b_unprotected_size = (32 - c2_ver->MemorySizeSram2B) * 1024; + // STM(TM) Copro(TM) bug(TM) workaround + sram2b_unprotected_size -= 1024 * RAM2B_COPRO_GAP_SIZE_KB; - memory->region[SRAM_A].start = (uint8_t*)&__sram2a_free__; - memory->region[SRAM_B].start = (uint8_t*)&__sram2b_start__; + memory->region[SRAM_A].start = (uint8_t*)&__sram2a_free__; + memory->region[SRAM_B].start = (uint8_t*)&__sram2b_start__; - if(sram2a_unprotected_size > sram2a_busy_size) { - memory->region[SRAM_A].size = sram2a_unprotected_size - sram2a_busy_size; - } else { - memory->region[SRAM_A].size = 0; + if(sram2a_unprotected_size > sram2a_busy_size) { + memory->region[SRAM_A].size = sram2a_unprotected_size - sram2a_busy_size; + } else { + memory->region[SRAM_A].size = 0; + } + memory->region[SRAM_B].size = sram2b_unprotected_size; + + FURI_LOG_I( + TAG, "SRAM2A: 0x%p, %lu", memory->region[SRAM_A].start, memory->region[SRAM_A].size); + FURI_LOG_I( + TAG, "SRAM2B: 0x%p, %lu", memory->region[SRAM_B].start, memory->region[SRAM_B].size); + + if((memory->region[SRAM_A].size > 0) || (memory->region[SRAM_B].size > 0)) { + if((memory->region[SRAM_A].size > 0)) { + FURI_LOG_I(TAG, "SRAM2A clear"); + memset(memory->region[SRAM_A].start, 0, memory->region[SRAM_A].size); } - memory->region[SRAM_B].size = sram2b_unprotected_size; - - FURI_LOG_I( - TAG, "SRAM2A: 0x%p, %lu", memory->region[SRAM_A].start, memory->region[SRAM_A].size); - FURI_LOG_I( - TAG, "SRAM2B: 0x%p, %lu", memory->region[SRAM_B].start, memory->region[SRAM_B].size); - - if((memory->region[SRAM_A].size > 0) || (memory->region[SRAM_B].size > 0)) { - if((memory->region[SRAM_A].size > 0)) { - FURI_LOG_I(TAG, "SRAM2A clear"); - memset(memory->region[SRAM_A].start, 0, memory->region[SRAM_A].size); - } - if((memory->region[SRAM_B].size > 0)) { - FURI_LOG_I(TAG, "SRAM2B clear"); - memset(memory->region[SRAM_B].start, 0, memory->region[SRAM_B].size); - } - furi_hal_memory = memory; - FURI_LOG_I(TAG, "Enabled"); - } else { - free(memory); - FURI_LOG_E(TAG, "No SRAM2 available"); + if((memory->region[SRAM_B].size > 0)) { + FURI_LOG_I(TAG, "SRAM2B clear"); + memset(memory->region[SRAM_B].start, 0, memory->region[SRAM_B].size); } + furi_hal_memory = memory; + FURI_LOG_I(TAG, "Enabled"); } else { free(memory); - FURI_LOG_E(TAG, "No Core2 available"); + FURI_LOG_E(TAG, "No SRAM2 available"); } } @@ -89,15 +86,20 @@ void* furi_hal_memory_alloc(size_t size) { return NULL; } + void* allocated_memory = NULL; + FURI_CRITICAL_ENTER(); for(int i = 0; i < SRAM_MAX; i++) { if(furi_hal_memory->region[i].size >= size) { void* ptr = furi_hal_memory->region[i].start; furi_hal_memory->region[i].start += size; furi_hal_memory->region[i].size -= size; - return ptr; + allocated_memory = ptr; + break; } } - return NULL; + FURI_CRITICAL_EXIT(); + + return allocated_memory; } size_t furi_hal_memory_get_free() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 8910d887bcb7..ffda124a4078 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -1,6 +1,6 @@ #include -#include -#include +#include "furi_hal_nfc.h" +#include #include #include #include @@ -467,7 +467,7 @@ bool furi_hal_nfc_emulate_nfca( buff_tx, buff_tx_len, buff_rx, - sizeof(buff_rx), + rfalConvBytesToBits(buff_rx_size), &buff_rx_len, data_type, RFAL_FWT_NONE); @@ -491,7 +491,7 @@ bool furi_hal_nfc_emulate_nfca( buff_tx, buff_tx_len, buff_rx, - sizeof(buff_rx), + rfalConvBytesToBits(buff_rx_size), &buff_rx_len, data_type, RFAL_FWT_NONE); @@ -819,3 +819,17 @@ FuriHalNfcReturn furi_hal_nfc_ll_txrx_bits( void furi_hal_nfc_ll_poll() { rfalWorker(); } + +void furi_hal_nfc_field_detect_start() { + st25r3916WriteRegister( + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_en_fd_mask); + st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ | ST25R3916_REG_MODE_om0); +} + +bool furi_hal_nfc_field_is_present() { + return st25r3916CheckReg( + ST25R3916_REG_AUX_DISPLAY, + ST25R3916_REG_AUX_DISPLAY_efd_o, + ST25R3916_REG_AUX_DISPLAY_efd_o); +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.h b/firmware/targets/f7/furi_hal/furi_hal_nfc.h index dc3f873f3460..c106f70a05c4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.h +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.h @@ -13,7 +13,7 @@ extern "C" { #endif #include -#include +#include #define FURI_HAL_NFC_UID_MAX_LEN 10 #define FURI_HAL_NFC_DATA_BUFF_SIZE (512) @@ -423,6 +423,10 @@ FuriHalNfcReturn furi_hal_nfc_ll_txrx_bits( void furi_hal_nfc_ll_poll(); +void furi_hal_nfc_field_detect_start(); + +bool furi_hal_nfc_field_is_present(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index ec405f1080df..035919d784df 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -37,16 +38,18 @@ typedef struct { volatile uint8_t insomnia; volatile uint8_t suppress_charge; - uint8_t gauge_initialized; - uint8_t charger_initialized; + bool gauge_ok; + bool charger_ok; } FuriHalPower; static volatile FuriHalPower furi_hal_power = { .insomnia = 0, .suppress_charge = 0, + .gauge_ok = false, + .charger_ok = false, }; -#include +extern const BQ27220DMData furi_hal_power_gauge_data_memory[]; void furi_hal_power_init() { #ifdef FURI_HAL_POWER_DEBUG @@ -63,8 +66,13 @@ void furi_hal_power_init() { LL_C2_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - bq27220_init(&furi_hal_i2c_handle_power, &cedv); - bq25896_init(&furi_hal_i2c_handle_power); + // Find and init gauge + if(bq27220_init(&furi_hal_i2c_handle_power)) { + furi_hal_power.gauge_ok = bq27220_apply_data_memory( + &furi_hal_i2c_handle_power, furi_hal_power_gauge_data_memory); + } + // Find and init charger + furi_hal_power.charger_ok = bq25896_init(&furi_hal_i2c_handle_power); furi_hal_i2c_release(&furi_hal_i2c_handle_power); FURI_LOG_I(TAG, "Init OK"); @@ -78,14 +86,29 @@ bool furi_hal_power_gauge_is_ok() { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) == BQ27220_ERROR || - bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) == - BQ27220_ERROR) { + if(!bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) || + !bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status)) { ret = false; } else { ret &= battery_status.BATTPRES; ret &= operation_status.INITCOMP; - ret &= (cedv.design_cap == bq27220_get_design_capacity(&furi_hal_i2c_handle_power)); + ret &= furi_hal_power.gauge_ok; + } + + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + + return ret; +} + +bool furi_hal_power_is_shutdown_requested() { + bool ret = false; + + BatteryStatus battery_status; + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + + if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) != BQ27220_ERROR) { + ret = battery_status.SYSDWN; } furi_hal_i2c_release(&furi_hal_i2c_handle_power); @@ -284,10 +307,15 @@ void furi_hal_power_reset() { NVIC_SystemReset(); } -void furi_hal_power_enable_otg() { +bool furi_hal_power_enable_otg() { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + bq25896_set_boost_lim(&furi_hal_i2c_handle_power, BoostLim_2150); bq25896_enable_otg(&furi_hal_i2c_handle_power); + furi_delay_ms(30); + bool ret = bq25896_is_otg_enabled(&furi_hal_i2c_handle_power); + bq25896_set_boost_lim(&furi_hal_i2c_handle_power, BoostLim_1400); furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; } void furi_hal_power_disable_otg() { @@ -317,6 +345,13 @@ void furi_hal_power_set_battery_charge_voltage_limit(float voltage) { furi_hal_i2c_release(&furi_hal_i2c_handle_power); } +bool furi_hal_power_check_otg_fault() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + bool ret = bq25896_check_otg_fault(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + void furi_hal_power_check_otg_status() { furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); if(bq25896_check_otg_fault(&furi_hal_i2c_handle_power)) @@ -564,9 +599,8 @@ void furi_hal_power_debug_get(PropertyValueCallback out, void* context) { const uint32_t ntc_mpct = bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power); - if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) != BQ27220_ERROR && - bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) != - BQ27220_ERROR) { + if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) && + bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status)) { property_value_out(&property_context, "%lu", 2, "charger", "ntc", ntc_mpct); property_value_out(&property_context, "%d", 2, "gauge", "calmd", operation_status.CALMD); property_value_out(&property_context, "%d", 2, "gauge", "sec", operation_status.SEC); diff --git a/firmware/targets/f7/furi_hal/furi_hal_power_calibration.h b/firmware/targets/f7/furi_hal/furi_hal_power_calibration.h deleted file mode 100644 index 5eb0f938b2e6..000000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_power_calibration.h +++ /dev/null @@ -1,37 +0,0 @@ -const ParamCEDV cedv = { - .cedv_conf.gauge_conf = - { - .CCT = 1, - .CSYNC = 0, - .EDV_CMP = 0, - .SC = 1, - .FIXED_EDV0 = 1, - .FCC_LIM = 1, - .FC_FOR_VDQ = 1, - .IGNORE_SD = 1, - .SME0 = 0, - }, - .full_charge_cap = 2101, - .design_cap = 2101, - .EDV0 = 3300, - .EDV1 = 3321, - .EDV2 = 3355, - .EMF = 3679, - .C0 = 430, - .C1 = 0, - .R1 = 408, - .R0 = 334, - .T0 = 4626, - .TC = 11, - .DOD0 = 4044, - .DOD10 = 3905, - .DOD20 = 3807, - .DOD30 = 3718, - .DOD40 = 3642, - .DOD50 = 3585, - .DOD60 = 3546, - .DOD70 = 3514, - .DOD80 = 3477, - .DOD90 = 3411, - .DOD100 = 3299, -}; diff --git a/firmware/targets/f7/furi_hal/furi_hal_power_config.c b/firmware/targets/f7/furi_hal/furi_hal_power_config.c new file mode 100644 index 000000000000..488edce91eee --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_power_config.c @@ -0,0 +1,149 @@ +#include + +const BQ27220DMGaugingConfig furi_hal_power_gauge_data_memory_gauging_config = { + .CCT = 1, + .CSYNC = 0, + .EDV_CMP = 0, + .SC = 1, + .FIXED_EDV0 = 1, + .FCC_LIM = 1, + .FC_FOR_VDQ = 1, + .IGNORE_SD = 1, + .SME0 = 0, +}; + +const BQ27220DMData furi_hal_power_gauge_data_memory[] = { + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1GaugingConfig, + .type = BQ27220DMTypePtr16, + .value.u32 = (uint32_t)&furi_hal_power_gauge_data_memory_gauging_config, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1FullChargeCapacity, + .type = BQ27220DMTypeU16, + .value.u16 = 2100, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1DesignCapacity, + .type = BQ27220DMTypeU16, + .value.u16 = 2100, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EMF, + .type = BQ27220DMTypeU16, + .value.u16 = 3679, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1C0, + .type = BQ27220DMTypeU16, + .value.u16 = 430, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1R0, + .type = BQ27220DMTypeU16, + .value.u16 = 334, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1T0, + .type = BQ27220DMTypeU16, + .value.u16 = 4626, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1R1, + .type = BQ27220DMTypeU16, + .value.u16 = 408, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1TC, + .type = BQ27220DMTypeU8, + .value.u8 = 11, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1C1, + .type = BQ27220DMTypeU8, + .value.u8 = 0, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD0, + .type = BQ27220DMTypeU16, + .value.u16 = 4044, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD10, + .type = BQ27220DMTypeU16, + .value.u16 = 3905, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD20, + .type = BQ27220DMTypeU16, + .value.u16 = 3807, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD30, + .type = BQ27220DMTypeU16, + .value.u16 = 3718, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD40, + .type = BQ27220DMTypeU16, + .value.u16 = 3642, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD50, + .type = BQ27220DMTypeU16, + .value.u16 = 3585, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD60, + .type = BQ27220DMTypeU16, + .value.u16 = 3546, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD70, + .type = BQ27220DMTypeU16, + .value.u16 = 3514, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD80, + .type = BQ27220DMTypeU16, + .value.u16 = 3477, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD90, + .type = BQ27220DMTypeU16, + .value.u16 = 3411, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD100, + .type = BQ27220DMTypeU16, + .value.u16 = 3299, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV0, + .type = BQ27220DMTypeU16, + .value.u16 = 3300, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV1, + .type = BQ27220DMTypeU16, + .value.u16 = 3321, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV2, + .type = BQ27220DMTypeU16, + .value.u16 = 3355, + }, + { + .address = BQ27220DMAddressCalibrationCurrentDeadband, + .type = BQ27220DMTypeU8, + .value.u8 = 1, + }, + { + .address = BQ27220DMAddressConfigurationPowerSleepCurrent, + .type = BQ27220DMTypeI16, + .value.i16 = 1, + }, + { + .type = BQ27220DMTypeEnd, + }, +}; diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.c b/firmware/targets/f7/furi_hal/furi_hal_pwm.c index 8f84b5fd8d59..879460e6bd54 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_pwm.c +++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.c @@ -1,8 +1,7 @@ #include -#include #include +#include -#include #include #include #include @@ -29,9 +28,7 @@ void furi_hal_pwm_start(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) GpioSpeedVeryHigh, GpioAltFn1TIM1); - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(TIM1); - FURI_CRITICAL_EXIT(); + furi_hal_bus_enable(FuriHalBusTIM1); LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP); LL_TIM_SetRepetitionCounter(TIM1, 0); @@ -58,9 +55,7 @@ void furi_hal_pwm_start(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) GpioSpeedVeryHigh, GpioAltFn14LPTIM2); - FURI_CRITICAL_ENTER(); - LL_LPTIM_DeInit(LPTIM2); - FURI_CRITICAL_EXIT(); + furi_hal_bus_enable(FuriHalBusLPTIM2); LL_LPTIM_SetUpdateMode(LPTIM2, LL_LPTIM_UPDATE_MODE_ENDOFPERIOD); LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_PCLK1); @@ -80,17 +75,22 @@ void furi_hal_pwm_start(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) void furi_hal_pwm_stop(FuriHalPwmOutputId channel) { if(channel == FuriHalPwmOutputIdTim1PA7) { furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(TIM1); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(FuriHalBusTIM1); } else if(channel == FuriHalPwmOutputIdLptim2PA4) { furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeAnalog); - FURI_CRITICAL_ENTER(); - LL_LPTIM_DeInit(LPTIM2); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(FuriHalBusLPTIM2); } } +bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel) { + if(channel == FuriHalPwmOutputIdTim1PA7) { + return furi_hal_bus_is_enabled(FuriHalBusTIM1); + } else if(channel == FuriHalPwmOutputIdLptim2PA4) { + return furi_hal_bus_is_enabled(FuriHalBusLPTIM2); + } + return false; +} + void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) { furi_assert(freq > 0); uint32_t freq_div = 64000000LU / freq; diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.h b/firmware/targets/f7/furi_hal/furi_hal_pwm.h index a8682c5fbb16..16acca05efe5 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_pwm.h +++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.h @@ -9,6 +9,7 @@ extern "C" { #endif #include +#include typedef enum { FuriHalPwmOutputIdTim1PA7, @@ -37,6 +38,13 @@ void furi_hal_pwm_stop(FuriHalPwmOutputId channel); */ void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty); +/** Is PWM channel running? + * + * @param[in] channel PWM channel (FuriHalPwmOutputId) + * @return bool - true if running +*/ +bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_random.c b/firmware/targets/f7/furi_hal/furi_hal_random.c index d3461c4d12e0..cf4b552f6d8a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_random.c +++ b/firmware/targets/f7/furi_hal/furi_hal_random.c @@ -1,8 +1,9 @@ #include +#include #include -#include #include +#include #include #include @@ -32,6 +33,11 @@ static uint32_t furi_hal_random_read_rng() { return LL_RNG_ReadRandData32(RNG); } +void furi_hal_random_init() { + furi_hal_bus_enable(FuriHalBusRNG); + LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48); +} + uint32_t furi_hal_random_get() { while(LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID)) ; @@ -40,6 +46,7 @@ uint32_t furi_hal_random_get() { const uint32_t random_val = furi_hal_random_read_rng(); LL_RNG_Disable(RNG); + ; LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0); return random_val; diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index abfd977e52cc..34b26b831cfa 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -80,6 +81,7 @@ const GpioPinRecord gpio_pins[] = { /* Dangerous pins, may damage hardware */ {.pin = &gpio_usart_rx, .name = "PB7", .debug = true}, {.pin = &gpio_speaker, .name = "PB8", .debug = true}, + {.pin = &gpio_infrared_tx, .name = "PB9", .debug = true}, }; const size_t gpio_pins_count = sizeof(gpio_pins) / sizeof(GpioPinRecord); @@ -106,6 +108,13 @@ static void furi_hal_resources_init_input_pins(GpioMode mode) { } void furi_hal_resources_init_early() { + furi_hal_bus_enable(FuriHalBusGPIOA); + furi_hal_bus_enable(FuriHalBusGPIOB); + furi_hal_bus_enable(FuriHalBusGPIOC); + furi_hal_bus_enable(FuriHalBusGPIOD); + furi_hal_bus_enable(FuriHalBusGPIOE); + furi_hal_bus_enable(FuriHalBusGPIOH); + furi_hal_resources_init_input_pins(GpioModeInput); // SD Card stepdown control @@ -150,6 +159,12 @@ void furi_hal_resources_init_early() { void furi_hal_resources_deinit_early() { furi_hal_resources_init_input_pins(GpioModeAnalog); + furi_hal_bus_disable(FuriHalBusGPIOA); + furi_hal_bus_disable(FuriHalBusGPIOB); + furi_hal_bus_disable(FuriHalBusGPIOC); + furi_hal_bus_disable(FuriHalBusGPIOD); + furi_hal_bus_disable(FuriHalBusGPIOE); + furi_hal_bus_disable(FuriHalBusGPIOH); } void furi_hal_resources_init() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c index 145e49df2c14..67f11d6ff7bc 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -9,18 +10,34 @@ #include #define FURI_HAL_RFID_READ_TIMER TIM1 +#define FURI_HAL_RFID_READ_TIMER_BUS FuriHalBusTIM1 #define FURI_HAL_RFID_READ_TIMER_CHANNEL LL_TIM_CHANNEL_CH1N // We can't use N channel for LL_TIM_OC_Init, so... #define FURI_HAL_RFID_READ_TIMER_CHANNEL_CONFIG LL_TIM_CHANNEL_CH1 #define FURI_HAL_RFID_EMULATE_TIMER TIM2 +#define FURI_HAL_RFID_EMULATE_TIMER_BUS FuriHalBusTIM2 #define FURI_HAL_RFID_EMULATE_TIMER_IRQ FuriHalInterruptIdTIM2 #define FURI_HAL_RFID_EMULATE_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 #define RFID_CAPTURE_TIM TIM2 +#define RFID_CAPTURE_TIM_BUS FuriHalBusTIM2 #define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 #define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 +// Field presence detection +#define FURI_HAL_RFID_FIELD_FREQUENCY_MIN 80000 +#define FURI_HAL_RFID_FIELD_FREQUENCY_MAX 200000 + +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER TIM2 +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS FuriHalBusTIM2 +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 + +#define FURI_HAL_RFID_FIELD_TIMEOUT_TIMER TIM1 +#define FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS FuriHalBusTIM1 + +#define FURI_HAL_RFID_FIELD_DMAMUX_DMA LL_DMAMUX_REQ_TIM1_UP + /* DMA Channels definition */ #define RFID_DMA DMA2 #define RFID_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 @@ -30,10 +47,15 @@ #define RFID_DMA_CH2_DEF RFID_DMA, RFID_DMA_CH2_CHANNEL typedef struct { - FuriHalRfidEmulateCallback callback; + uint32_t counter; + uint32_t set_tim_counter_cnt; +} FuriHalRfidField; + +typedef struct { FuriHalRfidDMACallback dma_callback; FuriHalRfidReadCaptureCallback read_capture_callback; void* context; + FuriHalRfidField field; } FuriHalRfid; FuriHalRfid* furi_hal_rfid = NULL; @@ -48,6 +70,8 @@ FuriHalRfid* furi_hal_rfid = NULL; void furi_hal_rfid_init() { furi_assert(furi_hal_rfid == NULL); furi_hal_rfid = malloc(sizeof(FuriHalRfid)); + furi_hal_rfid->field.counter = 0; + furi_hal_rfid->field.set_tim_counter_cnt = 0; furi_hal_rfid_pins_reset(); @@ -56,11 +80,7 @@ void furi_hal_rfid_init() { COMP_InitStruct.InputPlus = LL_COMP_INPUT_PLUS_IO1; COMP_InitStruct.InputMinus = LL_COMP_INPUT_MINUS_1_2VREFINT; COMP_InitStruct.InputHysteresis = LL_COMP_HYSTERESIS_HIGH; -#ifdef INVERT_RFID_IN - COMP_InitStruct.OutputPolarity = LL_COMP_OUTPUTPOL_INVERTED; -#else COMP_InitStruct.OutputPolarity = LL_COMP_OUTPUTPOL_NONINVERTED; -#endif COMP_InitStruct.OutputBlankingSource = LL_COMP_BLANKINGSRC_NONE; LL_COMP_Init(COMP1, &COMP_InitStruct); LL_COMP_SetCommonWindowMode(__LL_COMP_COMMON_INSTANCE(COMP1), LL_COMP_WINDOWMODE_DISABLE); @@ -92,7 +112,7 @@ void furi_hal_rfid_pins_reset() { furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } -void furi_hal_rfid_pins_emulate() { +static void furi_hal_rfid_pins_emulate() { // ibutton low furi_hal_ibutton_pin_configure(); furi_hal_ibutton_pin_write(false); @@ -113,7 +133,7 @@ void furi_hal_rfid_pins_emulate() { &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); } -void furi_hal_rfid_pins_read() { +static void furi_hal_rfid_pins_read() { // ibutton low furi_hal_ibutton_pin_configure(); furi_hal_ibutton_pin_write(false); @@ -134,6 +154,23 @@ void furi_hal_rfid_pins_read() { furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } +static void furi_hal_rfid_pins_field() { + // ibutton low + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); + + // pull pin to timer out + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false); + + // pull rfid antenna from carrier side + furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_rfid_carrier_out, false); + + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); +} + void furi_hal_rfid_pin_pull_release() { furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, true); } @@ -142,10 +179,10 @@ void furi_hal_rfid_pin_pull_pulldown() { furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false); } -void furi_hal_rfid_tim_read(float freq, float duty_cycle) { - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(FURI_HAL_RFID_READ_TIMER); - FURI_CRITICAL_EXIT(); +void furi_hal_rfid_tim_read_start(float freq, float duty_cycle) { + furi_hal_bus_enable(FURI_HAL_RFID_READ_TIMER_BUS); + + furi_hal_rfid_pins_read(); LL_TIM_InitTypeDef TIM_InitStruct = {0}; TIM_InitStruct.Autoreload = (SystemCoreClock / freq) - 1; @@ -160,23 +197,23 @@ void furi_hal_rfid_tim_read(float freq, float duty_cycle) { FURI_HAL_RFID_READ_TIMER, FURI_HAL_RFID_READ_TIMER_CHANNEL_CONFIG, &TIM_OC_InitStruct); LL_TIM_EnableCounter(FURI_HAL_RFID_READ_TIMER); + + furi_hal_rfid_tim_read_continue(); } -void furi_hal_rfid_tim_read_start() { +void furi_hal_rfid_tim_read_continue() { LL_TIM_EnableAllOutputs(FURI_HAL_RFID_READ_TIMER); } -void furi_hal_rfid_tim_read_stop() { +void furi_hal_rfid_tim_read_pause() { LL_TIM_DisableAllOutputs(FURI_HAL_RFID_READ_TIMER); } -void furi_hal_rfid_tim_emulate(float freq) { - UNUSED(freq); // FIXME - // basic PWM setup with needed freq and internal clock - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); - FURI_CRITICAL_EXIT(); +void furi_hal_rfid_tim_read_stop() { + furi_hal_bus_disable(FURI_HAL_RFID_READ_TIMER_BUS); +} +static void furi_hal_rfid_tim_emulate() { LL_TIM_SetPrescaler(FURI_HAL_RFID_EMULATE_TIMER, 0); LL_TIM_SetCounterMode(FURI_HAL_RFID_EMULATE_TIMER, LL_TIM_COUNTERMODE_UP); LL_TIM_SetAutoReload(FURI_HAL_RFID_EMULATE_TIMER, 1); @@ -201,32 +238,6 @@ void furi_hal_rfid_tim_emulate(float freq) { LL_TIM_GenerateEvent_UPDATE(FURI_HAL_RFID_EMULATE_TIMER); } -static void furi_hal_rfid_emulate_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(FURI_HAL_RFID_EMULATE_TIMER)) { - LL_TIM_ClearFlag_UPDATE(FURI_HAL_RFID_EMULATE_TIMER); - furi_hal_rfid->callback(furi_hal_rfid->context); - } -} - -void furi_hal_rfid_tim_emulate_start(FuriHalRfidEmulateCallback callback, void* context) { - furi_assert(furi_hal_rfid); - - furi_hal_rfid->callback = callback; - furi_hal_rfid->context = context; - - furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, furi_hal_rfid_emulate_isr, NULL); - - LL_TIM_EnableIT_UPDATE(FURI_HAL_RFID_EMULATE_TIMER); - LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); - LL_TIM_EnableCounter(FURI_HAL_RFID_EMULATE_TIMER); -} - -void furi_hal_rfid_tim_emulate_stop() { - LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER); - LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); - furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, NULL, NULL); -} - static void furi_hal_capture_dma_isr(void* context) { UNUSED(context); @@ -247,15 +258,13 @@ static void furi_hal_capture_dma_isr(void* context) { } void furi_hal_rfid_tim_read_capture_start(FuriHalRfidReadCaptureCallback callback, void* context) { - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(RFID_CAPTURE_TIM); - FURI_CRITICAL_EXIT(); - furi_assert(furi_hal_rfid); furi_hal_rfid->read_capture_callback = callback; furi_hal_rfid->context = context; + furi_hal_bus_enable(RFID_CAPTURE_TIM_BUS); + // Timer: base LL_TIM_InitTypeDef TIM_InitStruct = {0}; TIM_InitStruct.Prescaler = 64 - 1; @@ -303,10 +312,7 @@ void furi_hal_rfid_tim_read_capture_stop() { furi_hal_rfid_comp_stop(); furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, NULL, NULL); - - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(RFID_CAPTURE_TIM); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(RFID_CAPTURE_TIM_BUS); } static void furi_hal_rfid_dma_isr() { @@ -341,7 +347,8 @@ void furi_hal_rfid_tim_emulate_dma_start( furi_hal_rfid_pins_emulate(); // configure timer - furi_hal_rfid_tim_emulate(125000); + furi_hal_bus_enable(FURI_HAL_RFID_EMULATE_TIMER_BUS); + furi_hal_rfid_tim_emulate(); LL_TIM_OC_SetPolarity( FURI_HAL_RFID_EMULATE_TIMER, FURI_HAL_RFID_EMULATE_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_EMULATE_TIMER); @@ -405,32 +412,12 @@ void furi_hal_rfid_tim_emulate_dma_stop() { LL_DMA_DeInit(RFID_DMA_CH1_DEF); LL_DMA_DeInit(RFID_DMA_CH2_DEF); - LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); - - FURI_CRITICAL_EXIT(); -} - -void furi_hal_rfid_tim_reset() { - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(FURI_HAL_RFID_READ_TIMER); - LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); + furi_hal_bus_disable(FURI_HAL_RFID_EMULATE_TIMER_BUS); FURI_CRITICAL_EXIT(); } -void furi_hal_rfid_set_emulate_period(uint32_t period) { - LL_TIM_SetAutoReload(FURI_HAL_RFID_EMULATE_TIMER, period); -} - -void furi_hal_rfid_set_emulate_pulse(uint32_t pulse) { -#if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3 - LL_TIM_OC_SetCompareCH3(FURI_HAL_RFID_EMULATE_TIMER, pulse); -#else -#error Update this code. Would you kindly? -#endif -} - void furi_hal_rfid_set_read_period(uint32_t period) { LL_TIM_SetAutoReload(FURI_HAL_RFID_READ_TIMER, period); } @@ -443,12 +430,6 @@ void furi_hal_rfid_set_read_pulse(uint32_t pulse) { #endif } -void furi_hal_rfid_change_read_config(float freq, float duty_cycle) { - uint32_t period = (uint32_t)((SystemCoreClock) / freq) - 1; - furi_hal_rfid_set_read_period(period); - furi_hal_rfid_set_read_pulse(period * duty_cycle); -} - void furi_hal_rfid_comp_start() { LL_COMP_Enable(COMP1); // Magic @@ -483,4 +464,125 @@ void COMP_IRQHandler() { (LL_COMP_ReadOutputLevel(COMP1) == LL_COMP_OUTPUT_LEVEL_LOW), furi_hal_rfid_comp_callback_context); } +} + +static void furi_hal_rfid_field_tim_setup() { + // setup timer counter + furi_hal_bus_enable(FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS); + + LL_TIM_SetPrescaler(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + LL_TIM_SetCounterMode(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0xFFFFFFFF); + LL_TIM_DisableARRPreload(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_SetRepetitionCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + + LL_TIM_SetClockDivision(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); + LL_TIM_ConfigETR( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + LL_TIM_ETR_POLARITY_INVERTED, + LL_TIM_ETR_PRESCALER_DIV1, + LL_TIM_ETR_FILTER_FDIV1); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 1; + LL_TIM_OC_Init( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL, + &TIM_OC_InitStruct); + + LL_TIM_GenerateEvent_UPDATE(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_OC_SetPolarity( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL, + LL_TIM_OCPOLARITY_HIGH); + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + // setup timer timeouts dma + furi_hal_bus_enable(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS); + + LL_TIM_SetPrescaler(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 64000 - 1); + LL_TIM_SetCounterMode(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 100 - 1); // 100 ms + LL_TIM_SetClockDivision(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); + + LL_TIM_DisableARRPreload(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + LL_TIM_GenerateEvent_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); +} + +void furi_hal_rfid_field_detect_start(void) { + // setup pins + furi_hal_rfid_pins_field(); + + // configure timer + furi_hal_rfid_field_tim_setup(); + + // configure DMA "TIM_COUNTER_CNT -> counter" + LL_DMA_SetMemoryAddress(RFID_DMA_CH1_DEF, (uint32_t) & (furi_hal_rfid->field.counter)); + LL_DMA_SetPeriphAddress( + RFID_DMA_CH1_DEF, (uint32_t) & (FURI_HAL_RFID_FIELD_COUNTER_TIMER->CNT)); + LL_DMA_ConfigTransfer( + RFID_DMA_CH1_DEF, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_MEDIUM); + LL_DMA_SetDataLength(RFID_DMA_CH1_DEF, 1); + LL_DMA_SetPeriphRequest(RFID_DMA_CH1_DEF, FURI_HAL_RFID_FIELD_DMAMUX_DMA); + LL_DMA_EnableChannel(RFID_DMA_CH1_DEF); + + // configure DMA "mem -> TIM_COUNTER_CNT" + LL_DMA_SetMemoryAddress( + RFID_DMA_CH2_DEF, (uint32_t) & (furi_hal_rfid->field.set_tim_counter_cnt)); + LL_DMA_SetPeriphAddress( + RFID_DMA_CH2_DEF, (uint32_t) & (FURI_HAL_RFID_FIELD_COUNTER_TIMER->CNT)); + LL_DMA_ConfigTransfer( + RFID_DMA_CH2_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_LOW); + LL_DMA_SetDataLength(RFID_DMA_CH2_DEF, 1); + LL_DMA_SetPeriphRequest(RFID_DMA_CH2_DEF, FURI_HAL_RFID_FIELD_DMAMUX_DMA); + LL_DMA_EnableChannel(RFID_DMA_CH2_DEF); + + // start tim counter + LL_TIM_EnableAllOutputs(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + LL_TIM_SetCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + // start tim timeout + LL_TIM_SetCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + LL_TIM_EnableIT_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); +} + +void furi_hal_rfid_field_detect_stop(void) { + LL_TIM_DisableCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_DisableAllOutputs(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + LL_TIM_DisableCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + + FURI_CRITICAL_ENTER(); + + LL_DMA_DeInit(RFID_DMA_CH1_DEF); + LL_DMA_DeInit(RFID_DMA_CH2_DEF); + + furi_hal_bus_disable(FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS); + furi_hal_bus_disable(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS); + + furi_hal_rfid_pins_reset(); + + FURI_CRITICAL_EXIT(); +} + +bool furi_hal_rfid_field_is_present(uint32_t* frequency) { + *frequency = furi_hal_rfid->field.counter * 10; + return ( + (*frequency >= FURI_HAL_RFID_FIELD_FREQUENCY_MIN) && + (*frequency <= FURI_HAL_RFID_FIELD_FREQUENCY_MAX)); } \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.h b/firmware/targets/f7/furi_hal/furi_hal_rfid.h index 36563c1d16ac..7087ba991fac 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.h +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.h @@ -21,14 +21,6 @@ void furi_hal_rfid_init(); */ void furi_hal_rfid_pins_reset(); -/** Config rfid pins to emulate state - */ -void furi_hal_rfid_pins_emulate(); - -/** Config rfid pins to read state - */ -void furi_hal_rfid_pins_read(); - /** Release rfid pull pin */ void furi_hal_rfid_pin_pull_release(); @@ -37,32 +29,23 @@ void furi_hal_rfid_pin_pull_release(); */ void furi_hal_rfid_pin_pull_pulldown(); -/** Config rfid timer to read state - * +/** Start read timer * @param freq timer frequency * @param duty_cycle timer duty cycle, 0.0-1.0 */ -void furi_hal_rfid_tim_read(float freq, float duty_cycle); - -/** Start read timer - */ -void furi_hal_rfid_tim_read_start(); +void furi_hal_rfid_tim_read_start(float freq, float duty_cycle); -/** Stop read timer +/** Pause read timer, to be able to continue later */ -void furi_hal_rfid_tim_read_stop(); +void furi_hal_rfid_tim_read_pause(); -/** Config rfid timer to emulate state - * - * @param freq timer frequency +/** Continue read timer */ -void furi_hal_rfid_tim_emulate(float freq); +void furi_hal_rfid_tim_read_continue(); -typedef void (*FuriHalRfidEmulateCallback)(void* context); - -/** Start emulation timer +/** Stop read timer */ -void furi_hal_rfid_tim_emulate_start(FuriHalRfidEmulateCallback callback, void* context); +void furi_hal_rfid_tim_read_stop(); typedef void (*FuriHalRfidReadCaptureCallback)(bool level, uint32_t duration, void* context); @@ -81,26 +64,6 @@ void furi_hal_rfid_tim_emulate_dma_start( void furi_hal_rfid_tim_emulate_dma_stop(); -/** Stop emulation timer - */ -void furi_hal_rfid_tim_emulate_stop(); - -/** Config rfid timers to reset state - */ -void furi_hal_rfid_tim_reset(); - -/** Set emulation timer period - * - * @param period overall duration - */ -void furi_hal_rfid_set_emulate_period(uint32_t period); - -/** Set emulation timer pulse - * - * @param pulse duration of high level - */ -void furi_hal_rfid_set_emulate_pulse(uint32_t pulse); - /** Set read timer period * * @param period overall duration @@ -113,13 +76,6 @@ void furi_hal_rfid_set_read_period(uint32_t period); */ void furi_hal_rfid_set_read_pulse(uint32_t pulse); -/** Сhanges the configuration of the RFID timer "on a fly" - * - * @param freq new frequency - * @param duty_cycle new duty cycle - */ -void furi_hal_rfid_change_read_config(float freq, float duty_cycle); - /** Start/Enable comparator */ void furi_hal_rfid_comp_start(); @@ -131,6 +87,20 @@ typedef void (*FuriHalRfidCompCallback)(bool level, void* context); /** Set comparator callback */ void furi_hal_rfid_comp_set_callback(FuriHalRfidCompCallback callback, void* context); +/** Start/Enable Field Presence detect */ +void furi_hal_rfid_field_detect_start(); + +/** Stop/Disable Field Presence detect */ +void furi_hal_rfid_field_detect_stop(); + +/** Check Field Presence + * + * @param[out] frequency pointer to frequency value to be set if filed detected + * + * @return true if field is present, false if not + */ +bool furi_hal_rfid_field_is_present(uint32_t* frequency); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index 7bd45c35d71d..a8e25faad66c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -2,8 +2,8 @@ #include #include -#include #include +#include #include #include #include @@ -44,10 +44,8 @@ _Static_assert(sizeof(SystemReg) == 4, "SystemReg size mismatch"); #define FURI_HAL_RTC_SECONDS_PER_DAY (FURI_HAL_RTC_SECONDS_PER_HOUR * 24) #define FURI_HAL_RTC_MONTHS_COUNT 12 #define FURI_HAL_RTC_EPOCH_START_YEAR 1970 -#define FURI_HAL_RTC_IS_LEAP_YEAR(year) \ - ((((year) % 4 == 0) && ((year) % 100 != 0)) || ((year) % 400 == 0)) -static const uint8_t furi_hal_rtc_days_per_month[][FURI_HAL_RTC_MONTHS_COUNT] = { +static const uint8_t furi_hal_rtc_days_per_month[2][FURI_HAL_RTC_MONTHS_COUNT] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; @@ -395,7 +393,7 @@ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { uint8_t leap_years = 0; for(uint16_t y = FURI_HAL_RTC_EPOCH_START_YEAR; y < datetime->year; y++) { - if(FURI_HAL_RTC_IS_LEAP_YEAR(y)) { + if(furi_hal_rtc_is_leap_year(y)) { leap_years++; } else { years++; @@ -406,10 +404,10 @@ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { ((years * furi_hal_rtc_days_per_year[0]) + (leap_years * furi_hal_rtc_days_per_year[1])) * FURI_HAL_RTC_SECONDS_PER_DAY; - uint8_t year_index = (FURI_HAL_RTC_IS_LEAP_YEAR(datetime->year)) ? 1 : 0; + bool leap_year = furi_hal_rtc_is_leap_year(datetime->year); - for(uint8_t m = 0; m < (datetime->month - 1); m++) { - timestamp += furi_hal_rtc_days_per_month[year_index][m] * FURI_HAL_RTC_SECONDS_PER_DAY; + for(uint8_t m = 1; m < datetime->month; m++) { + timestamp += furi_hal_rtc_get_days_per_month(leap_year, m) * FURI_HAL_RTC_SECONDS_PER_DAY; } timestamp += (datetime->day - 1) * FURI_HAL_RTC_SECONDS_PER_DAY; @@ -419,3 +417,15 @@ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { return timestamp; } + +uint16_t furi_hal_rtc_get_days_per_year(uint16_t year) { + return furi_hal_rtc_days_per_year[furi_hal_rtc_is_leap_year(year) ? 1 : 0]; +} + +bool furi_hal_rtc_is_leap_year(uint16_t year) { + return (((year) % 4 == 0) && ((year) % 100 != 0)) || ((year) % 400 == 0); +} + +uint8_t furi_hal_rtc_get_days_per_month(bool leap_year, uint8_t month) { + return furi_hal_rtc_days_per_month[leap_year ? 1 : 0][month - 1]; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_speaker.c b/firmware/targets/f7/furi_hal/furi_hal_speaker.c index 5421509cca51..ad7ed994af28 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_speaker.c +++ b/firmware/targets/f7/furi_hal/furi_hal_speaker.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -20,16 +21,11 @@ static FuriMutex* furi_hal_speaker_mutex = NULL; void furi_hal_speaker_init() { furi_assert(furi_hal_speaker_mutex == NULL); furi_hal_speaker_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(FURI_HAL_SPEAKER_TIMER); - FURI_CRITICAL_EXIT(); FURI_LOG_I(TAG, "Init OK"); } void furi_hal_speaker_deinit() { furi_check(furi_hal_speaker_mutex != NULL); - LL_TIM_DeInit(FURI_HAL_SPEAKER_TIMER); - furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_mutex_free(furi_hal_speaker_mutex); furi_hal_speaker_mutex = NULL; } @@ -39,6 +35,7 @@ bool furi_hal_speaker_acquire(uint32_t timeout) { if(furi_mutex_acquire(furi_hal_speaker_mutex, timeout) == FuriStatusOk) { furi_hal_power_insomnia_enter(); + furi_hal_bus_enable(FuriHalBusTIM16); furi_hal_gpio_init_ex( &gpio_speaker, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); return true; @@ -53,6 +50,8 @@ void furi_hal_speaker_release() { furi_hal_speaker_stop(); furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + + furi_hal_bus_disable(FuriHalBusTIM16); furi_hal_power_insomnia_exit(); furi_check(furi_mutex_release(furi_hal_speaker_mutex) == FuriStatusOk); diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index 42b854799557..17769832b6ad 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -12,10 +12,10 @@ #define TAG "FuriHalSpi" #define SPI_DMA DMA2 -#define SPI_DMA_RX_CHANNEL LL_DMA_CHANNEL_3 -#define SPI_DMA_TX_CHANNEL LL_DMA_CHANNEL_4 -#define SPI_DMA_RX_IRQ FuriHalInterruptIdDma2Ch3 -#define SPI_DMA_TX_IRQ FuriHalInterruptIdDma2Ch4 +#define SPI_DMA_RX_CHANNEL LL_DMA_CHANNEL_6 +#define SPI_DMA_TX_CHANNEL LL_DMA_CHANNEL_7 +#define SPI_DMA_RX_IRQ FuriHalInterruptIdDma2Ch6 +#define SPI_DMA_TX_IRQ FuriHalInterruptIdDma2Ch7 #define SPI_DMA_RX_DEF SPI_DMA, SPI_DMA_RX_CHANNEL #define SPI_DMA_TX_DEF SPI_DMA, SPI_DMA_TX_CHANNEL @@ -170,18 +170,18 @@ bool furi_hal_spi_bus_trx( } static void spi_dma_isr() { -#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 - if(LL_DMA_IsActiveFlag_TC3(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_RX_DEF)) { - LL_DMA_ClearFlag_TC3(SPI_DMA); +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6 + if(LL_DMA_IsActiveFlag_TC6(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_RX_DEF)) { + LL_DMA_ClearFlag_TC6(SPI_DMA); furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); } #else #error Update this code. Would you kindly? #endif -#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 - if(LL_DMA_IsActiveFlag_TC4(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_TX_DEF)) { - LL_DMA_ClearFlag_TC4(SPI_DMA); +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7 + if(LL_DMA_IsActiveFlag_TC7(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_TX_DEF)) { + LL_DMA_ClearFlag_TC7(SPI_DMA); furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); } #else @@ -241,8 +241,8 @@ bool furi_hal_spi_bus_trx_dma( dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); -#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 - LL_DMA_ClearFlag_TC4(SPI_DMA); +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7 + LL_DMA_ClearFlag_TC7(SPI_DMA); #else #error Update this code. Would you kindly? #endif @@ -315,8 +315,8 @@ bool furi_hal_spi_bus_trx_dma( dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config); -#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 - LL_DMA_ClearFlag_TC3(SPI_DMA); +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6 + LL_DMA_ClearFlag_TC6(SPI_DMA); #else #error Update this code. Would you kindly? #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c index 9cf332dacd6c..757ac23661d3 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #define TAG "FuriHalSpiConfig" @@ -100,28 +101,17 @@ void furi_hal_spi_config_init() { static void furi_hal_spi_bus_r_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { if(event == FuriHalSpiBusEventInit) { furi_hal_spi_bus_r_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); bus->current_handle = NULL; } else if(event == FuriHalSpiBusEventDeinit) { furi_mutex_free(furi_hal_spi_bus_r_mutex); - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); - LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); } else if(event == FuriHalSpiBusEventLock) { furi_check(furi_mutex_acquire(furi_hal_spi_bus_r_mutex, FuriWaitForever) == FuriStatusOk); } else if(event == FuriHalSpiBusEventUnlock) { furi_check(furi_mutex_release(furi_hal_spi_bus_r_mutex) == FuriStatusOk); } else if(event == FuriHalSpiBusEventActivate) { - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); + furi_hal_bus_enable(FuriHalBusSPI1); } else if(event == FuriHalSpiBusEventDeactivate) { - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(FuriHalBusSPI1); } } @@ -135,28 +125,17 @@ FuriMutex* furi_hal_spi_bus_d_mutex = NULL; static void furi_hal_spi_bus_d_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { if(event == FuriHalSpiBusEventInit) { furi_hal_spi_bus_d_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); bus->current_handle = NULL; } else if(event == FuriHalSpiBusEventDeinit) { furi_mutex_free(furi_hal_spi_bus_d_mutex); - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); } else if(event == FuriHalSpiBusEventLock) { furi_check(furi_mutex_acquire(furi_hal_spi_bus_d_mutex, FuriWaitForever) == FuriStatusOk); } else if(event == FuriHalSpiBusEventUnlock) { furi_check(furi_mutex_release(furi_hal_spi_bus_d_mutex) == FuriStatusOk); } else if(event == FuriHalSpiBusEventActivate) { - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); + furi_hal_bus_enable(FuriHalBusSPI2); } else if(event == FuriHalSpiBusEventDeactivate) { - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(FuriHalBusSPI2); } } @@ -213,6 +192,52 @@ inline static void furi_hal_spi_bus_r_handle_event_callback( } } +inline static void furi_hal_spi_bus_external_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventDeinit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullDown, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullDown, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullDown, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + + furi_hal_gpio_write(handle->cs, false); + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_write(handle->cs, true); + + furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_SPI_Disable(handle->bus->spi); + } +} + inline static void furi_hal_spi_bus_nfc_handle_event_callback( FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event, @@ -312,7 +337,8 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc = { static void furi_hal_spi_bus_handle_external_event_callback( FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { - furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); + furi_hal_spi_bus_external_handle_event_callback( + handle, event, &furi_hal_spi_preset_1edge_low_2m); } FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_types.h b/firmware/targets/f7/furi_hal/furi_hal_spi_types.h index d2273f38ba5e..ecc18d50db25 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_types.h +++ b/firmware/targets/f7/furi_hal/furi_hal_spi_types.h @@ -6,8 +6,6 @@ #include #include -#include -#include #ifdef __cplusplus extern "C" { diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 59646461352a..ac5adefb8dfd 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -1,12 +1,12 @@ #include -#include - +#include #include #include #include #include #include #include +#include #include @@ -26,17 +26,36 @@ static uint32_t furi_hal_subghz_debug_gpio_buff[2]; #define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL #define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL +/** SubGhz state */ +typedef enum { + SubGhzStateInit, /**< Init pending */ + + SubGhzStateIdle, /**< Idle, energy save mode */ + + SubGhzStateAsyncRx, /**< Async RX started */ + + SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ + SubGhzStateAsyncTxLast, /**< Async TX continue, DMA completed and timer got last value to go */ + SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ + +} SubGhzState; + +/** SubGhz regulation, receive transmission on the current frequency for the + * region */ +typedef enum { + SubGhzRegulationOnlyRx, /**only Rx*/ + SubGhzRegulationTxRx, /**TxRx*/ +} SubGhzRegulation; + typedef struct { volatile SubGhzState state; volatile SubGhzRegulation regulation; - volatile FuriHalSubGhzPreset preset; const GpioPin* async_mirror_pin; } FuriHalSubGhz; volatile FuriHalSubGhz furi_hal_subghz = { .state = SubGhzStateInit, .regulation = SubGhzRegulationTxRx, - .preset = FuriHalSubGhzPresetIDLE, .async_mirror_pin = NULL, }; @@ -44,10 +63,13 @@ void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin) { furi_hal_subghz.async_mirror_pin = pin; } +const GpioPin* furi_hal_subghz_get_data_gpio() { + return &gpio_cc1101_g0; +} + void furi_hal_subghz_init() { furi_assert(furi_hal_subghz.state == SubGhzStateInit); furi_hal_subghz.state = SubGhzStateIdle; - furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); @@ -101,8 +123,6 @@ void furi_hal_subghz_sleep() { cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - - furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; } void furi_hal_subghz_dump_state() { @@ -114,34 +134,7 @@ void furi_hal_subghz_dump_state() { furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } -void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { - if(preset == FuriHalSubGhzPresetOok650Async) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable); - } else if(preset == FuriHalSubGhzPresetOok270Async) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable); - } else if(preset == FuriHalSubGhzPreset2FSKDev238Async) { - furi_hal_subghz_load_registers( - (uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); - } else if(preset == FuriHalSubGhzPreset2FSKDev476Async) { - furi_hal_subghz_load_registers( - (uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); - } else if(preset == FuriHalSubGhzPresetMSK99_97KbAsync) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_msk_99_97kb_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_msk_async_patable); - } else if(preset == FuriHalSubGhzPresetGFSK9_99KbAsync) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_gfsk_9_99kb_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_gfsk_async_patable); - } else { - furi_crash("SubGhz: Missing config."); - } - furi_hal_subghz.preset = preset; -} - -void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { +void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data) { //load config furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_reset(&furi_hal_spi_bus_handle_subghz); @@ -156,7 +149,6 @@ void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { //load pa table memcpy(&pa[0], &preset_data[i + 2], 8); furi_hal_subghz_load_patable(pa); - furi_hal_subghz.preset = FuriHalSubGhzPresetCustom; //show debug if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { @@ -172,7 +164,7 @@ void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { } } -void furi_hal_subghz_load_registers(uint8_t* data) { +void furi_hal_subghz_load_registers(const uint8_t* data) { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_reset(&furi_hal_spi_bus_handle_subghz); uint32_t i = 0; @@ -433,6 +425,8 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* furi_hal_gpio_init_ex( &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + furi_hal_bus_enable(FuriHalBusTIM2); + // Timer: base LL_TIM_InitTypeDef TIM_InitStruct = {0}; TIM_InitStruct.Prescaler = 64 - 1; @@ -496,7 +490,7 @@ void furi_hal_subghz_stop_async_rx() { furi_hal_subghz_idle(); FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(TIM2); + furi_hal_bus_disable(FuriHalBusTIM2); // Stop debug furi_hal_subghz_stop_debug(); @@ -658,6 +652,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_DMA_EnableIT_HT(SUBGHZ_DMA_CH1_DEF); LL_DMA_EnableChannel(SUBGHZ_DMA_CH1_DEF); + furi_hal_bus_enable(FuriHalBusTIM2); + // Configure TIM2 LL_TIM_InitTypeDef TIM_InitStruct = {0}; TIM_InitStruct.Prescaler = 64 - 1; @@ -740,7 +736,7 @@ void furi_hal_subghz_stop_async_tx() { // Deinitialize Timer FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(TIM2); + furi_hal_bus_disable(FuriHalBusTIM2); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); // Deinitialize DMA diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index 102981dbeb58..855ce31619ce 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -5,6 +5,8 @@ #pragma once +#include + #include #include #include @@ -20,18 +22,6 @@ extern "C" { #define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2) #define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 999 -/** Radio Presets */ -typedef enum { - FuriHalSubGhzPresetIDLE, /**< default configuration */ - FuriHalSubGhzPresetOok270Async, /**< OOK, bandwidth 270kHz, asynchronous */ - FuriHalSubGhzPresetOok650Async, /**< OOK, bandwidth 650kHz, asynchronous */ - FuriHalSubGhzPreset2FSKDev238Async, /**< FM, deviation 2.380371 kHz, asynchronous */ - FuriHalSubGhzPreset2FSKDev476Async, /**< FM, deviation 47.60742 kHz, asynchronous */ - FuriHalSubGhzPresetMSK99_97KbAsync, /**< MSK, deviation 47.60742 kHz, 99.97Kb/s, asynchronous */ - FuriHalSubGhzPresetGFSK9_99KbAsync, /**< GFSK, deviation 19.042969 kHz, 9.996Kb/s, asynchronous */ - FuriHalSubGhzPresetCustom, /**Custom Preset*/ -} FuriHalSubGhzPreset; - /** Switchable Radio Paths */ typedef enum { FuriHalSubGhzPathIsolate, /**< Isolate Radio from antenna */ @@ -40,27 +30,6 @@ typedef enum { FuriHalSubGhzPath868, /**< Center Frequency: 868MHz. Path 3: SW1RF3-SW2RF3, LCLC */ } FuriHalSubGhzPath; -/** SubGhz state */ -typedef enum { - SubGhzStateInit, /**< Init pending */ - - SubGhzStateIdle, /**< Idle, energy save mode */ - - SubGhzStateAsyncRx, /**< Async RX started */ - - SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzStateAsyncTxLast, /**< Async TX continue, DMA completed and timer got last value to go */ - SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ - -} SubGhzState; - -/** SubGhz regulation, receive transmission on the current frequency for the - * region */ -typedef enum { - SubGhzRegulationOnlyRx, /**only Rx*/ - SubGhzRegulationTxRx, /**TxRx*/ -} SubGhzRegulation; - /* Mirror RX/TX async modulation signal to specified pin * * @warning Configures pin to output mode. Make sure it is not connected @@ -70,6 +39,12 @@ typedef enum { */ void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin); +/** Get data GPIO + * + * @return pointer to the gpio pin structure + */ +const GpioPin* furi_hal_subghz_get_data_gpio(); + /** Initialize and switch to power save mode Used by internal API-HAL * initialization routine Can be used to reinitialize device to safe state and * send it to sleep @@ -84,23 +59,17 @@ void furi_hal_subghz_sleep(); */ void furi_hal_subghz_dump_state(); -/** Load registers from preset by preset name - * - * @param preset to load - */ -void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset); - /** Load custom registers from preset * * @param preset_data registers to load */ -void furi_hal_subghz_load_custom_preset(uint8_t* preset_data); +void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data); /** Load registers * * @param data Registers data */ -void furi_hal_subghz_load_registers(uint8_t* data); +void furi_hal_subghz_load_registers(const uint8_t* data); /** Load PATABLE * diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h b/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h deleted file mode 100644 index 5ea17b6ddd0d..000000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h +++ /dev/null @@ -1,314 +0,0 @@ -#pragma once - -#include - -static const uint8_t furi_hal_subghz_preset_ook_270khz_async_regs[][2] = { - // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration - - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - - /* FIFO and internals */ - {CC1101_FIFOTHR, 0x47}, // The only important bit is ADC_RETENTION, FIFO Tx=33 Rx=32 - - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - - /* Frequency Synthesizer Control */ - {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - - // Modem Configuration - {CC1101_MDMCFG0, 0x00}, // Channel spacing is 25kHz - {CC1101_MDMCFG1, 0x00}, // Channel spacing is 25kHz - {CC1101_MDMCFG2, 0x30}, // Format ASK/OOK, No preamble/sync - {CC1101_MDMCFG3, 0x32}, // Data rate is 3.79372 kBaud - {CC1101_MDMCFG4, 0x67}, // Rx BW filter is 270.833333kHz - - /* Main Radio Control State Machine */ - {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) - - /* Frequency Offset Compensation Configuration */ - {CC1101_FOCCFG, - 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off - - /* Automatic Gain Control */ - {CC1101_AGCCTRL0, - 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCCTRL1, - 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB - - /* Wake on radio and timeouts control */ - {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours - - /* Frontend configuration */ - {CC1101_FREND0, 0x11}, // Adjusts current TX LO buffer + high is PATABLE[1] - {CC1101_FREND1, 0xB6}, // - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_ook_650khz_async_regs[][2] = { - // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration - - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - - /* FIFO and internals */ - {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION - - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - - /* Frequency Synthesizer Control */ - {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - - // Modem Configuration - {CC1101_MDMCFG0, 0x00}, // Channel spacing is 25kHz - {CC1101_MDMCFG1, 0x00}, // Channel spacing is 25kHz - {CC1101_MDMCFG2, 0x30}, // Format ASK/OOK, No preamble/sync - {CC1101_MDMCFG3, 0x32}, // Data rate is 3.79372 kBaud - {CC1101_MDMCFG4, 0x17}, // Rx BW filter is 650.000kHz - - /* Main Radio Control State Machine */ - {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) - - /* Frequency Offset Compensation Configuration */ - {CC1101_FOCCFG, - 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off - - /* Automatic Gain Control */ - // {CC1101_AGCTRL0,0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - // {CC1101_AGCTRL1,0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - // {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB - //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. - {CC1101_AGCCTRL0, - 0x91}, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary - {CC1101_AGCCTRL1, - 0x0}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB - - /* Wake on radio and timeouts control */ - {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours - - /* Frontend configuration */ - {CC1101_FREND0, 0x11}, // Adjusts current TX LO buffer + high is PATABLE[1] - {CC1101_FREND1, 0xB6}, // - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs[][2] = { - - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - - /* Frequency Synthesizer Control */ - {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - {CC1101_PKTCTRL1, 0x04}, - - // // Modem Configuration - {CC1101_MDMCFG0, 0x00}, - {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) - {CC1101_MDMCFG3, 0x83}, // Data rate is 4.79794 kBaud - {CC1101_MDMCFG4, 0x67}, //Rx BW filter is 270.833333 kHz - {CC1101_DEVIATN, 0x04}, //Deviation 2.380371 kHz - - /* Main Radio Control State Machine */ - {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) - - /* Frequency Offset Compensation Configuration */ - {CC1101_FOCCFG, - 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off - - /* Automatic Gain Control */ - {CC1101_AGCCTRL0, - 0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary - {CC1101_AGCCTRL1, - 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB - - /* Wake on radio and timeouts control */ - {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours - - /* Frontend configuration */ - {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer - {CC1101_FREND1, 0x56}, - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs[][2] = { - - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - - /* Frequency Synthesizer Control */ - {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - {CC1101_PKTCTRL1, 0x04}, - - // // Modem Configuration - {CC1101_MDMCFG0, 0x00}, - {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) - {CC1101_MDMCFG3, 0x83}, // Data rate is 4.79794 kBaud - {CC1101_MDMCFG4, 0x67}, //Rx BW filter is 270.833333 kHz - {CC1101_DEVIATN, 0x47}, //Deviation 47.60742 kHz - - /* Main Radio Control State Machine */ - {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) - - /* Frequency Offset Compensation Configuration */ - {CC1101_FOCCFG, - 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off - - /* Automatic Gain Control */ - {CC1101_AGCCTRL0, - 0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary - {CC1101_AGCCTRL1, - 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB - - /* Wake on radio and timeouts control */ - {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours - - /* Frontend configuration */ - {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer - {CC1101_FREND1, 0x56}, - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_msk_99_97kb_async_regs[][2] = { - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x06}, - - {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION - {CC1101_SYNC1, 0x46}, - {CC1101_SYNC0, 0x4C}, - {CC1101_ADDR, 0x00}, - {CC1101_PKTLEN, 0x00}, - {CC1101_CHANNR, 0x00}, - - {CC1101_PKTCTRL0, 0x05}, - - {CC1101_FSCTRL0, 0x23}, - {CC1101_FSCTRL1, 0x06}, - - {CC1101_MDMCFG0, 0xF8}, - {CC1101_MDMCFG1, 0x22}, - {CC1101_MDMCFG2, 0x72}, - {CC1101_MDMCFG3, 0xF8}, - {CC1101_MDMCFG4, 0x5B}, - {CC1101_DEVIATN, 0x47}, - - {CC1101_MCSM0, 0x18}, - {CC1101_FOCCFG, 0x16}, - - {CC1101_AGCCTRL0, 0xB2}, - {CC1101_AGCCTRL1, 0x00}, - {CC1101_AGCCTRL2, 0xC7}, - - {CC1101_FREND0, 0x10}, - {CC1101_FREND1, 0x56}, - - {CC1101_BSCFG, 0x1C}, - {CC1101_FSTEST, 0x59}, - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_gfsk_9_99kb_async_regs[][2] = { - - {CC1101_IOCFG0, 0x06}, //GDO0 Output Pin Configuration - {CC1101_FIFOTHR, 0x47}, //RX FIFO and TX FIFO Thresholds - - //1 : CRC calculation in TX and CRC check in RX enabled, - //1 : Variable packet length mode. Packet length configured by the first byte after sync word - {CC1101_PKTCTRL0, 0x05}, - - {CC1101_FSCTRL1, 0x06}, //Frequency Synthesizer Control - - {CC1101_SYNC1, 0x46}, - {CC1101_SYNC0, 0x4C}, - {CC1101_ADDR, 0x00}, - {CC1101_PKTLEN, 0x00}, - - {CC1101_MDMCFG4, 0xC8}, //Modem Configuration 9.99 - {CC1101_MDMCFG3, 0x93}, //Modem Configuration - {CC1101_MDMCFG2, 0x12}, // 2: 16/16 sync word bits detected - - {CC1101_DEVIATN, 0x34}, //Deviation = 19.042969 - {CC1101_MCSM0, 0x18}, //Main Radio Control State Machine Configuration - {CC1101_FOCCFG, 0x16}, //Frequency Offset Compensation Configuration - - {CC1101_AGCCTRL2, 0x43}, //AGC Control - {CC1101_AGCCTRL1, 0x40}, - {CC1101_AGCCTRL0, 0x91}, - - {CC1101_WORCTRL, 0xFB}, //Wake On Radio Control - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_ook_async_patable[8] = { - 0x00, - 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_ook_async_patable_au[8] = { - 0x00, - 0x37, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = { - 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_msk_async_patable[8] = { - 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_gfsk_async_patable[8] = { - 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; diff --git a/firmware/targets/f7/furi_hal/furi_hal_target_hw.h b/firmware/targets/f7/furi_hal/furi_hal_target_hw.h index 128122f84392..2d25a80c3e2b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_target_hw.h +++ b/firmware/targets/f7/furi_hal/furi_hal_target_hw.h @@ -3,4 +3,4 @@ #include #include #include -#include +#include diff --git a/firmware/targets/f7/furi_hal/furi_hal_uart.c b/firmware/targets/f7/furi_hal/furi_hal_uart.c index 71b5c7ba0444..209c6be6a2ed 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_uart.c +++ b/firmware/targets/f7/furi_hal/furi_hal_uart.c @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -13,6 +14,9 @@ static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); static void* irq_ctx[2]; static void furi_hal_usart_init(uint32_t baud) { + furi_hal_bus_enable(FuriHalBusUSART1); + LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); + furi_hal_gpio_init_ex( &gpio_usart_tx, GpioModeAltFunctionPushPull, @@ -50,6 +54,9 @@ static void furi_hal_usart_init(uint32_t baud) { } static void furi_hal_lpuart_init(uint32_t baud) { + furi_hal_bus_enable(FuriHalBusLPUART1); + LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); + furi_hal_gpio_init_ex( &gpio_ext_pc0, GpioModeAltFunctionPushPull, @@ -86,10 +93,11 @@ static void furi_hal_lpuart_init(uint32_t baud) { } void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud) { - if(ch == FuriHalUartIdLPUART1) + if(ch == FuriHalUartIdLPUART1) { furi_hal_lpuart_init(baud); - else if(ch == FuriHalUartIdUSART1) + } else if(ch == FuriHalUartIdUSART1) { furi_hal_usart_init(baud); + } } void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { @@ -126,11 +134,15 @@ void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { void furi_hal_uart_deinit(FuriHalUartId ch) { furi_hal_uart_set_irq_cb(ch, NULL, NULL); if(ch == FuriHalUartIdUSART1) { - LL_USART_Disable(USART1); + if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) { + furi_hal_bus_disable(FuriHalBusUSART1); + } furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } else if(ch == FuriHalUartIdLPUART1) { - LL_LPUART_Disable(LPUART1); + if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) { + furi_hal_bus_disable(FuriHalBusLPUART1); + } furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c index 011add953238..b88168d5d0ca 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb.c @@ -2,7 +2,9 @@ #include #include #include + #include +#include #include #include @@ -86,6 +88,8 @@ static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep); /* Low-level init */ void furi_hal_usb_init(void) { + LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1); + LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; LL_PWR_EnableVddUSB(); @@ -98,7 +102,10 @@ void furi_hal_usb_init(void) { LL_GPIO_Init(GPIOA, &GPIO_InitStruct); usbd_init(&udev, &usbd_hw, USB_EP0_SIZE, ubuf, sizeof(ubuf)); + + FURI_CRITICAL_ENTER(); usbd_enable(&udev, true); + FURI_CRITICAL_EXIT(); usbd_reg_descr(&udev, usb_descriptor_get); usbd_reg_event(&udev, usbd_evt_susp, susp_evt); @@ -359,8 +366,10 @@ static void usb_process_mode_reinit() { usbd_connect(&udev, false); usb.enabled = false; + FURI_CRITICAL_ENTER(); usbd_enable(&udev, false); usbd_enable(&udev, true); + FURI_CRITICAL_EXIT(); furi_delay_ms(USB_RECONNECT_DELAY); usb_process_mode_start(usb.interface, usb.interface_context); diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c index d27613410bdc..334aa010264b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -436,7 +436,11 @@ static bool hid_send_report(uint8_t report_id) { if((hid_semaphore == NULL) || (hid_connected == false)) return false; if((boot_protocol == true) && (report_id != ReportIdKeyboard)) return false; - furi_check(furi_semaphore_acquire(hid_semaphore, FuriWaitForever) == FuriStatusOk); + FuriStatus status = furi_semaphore_acquire(hid_semaphore, HID_INTERVAL * 2); + if(status == FuriStatusErrorTimeout) { + return false; + } + furi_check(status == FuriStatusOk); if(hid_connected == false) { return false; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_version_device.c b/firmware/targets/f7/furi_hal/furi_hal_version_device.c index c059c2cbe692..be3f4bf3f3c7 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version_device.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version_device.c @@ -19,3 +19,7 @@ const char* furi_hal_version_get_fcc_id() { const char* furi_hal_version_get_ic_id() { return "27624-FZ"; } + +const char* furi_hal_version_get_mic_id() { + return "210-175991"; +} diff --git a/firmware/targets/f7/platform_specific/intrinsic_export.h b/firmware/targets/f7/platform_specific/intrinsic_export.h index 8dbc4bd03209..d3c7be5e0429 100644 --- a/firmware/targets/f7/platform_specific/intrinsic_export.h +++ b/firmware/targets/f7/platform_specific/intrinsic_export.h @@ -1,10 +1,13 @@ +#pragma once #include +#include #ifdef __cplusplus extern "C" { #endif void __clear_cache(void*, void*); +void* __aeabi_uldivmod(uint64_t, uint64_t); #ifdef __cplusplus } diff --git a/firmware/targets/f7/platform_specific/math_wrapper.h b/firmware/targets/f7/platform_specific/math_wrapper.h new file mode 100644 index 000000000000..83f5a8b75d3a --- /dev/null +++ b/firmware/targets/f7/platform_specific/math_wrapper.h @@ -0,0 +1,2 @@ +#pragma once +#include \ No newline at end of file diff --git a/firmware/targets/f7/src/update.c b/firmware/targets/f7/src/update.c index c1e1084c218a..c6235a150f6d 100644 --- a/firmware/targets/f7/src/update.c +++ b/firmware/targets/f7/src/update.c @@ -38,6 +38,12 @@ static bool flipper_update_mount_sd() { } static bool flipper_update_init() { + // TODO: Configure missing peripherals properly + furi_hal_bus_enable(FuriHalBusHSEM); + furi_hal_bus_enable(FuriHalBusIPCC); + furi_hal_bus_enable(FuriHalBusRNG); + furi_hal_bus_enable(FuriHalBusUSART1); + furi_hal_clock_init(); furi_hal_rtc_init(); furi_hal_interrupt_init(); diff --git a/firmware/targets/f7/target.json b/firmware/targets/f7/target.json index c503644536ed..f58200c4688d 100644 --- a/firmware/targets/f7/target.json +++ b/firmware/targets/f7/target.json @@ -28,7 +28,9 @@ "flipperformat", "toolbox", "nfc", + "digital_signal", "pulse_reader", + "signal_reader", "microtar", "usb_stm32", "st25rfal002", @@ -37,11 +39,13 @@ "assets", "one_wire", "ibutton", + "music_worker", "misc", "mbedtls", "lfrfid", "flipper_application", "flipperformat", - "toolbox" + "toolbox", + "flipper7" ] } \ No newline at end of file diff --git a/firmware/targets/furi_hal_include/f_hal_nfc.h b/firmware/targets/furi_hal_include/f_hal_nfc.h new file mode 100644 index 000000000000..7cf04bd35a89 --- /dev/null +++ b/firmware/targets/furi_hal_include/f_hal_nfc.h @@ -0,0 +1,172 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define F_HAL_NFC_TIMER_OFFSET_FC (500) + +#define F_HAL_NFC_EVENT_WAIT_FOREVER (0xFFFFFFFFU) + +typedef enum { + FHalNfcEventOscOn = (1U << 0), + FHalNfcEventFieldOn = (1U << 1), + FHalNfcEventFieldOff = (1U << 2), + FHalNfcEventListenerActive = (1U << 3), + FHalNfcEventListenerActiveA = (1U << 4), + FHalNfcEventTxStart = (1U << 5), + FHalNfcEventTxEnd = (1U << 6), + FHalNfcEventRxStart = (1U << 7), + FHalNfcEventRxEnd = (1U << 8), + FHalNfcEventCollision = (1U << 9), + FHalNfcEventTimerFwtExpired = (1U << 10), + FHalNfcEventTimerBlockTxExpired = (1U << 11), + FHalNfcEventTimeout = (1U << 12), + FHalNfcEventAbortRequest = (1U << 13), +} FHalNfcEvent; + +typedef enum { + FHalNfcErrorNone, + FHalNfcErrorBusy, + FHalNfcErrorChipCommunication, + FHalNfcErrorCommunication, + FHalNfcErrorOscillator, + FHalNfcErrorIsrTimeout, + FHalNfcErrorCommunicationTimeout, + FHalNfcErrorBufferOverflow, + FHalNfcErrorDataFormat, +} FHalNfcError; + +typedef enum { + FHalNfcModePoller, + FHalNfcModeListener, + + FHalNfcModeNum, +} FHalNfcMode; + +typedef enum { + FHalNfcTechIso14443a, + FHalNfcTechIso14443b, + FHalNfcTechIso15693, + FHalNfcTechFelica, + + FHalNfcTechNum, + FHalNfcTechInvalid, +} FHalNfcTech; + +typedef enum { + FHalNfcaShortFrameAllReq, + FHalNfcaShortFrameSensReq, +} FHalNfcaShortFrame; + +/** + * @brief Nfc HAL initialization + * + * @return FHalNfcError + */ +FHalNfcError f_hal_nfc_init(); + +FHalNfcError f_hal_nfc_is_hal_ready(); + +/** + * @brief Start Nfc hardware low power mode + * + * @return FHalNfcError + */ +FHalNfcError f_hal_nfc_low_power_mode_start(); + +/** + * @brief Stop Nfc hardware low power mode + * + * @return FHalNfcError + */ +FHalNfcError f_hal_nfc_low_power_mode_stop(); + +/** + * @brief Configure Nfc HAL mode + * + * @param mode FHalNfcMode instance + * @param bitrate FHalNfcBitrate instance + * + * @return FHalNfcError + */ +FHalNfcError f_hal_nfc_set_mode(FHalNfcMode mode, FHalNfcTech tech); + +FHalNfcError f_hal_nfc_reset_mode(); + +/** + * @brief Turn on field in poller mode + * + * @return FHalNfcError + */ +FHalNfcError f_hal_nfc_poller_field_on(); + +FHalNfcError f_hal_nfc_acquire(); + +FHalNfcError f_hal_nfc_release(); + +FHalNfcEvent f_hal_nfc_poller_wait_event(uint32_t timeout_ms); + +FHalNfcEvent f_hal_nfc_listener_wait_event(uint32_t timeout_ms); + +FHalNfcError f_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits); + +FHalNfcError f_hal_nfc_poller_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); + +FHalNfcError f_hal_nfc_listener_tx(const uint8_t* tx_data, size_t tx_bits); + +FHalNfcError f_hal_nfc_listener_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); + +FHalNfcError f_hal_nfc_listener_sleep(); + +FHalNfcError f_hal_nfc_listener_idle(); + +FHalNfcError f_hal_nfc_listener_enable_rx(); + +FHalNfcError f_hal_nfc_trx_reset(); + +FHalNfcError f_hal_nfc_abort(); + +FHalNfcError f_hal_nfc_event_start(); + +void f_hal_nfc_timer_fwt_start(uint32_t time_fc); + +void f_hal_nfc_timer_fwt_stop(); + +void f_hal_nfc_timer_block_tx_start(uint32_t time_fc); + +void f_hal_nfc_timer_block_tx_start_us(uint32_t time_us); + +void f_hal_nfc_timer_block_tx_stop(); + +bool f_hal_nfc_timer_block_tx_is_running(); + +/******************* NFCA specific API *******************/ + +FHalNfcError f_hal_nfc_iso14443a_poller_trx_short_frame(FHalNfcaShortFrame frame); + +FHalNfcError f_hal_nfc_iso14443a_tx_sdd_frame(const uint8_t* tx_data, size_t tx_bits); + +FHalNfcError + f_hal_nfc_iso14443a_rx_sdd_frame(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); + +FHalNfcError f_hal_nfc_iso14443a_listener_set_col_res_data( + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak); + +FHalNfcError f_hal_nfc_iso14443a_poller_tx_custom_parity(const uint8_t* tx_data, size_t tx_bits); + +FHalNfcError f_hal_nfc_iso14443a_listener_tx_custom_parity( + const uint8_t* tx_data, + const uint8_t* tx_parity, + size_t tx_bits); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index 2eb4688d4288..9341dccecbe7 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -12,9 +12,11 @@ struct STOP_EXTERNING_ME {}; #include #include +#include #include #include #include +#include #include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 6ba38cb5e630..4d538265dbb2 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h index 1b6e79ab0785..0472d31d1815 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h @@ -1,6 +1,6 @@ #pragma once -#include "serial_service.h" +#include #ifdef __cplusplus extern "C" { diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index 00182fa28615..5edda6ba1974 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -34,6 +34,12 @@ void furi_hal_power_init(); */ bool furi_hal_power_gauge_is_ok(); +/** Check if gauge requests system shutdown + * + * @return true if system shutdown requested + */ +bool furi_hal_power_is_shutdown_requested(); + /** Get current insomnia level * * @return insomnia level: 0 - no insomnia, >0 - insomnia, bearer count. @@ -99,12 +105,16 @@ void furi_hal_power_reset(); /** OTG enable */ -void furi_hal_power_enable_otg(); +bool furi_hal_power_enable_otg(); /** OTG disable */ void furi_hal_power_disable_otg(); +/** Check OTG status fault + */ +bool furi_hal_power_check_otg_fault(); + /** Check OTG status and disable it if falt happened */ void furi_hal_power_check_otg_status(); diff --git a/firmware/targets/furi_hal_include/furi_hal_random.h b/firmware/targets/furi_hal_include/furi_hal_random.h index ee69326f416c..5ca9cb723c3d 100644 --- a/firmware/targets/furi_hal_include/furi_hal_random.h +++ b/firmware/targets/furi_hal_include/furi_hal_random.h @@ -6,6 +6,9 @@ extern "C" { #endif +/** Initialize random subsystem */ +void furi_hal_random_init(); + /** Get random value * * @return random value diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index e706c5c76a02..186d22f07980 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -255,6 +255,30 @@ uint32_t furi_hal_rtc_get_timestamp(); */ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime); +/** Gets the number of days in the year according to the Gregorian calendar. + * + * @param year Input year. + * + * @return number of days in `year`. + */ +uint16_t furi_hal_rtc_get_days_per_year(uint16_t year); + +/** Check if a year a leap year in the Gregorian calendar. + * + * @param year Input year. + * + * @return true if `year` is a leap year. + */ +bool furi_hal_rtc_is_leap_year(uint16_t year); + +/** Get the number of days in the month. + * + * @param leap_year true to calculate based on leap years + * @param month month to check, where 1 = January + * @return the number of days in the month + */ +uint8_t furi_hal_rtc_get_days_per_month(bool leap_year, uint8_t month); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index aec4fc787aa4..a9339a6c0eea 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -85,6 +85,12 @@ const char* furi_hal_version_get_fcc_id(); */ const char* furi_hal_version_get_ic_id(); +/** Get MIC id + * + * @return MIC id as C-string + */ +const char* furi_hal_version_get_mic_id(); + /** Get OTP version * * @return OTP Version diff --git a/furi/core/check.c b/furi/core/check.c index 478f3aacccf1..f7dcfc595902 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -36,7 +36,7 @@ PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[13] = {0}; * */ #define RESTORE_REGISTERS_AND_HALT_MCU(debug) \ - register const bool r0 asm("r0") = debug; \ + register bool r0 asm("r0") = debug; \ asm volatile("cbnz r0, with_debugger%= \n" \ "ldr r12, =__furi_check_registers\n" \ "ldm r12, {r0-r11} \n" \ @@ -166,7 +166,11 @@ FURI_NORETURN void __furi_crash() { RESTORE_REGISTERS_AND_HALT_MCU(true); #ifndef FURI_DEBUG } else { - furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); + uint32_t ptr = (uint32_t)__furi_check_message; + if(ptr < FLASH_BASE || ptr > (FLASH_BASE + FLASH_SIZE)) { + ptr = (uint32_t) "Check serial logs"; + } + furi_hal_rtc_set_fault_data(ptr); furi_hal_console_puts("\r\nRebooting system.\r\n"); furi_hal_console_puts("\033[0m\r\n"); furi_hal_power_reset(); diff --git a/furi/core/check.h b/furi/core/check.h index ea83f2219c2b..004422e807e5 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -13,6 +13,8 @@ */ #pragma once +#include + #ifdef __cplusplus extern "C" { #define FURI_NORETURN [[noreturn]] @@ -48,28 +50,47 @@ FURI_NORETURN void __furi_halt(); } while(0) /** Check condition and crash if check failed */ -#define furi_check(__e) \ - do { \ - if(!(__e)) { \ - furi_crash(__FURI_CHECK_MESSAGE_FLAG); \ - } \ +#define __furi_check(__e, __m) \ + do { \ + if(!(__e)) { \ + furi_crash(__m); \ + } \ } while(0) +/** Check condition and crash if failed + * + * @param condition to check + * @param optional message + */ +#define furi_check(...) \ + M_APPLY(__furi_check, M_DEFAULT_ARGS(2, (__FURI_CHECK_MESSAGE_FLAG), __VA_ARGS__)) + /** Only in debug build: Assert condition and crash if assert failed */ #ifdef FURI_DEBUG -#define furi_assert(__e) \ - do { \ - if(!(__e)) { \ - furi_crash(__FURI_ASSERT_MESSAGE_FLAG); \ - } \ +#define __furi_assert(__e, __m) \ + do { \ + if(!(__e)) { \ + furi_crash(__m); \ + } \ } while(0) #else -#define furi_assert(__e) \ - do { \ - ((void)(__e)); \ +#define __furi_assert(__e, __m) \ + do { \ + ((void)(__e)); \ + ((void)(__m)); \ } while(0) #endif +/** Assert condition and crash if failed + * + * @warning only will do check if firmware compiled in debug mode + * + * @param condition to check + * @param optional message + */ +#define furi_assert(...) \ + M_APPLY(__furi_assert, M_DEFAULT_ARGS(2, (__FURI_ASSERT_MESSAGE_FLAG), __VA_ARGS__)) + #ifdef __cplusplus } #endif diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index d7bfaf2076d0..5bd218d35762 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -31,29 +31,22 @@ extern "C" { #define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED()) #endif +typedef struct { + uint32_t isrm; + bool from_isr; + bool kernel_running; +} __FuriCriticalInfo; + +__FuriCriticalInfo __furi_critical_enter(void); + +void __furi_critical_exit(__FuriCriticalInfo info); + #ifndef FURI_CRITICAL_ENTER -#define FURI_CRITICAL_ENTER() \ - uint32_t __isrm = 0; \ - bool __from_isr = FURI_IS_ISR(); \ - bool __kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); \ - if(__from_isr) { \ - __isrm = taskENTER_CRITICAL_FROM_ISR(); \ - } else if(__kernel_running) { \ - taskENTER_CRITICAL(); \ - } else { \ - __disable_irq(); \ - } +#define FURI_CRITICAL_ENTER() __FuriCriticalInfo __furi_critical_info = __furi_critical_enter(); #endif #ifndef FURI_CRITICAL_EXIT -#define FURI_CRITICAL_EXIT() \ - if(__from_isr) { \ - taskEXIT_CRITICAL_FROM_ISR(__isrm); \ - } else if(__kernel_running) { \ - taskEXIT_CRITICAL(); \ - } else { \ - __enable_irq(); \ - } +#define FURI_CRITICAL_EXIT() __furi_critical_exit(__furi_critical_info); #endif #ifdef __cplusplus diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h index 830bb191c2ed..4309c20c5895 100644 --- a/furi/core/core_defines.h +++ b/furi/core/core_defines.h @@ -78,6 +78,11 @@ extern "C" { #define TOSTRING(x) STRINGIFY(x) #endif +#ifndef CONCATENATE +#define CONCATENATE(a, b) CONCATENATE_(a, b) +#define CONCATENATE_(a, b) a##b +#endif + #ifndef REVERSE_BYTES_U32 #define REVERSE_BYTES_U32(x) \ ((((x)&0x000000FF) << 24) | (((x)&0x0000FF00) << 8) | (((x)&0x00FF0000) >> 8) | \ diff --git a/furi/core/critical.c b/furi/core/critical.c new file mode 100644 index 000000000000..57fe2403be71 --- /dev/null +++ b/furi/core/critical.c @@ -0,0 +1,29 @@ +#include "common_defines.h" + +__FuriCriticalInfo __furi_critical_enter(void) { + __FuriCriticalInfo info; + + info.isrm = 0; + info.from_isr = FURI_IS_ISR(); + info.kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); + + if(info.from_isr) { + info.isrm = taskENTER_CRITICAL_FROM_ISR(); + } else if(info.kernel_running) { + taskENTER_CRITICAL(); + } else { + __disable_irq(); + } + + return info; +} + +void __furi_critical_exit(__FuriCriticalInfo info) { + if(info.from_isr) { + taskEXIT_CRITICAL_FROM_ISR(info.isrm); + } else if(info.kernel_running) { + taskEXIT_CRITICAL(); + } else { + __enable_irq(); + } +} \ No newline at end of file diff --git a/furi/core/log.c b/furi/core/log.c index d910ecf21132..53467ecdb2d7 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -14,6 +14,21 @@ typedef struct { static FuriLogParams furi_log; +typedef struct { + const char* str; + FuriLogLevel level; +} FuriLogLevelDescription; + +static const FuriLogLevelDescription FURI_LOG_LEVEL_DESCRIPTIONS[] = { + {"default", FuriLogLevelDefault}, + {"none", FuriLogLevelNone}, + {"error", FuriLogLevelError}, + {"warn", FuriLogLevelWarn}, + {"info", FuriLogLevelInfo}, + {"debug", FuriLogLevelDebug}, + {"trace", FuriLogLevelTrace}, +}; + void furi_log_init() { // Set default logging parameters furi_log.log_level = FURI_LOG_LEVEL_DEFAULT; @@ -117,3 +132,23 @@ void furi_log_set_timestamp(FuriLogTimestamp timestamp) { furi_assert(timestamp); furi_log.timestamp = timestamp; } + +bool furi_log_level_to_string(FuriLogLevel level, const char** str) { + for(size_t i = 0; i < COUNT_OF(FURI_LOG_LEVEL_DESCRIPTIONS); i++) { + if(level == FURI_LOG_LEVEL_DESCRIPTIONS[i].level) { + *str = FURI_LOG_LEVEL_DESCRIPTIONS[i].str; + return true; + } + } + return false; +} + +bool furi_log_level_from_string(const char* str, FuriLogLevel* level) { + for(size_t i = 0; i < COUNT_OF(FURI_LOG_LEVEL_DESCRIPTIONS); i++) { + if(strcmp(str, FURI_LOG_LEVEL_DESCRIPTIONS[i].str) == 0) { + *level = FURI_LOG_LEVEL_DESCRIPTIONS[i].level; + return true; + } + } + return false; +} \ No newline at end of file diff --git a/furi/core/log.h b/furi/core/log.h index 46ae7f00713f..5d11add9b917 100644 --- a/furi/core/log.h +++ b/furi/core/log.h @@ -7,6 +7,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -87,6 +88,23 @@ void furi_log_set_puts(FuriLogPuts puts); */ void furi_log_set_timestamp(FuriLogTimestamp timestamp); +/** Log level to string + * + * @param[in] level The level + * + * @return The string + */ +bool furi_log_level_to_string(FuriLogLevel level, const char** str); + +/** Log level from string + * + * @param[in] str The string + * @param level The level + * + * @return True if success, False otherwise + */ +bool furi_log_level_from_string(const char* str, FuriLogLevel* level); + /** Log methods * * @param tag The application tag diff --git a/furi/core/string.c b/furi/core/string.c index 4384fe06a224..682c8d409778 100644 --- a/furi/core/string.c +++ b/furi/core/string.c @@ -296,7 +296,9 @@ static FuriStringUTF8State state_to_furi_state(m_str1ng_utf8_state_e state) { } void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode) { + string_unicode_t m_u = *unicode; m_str1ng_utf8_state_e m_state = furi_state_to_state(*state); - m_str1ng_utf8_decode(c, &m_state, unicode); + m_str1ng_utf8_decode(c, &m_state, &m_u); *state = state_to_furi_state(m_state); + *unicode = m_u; } diff --git a/furi/core/string.h b/furi/core/string.h index 0523d3ba04ed..7529deacd7c1 100644 --- a/furi/core/string.h +++ b/furi/core/string.h @@ -633,20 +633,17 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico * @brief Search for a string (or C string) in a string * (string, [c]string[, start=0]) */ -#define furi_string_search(v, ...) \ - M_APPLY( \ - FURI_STRING_SELECT3, \ - furi_string_search, \ - furi_string_search_str, \ - v, \ - M_IF_DEFAULT1(0, __VA_ARGS__)) - +#define furi_string_search(...) \ + M_APPLY( \ + FURI_STRING_SELECT3, \ + furi_string_search, \ + furi_string_search_str, \ + M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Search for a C string in a string * (string, cstring[, start=0]) */ -#define furi_string_search_str(v, ...) \ - M_APPLY(furi_string_search_str, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_str(...) furi_string_search_str(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Test if the string starts with the given string (or C string). @@ -672,41 +669,36 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico * @brief Trim a string from the given set of characters (default is " \n\r\t"). * (string[, set=" \n\r\t"]) */ -#define furi_string_trim(...) M_APPLY(furi_string_trim, M_IF_DEFAULT1(" \n\r\t", __VA_ARGS__)) +#define furi_string_trim(...) furi_string_trim(M_DEFAULT_ARGS(2, (" \n\r\t"), __VA_ARGS__)) /** * @brief Search for a character in a string. * (string, character[, start=0]) */ -#define furi_string_search_char(v, ...) \ - M_APPLY(furi_string_search_char, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_char(...) furi_string_search_char(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Reverse Search for a character in a string. * (string, character[, start=0]) */ -#define furi_string_search_rchar(v, ...) \ - M_APPLY(furi_string_search_rchar, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_rchar(...) furi_string_search_rchar(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Replace a string to another string (or C string to another C string) in a string. * (string, [c]string, [c]string[, start=0]) */ -#define furi_string_replace(a, b, ...) \ - M_APPLY( \ - FURI_STRING_SELECT4, \ - furi_string_replace, \ - furi_string_replace_str, \ - a, \ - b, \ - M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_replace(...) \ + M_APPLY( \ + FURI_STRING_SELECT4, \ + furi_string_replace, \ + furi_string_replace_str, \ + M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) /** * @brief Replace a C string to another C string in a string. * (string, cstring, cstring[, start=0]) */ -#define furi_string_replace_str(a, b, ...) \ - M_APPLY(furi_string_replace_str, a, b, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_replace_str(...) furi_string_replace_str(M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) /** * @brief INIT OPLIST for FuriString. @@ -743,4 +735,4 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/furi/core/thread.c b/furi/core/thread.c index facbcb411796..657b867d1e71 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -56,6 +56,8 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread); /** Catch threads that are trying to exit wrong way */ __attribute__((__noreturn__)) void furi_thread_catch() { //-V1082 + // If you're here it means you're probably doing something wrong + // with critical sections or with scheduler state asm volatile("nop"); // extra magic furi_crash("You are doing it wrong"); //-V779 __builtin_unreachable(); diff --git a/lib/ReadMe.md b/lib/ReadMe.md index 93236b2677fb..138bef2b343b 100644 --- a/lib/ReadMe.md +++ b/lib/ReadMe.md @@ -27,6 +27,7 @@ - `nfc` - NFC library, used by NFC application - `one_wire` - OneWire library, used by iButton application - `print` - Tiny printf implementation +- `digital_signal` - Digital Signal library used by NFC for software implemented protocols - `pulse_reader` - Pulse Reader library used by NFC for software implemented protocols - `qrcode` - QR-Code library - `stm32wb_cmsis` - STM32WB series CMSIS headers, extends CMSIS Core diff --git a/lib/SConscript b/lib/SConscript index 8727746d8181..9dfa35671a3b 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -5,6 +5,7 @@ env.Append( Dir("app-scened-template"), Dir("digital_signal"), Dir("pulse_reader"), + Dir("signal_reader"), Dir("drivers"), Dir("flipper_format"), Dir("infrared"), @@ -15,7 +16,7 @@ env.Append( Dir("u8g2"), Dir("update_util"), Dir("print"), - Dir("pulse_reader"), + Dir("music_worker"), ], ) @@ -95,11 +96,14 @@ libs = env.BuildModules( "mbedtls", "subghz", "nfc", + "digital_signal", "pulse_reader", + "signal_reader", "appframe", "misc", "lfrfid", "flipper_application", + "music_worker", ], ) diff --git a/lib/digital_signal/SConscript b/lib/digital_signal/SConscript new file mode 100644 index 000000000000..2ddf7a58b098 --- /dev/null +++ b/lib/digital_signal/SConscript @@ -0,0 +1,20 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/digital_signal", + ], + SDK_HEADERS=[ + File("digital_signal.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="digital_signal") +libenv.ApplyLibFlags() +libenv.Append(CCFLAGS=["-O3", "-funroll-loops", "-Ofast"]) + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 51a87d22a5b3..0a5e1eb76a68 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -9,7 +9,7 @@ #include /* must be on bank B */ -#define DEBUG_OUTPUT gpio_ext_pb3 +// For debugging purposes use `--extra-define=DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN=gpio_ext_pb3` fbt option struct ReloadBuffer { uint32_t* buffer; /* DMA ringbuffer */ @@ -51,8 +51,16 @@ struct DigitalSignalInternals { #define T_TIM 1562 /* 15.625 ns *100 */ #define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ +/* end marker in DMA ringbuffer, will get written into timer register at the end */ +#define SEQ_TIMER_MAX 0xFFFFFFFF + +/* time to wait in loops before returning */ +#define SEQ_LOCK_WAIT_MS 10UL +#define SEQ_LOCK_WAIT_TICKS (SEQ_LOCK_WAIT_MS * 1000 * 64) + /* maximum entry count of the sequence dma ring buffer */ -#define SEQUENCE_DMA_RINGBUFFER_SIZE 32 +#define RINGBUFFER_SIZE 128 + /* maximum number of DigitalSignals in a sequence */ #define SEQUENCE_SIGNALS_SIZE 32 /* @@ -194,9 +202,9 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { uint32_t bit_set = internals->gpio->pin; uint32_t bit_reset = internals->gpio->pin << 16; -#ifdef DEBUG_OUTPUT - bit_set |= DEBUG_OUTPUT.pin; - bit_reset |= DEBUG_OUTPUT.pin << 16; +#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN + bit_set |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin; + bit_reset |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << 16; #endif if(signal->start_level) { @@ -212,8 +220,7 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { internals->reload_reg_entries = 0; for(size_t pos = 0; pos < signal->edge_cnt; pos++) { - uint32_t edge_scaled = (internals->factor * signal->edge_timings[pos]) / (1024 * 1024); - uint32_t pulse_duration = edge_scaled + internals->reload_reg_remainder; + uint32_t pulse_duration = signal->edge_timings[pos] + internals->reload_reg_remainder; if(pulse_duration < 10 || pulse_duration > 10000000) { FURI_LOG_D( TAG, @@ -243,15 +250,17 @@ static void digital_signal_stop_timer() { LL_TIM_DisableCounter(TIM2); LL_TIM_DisableUpdateEvent(TIM2); LL_TIM_DisableDMAReq_UPDATE(TIM2); + + furi_hal_bus_disable(FuriHalBusTIM2); } static void digital_signal_setup_timer() { - digital_signal_stop_timer(); + furi_hal_bus_enable(FuriHalBusTIM2); LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); LL_TIM_SetPrescaler(TIM2, 0); - LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); + LL_TIM_SetAutoReload(TIM2, SEQ_TIMER_MAX); LL_TIM_SetCounter(TIM2, 0); } @@ -334,7 +343,7 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { sequence->bake = false; sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer)); - sequence->dma_buffer->size = SEQUENCE_DMA_RINGBUFFER_SIZE; + sequence->dma_buffer->size = RINGBUFFER_SIZE; sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t)); sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; @@ -453,39 +462,23 @@ static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { return ret; } -static void digital_sequence_update_pos(DigitalSequence* sequence) { - struct ReloadBuffer* dma_buffer = sequence->dma_buffer; - - dma_buffer->read_pos = dma_buffer->size - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); -} - -static const uint32_t wait_ms = 10; -static const uint32_t wait_ticks = wait_ms * 1000 * 64; - static void digital_sequence_finish(DigitalSequence* sequence) { struct ReloadBuffer* dma_buffer = sequence->dma_buffer; if(dma_buffer->dma_active) { uint32_t prev_timer = DWT->CYCCNT; - uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; do { - uint32_t last_pos = dma_buffer->read_pos; - - digital_sequence_update_pos(sequence); - - /* we are finished, when the DMA transferred the 0xFFFFFFFF-timer which is the current write_pos */ - if(dma_buffer->read_pos == end_pos) { + /* we are finished, when the DMA transferred the SEQ_TIMER_MAX marker */ + if(TIM2->ARR == SEQ_TIMER_MAX) { break; } - - if(last_pos != dma_buffer->read_pos) { //-V547 - prev_timer = DWT->CYCCNT; - } - if(DWT->CYCCNT - prev_timer > wait_ticks) { + if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) { + dma_buffer->read_pos = + RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); FURI_LOG_D( TAG, "[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)", - wait_ms, + SEQ_LOCK_WAIT_MS, TIM2->ARR, dma_buffer->read_pos, dma_buffer->write_pos); @@ -503,23 +496,30 @@ static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t len if(dma_buffer->dma_active) { uint32_t prev_timer = DWT->CYCCNT; - uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; do { - uint32_t last_pos = dma_buffer->read_pos; - digital_sequence_update_pos(sequence); + dma_buffer->read_pos = RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); - if(dma_buffer->read_pos != end_pos) { + uint32_t free = + (RINGBUFFER_SIZE + dma_buffer->read_pos - dma_buffer->write_pos) % RINGBUFFER_SIZE; + + if(free > 2) { break; } - if(last_pos != dma_buffer->read_pos) { //-V547 - prev_timer = DWT->CYCCNT; - } - if(DWT->CYCCNT - prev_timer > wait_ticks) { + if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) { FURI_LOG_D( TAG, "[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)", - wait_ms, + SEQ_LOCK_WAIT_MS, + TIM2->ARR, + dma_buffer->read_pos, + dma_buffer->write_pos); + break; + } + if(TIM2->ARR == SEQ_TIMER_MAX) { + FURI_LOG_D( + TAG, + "[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)", TIM2->ARR, dma_buffer->read_pos, dma_buffer->write_pos); @@ -529,8 +529,9 @@ static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t len } dma_buffer->buffer[dma_buffer->write_pos] = length; - dma_buffer->write_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; - dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF; + dma_buffer->write_pos++; + dma_buffer->write_pos %= RINGBUFFER_SIZE; + dma_buffer->buffer[dma_buffer->write_pos] = SEQ_TIMER_MAX; } bool digital_sequence_send(DigitalSequence* sequence) { @@ -539,8 +540,9 @@ bool digital_sequence_send(DigitalSequence* sequence) { struct ReloadBuffer* dma_buffer = sequence->dma_buffer; furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -#ifdef DEBUG_OUTPUT - furi_hal_gpio_init(&DEBUG_OUTPUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN + furi_hal_gpio_init( + &DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); #endif if(sequence->bake) { @@ -551,90 +553,100 @@ bool digital_sequence_send(DigitalSequence* sequence) { return true; } - int32_t remainder = 0; - bool traded_first = false; + if(!sequence->sequence_used) { + return false; + } - FURI_CRITICAL_ENTER(); + int32_t remainder = 0; + uint32_t trade_for_next = 0; + uint32_t seq_pos_next = 1; dma_buffer->dma_active = false; - dma_buffer->buffer[0] = 0xFFFFFFFF; + dma_buffer->buffer[0] = SEQ_TIMER_MAX; dma_buffer->read_pos = 0; dma_buffer->write_pos = 0; - for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) { - uint8_t signal_index = sequence->sequence[seq_pos]; - DigitalSignal* sig = sequence->signals[signal_index]; - bool last_signal = ((seq_pos + 1) == sequence->sequence_used); + /* already prepare the current signal pointer */ + DigitalSignal* sig = sequence->signals[sequence->sequence[0]]; + DigitalSignal* sig_next = NULL; + /* re-use the GPIO buffer from the first signal */ + sequence->gpio_buff = sig->internals->gpio_buff; + + FURI_CRITICAL_ENTER(); + + while(sig) { + bool last_signal = (seq_pos_next >= sequence->sequence_used); - /* all signals are prepared and we can re-use the GPIO buffer from the fist signal */ - if(seq_pos == 0) { - sequence->gpio_buff = sig->internals->gpio_buff; + if(!last_signal) { + sig_next = sequence->signals[sequence->sequence[seq_pos_next++]]; } for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) { - if(traded_first) { - traded_first = false; - continue; - } - uint32_t pulse_length = 0; - bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries); + bool last_pulse = ((pulse_pos + 1) >= sig->internals->reload_reg_entries); + uint32_t pulse_length = sig->reload_reg_buff[pulse_pos] + trade_for_next; - pulse_length = sig->reload_reg_buff[pulse_pos]; + trade_for_next = 0; /* when we are too late more than half a tick, make the first edge temporarily longer */ if(remainder >= T_TIM_DIV2) { remainder -= T_TIM; pulse_length += 1; } - remainder += sig->internals->reload_reg_remainder; - - /* last pulse in that signal and have a next signal? */ - if(last_pulse) { - if((seq_pos + 1) < sequence->sequence_used) { - DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]]; - /* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */ - /* beware, we do not want the level after the last edge, but the last level before that edge */ - bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); + /* last pulse in current signal and have a next signal? */ + if(last_pulse && sig_next) { + /* when a signal ends with the same level as the next signal begins, let the next signal generate the whole pulse. + beware, we do not want the level after the last edge, but the last level before that edge */ + bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); - /* take from the next, add it to the current if they have the same level */ - if(end_level == sig_next->start_level) { - pulse_length += sig_next->reload_reg_buff[0]; - traded_first = true; - } + /* if they have the same level, pass the duration to the next pulse(s) */ + if(end_level == sig_next->start_level) { + trade_for_next = pulse_length; } } - digital_sequence_queue_pulse(sequence, pulse_length); + /* if it was decided, that the next signal's first pulse shall also handle our "length", then do not queue here */ + if(!trade_for_next) { + digital_sequence_queue_pulse(sequence, pulse_length); - /* start transmission when buffer was filled enough */ - bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4); - - /* or it was the last pulse */ - if(last_pulse && last_signal) { - start_send = true; - } + if(!dma_buffer->dma_active) { + /* start transmission when buffer was filled enough */ + bool start_send = sequence->dma_buffer->write_pos >= (RINGBUFFER_SIZE - 2); - /* start transmission */ - if(start_send && !dma_buffer->dma_active) { - digital_sequence_setup_dma(sequence); - digital_signal_setup_timer(); + /* or it was the last pulse */ + if(last_pulse && last_signal) { + start_send = true; + } - /* if the send time is specified, wait till the core timer passed beyond that time */ - if(sequence->send_time_active) { - sequence->send_time_active = false; - while(sequence->send_time - DWT->CYCCNT < 0x80000000) { + /* start transmission */ + if(start_send) { + digital_sequence_setup_dma(sequence); + digital_signal_setup_timer(); + + /* if the send time is specified, wait till the core timer passed beyond that time */ + if(sequence->send_time_active) { + sequence->send_time_active = false; + while(sequence->send_time - DWT->CYCCNT < 0x80000000) { + } + } + digital_signal_start_timer(); + dma_buffer->dma_active = true; } } - digital_signal_start_timer(); - dma_buffer->dma_active = true; } } + + remainder += sig->internals->reload_reg_remainder; + sig = sig_next; + sig_next = NULL; } /* wait until last dma transaction was finished */ - digital_sequence_finish(sequence); FURI_CRITICAL_EXIT(); + digital_sequence_finish(sequence); + + // TODO reconfig GPIO to initial state + furi_hal_gpio_write(sequence->gpio, false); return true; } diff --git a/lib/digital_signal/presets/nfc/iso14443_3a_signal.c b/lib/digital_signal/presets/nfc/iso14443_3a_signal.c new file mode 100644 index 000000000000..b57ab767aa89 --- /dev/null +++ b/lib/digital_signal/presets/nfc/iso14443_3a_signal.c @@ -0,0 +1,123 @@ +#include "iso14443_3a_signal.h" + +#include + +#define ISO14443_3A_SIGNAL_BIT_MAX_EDGES (10) +#define ISO14443_3A_SIGNAL_MAX_EDGES (1350) + +#define ISO14443_3A_SIGNAL_F_SIG (13560000.0) +#define ISO14443_3A_SIGNAL_T_SIG 7374 //73.746ns*100 +#define ISO14443_3A_SIGNAL_T_SIG_X8 58992 //T_SIG*8 +#define ISO14443_3A_SIGNAL_T_SIG_X8_X8 471936 //T_SIG*8*8 +#define ISO14443_3A_SIGNAL_T_SIG_X8_X9 530928 //T_SIG*8*9 + +struct Iso14443_3aSignal { + const GpioPin* pin; + DigitalSignal* one; + DigitalSignal* zero; + DigitalSignal* tx_signal; +}; + +static void iso14443_3a_add_bit(DigitalSignal* signal, bool bit) { + if(bit) { + signal->start_level = true; + for(size_t i = 0; i < 7; i++) { + signal->edge_timings[i] = ISO14443_3A_SIGNAL_T_SIG_X8; + } + signal->edge_timings[7] = ISO14443_3A_SIGNAL_T_SIG_X8_X9; + signal->edge_cnt = 8; + } else { + signal->start_level = false; + signal->edge_timings[0] = ISO14443_3A_SIGNAL_T_SIG_X8_X8; + for(size_t i = 1; i < 9; i++) { + signal->edge_timings[i] = ISO14443_3A_SIGNAL_T_SIG_X8; + } + signal->edge_cnt = 9; + } +} + +static void iso14443_3a_add_byte(Iso14443_3aSignal* instance, uint8_t byte, bool parity) { + for(size_t i = 0; i < 8; i++) { + if(byte & (1 << i)) { + digital_signal_append(instance->tx_signal, instance->one); + } else { + digital_signal_append(instance->tx_signal, instance->zero); + } + } + if(parity) { + digital_signal_append(instance->tx_signal, instance->one); + } else { + digital_signal_append(instance->tx_signal, instance->zero); + } +} + +static void iso14443_3a_signal_encode( + Iso14443_3aSignal* instance, + const uint8_t* tx_data, + const uint8_t* tx_parity, + size_t tx_bits) { + furi_assert(instance); + furi_assert(tx_data); + furi_assert(tx_parity); + + instance->tx_signal->edge_cnt = 0; + instance->tx_signal->start_level = true; + // Start of frame + digital_signal_append(instance->tx_signal, instance->one); + + if(tx_bits < 8) { + for(size_t i = 0; i < tx_bits; i++) { + if(FURI_BIT(tx_data[0], i)) { + digital_signal_append(instance->tx_signal, instance->one); + } else { + digital_signal_append(instance->tx_signal, instance->zero); + } + } + } else { + for(size_t i = 0; i < tx_bits / 8; i++) { + bool parity = FURI_BIT(tx_parity[i / 8], i % 8); + iso14443_3a_add_byte(instance, tx_data[i], parity); + } + } +} + +Iso14443_3aSignal* iso14443_3a_signal_alloc(const GpioPin* pin) { + furi_assert(pin); + + Iso14443_3aSignal* instance = malloc(sizeof(Iso14443_3aSignal)); + instance->pin = pin; + instance->one = digital_signal_alloc(ISO14443_3A_SIGNAL_BIT_MAX_EDGES); + instance->zero = digital_signal_alloc(ISO14443_3A_SIGNAL_BIT_MAX_EDGES); + iso14443_3a_add_bit(instance->one, true); + iso14443_3a_add_bit(instance->zero, false); + instance->tx_signal = digital_signal_alloc(ISO14443_3A_SIGNAL_MAX_EDGES); + + return instance; +} + +void iso14443_3a_signal_free(Iso14443_3aSignal* instance) { + furi_assert(instance); + furi_assert(instance->one); + furi_assert(instance->zero); + furi_assert(instance->tx_signal); + + digital_signal_free(instance->one); + digital_signal_free(instance->zero); + digital_signal_free(instance->tx_signal); + free(instance); +} + +void iso14443_3a_signal_tx( + Iso14443_3aSignal* instance, + const uint8_t* tx_data, + const uint8_t* tx_parity, + size_t tx_bits) { + furi_assert(instance); + furi_assert(tx_data); + furi_assert(tx_parity); + + FURI_CRITICAL_ENTER(); + iso14443_3a_signal_encode(instance, tx_data, tx_parity, tx_bits); + digital_signal_send(instance->tx_signal, instance->pin); + FURI_CRITICAL_EXIT(); +} diff --git a/lib/digital_signal/presets/nfc/iso14443_3a_signal.h b/lib/digital_signal/presets/nfc/iso14443_3a_signal.h new file mode 100644 index 000000000000..3abec139c675 --- /dev/null +++ b/lib/digital_signal/presets/nfc/iso14443_3a_signal.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso14443_3aSignal Iso14443_3aSignal; + +Iso14443_3aSignal* iso14443_3a_signal_alloc(const GpioPin* pin); + +void iso14443_3a_signal_free(Iso14443_3aSignal* instance); + +void iso14443_3a_signal_tx( + Iso14443_3aSignal* instance, + const uint8_t* tx_data, + const uint8_t* tx_parity, + size_t tx_bits); + +#ifdef __cplusplus +} +#endif diff --git a/lib/digital_signal/presets/nfc/iso15693_signal.c b/lib/digital_signal/presets/nfc/iso15693_signal.c new file mode 100644 index 000000000000..7e8e2571d190 --- /dev/null +++ b/lib/digital_signal/presets/nfc/iso15693_signal.c @@ -0,0 +1,197 @@ +#include "iso15693_signal.h" + +#include + +#define BITS_IN_BYTE (8U) + +#define ISO15693_SIGNAL_COEFF_HI (1U) +#define ISO15693_SIGNAL_COEFF_LO (4U) + +#define ISO15693_SIGNAL_ZERO_EDGES (16U) +#define ISO15693_SIGNAL_ONE_EDGES (ISO15693_SIGNAL_ZERO_EDGES + 1U) +#define ISO15693_SIGNAL_EOF_EDGES (64U) +#define ISO15693_SIGNAL_SOF_EDGES (ISO15693_SIGNAL_EOF_EDGES + 1U) +#define ISO15693_SIGNAL_EDGES (1350U) + +#define ISO15693_SIGNAL_FC (13.56e6) +#define ISO15693_SIGNAL_FC_16 (16.0e11 / ISO15693_SIGNAL_FC) +#define ISO15693_SIGNAL_FC_256 (256.0e11 / ISO15693_SIGNAL_FC) +#define ISO15693_SIGNAL_FC_768 (768.0e11 / ISO15693_SIGNAL_FC) + +typedef enum { + Iso15693SignalIndexSof, + Iso15693SignalIndexEof, + Iso15693SignalIndexOne, + Iso15693SignalIndexZero, + Iso15693SignalIndexNum, +} Iso15693SignalIndex; + +typedef DigitalSignal* Iso15693SignalBank[Iso15693SignalIndexNum]; + +struct Iso15693Signal { + DigitalSequence* tx_sequence; + Iso15693SignalBank banks[Iso15693SignalDataRateNum]; +}; + +// Add an unmodulated signal for the length of Fc / 256 * k (where k = 1 or 4) +static void iso15693_add_silence(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI : + ISO15693_SIGNAL_COEFF_LO; + digital_signal_add_pulse(signal, ISO15693_SIGNAL_FC_256 * k, false); +} + +// Add 8 * k subcarrier pulses of Fc / 16 (where k = 1 or 4) +static void iso15693_add_subcarrier(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI : + ISO15693_SIGNAL_COEFF_LO; + for(uint32_t i = 0; i < ISO15693_SIGNAL_ZERO_EDGES * k; ++i) { + digital_signal_add_pulse(signal, ISO15693_SIGNAL_FC_16, !(i % 2)); + } +} + +static void iso15693_add_bit(DigitalSignal* signal, Iso15693SignalDataRate data_rate, bool bit) { + if(bit) { + iso15693_add_silence(signal, data_rate); + iso15693_add_subcarrier(signal, data_rate); + } else { + iso15693_add_subcarrier(signal, data_rate); + iso15693_add_silence(signal, data_rate); + } +} + +static inline void iso15693_add_sof(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) { + iso15693_add_silence(signal, data_rate); + } + + for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) { + iso15693_add_subcarrier(signal, data_rate); + } + + iso15693_add_bit(signal, data_rate, true); +} + +static inline void iso15693_add_eof(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + iso15693_add_bit(signal, data_rate, false); + + for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) { + iso15693_add_subcarrier(signal, data_rate); + } + + for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) { + iso15693_add_silence(signal, data_rate); + } +} + +static inline uint32_t + iso15693_get_sequence_index(Iso15693SignalIndex index, Iso15693SignalDataRate data_rate) { + return index + data_rate * Iso15693SignalIndexNum; +} + +static inline void + iso15693_add_byte(Iso15693Signal* instance, Iso15693SignalDataRate data_rate, uint8_t byte) { + for(size_t i = 0; i < BITS_IN_BYTE; i++) { + const uint8_t bit = byte & (1U << i); + digital_sequence_add( + instance->tx_sequence, + iso15693_get_sequence_index( + bit ? Iso15693SignalIndexOne : Iso15693SignalIndexZero, data_rate)); + } +} + +static inline void iso15693_signal_encode( + Iso15693Signal* instance, + Iso15693SignalDataRate data_rate, + const uint8_t* tx_data, + size_t tx_data_size) { + digital_sequence_add( + instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexSof, data_rate)); + + for(size_t i = 0; i < tx_data_size; i++) { + iso15693_add_byte(instance, data_rate, tx_data[i]); + } + + digital_sequence_add( + instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexEof, data_rate)); +} + +static void iso15693_signal_bank_fill(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) { + const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI : + ISO15693_SIGNAL_COEFF_LO; + DigitalSignal** bank = instance->banks[data_rate]; + + // FIXME: possibly a couple of wasted edges when k > 1 + bank[Iso15693SignalIndexSof] = digital_signal_alloc(ISO15693_SIGNAL_SOF_EDGES * k); + bank[Iso15693SignalIndexEof] = digital_signal_alloc(ISO15693_SIGNAL_EOF_EDGES * k); + bank[Iso15693SignalIndexOne] = digital_signal_alloc(ISO15693_SIGNAL_ONE_EDGES * k); + bank[Iso15693SignalIndexZero] = digital_signal_alloc(ISO15693_SIGNAL_ZERO_EDGES * k); + + iso15693_add_sof(bank[Iso15693SignalIndexSof], data_rate); + iso15693_add_eof(bank[Iso15693SignalIndexEof], data_rate); + iso15693_add_bit(bank[Iso15693SignalIndexOne], data_rate, true); + iso15693_add_bit(bank[Iso15693SignalIndexZero], data_rate, false); +} + +static void + iso15693_signal_bank_clear(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) { + DigitalSignal** bank = instance->banks[data_rate]; + + for(uint32_t i = 0; i < Iso15693SignalIndexNum; ++i) { + digital_signal_free(bank[i]); + } +} + +static void + iso15693_signal_bank_register(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) { + for(uint32_t i = 0; i < Iso15693SignalIndexNum; ++i) { + digital_sequence_set_signal( + instance->tx_sequence, + iso15693_get_sequence_index(i, data_rate), + instance->banks[data_rate][i]); + } +} + +Iso15693Signal* iso15693_signal_alloc(const GpioPin* pin) { + furi_assert(pin); + + Iso15693Signal* instance = malloc(sizeof(Iso15693Signal)); + + instance->tx_sequence = digital_sequence_alloc(BITS_IN_BYTE * 255 + 2, pin); + + for(uint32_t i = 0; i < Iso15693SignalDataRateNum; ++i) { + iso15693_signal_bank_fill(instance, i); + iso15693_signal_bank_register(instance, i); + } + + return instance; +} + +void iso15693_signal_free(Iso15693Signal* instance) { + furi_assert(instance); + + digital_sequence_free(instance->tx_sequence); + + for(uint32_t i = 0; i < Iso15693SignalDataRateNum; ++i) { + iso15693_signal_bank_clear(instance, i); + } + + free(instance); +} + +void iso15693_signal_tx( + Iso15693Signal* instance, + Iso15693SignalDataRate data_rate, + const uint8_t* tx_data, + size_t tx_data_size) { + furi_assert(instance); + furi_assert(data_rate < Iso15693SignalDataRateNum); + furi_assert(tx_data); + furi_assert(tx_data_size / BITS_IN_BYTE); + + FURI_CRITICAL_ENTER(); + digital_sequence_clear(instance->tx_sequence); + iso15693_signal_encode(instance, data_rate, tx_data, tx_data_size); + digital_sequence_send(instance->tx_sequence); + + FURI_CRITICAL_EXIT(); +} diff --git a/lib/digital_signal/presets/nfc/iso15693_signal.h b/lib/digital_signal/presets/nfc/iso15693_signal.h new file mode 100644 index 000000000000..e5f2c3ca7fb2 --- /dev/null +++ b/lib/digital_signal/presets/nfc/iso15693_signal.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso15693Signal Iso15693Signal; + +typedef enum { + Iso15693SignalDataRateHi, + Iso15693SignalDataRateLo, + Iso15693SignalDataRateNum, +} Iso15693SignalDataRate; + +Iso15693Signal* iso15693_signal_alloc(const GpioPin* pin); + +void iso15693_signal_free(Iso15693Signal* instance); + +void iso15693_signal_tx( + Iso15693Signal* instance, + Iso15693SignalDataRate data_rate, + const uint8_t* tx_data, + size_t tx_data_size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/drivers/SConscript b/lib/drivers/SConscript index 3b7ee2401e63..103472ccb35b 100644 --- a/lib/drivers/SConscript +++ b/lib/drivers/SConscript @@ -4,6 +4,9 @@ env.Append( CPPPATH=[ "#/lib/drivers", ], + SDK_HEADERS=[ + File("cc1101_regs.h"), + ], ) diff --git a/lib/drivers/bq25896.c b/lib/drivers/bq25896.c index 4c1d687cb024..76aae5e82369 100644 --- a/lib/drivers/bq25896.c +++ b/lib/drivers/bq25896.c @@ -35,13 +35,15 @@ typedef struct { static bq25896_regs_t bq25896_regs; -void bq25896_init(FuriHalI2cBusHandle* handle) { +bool bq25896_init(FuriHalI2cBusHandle* handle) { + bool result = true; + bq25896_regs.r14.REG_RST = 1; - furi_hal_i2c_write_reg_8( + result &= furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x14, *(uint8_t*)&bq25896_regs.r14, BQ25896_I2C_TIMEOUT); // Readout all registers - furi_hal_i2c_read_mem( + result &= furi_hal_i2c_read_mem( handle, BQ25896_ADDRESS, 0x00, @@ -52,26 +54,34 @@ void bq25896_init(FuriHalI2cBusHandle* handle) { // Poll ADC forever bq25896_regs.r02.CONV_START = 1; bq25896_regs.r02.CONV_RATE = 1; - furi_hal_i2c_write_reg_8( + result &= furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x02, *(uint8_t*)&bq25896_regs.r02, BQ25896_I2C_TIMEOUT); bq25896_regs.r07.WATCHDOG = WatchdogDisable; - furi_hal_i2c_write_reg_8( + result &= furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x07, *(uint8_t*)&bq25896_regs.r07, BQ25896_I2C_TIMEOUT); // OTG power configuration bq25896_regs.r0A.BOOSTV = 0x8; // BOOST Voltage: 5.062V - bq25896_regs.r0A.BOOST_LIM = BOOST_LIM_1400; // BOOST Current limit: 1.4A - furi_hal_i2c_write_reg_8( + bq25896_regs.r0A.BOOST_LIM = BoostLim_1400; // BOOST Current limit: 1.4A + result &= furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x0A, *(uint8_t*)&bq25896_regs.r0A, BQ25896_I2C_TIMEOUT); - furi_hal_i2c_read_mem( + result &= furi_hal_i2c_read_mem( handle, BQ25896_ADDRESS, 0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs), BQ25896_I2C_TIMEOUT); + + return result; +} + +void bq25896_set_boost_lim(FuriHalI2cBusHandle* handle, BoostLim boost_lim) { + bq25896_regs.r0A.BOOST_LIM = boost_lim; + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x0A, *(uint8_t*)&bq25896_regs.r0A, BQ25896_I2C_TIMEOUT); } void bq25896_poweroff(FuriHalI2cBusHandle* handle) { diff --git a/lib/drivers/bq25896.h b/lib/drivers/bq25896.h index f3d1d0e0583e..d35625ab3f5e 100644 --- a/lib/drivers/bq25896.h +++ b/lib/drivers/bq25896.h @@ -7,7 +7,10 @@ #include /** Initialize Driver */ -void bq25896_init(FuriHalI2cBusHandle* handle); +bool bq25896_init(FuriHalI2cBusHandle* handle); + +/** Set boost lim*/ +void bq25896_set_boost_lim(FuriHalI2cBusHandle* handle, BoostLim boost_lim); /** Send device into shipping mode */ void bq25896_poweroff(FuriHalI2cBusHandle* handle); diff --git a/lib/drivers/bq25896_reg.h b/lib/drivers/bq25896_reg.h index 3cb3d71402da..a6ca3e1c77ee 100644 --- a/lib/drivers/bq25896_reg.h +++ b/lib/drivers/bq25896_reg.h @@ -159,14 +159,16 @@ typedef struct { #define BOOSTV_128 (1 << 1) #define BOOSTV_64 (1 << 0) -#define BOOST_LIM_500 (0b000) -#define BOOST_LIM_750 (0b001) -#define BOOST_LIM_1200 (0b010) -#define BOOST_LIM_1400 (0b011) -#define BOOST_LIM_1650 (0b100) -#define BOOST_LIM_1875 (0b101) -#define BOOST_LIM_2150 (0b110) -#define BOOST_LIM_RSVD (0b111) +typedef enum { + BoostLim_500 = 0b000, + BoostLim_750 = 0b001, + BoostLim_1200 = 0b010, + BoostLim_1400 = 0b011, + BoostLim_1650 = 0b100, + BoostLim_1875 = 0b101, + BoostLim_2150 = 0b110, + BoostLim_Rsvd = 0b111, +} BoostLim; typedef struct { uint8_t BOOST_LIM : 3; // Boost Mode Current Limit diff --git a/lib/drivers/bq27220.c b/lib/drivers/bq27220.c index f64120fa83ec..92dbfcd6a29a 100644 --- a/lib/drivers/bq27220.c +++ b/lib/drivers/bq27220.c @@ -1,12 +1,16 @@ + #include "bq27220.h" #include "bq27220_reg.h" +#include "bq27220_data_memory.h" + +_Static_assert(sizeof(BQ27220DMGaugingConfig) == 2, "Incorrect structure size"); #include #include #define TAG "Gauge" -uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) { +static uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) { uint16_t buf = 0; furi_hal_i2c_read_mem( @@ -15,14 +19,14 @@ uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) { return buf; } -bool bq27220_control(FuriHalI2cBusHandle* handle, uint16_t control) { +static bool bq27220_control(FuriHalI2cBusHandle* handle, uint16_t control) { bool ret = furi_hal_i2c_write_mem( handle, BQ27220_ADDRESS, CommandControl, (uint8_t*)&control, 2, BQ27220_I2C_TIMEOUT); return ret; } -uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) { +static uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) { uint8_t ret = 0; for(uint16_t i = 0; i < len; i++) { ret += data[i]; @@ -30,80 +34,181 @@ uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) { return 0xFF - ret; } -bool bq27220_set_parameter_u16(FuriHalI2cBusHandle* handle, uint16_t address, uint16_t value) { - bool ret; - uint8_t buffer[4]; +static bool bq27220_parameter_check( + FuriHalI2cBusHandle* handle, + uint16_t address, + uint32_t value, + size_t size, + bool update) { + furi_assert(size == 1 || size == 2 || size == 4); + bool ret = false; + uint8_t buffer[6] = {0}; + uint8_t old_data[4] = {0}; + + do { + buffer[0] = address & 0xFF; + buffer[1] = (address >> 8) & 0xFF; + + for(size_t i = 0; i < size; i++) { + buffer[1 + size - i] = (value >> (i * 8)) & 0xFF; + } - buffer[0] = address & 0xFF; - buffer[1] = (address >> 8) & 0xFF; - buffer[2] = (value >> 8) & 0xFF; - buffer[3] = value & 0xFF; - ret = furi_hal_i2c_write_mem( - handle, BQ27220_ADDRESS, CommandSelectSubclass, buffer, 4, BQ27220_I2C_TIMEOUT); + if(update) { + if(!furi_hal_i2c_write_mem( + handle, + BQ27220_ADDRESS, + CommandSelectSubclass, + buffer, + size + 2, + BQ27220_I2C_TIMEOUT)) { + FURI_LOG_I(TAG, "DM write failed"); + break; + } - furi_delay_us(10000); + furi_delay_us(10000); - uint8_t checksum = bq27220_get_checksum(buffer, 4); - buffer[0] = checksum; - buffer[1] = 6; - ret &= furi_hal_i2c_write_mem( - handle, BQ27220_ADDRESS, CommandMACDataSum, buffer, 2, BQ27220_I2C_TIMEOUT); + uint8_t checksum = bq27220_get_checksum(buffer, size + 2); + buffer[0] = checksum; + buffer[1] = 4 + size; // TODO: why 4? + if(!furi_hal_i2c_write_mem( + handle, BQ27220_ADDRESS, CommandMACDataSum, buffer, 2, BQ27220_I2C_TIMEOUT)) { + FURI_LOG_I(TAG, "CRC write failed"); + break; + } + + furi_delay_us(10000); + ret = true; + } else { + if(!furi_hal_i2c_write_mem( + handle, BQ27220_ADDRESS, CommandSelectSubclass, buffer, 2, BQ27220_I2C_TIMEOUT)) { + FURI_LOG_I(TAG, "DM SelectSubclass for read failed"); + break; + } + + if(!furi_hal_i2c_rx(handle, BQ27220_ADDRESS, old_data, size, BQ27220_I2C_TIMEOUT)) { + FURI_LOG_I(TAG, "DM read failed"); + break; + } + + if(*(uint32_t*)&(old_data[0]) != *(uint32_t*)&(buffer[2])) { + FURI_LOG_W( //-V641 + TAG, + "Data at 0x%04x(%zu): 0x%08lx!=0x%08lx", + address, + size, + *(uint32_t*)&(old_data[0]), + *(uint32_t*)&(buffer[2])); + } else { + ret = true; + } + } + } while(0); - furi_delay_us(10000); return ret; } -bool bq27220_init(FuriHalI2cBusHandle* handle, const ParamCEDV* cedv) { - uint32_t timeout = 100; - uint16_t design_cap = bq27220_get_design_capacity(handle); - if(cedv->design_cap == design_cap) { - FURI_LOG_I(TAG, "Skip battery profile update"); - return true; +static bool bq27220_data_memory_check( + FuriHalI2cBusHandle* handle, + const BQ27220DMData* data_memory, + bool update) { + if(update) { + if(!bq27220_control(handle, Control_ENTER_CFG_UPDATE)) { + FURI_LOG_E(TAG, "ENTER_CFG_UPDATE command failed"); + return false; + }; + + // Wait for enter CFG update mode + uint32_t timeout = 100; + OperationStatus status = {0}; + while((status.CFGUPDATE != true) && (timeout-- > 0)) { + bq27220_get_operation_status(handle, &status); + } + + if(timeout == 0) { + FURI_LOG_E(TAG, "CFGUPDATE mode failed"); + return false; + } } - FURI_LOG_I(TAG, "Start updating battery profile"); - OperationStatus status = {0}; - if(!bq27220_control(handle, Control_ENTER_CFG_UPDATE)) { - FURI_LOG_E(TAG, "Can't configure update"); - return false; - }; - while((status.CFGUPDATE != true) && (timeout-- > 0)) { - bq27220_get_operation_status(handle, &status); + // Process data memory records + bool result = true; + while(data_memory->type != BQ27220DMTypeEnd) { + if(data_memory->type == BQ27220DMTypeWait) { + furi_delay_us(data_memory->value.u32); + } else if(data_memory->type == BQ27220DMTypeU8) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.u8, 1, update); + } else if(data_memory->type == BQ27220DMTypeU16) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.u16, 2, update); + } else if(data_memory->type == BQ27220DMTypeU32) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.u32, 4, update); + } else if(data_memory->type == BQ27220DMTypeI8) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.i8, 1, update); + } else if(data_memory->type == BQ27220DMTypeI16) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.i16, 2, update); + } else if(data_memory->type == BQ27220DMTypeI32) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.i32, 4, update); + } else if(data_memory->type == BQ27220DMTypeF32) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.u32, 4, update); + } else if(data_memory->type == BQ27220DMTypePtr8) { + result &= bq27220_parameter_check( + handle, data_memory->address, *(uint8_t*)data_memory->value.u32, 1, update); + } else if(data_memory->type == BQ27220DMTypePtr16) { + result &= bq27220_parameter_check( + handle, data_memory->address, *(uint16_t*)data_memory->value.u32, 2, update); + } else if(data_memory->type == BQ27220DMTypePtr32) { + result &= bq27220_parameter_check( + handle, data_memory->address, *(uint32_t*)data_memory->value.u32, 4, update); + } else { + furi_crash("Invalid DM Type"); + } + data_memory++; } - bq27220_set_parameter_u16(handle, AddressGaugingConfig, cedv->cedv_conf.gauge_conf_raw); - bq27220_set_parameter_u16(handle, AddressFullChargeCapacity, cedv->full_charge_cap); - bq27220_set_parameter_u16(handle, AddressDesignCapacity, cedv->design_cap); - bq27220_set_parameter_u16(handle, AddressEMF, cedv->EMF); - bq27220_set_parameter_u16(handle, AddressC0, cedv->C0); - bq27220_set_parameter_u16(handle, AddressR0, cedv->R0); - bq27220_set_parameter_u16(handle, AddressT0, cedv->T0); - bq27220_set_parameter_u16(handle, AddressR1, cedv->R1); - bq27220_set_parameter_u16(handle, AddressTC, (cedv->TC) << 8 | cedv->C1); - bq27220_set_parameter_u16(handle, AddressStartDOD0, cedv->DOD0); - bq27220_set_parameter_u16(handle, AddressStartDOD10, cedv->DOD10); - bq27220_set_parameter_u16(handle, AddressStartDOD20, cedv->DOD20); - bq27220_set_parameter_u16(handle, AddressStartDOD30, cedv->DOD30); - bq27220_set_parameter_u16(handle, AddressStartDOD40, cedv->DOD40); - bq27220_set_parameter_u16(handle, AddressStartDOD50, cedv->DOD40); - bq27220_set_parameter_u16(handle, AddressStartDOD60, cedv->DOD60); - bq27220_set_parameter_u16(handle, AddressStartDOD70, cedv->DOD70); - bq27220_set_parameter_u16(handle, AddressStartDOD80, cedv->DOD80); - bq27220_set_parameter_u16(handle, AddressStartDOD90, cedv->DOD90); - bq27220_set_parameter_u16(handle, AddressStartDOD100, cedv->DOD100); - bq27220_set_parameter_u16(handle, AddressEDV0, cedv->EDV0); - bq27220_set_parameter_u16(handle, AddressEDV1, cedv->EDV1); - bq27220_set_parameter_u16(handle, AddressEDV2, cedv->EDV2); - - bq27220_control(handle, Control_EXIT_CFG_UPDATE_REINIT); - furi_delay_us(10000); - design_cap = bq27220_get_design_capacity(handle); - if(cedv->design_cap == design_cap) { - FURI_LOG_I(TAG, "Battery profile update success"); - return true; - } else { - FURI_LOG_E(TAG, "Battery profile update failed"); + + // Finalize configuration update + if(update) { + bq27220_control(handle, Control_EXIT_CFG_UPDATE_REINIT); + furi_delay_us(10000); + } + + return result; +} + +bool bq27220_init(FuriHalI2cBusHandle* handle) { + // Request device number(chip PN) + if(!bq27220_control(handle, Control_DEVICE_NUMBER)) { + FURI_LOG_E(TAG, "Device is not present"); + return false; + }; + // Check control response + uint16_t data = 0; + data = bq27220_read_word(handle, CommandControl); + if(data != 0xFF00) { + FURI_LOG_E(TAG, "Invalid control response: %x", data); return false; + }; + + data = bq27220_read_word(handle, CommandMACData); + FURI_LOG_I(TAG, "Device Number %04x", data); + + return data == 0x0220; +} + +bool bq27220_apply_data_memory(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory) { + FURI_LOG_I(TAG, "Verifying data memory"); + if(!bq27220_data_memory_check(handle, data_memory, false)) { + FURI_LOG_I(TAG, "Updating data memory"); + bq27220_data_memory_check(handle, data_memory, true); } + FURI_LOG_I(TAG, "Data memory verification complete"); + + return true; } uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle) { @@ -114,24 +219,23 @@ int16_t bq27220_get_current(FuriHalI2cBusHandle* handle) { return bq27220_read_word(handle, CommandCurrent); } -uint8_t bq27220_get_battery_status(FuriHalI2cBusHandle* handle, BatteryStatus* battery_status) { +bool bq27220_get_battery_status(FuriHalI2cBusHandle* handle, BatteryStatus* battery_status) { uint16_t data = bq27220_read_word(handle, CommandBatteryStatus); if(data == BQ27220_ERROR) { - return BQ27220_ERROR; + return false; } else { *(uint16_t*)battery_status = data; - return BQ27220_SUCCESS; + return true; } } -uint8_t - bq27220_get_operation_status(FuriHalI2cBusHandle* handle, OperationStatus* operation_status) { +bool bq27220_get_operation_status(FuriHalI2cBusHandle* handle, OperationStatus* operation_status) { uint16_t data = bq27220_read_word(handle, CommandOperationStatus); if(data == BQ27220_ERROR) { - return BQ27220_ERROR; + return false; } else { *(uint16_t*)operation_status = data; - return BQ27220_SUCCESS; + return true; } } diff --git a/lib/drivers/bq27220.h b/lib/drivers/bq27220.h index c822301a4796..ca9e0312e87e 100644 --- a/lib/drivers/bq27220.h +++ b/lib/drivers/bq27220.h @@ -47,60 +47,17 @@ typedef struct { _Static_assert(sizeof(OperationStatus) == 2, "Incorrect structure size"); -typedef struct { - // Low byte, Low bit first - bool CCT : 1; - bool CSYNC : 1; - bool RSVD0 : 1; - bool EDV_CMP : 1; - bool SC : 1; - bool FIXED_EDV0 : 1; - uint8_t RSVD1 : 2; - // High byte, Low bit first - bool FCC_LIM : 1; - bool RSVD2 : 1; - bool FC_FOR_VDQ : 1; - bool IGNORE_SD : 1; - bool SME0 : 1; - uint8_t RSVD3 : 3; -} GaugingConfig; - -_Static_assert(sizeof(GaugingConfig) == 2, "Incorrect structure size"); +typedef struct BQ27220DMData BQ27220DMData; -typedef struct { - union { - GaugingConfig gauge_conf; - uint16_t gauge_conf_raw; - } cedv_conf; - uint16_t full_charge_cap; - uint16_t design_cap; - uint16_t EDV0; - uint16_t EDV1; - uint16_t EDV2; - uint16_t EMF; - uint16_t C0; - uint16_t R0; - uint16_t T0; - uint16_t R1; - uint8_t TC; - uint8_t C1; - uint16_t DOD0; - uint16_t DOD10; - uint16_t DOD20; - uint16_t DOD30; - uint16_t DOD40; - uint16_t DOD50; - uint16_t DOD60; - uint16_t DOD70; - uint16_t DOD80; - uint16_t DOD90; - uint16_t DOD100; -} ParamCEDV; +/** Initialize Driver + * @return true on success, false otherwise + */ +bool bq27220_init(FuriHalI2cBusHandle* handle); /** Initialize Driver * @return true on success, false otherwise */ -bool bq27220_init(FuriHalI2cBusHandle* handle, const ParamCEDV* cedv); +bool bq27220_apply_data_memory(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory); /** Get battery voltage in mV or error */ uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle); @@ -109,11 +66,10 @@ uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle); int16_t bq27220_get_current(FuriHalI2cBusHandle* handle); /** Get battery status */ -uint8_t bq27220_get_battery_status(FuriHalI2cBusHandle* handle, BatteryStatus* battery_status); +bool bq27220_get_battery_status(FuriHalI2cBusHandle* handle, BatteryStatus* battery_status); /** Get operation status */ -uint8_t - bq27220_get_operation_status(FuriHalI2cBusHandle* handle, OperationStatus* operation_status); +bool bq27220_get_operation_status(FuriHalI2cBusHandle* handle, OperationStatus* operation_status); /** Get temperature in units of 0.1°K */ uint16_t bq27220_get_temperature(FuriHalI2cBusHandle* handle); diff --git a/lib/drivers/bq27220_data_memory.h b/lib/drivers/bq27220_data_memory.h new file mode 100644 index 000000000000..ae00be883600 --- /dev/null +++ b/lib/drivers/bq27220_data_memory.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include + +typedef enum { + BQ27220DMTypeEnd, + BQ27220DMTypeWait, + BQ27220DMTypeU8, + BQ27220DMTypeU16, + BQ27220DMTypeU32, + BQ27220DMTypeI8, + BQ27220DMTypeI16, + BQ27220DMTypeI32, + BQ27220DMTypeF32, + BQ27220DMTypePtr8, + BQ27220DMTypePtr16, + BQ27220DMTypePtr32, +} BQ27220DMType; + +typedef enum { + BQ27220DMAddressGasGaugingCEDVProfile1GaugingConfig = 0x929B, + BQ27220DMAddressGasGaugingCEDVProfile1FullChargeCapacity = 0x929D, + BQ27220DMAddressGasGaugingCEDVProfile1DesignCapacity = 0x929F, + BQ27220DMAddressGasGaugingCEDVProfile1EMF = 0x92A3, + BQ27220DMAddressGasGaugingCEDVProfile1C0 = 0x92A9, + BQ27220DMAddressGasGaugingCEDVProfile1R0 = 0x92AB, + BQ27220DMAddressGasGaugingCEDVProfile1T0 = 0x92AD, + BQ27220DMAddressGasGaugingCEDVProfile1R1 = 0x92AF, + BQ27220DMAddressGasGaugingCEDVProfile1TC = 0x92B1, + BQ27220DMAddressGasGaugingCEDVProfile1C1 = 0x92B2, + BQ27220DMAddressGasGaugingCEDVProfile1EDV0 = 0x92B4, + BQ27220DMAddressGasGaugingCEDVProfile1EDV1 = 0x92B7, + BQ27220DMAddressGasGaugingCEDVProfile1EDV2 = 0x92BA, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD0 = 0x92BD, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD10 = 0x92BF, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD20 = 0x92C1, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD30 = 0x92C3, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD40 = 0x92C5, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD50 = 0x92C7, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD60 = 0x92C9, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD70 = 0x92CB, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD80 = 0x92CD, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD90 = 0x92CF, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD100 = 0x92D1, + BQ27220DMAddressCalibrationCurrentDeadband = 0x91DE, + BQ27220DMAddressConfigurationPowerSleepCurrent = 0x9217, + BQ27220DMAddressConfigurationCurrentThresholdsDischargeDetectionThreshold = 0x9228, + BQ27220DMAddressConfigurationDataInitialStandby = 0x923C, +} BQ27220DMAddress; + +typedef struct BQ27220DMData BQ27220DMData; + +struct BQ27220DMData { + uint16_t type; + uint16_t address; + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + int8_t i8; + int16_t i16; + int32_t i32; + float f32; + } value; +}; + +typedef struct { + // Low byte, Low bit first + const bool CCT : 1; + const bool CSYNC : 1; + const bool RSVD0 : 1; + const bool EDV_CMP : 1; + const bool SC : 1; + const bool FIXED_EDV0 : 1; + const uint8_t RSVD1 : 2; + // High byte, Low bit first + const bool FCC_LIM : 1; + const bool RSVD2 : 1; + const bool FC_FOR_VDQ : 1; + const bool IGNORE_SD : 1; + const bool SME0 : 1; + const uint8_t RSVD3 : 3; +} BQ27220DMGaugingConfig; diff --git a/lib/drivers/bq27220_reg.h b/lib/drivers/bq27220_reg.h index fa81b66eb4ad..2e6e54aabef2 100644 --- a/lib/drivers/bq27220_reg.h +++ b/lib/drivers/bq27220_reg.h @@ -66,28 +66,3 @@ #define Control_EXIT_CFG_UPDATE_REINIT 0x0091 #define Control_EXIT_CFG_UPDATE 0x0092 #define Control_RETURN_TO_ROM 0x0F00 - -#define AddressGaugingConfig 0x929B -#define AddressFullChargeCapacity 0x929D -#define AddressDesignCapacity 0x929F -#define AddressEMF 0x92A3 -#define AddressC0 0x92A9 -#define AddressR0 0x92AB -#define AddressT0 0x92AD -#define AddressR1 0x92AF -#define AddressTC 0x92B1 -#define AddressC1 0x92B2 -#define AddressEDV0 0x92B4 -#define AddressEDV1 0x92B7 -#define AddressEDV2 0x92BA -#define AddressStartDOD0 0x92BD -#define AddressStartDOD10 0x92BF -#define AddressStartDOD20 0x92C1 -#define AddressStartDOD30 0x92C3 -#define AddressStartDOD40 0x92C5 -#define AddressStartDOD50 0x92C7 -#define AddressStartDOD60 0x92C9 -#define AddressStartDOD70 0x92CB -#define AddressStartDOD80 0x92CD -#define AddressStartDOD90 0x92CF -#define AddressStartDOD100 0x92D1 diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index d0feb0218785..85d915acdcce 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -1,14 +1,27 @@ #include "cc1101.h" #include #include +#include + +static bool cc1101_spi_trx(FuriHalSpiBusHandle* handle, uint8_t* tx, uint8_t* rx, uint8_t size) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(CC1101_TIMEOUT * 1000); + + while(furi_hal_gpio_read(handle->miso)) { + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + return false; + } + } + if(!furi_hal_spi_bus_trx(handle, tx, rx, size, CC1101_TIMEOUT)) return false; + return true; +} CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe) { uint8_t tx[1] = {strobe}; CC1101Status rx[1] = {0}; + rx[0].CHIP_RDYn = 1; - while(furi_hal_gpio_read(handle->miso)) - ; - furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 1, CC1101_TIMEOUT); + cc1101_spi_trx(handle, tx, (uint8_t*)rx, 1); assert(rx[0].CHIP_RDYn == 0); return rx[0]; @@ -17,10 +30,10 @@ CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe) { CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) { uint8_t tx[2] = {reg, data}; CC1101Status rx[2] = {0}; + rx[0].CHIP_RDYn = 1; + rx[1].CHIP_RDYn = 1; - while(furi_hal_gpio_read(handle->miso)) - ; - furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT); + cc1101_spi_trx(handle, tx, (uint8_t*)rx, 2); assert((rx[0].CHIP_RDYn | rx[1].CHIP_RDYn) == 0); return rx[1]; @@ -30,10 +43,9 @@ CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* assert(sizeof(CC1101Status) == 1); uint8_t tx[2] = {reg | CC1101_READ, 0}; CC1101Status rx[2] = {0}; + rx[0].CHIP_RDYn = 1; - while(furi_hal_gpio_read(handle->miso)) - ; - furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT); + cc1101_spi_trx(handle, tx, (uint8_t*)rx, 2); assert((rx[0].CHIP_RDYn) == 0); *data = *(uint8_t*)&rx[1]; @@ -58,40 +70,40 @@ uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle) { return rssi; } -void cc1101_reset(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SRES); +CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SRES); } CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SNOP); } -void cc1101_shutdown(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SPWD); +CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SPWD); } -void cc1101_calibrate(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SCAL); +CC1101Status cc1101_calibrate(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SCAL); } -void cc1101_switch_to_idle(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SIDLE); +CC1101Status cc1101_switch_to_idle(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SIDLE); } -void cc1101_switch_to_rx(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SRX); +CC1101Status cc1101_switch_to_rx(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SRX); } -void cc1101_switch_to_tx(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_STX); +CC1101Status cc1101_switch_to_tx(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_STX); } -void cc1101_flush_rx(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SFRX); +CC1101Status cc1101_flush_rx(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SFRX); } -void cc1101_flush_tx(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SFTX); +CC1101Status cc1101_flush_tx(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SFTX); } uint32_t cc1101_set_frequency(FuriHalSpiBusHandle* handle, uint32_t value) { @@ -123,12 +135,12 @@ uint32_t cc1101_set_intermediate_frequency(FuriHalSpiBusHandle* handle, uint32_t void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]) { uint8_t tx[9] = {CC1101_PATABLE | CC1101_BURST}; //-V1009 CC1101Status rx[9] = {0}; + rx[0].CHIP_RDYn = 1; + rx[8].CHIP_RDYn = 1; memcpy(&tx[1], &value[0], 8); - while(furi_hal_gpio_read(handle->miso)) - ; - furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, sizeof(rx), CC1101_TIMEOUT); + cc1101_spi_trx(handle, tx, (uint8_t*)rx, sizeof(rx)); assert((rx[0].CHIP_RDYn | rx[8].CHIP_RDYn) == 0); } @@ -139,12 +151,7 @@ uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint buff_tx[0] = CC1101_FIFO | CC1101_BURST; memcpy(&buff_tx[1], data, size); - // Start transaction - // Wait IC to become ready - while(furi_hal_gpio_read(handle->miso)) - ; - // Tell IC what we want - furi_hal_spi_bus_trx(handle, buff_tx, (uint8_t*)buff_rx, size + 1, CC1101_TIMEOUT); + cc1101_spi_trx(handle, buff_tx, (uint8_t*)buff_rx, size + 1); return size; } @@ -153,13 +160,7 @@ uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* si uint8_t buff_trx[2]; buff_trx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST; - // Start transaction - // Wait IC to become ready - while(furi_hal_gpio_read(handle->miso)) - ; - - // First byte - packet length - furi_hal_spi_bus_trx(handle, buff_trx, buff_trx, 2, CC1101_TIMEOUT); + cc1101_spi_trx(handle, buff_trx, buff_trx, 2); // Check that the packet is placed in the receive buffer if(buff_trx[1] > 64) { diff --git a/lib/drivers/cc1101.h b/lib/drivers/cc1101.h index af1f15569d1c..d8ee05d5289a 100644 --- a/lib/drivers/cc1101.h +++ b/lib/drivers/cc1101.h @@ -46,8 +46,10 @@ CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* /** Reset * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_reset(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle); /** Get status * @@ -60,8 +62,10 @@ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle); /** Enable shutdown mode * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_shutdown(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle); /** Get Partnumber * @@ -90,38 +94,46 @@ uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle); /** Calibrate oscillator * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_calibrate(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_calibrate(FuriHalSpiBusHandle* handle); /** Switch to idle * * @param handle - pointer to FuriHalSpiHandle */ -void cc1101_switch_to_idle(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_switch_to_idle(FuriHalSpiBusHandle* handle); /** Switch to RX * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_switch_to_rx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_switch_to_rx(FuriHalSpiBusHandle* handle); /** Switch to TX * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_switch_to_tx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_switch_to_tx(FuriHalSpiBusHandle* handle); /** Flush RX FIFO * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_flush_rx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_flush_rx(FuriHalSpiBusHandle* handle); /** Flush TX FIFO * * @param handle - pointer to FuriHalSpiHandle */ -void cc1101_flush_tx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_flush_tx(FuriHalSpiBusHandle* handle); /** Set Frequency * diff --git a/lib/drivers/cc1101_regs.h b/lib/drivers/cc1101_regs.h index a326dc92ce80..e0aed6bd93c8 100644 --- a/lib/drivers/cc1101_regs.h +++ b/lib/drivers/cc1101_regs.h @@ -14,7 +14,7 @@ extern "C" { #define CC1101_IFDIV 0x400 /* IO Bus constants */ -#define CC1101_TIMEOUT 500 +#define CC1101_TIMEOUT 250 /* Bits and pieces */ #define CC1101_READ (1 << 7) /** Read Bit */ diff --git a/lib/drivers/st25r3916.c b/lib/drivers/st25r3916.c new file mode 100644 index 000000000000..de27204ec44f --- /dev/null +++ b/lib/drivers/st25r3916.c @@ -0,0 +1,82 @@ +#include "st25r3916.h" + +#include + +bool st25r3916_init(void) { + return true; +} + +void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask) { + furi_assert(handle); + + uint8_t irq_mask_regs[4] = { + mask & 0xff, + (mask >> 8) & 0xff, + (mask >> 16) & 0xff, + (mask >> 24) & 0xff, + }; + st25r3916_write_burst_regs(handle, ST25R3916_REG_IRQ_MASK_MAIN, irq_mask_regs, 4); +} + +uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle) { + furi_assert(handle); + + uint8_t irq_regs[4] = {}; + uint32_t irq = 0; + st25r3916_read_burst_regs(handle, ST25R3916_REG_IRQ_MASK_MAIN, irq_regs, 4); + // FURI_LOG_I( + // "Mask Irq", "%02X %02X %02X %02X", irq_regs[0], irq_regs[1], irq_regs[2], irq_regs[3]); + st25r3916_read_burst_regs(handle, ST25R3916_REG_IRQ_MAIN, irq_regs, 4); + irq = (uint32_t)irq_regs[0]; + irq |= (uint32_t)irq_regs[1] << 8; + irq |= (uint32_t)irq_regs[2] << 16; + irq |= (uint32_t)irq_regs[3] << 24; + // FURI_LOG_I("iRQ", "%02X %02X %02X %02X", irq_regs[0], irq_regs[1], irq_regs[2], irq_regs[3]); + + return irq; +} + +void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits) { + furi_assert(handle); + furi_assert(buff); + + size_t bytes = (bits + 7) / 8; + + st25r3916_write_reg(handle, ST25R3916_REG_NUM_TX_BYTES2, (uint8_t)(bits & 0xFFU)); + st25r3916_write_reg(handle, ST25R3916_REG_NUM_TX_BYTES1, (uint8_t)((bits >> 8) & 0xFFU)); + + st25r3916_reg_write_fifo(handle, buff, bytes); +} + +bool st25r3916_read_fifo( + FuriHalSpiBusHandle* handle, + uint8_t* buff, + size_t buff_size, + size_t* buff_bits) { + furi_assert(handle); + furi_assert(buff); + + bool read_success = false; + + uint8_t fifo_status[2] = {}; + st25r3916_read_burst_regs(handle, ST25R3916_REG_FIFO_STATUS1, fifo_status, 2); + size_t bytes = ((fifo_status[1] & ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >> + ST25R3916_REG_FIFO_STATUS2_fifo_b_shift) | + fifo_status[0]; + uint8_t bits = + ((fifo_status[1] & ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask) >> + ST25R3916_REG_FIFO_STATUS2_fifo_lb_shift); + + if(bytes <= buff_size) { + st25r3916_reg_read_fifo(handle, buff, bytes); + read_success = true; + } + + if(bits) { + *buff_bits = (bytes - 1) * 8 + bits; + } else { + *buff_bits = bytes * 8; + } + + return read_success; +} diff --git a/lib/drivers/st25r3916.h b/lib/drivers/st25r3916.h new file mode 100644 index 000000000000..213e52813f45 --- /dev/null +++ b/lib/drivers/st25r3916.h @@ -0,0 +1,102 @@ +#pragma once + +#include "st25r3916_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ST25R3916_IRQ_MASK_ALL \ + (uint32_t)(0xFFFFFFFFUL) /*!< All ST25R3916 interrupt sources */ +#define ST25R3916_IRQ_MASK_NONE \ + (uint32_t)(0x00000000UL) /*!< No ST25R3916 interrupt source */ + +/* Main interrupt register */ +#define ST25R3916_IRQ_MASK_OSC \ + (uint32_t)(0x00000080U) /*!< ST25R3916 oscillator stable interrupt */ +#define ST25R3916_IRQ_MASK_FWL \ + (uint32_t)(0x00000040U) /*!< ST25R3916 FIFO water level interrupt */ +#define ST25R3916_IRQ_MASK_RXS \ + (uint32_t)(0x00000020U) /*!< ST25R3916 start of receive interrupt */ +#define ST25R3916_IRQ_MASK_RXE \ + (uint32_t)(0x00000010U) /*!< ST25R3916 end of receive interrupt */ +#define ST25R3916_IRQ_MASK_TXE \ + (uint32_t)(0x00000008U) /*!< ST25R3916 end of transmission interrupt */ +#define ST25R3916_IRQ_MASK_COL \ + (uint32_t)(0x00000004U) /*!< ST25R3916 bit collision interrupt */ +#define ST25R3916_IRQ_MASK_RX_REST \ + (uint32_t)(0x00000002U) /*!< ST25R3916 automatic reception restart interrupt */ +#define ST25R3916_IRQ_MASK_RFU \ + (uint32_t)(0x00000001U) /*!< ST25R3916 RFU interrupt */ + +/* Timer and NFC interrupt register */ +#define ST25R3916_IRQ_MASK_DCT \ + (uint32_t)(0x00008000U) /*!< ST25R3916 termination of direct command interrupt. */ +#define ST25R3916_IRQ_MASK_NRE \ + (uint32_t)(0x00004000U) /*!< ST25R3916 no-response timer expired interrupt */ +#define ST25R3916_IRQ_MASK_GPE \ + (uint32_t)(0x00002000U) /*!< ST25R3916 general purpose timer expired interrupt */ +#define ST25R3916_IRQ_MASK_EON \ + (uint32_t)(0x00001000U) /*!< ST25R3916 external field on interrupt */ +#define ST25R3916_IRQ_MASK_EOF \ + (uint32_t)(0x00000800U) /*!< ST25R3916 external field off interrupt */ +#define ST25R3916_IRQ_MASK_CAC \ + (uint32_t)(0x00000400U) /*!< ST25R3916 collision during RF collision avoidance interrupt */ +#define ST25R3916_IRQ_MASK_CAT \ + (uint32_t)(0x00000200U) /*!< ST25R3916 minimum guard time expired interrupt */ +#define ST25R3916_IRQ_MASK_NFCT \ + (uint32_t)(0x00000100U) /*!< ST25R3916 initiator bit rate recognised interrupt */ + +/* Error and wake-up interrupt register */ +#define ST25R3916_IRQ_MASK_CRC \ + (uint32_t)(0x00800000U) /*!< ST25R3916 CRC error interrupt */ +#define ST25R3916_IRQ_MASK_PAR \ + (uint32_t)(0x00400000U) /*!< ST25R3916 parity error interrupt */ +#define ST25R3916_IRQ_MASK_ERR2 \ + (uint32_t)(0x00200000U) /*!< ST25R3916 soft framing error interrupt */ +#define ST25R3916_IRQ_MASK_ERR1 \ + (uint32_t)(0x00100000U) /*!< ST25R3916 hard framing error interrupt */ +#define ST25R3916_IRQ_MASK_WT \ + (uint32_t)(0x00080000U) /*!< ST25R3916 wake-up interrupt */ +#define ST25R3916_IRQ_MASK_WAM \ + (uint32_t)(0x00040000U) /*!< ST25R3916 wake-up due to amplitude interrupt */ +#define ST25R3916_IRQ_MASK_WPH \ + (uint32_t)(0x00020000U) /*!< ST25R3916 wake-up due to phase interrupt */ +#define ST25R3916_IRQ_MASK_WCAP \ + (uint32_t)(0x00010000U) /*!< ST25R3916 wake-up due to capacitance measurement */ + +/* Passive Target Interrupt Register */ +#define ST25R3916_IRQ_MASK_PPON2 \ + (uint32_t)(0x80000000U) /*!< ST25R3916 PPON2 Field on waiting Timer interrupt */ +#define ST25R3916_IRQ_MASK_SL_WL \ + (uint32_t)(0x40000000U) /*!< ST25R3916 Passive target slot number water level interrupt */ +#define ST25R3916_IRQ_MASK_APON \ + (uint32_t)(0x20000000U) /*!< ST25R3916 Anticollision done and Field On interrupt */ +#define ST25R3916_IRQ_MASK_RXE_PTA \ + (uint32_t)(0x10000000U) /*!< ST25R3916 RXE with an automatic response interrupt */ +#define ST25R3916_IRQ_MASK_WU_F \ + (uint32_t)(0x08000000U) /*!< ST25R3916 212/424b/s Passive target interrupt: Active */ +#define ST25R3916_IRQ_MASK_RFU2 \ + (uint32_t)(0x04000000U) /*!< ST25R3916 RFU2 interrupt */ +#define ST25R3916_IRQ_MASK_WU_A_X \ + (uint32_t)(0x02000000U) /*!< ST25R3916 106kb/s Passive target state interrupt: Active* */ +#define ST25R3916_IRQ_MASK_WU_A \ + (uint32_t)(0x01000000U) /*!< ST25R3916 106kb/s Passive target state interrupt: Active */ + +bool st25r3916_init(void); + +void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask); + +uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle); + +void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits); + +bool st25r3916_read_fifo( + FuriHalSpiBusHandle* handle, + uint8_t* buff, + size_t buff_size, + size_t* buff_bits); + +#ifdef __cplusplus +} +#endif diff --git a/lib/drivers/st25r3916_reg.c b/lib/drivers/st25r3916_reg.c new file mode 100644 index 000000000000..8ac4672383cb --- /dev/null +++ b/lib/drivers/st25r3916_reg.c @@ -0,0 +1,257 @@ +#include "st25r3916_reg.h" + +#include + +#define ST25R3916_WRITE_MODE \ + (0U << 6) /*!< ST25R3916 Operation Mode: Write */ +#define ST25R3916_READ_MODE \ + (1U << 6) /*!< ST25R3916 Operation Mode: Read */ +#define ST25R3916_CMD_MODE \ + (3U << 6) /*!< ST25R3916 Operation Mode: Direct Command */ +#define ST25R3916_FIFO_LOAD \ + (0x80U) /*!< ST25R3916 Operation Mode: FIFO Load */ +#define ST25R3916_FIFO_READ \ + (0x9FU) /*!< ST25R3916 Operation Mode: FIFO Read */ +#define ST25R3916_PT_A_CONFIG_LOAD \ + (0xA0U) /*!< ST25R3916 Operation Mode: Passive Target Memory A-Config Load */ +#define ST25R3916_PT_F_CONFIG_LOAD \ + (0xA8U) /*!< ST25R3916 Operation Mode: Passive Target Memory F-Config Load */ +#define ST25R3916_PT_TSN_DATA_LOAD \ + (0xACU) /*!< ST25R3916 Operation Mode: Passive Target Memory TSN Load */ +#define ST25R3916_PT_MEM_READ \ + (0xBFU) /*!< ST25R3916 Operation Mode: Passive Target Memory Read */ + +#define ST25R3916_CMD_LEN \ + (1U) /*!< ST25R3916 CMD length */ +#define ST25R3916_FIFO_DEPTH (512U) +#define ST25R3916_BUF_LEN \ + (ST25R3916_CMD_LEN + \ + ST25R3916_FIFO_DEPTH) /*!< ST25R3916 communication buffer: CMD + FIFO length */ + +static void st25r3916_reg_tx_byte(FuriHalSpiBusHandle* handle, uint8_t byte) { + uint8_t val = byte; + furi_hal_spi_bus_tx(handle, &val, 1, 5); +} + +void st25r3916_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) { + furi_assert(handle); + st25r3916_read_burst_regs(handle, reg, val, 1); +} + +void st25r3916_read_burst_regs( + FuriHalSpiBusHandle* handle, + uint8_t reg_start, + uint8_t* values, + uint8_t length) { + furi_assert(handle); + furi_assert(values); + furi_assert(length); + + furi_hal_gpio_write(handle->cs, false); + + if(reg_start & ST25R3916_SPACE_B) { + // Send direct command first + st25r3916_reg_tx_byte(handle, ST25R3916_CMD_SPACE_B_ACCESS); + } + st25r3916_reg_tx_byte(handle, (reg_start & ~ST25R3916_SPACE_B) | ST25R3916_READ_MODE); + furi_hal_spi_bus_rx(handle, values, length, 5); + + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) { + furi_assert(handle); + uint8_t reg_val = val; + st25r3916_write_burst_regs(handle, reg, ®_val, 1); +} + +void st25r3916_write_burst_regs( + FuriHalSpiBusHandle* handle, + uint8_t reg_start, + const uint8_t* values, + uint8_t length) { + furi_assert(handle); + furi_assert(values); + furi_assert(length); + + furi_hal_gpio_write(handle->cs, false); + + if(reg_start & ST25R3916_SPACE_B) { + // Send direct command first + st25r3916_reg_tx_byte(handle, ST25R3916_CMD_SPACE_B_ACCESS); + } + st25r3916_reg_tx_byte(handle, (reg_start & ~ST25R3916_SPACE_B) | ST25R3916_WRITE_MODE); + furi_hal_spi_bus_tx(handle, values, length, 5); + + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t length) { + furi_assert(handle); + furi_assert(buff); + furi_assert(length); + furi_assert(length <= ST25R3916_FIFO_DEPTH); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_FIFO_LOAD); + furi_hal_spi_bus_tx(handle, buff, length, 200); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { + furi_assert(handle); + furi_assert(buff); + furi_assert(length); + furi_assert(length <= ST25R3916_FIFO_DEPTH); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_FIFO_READ); + furi_hal_spi_bus_rx(handle, buff, length, 200); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length) { + furi_assert(handle); + furi_assert(values); + furi_assert(length); + furi_assert(length <= ST25R3916_PTM_LEN); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_PT_A_CONFIG_LOAD); + furi_hal_spi_bus_tx(handle, values, length, 200); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { + furi_assert(handle); + furi_assert(buff); + furi_assert(length); + furi_assert(length <= ST25R3916_PTM_LEN); + + uint8_t tmp_buff[ST25R3916_PTM_LEN + 1]; + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_PT_MEM_READ); + furi_hal_spi_bus_rx(handle, tmp_buff, length + 1, 200); + furi_hal_gpio_write(handle->cs, true); + memcpy(buff, tmp_buff + 1, length); +} + +void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length) { + furi_assert(handle); + furi_assert(values); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_PT_F_CONFIG_LOAD); + furi_hal_spi_bus_tx(handle, values, length, 200); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_write_pttsn_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { + furi_assert(handle); + furi_assert(buff); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_PT_TSN_DATA_LOAD); + furi_hal_spi_bus_tx(handle, buff, length, 200); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_direct_cmd(FuriHalSpiBusHandle* handle, uint8_t cmd) { + furi_assert(handle); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, cmd | ST25R3916_CMD_MODE); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) { + furi_assert(handle); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_CMD_TEST_ACCESS); + st25r3916_reg_tx_byte(handle, reg | ST25R3916_READ_MODE); + furi_hal_spi_bus_rx(handle, val, 1, 5); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) { + furi_assert(handle); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_CMD_TEST_ACCESS); + st25r3916_reg_tx_byte(handle, reg | ST25R3916_WRITE_MODE); + furi_hal_spi_bus_tx(handle, &val, 1, 5); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask) { + furi_assert(handle); + + uint8_t reg_val = 0; + st25r3916_read_reg(handle, reg, ®_val); + if((reg_val & ~clr_mask) != reg_val) { + reg_val &= ~clr_mask; + st25r3916_write_reg(handle, reg, reg_val); + } +} + +void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask) { + furi_assert(handle); + + uint8_t reg_val = 0; + st25r3916_read_reg(handle, reg, ®_val); + if((reg_val | set_mask) != reg_val) { + reg_val |= set_mask; + st25r3916_write_reg(handle, reg, reg_val); + } +} + +void st25r3916_change_reg_bits( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t mask, + uint8_t value) { + furi_assert(handle); + + st25r3916_modify_reg(handle, reg, mask, (mask & value)); +} + +void st25r3916_modify_reg( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t clr_mask, + uint8_t set_mask) { + furi_assert(handle); + + uint8_t reg_val = 0; + uint8_t new_val = 0; + st25r3916_read_reg(handle, reg, ®_val); + new_val = (reg_val & ~clr_mask) | set_mask; + if(new_val != reg_val) { + st25r3916_write_reg(handle, reg, new_val); + } +} + +void st25r3916_change_test_reg_bits( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t mask, + uint8_t value) { + furi_assert(handle); + + uint8_t reg_val = 0; + uint8_t new_val = 0; + st25r3916_read_test_reg(handle, reg, ®_val); + new_val = (reg_val & ~mask) | (mask & value); + if(new_val != reg_val) { + st25r3916_write_test_reg(handle, reg, new_val); + } +} + +bool st25r3916_check_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val) { + furi_assert(handle); + + uint8_t reg_val = 0; + st25r3916_read_reg(handle, reg, ®_val); + return ((reg_val & mask) == val); +} diff --git a/lib/drivers/st25r3916_reg.h b/lib/drivers/st25r3916_reg.h new file mode 100644 index 000000000000..29397ebdd924 --- /dev/null +++ b/lib/drivers/st25r3916_reg.h @@ -0,0 +1,1087 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ST25R3916 direct commands */ +#define ST25R3916_CMD_SET_DEFAULT \ + 0xC1U /*!< Puts the chip in default state (same as after power-up) */ +#define ST25R3916_CMD_STOP 0xC2U /*!< Stops all activities and clears FIFO */ +#define ST25R3916_CMD_TRANSMIT_WITH_CRC \ + 0xC4U /*!< Transmit with CRC */ +#define ST25R3916_CMD_TRANSMIT_WITHOUT_CRC \ + 0xC5U /*!< Transmit without CRC */ +#define ST25R3916_CMD_TRANSMIT_REQA \ + 0xC6U /*!< Transmit REQA */ +#define ST25R3916_CMD_TRANSMIT_WUPA \ + 0xC7U /*!< Transmit WUPA */ +#define ST25R3916_CMD_INITIAL_RF_COLLISION \ + 0xC8U /*!< NFC transmit with Initial RF Collision Avoidance */ +#define ST25R3916_CMD_RESPONSE_RF_COLLISION_N \ + 0xC9U /*!< NFC transmit with Response RF Collision Avoidance */ +#define ST25R3916_CMD_GOTO_SENSE \ + 0xCDU /*!< Passive target logic to Sense/Idle state */ +#define ST25R3916_CMD_GOTO_SLEEP \ + 0xCEU /*!< Passive target logic to Sleep/Halt state */ +#define ST25R3916_CMD_MASK_RECEIVE_DATA \ + 0xD0U /*!< Mask receive data */ +#define ST25R3916_CMD_UNMASK_RECEIVE_DATA \ + 0xD1U /*!< Unmask receive data */ +#define ST25R3916_CMD_AM_MOD_STATE_CHANGE \ + 0xD2U /*!< AM Modulation state change */ +#define ST25R3916_CMD_MEASURE_AMPLITUDE \ + 0xD3U /*!< Measure singal amplitude on RFI inputs */ +#define ST25R3916_CMD_RESET_RXGAIN \ + 0xD5U /*!< Reset RX Gain */ +#define ST25R3916_CMD_ADJUST_REGULATORS \ + 0xD6U /*!< Adjust regulators */ +#define ST25R3916_CMD_CALIBRATE_DRIVER_TIMING \ + 0xD8U /*!< Starts the sequence to adjust the driver timing */ +#define ST25R3916_CMD_MEASURE_PHASE \ + 0xD9U /*!< Measure phase between RFO and RFI signal */ +#define ST25R3916_CMD_CLEAR_RSSI \ + 0xDAU /*!< Clear RSSI bits and restart the measurement */ +#define ST25R3916_CMD_CLEAR_FIFO \ + 0xDBU /*!< Clears FIFO, Collision and IRQ status */ +#define ST25R3916_CMD_TRANSPARENT_MODE \ + 0xDCU /*!< Transparent mode */ +#define ST25R3916_CMD_CALIBRATE_C_SENSOR \ + 0xDDU /*!< Calibrate the capacitive sensor */ +#define ST25R3916_CMD_MEASURE_CAPACITANCE \ + 0xDEU /*!< Measure capacitance */ +#define ST25R3916_CMD_MEASURE_VDD \ + 0xDFU /*!< Measure power supply voltage */ +#define ST25R3916_CMD_START_GP_TIMER \ + 0xE0U /*!< Start the general purpose timer */ +#define ST25R3916_CMD_START_WUP_TIMER \ + 0xE1U /*!< Start the wake-up timer */ +#define ST25R3916_CMD_START_MASK_RECEIVE_TIMER \ + 0xE2U /*!< Start the mask-receive timer */ +#define ST25R3916_CMD_START_NO_RESPONSE_TIMER \ + 0xE3U /*!< Start the no-response timer */ +#define ST25R3916_CMD_START_PPON2_TIMER \ + 0xE4U /*!< Start PPon2 timer */ +#define ST25R3916_CMD_STOP_NRT \ + 0xE8U /*!< Stop No Response Timer */ +#define ST25R3916_CMD_SPACE_B_ACCESS \ + 0xFBU /*!< Enable R/W access to the test registers */ +#define ST25R3916_CMD_TEST_ACCESS \ + 0xFCU /*!< Enable R/W access to the test registers */ + +#define ST25R3916_SPACE_B 0x40U /*!< ST25R3916 Space-B indicator */ +#define ST25R3916_SPACE_B_REG_LEN 16U /*!< Number of register in the space B */ + +#define ST25R3916_FIFO_STATUS_LEN 2 /*!< Number of FIFO Status Register */ + +#define ST25R3916_PTM_A_LEN 15U /*!< Passive target memory A config length */ +#define ST25R3916_PTM_B_LEN 0U /*!< Passive target memory B config length */ +#define ST25R3916_PTM_F_LEN 21U /*!< Passive target memory F config length */ +#define ST25R3916_PTM_TSN_LEN 12U /*!< Passive target memory TSN data length */ + +/*! Full Passive target memory length */ +#define ST25R3916_PTM_LEN \ + (ST25R3916_PTM_A_LEN + ST25R3916_PTM_B_LEN + ST25R3916_PTM_F_LEN + ST25R3916_PTM_TSN_LEN) + +/* IO configuration registers */ +#define ST25R3916_REG_IO_CONF1 0x00U /*!< RW IO Configuration Register 1 */ +#define ST25R3916_REG_IO_CONF2 0x01U /*!< RW IO Configuration Register 2 */ + +/* Operation control and mode definition registers */ +#define ST25R3916_REG_OP_CONTROL 0x02U /*!< RW Operation Control Register */ +#define ST25R3916_REG_MODE 0x03U /*!< RW Mode Definition Register */ +#define ST25R3916_REG_BIT_RATE 0x04U /*!< RW Bit Rate Definition Register */ + +/* Protocol Configuration registers */ +#define ST25R3916_REG_ISO14443A_NFC \ + 0x05U /*!< RW ISO14443A and NFC 106 kBit/s Settings Register */ +#define ST25R3916_REG_EMD_SUP_CONF \ + (ST25R3916_SPACE_B | 0x05U) /*!< RW EMD Suppression Configuration Register */ +#define ST25R3916_REG_ISO14443B_1 \ + 0x06U /*!< RW ISO14443B Settings Register 1 */ +#define ST25R3916_REG_SUBC_START_TIME \ + (ST25R3916_SPACE_B | 0x06U) /*!< RW Subcarrier Start Time Register */ +#define ST25R3916_REG_ISO14443B_2 \ + 0x07U /*!< RW ISO14443B Settings Register 2 */ +#define ST25R3916_REG_PASSIVE_TARGET \ + 0x08U /*!< RW Passive Target Definition Register */ +#define ST25R3916_REG_STREAM_MODE \ + 0x09U /*!< RW Stream Mode Definition Register */ +#define ST25R3916_REG_AUX 0x0AU /*!< RW Auxiliary Definition Register */ + +/* Receiver Configuration registers */ +#define ST25R3916_REG_RX_CONF1 0x0BU /*!< RW Receiver Configuration Register 1 */ +#define ST25R3916_REG_RX_CONF2 0x0CU /*!< RW Receiver Configuration Register 2 */ +#define ST25R3916_REG_RX_CONF3 0x0DU /*!< RW Receiver Configuration Register 3 */ +#define ST25R3916_REG_RX_CONF4 0x0EU /*!< RW Receiver Configuration Register 4 */ +#define ST25R3916_REG_P2P_RX_CONF \ + (ST25R3916_SPACE_B | 0x0BU) /*!< RW P2P Receiver Configuration Register 1 */ +#define ST25R3916_REG_CORR_CONF1 \ + (ST25R3916_SPACE_B | 0x0CU) /*!< RW Correlator configuration register 1 */ +#define ST25R3916_REG_CORR_CONF2 \ + (ST25R3916_SPACE_B | 0x0DU) /*!< RW Correlator configuration register 2 */ + +/* Timer definition registers */ +#define ST25R3916_REG_MASK_RX_TIMER \ + 0x0FU /*!< RW Mask Receive Timer Register */ +#define ST25R3916_REG_NO_RESPONSE_TIMER1 \ + 0x10U /*!< RW No-response Timer Register 1 */ +#define ST25R3916_REG_NO_RESPONSE_TIMER2 \ + 0x11U /*!< RW No-response Timer Register 2 */ +#define ST25R3916_REG_TIMER_EMV_CONTROL \ + 0x12U /*!< RW Timer and EMV Control */ +#define ST25R3916_REG_GPT1 0x13U /*!< RW General Purpose Timer Register 1 */ +#define ST25R3916_REG_GPT2 0x14U /*!< RW General Purpose Timer Register 2 */ +#define ST25R3916_REG_PPON2 0x15U /*!< RW PPON2 Field waiting Timer Register */ +#define ST25R3916_REG_SQUELCH_TIMER \ + (ST25R3916_SPACE_B | 0x0FU) /*!< RW Squelch timeout Register */ +#define ST25R3916_REG_FIELD_ON_GT \ + (ST25R3916_SPACE_B | 0x15U) /*!< RW NFC Field on guard time */ + +/* Interrupt and associated reporting registers */ +#define ST25R3916_REG_IRQ_MASK_MAIN \ + 0x16U /*!< RW Mask Main Interrupt Register */ +#define ST25R3916_REG_IRQ_MASK_TIMER_NFC \ + 0x17U /*!< RW Mask Timer and NFC Interrupt Register */ +#define ST25R3916_REG_IRQ_MASK_ERROR_WUP \ + 0x18U /*!< RW Mask Error and Wake-up Interrupt Register */ +#define ST25R3916_REG_IRQ_MASK_TARGET \ + 0x19U /*!< RW Mask 3916 Target Interrupt Register */ +#define ST25R3916_REG_IRQ_MAIN 0x1AU /*!< R Main Interrupt Register */ +#define ST25R3916_REG_IRQ_TIMER_NFC \ + 0x1BU /*!< R Timer and NFC Interrupt Register */ +#define ST25R3916_REG_IRQ_ERROR_WUP \ + 0x1CU /*!< R Error and Wake-up Interrupt Register */ +#define ST25R3916_REG_IRQ_TARGET 0x1DU /*!< R ST25R3916 Target Interrupt Register */ +#define ST25R3916_REG_FIFO_STATUS1 \ + 0x1EU /*!< R FIFO Status Register 1 */ +#define ST25R3916_REG_FIFO_STATUS2 \ + 0x1FU /*!< R FIFO Status Register 2 */ +#define ST25R3916_REG_COLLISION_STATUS \ + 0x20U /*!< R Collision Display Register */ +#define ST25R3916_REG_PASSIVE_TARGET_STATUS \ + 0x21U /*!< R Passive target state status */ + +/* Definition of number of transmitted bytes */ +#define ST25R3916_REG_NUM_TX_BYTES1 \ + 0x22U /*!< RW Number of Transmitted Bytes Register 1 */ +#define ST25R3916_REG_NUM_TX_BYTES2 \ + 0x23U /*!< RW Number of Transmitted Bytes Register 2 */ + +/* NFCIP Bit Rate Display Register */ +#define ST25R3916_REG_NFCIP1_BIT_RATE \ + 0x24U /*!< R NFCIP Bit Rate Detection Display Register */ + +/* A/D Converter Output Register */ +#define ST25R3916_REG_AD_RESULT 0x25U /*!< R A/D Converter Output Register */ + +/* Antenna tuning registers */ +#define ST25R3916_REG_ANT_TUNE_A 0x26U /*!< RW Antenna Tuning Control (AAT-A) Register 1 */ +#define ST25R3916_REG_ANT_TUNE_B 0x27U /*!< RW Antenna Tuning Control (AAT-B) Register 2 */ + +/* Antenna Driver and Modulation registers */ +#define ST25R3916_REG_TX_DRIVER 0x28U /*!< RW TX driver register */ +#define ST25R3916_REG_PT_MOD 0x29U /*!< RW PT modulation Register */ +#define ST25R3916_REG_AUX_MOD \ + (ST25R3916_SPACE_B | 0x28U) /*!< RW Aux Modulation setting Register */ +#define ST25R3916_REG_TX_DRIVER_TIMING \ + (ST25R3916_SPACE_B | 0x29U) /*!< RW TX driver timing Register */ +#define ST25R3916_REG_RES_AM_MOD \ + (ST25R3916_SPACE_B | 0x2AU) /*!< RW Resistive AM modulation register */ +#define ST25R3916_REG_TX_DRIVER_STATUS \ + (ST25R3916_SPACE_B | 0x2BU) /*!< R TX driver timing readout Register */ + +/* External Field Detector Threshold Registers */ +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV \ + 0x2AU /*!< RW External Field Detector Activation Threshold Reg */ +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV \ + 0x2BU /*!< RW External Field Detector Deactivation Threshold Reg*/ + +/* Regulator registers */ +#define ST25R3916_REG_REGULATOR_CONTROL \ + 0x2CU /*!< RW Regulated Voltage Control Register */ +#define ST25R3916_REG_REGULATOR_RESULT \ + (ST25R3916_SPACE_B | 0x2CU) /*!< R Regulator Display Register */ + +/* Receiver State Display Register */ +#define ST25R3916_REG_RSSI_RESULT \ + 0x2DU /*!< R RSSI Display Register */ +#define ST25R3916_REG_GAIN_RED_STATE \ + 0x2EU /*!< R Gain Reduction State Register */ +#define ST25R3916_REG_CAP_SENSOR_CONTROL \ + 0x2FU /*!< RW Capacitive Sensor Control Register */ +#define ST25R3916_REG_CAP_SENSOR_RESULT \ + 0x30U /*!< R Capacitive Sensor Display Register */ +#define ST25R3916_REG_AUX_DISPLAY \ + 0x31U /*!< R Auxiliary Display Register */ + +/* Over/Undershoot Protection Configuration Registers */ +#define ST25R3916_REG_OVERSHOOT_CONF1 \ + (ST25R3916_SPACE_B | 0x30U) /*!< RW Overshoot Protection Configuration Register 1 */ +#define ST25R3916_REG_OVERSHOOT_CONF2 \ + (ST25R3916_SPACE_B | 0x31U) /*!< RW Overshoot Protection Configuration Register 2 */ +#define ST25R3916_REG_UNDERSHOOT_CONF1 \ + (ST25R3916_SPACE_B | 0x32U) /*!< RW Undershoot Protection Configuration Register 1 */ +#define ST25R3916_REG_UNDERSHOOT_CONF2 \ + (ST25R3916_SPACE_B | 0x33U) /*!< RW Undershoot Protection Configuration Register 2 */ + +/* Detection of card presence */ +#define ST25R3916_REG_WUP_TIMER_CONTROL \ + 0x32U /*!< RW Wake-up Timer Control Register */ +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF \ + 0x33U /*!< RW Amplitude Measurement Configuration Register */ +#define ST25R3916_REG_AMPLITUDE_MEASURE_REF \ + 0x34U /*!< RW Amplitude Measurement Reference Register */ +#define ST25R3916_REG_AMPLITUDE_MEASURE_AA_RESULT \ + 0x35U /*!< R Amplitude Measurement Auto Averaging Display Reg */ +#define ST25R3916_REG_AMPLITUDE_MEASURE_RESULT \ + 0x36U /*!< R Amplitude Measurement Display Register */ +#define ST25R3916_REG_PHASE_MEASURE_CONF \ + 0x37U /*!< RW Phase Measurement Configuration Register */ +#define ST25R3916_REG_PHASE_MEASURE_REF \ + 0x38U /*!< RW Phase Measurement Reference Register */ +#define ST25R3916_REG_PHASE_MEASURE_AA_RESULT \ + 0x39U /*!< R Phase Measurement Auto Averaging Display Register */ +#define ST25R3916_REG_PHASE_MEASURE_RESULT \ + 0x3AU /*!< R Phase Measurement Display Register */ +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF \ + 0x3BU /*!< RW Capacitance Measurement Configuration Register */ +#define ST25R3916_REG_CAPACITANCE_MEASURE_REF \ + 0x3CU /*!< RW Capacitance Measurement Reference Register */ +#define ST25R3916_REG_CAPACITANCE_MEASURE_AA_RESULT \ + 0x3DU /*!< R Capacitance Measurement Auto Averaging Display Reg*/ +#define ST25R3916_REG_CAPACITANCE_MEASURE_RESULT \ + 0x3EU /*!< R Capacitance Measurement Display Register */ + +/* IC identity */ +#define ST25R3916_REG_IC_IDENTITY \ + 0x3FU /*!< R Chip Id: 0 for old silicon, v2 silicon: 0x09 */ + +/*! Register bit definitions \cond DOXYGEN_SUPRESS */ + +#define ST25R3916_REG_IO_CONF1_single (1U << 7) +#define ST25R3916_REG_IO_CONF1_rfo2 (1U << 6) +#define ST25R3916_REG_IO_CONF1_i2c_thd1 (1U << 5) +#define ST25R3916_REG_IO_CONF1_i2c_thd0 (1U << 4) +#define ST25R3916_REG_IO_CONF1_i2c_thd_mask (3U << 4) +#define ST25R3916_REG_IO_CONF1_i2c_thd_shift (4U) +#define ST25R3916_REG_IO_CONF1_rfu (1U << 3) +#define ST25R3916_REG_IO_CONF1_out_cl1 (1U << 2) +#define ST25R3916_REG_IO_CONF1_out_cl0 (1U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_disabled (3U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_13_56MHZ (2U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_4_78MHZ (1U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_3_39MHZ (0U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_mask (3U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_shift (1U) +#define ST25R3916_REG_IO_CONF1_lf_clk_off (1U << 0) +#define ST25R3916_REG_IO_CONF1_lf_clk_off_on (1U << 0) +#define ST25R3916_REG_IO_CONF1_lf_clk_off_off (0U << 0) + +#define ST25R3916_REG_IO_CONF2_sup3V (1U << 7) +#define ST25R3916_REG_IO_CONF2_sup3V_3V (1U << 7) +#define ST25R3916_REG_IO_CONF2_sup3V_5V (0U << 7) +#define ST25R3916_REG_IO_CONF2_vspd_off (1U << 6) +#define ST25R3916_REG_IO_CONF2_aat_en (1U << 5) +#define ST25R3916_REG_IO_CONF2_miso_pd2 (1U << 4) +#define ST25R3916_REG_IO_CONF2_miso_pd1 (1U << 3) +#define ST25R3916_REG_IO_CONF2_io_drv_lvl (1U << 2) +#define ST25R3916_REG_IO_CONF2_slow_up (1U << 0) + +#define ST25R3916_REG_OP_CONTROL_en (1U << 7) +#define ST25R3916_REG_OP_CONTROL_rx_en (1U << 6) +#define ST25R3916_REG_OP_CONTROL_rx_chn (1U << 5) +#define ST25R3916_REG_OP_CONTROL_rx_man (1U << 4) +#define ST25R3916_REG_OP_CONTROL_tx_en (1U << 3) +#define ST25R3916_REG_OP_CONTROL_wu (1U << 2) +#define ST25R3916_REG_OP_CONTROL_en_fd_c1 (1U << 1) +#define ST25R3916_REG_OP_CONTROL_en_fd_c0 (1U << 0) +#define ST25R3916_REG_OP_CONTROL_en_fd_efd_off (0U << 0) +#define ST25R3916_REG_OP_CONTROL_en_fd_manual_efd_ca (1U << 0) +#define ST25R3916_REG_OP_CONTROL_en_fd_manual_efd_pdt (2U << 0) +#define ST25R3916_REG_OP_CONTROL_en_fd_auto_efd (3U << 0) +#define ST25R3916_REG_OP_CONTROL_en_fd_shift (0U) +#define ST25R3916_REG_OP_CONTROL_en_fd_mask (3U << 0) + +#define ST25R3916_REG_MODE_targ (1U << 7) +#define ST25R3916_REG_MODE_targ_targ (1U << 7) +#define ST25R3916_REG_MODE_targ_init (0U << 7) +#define ST25R3916_REG_MODE_om3 (1U << 6) +#define ST25R3916_REG_MODE_om2 (1U << 5) +#define ST25R3916_REG_MODE_om1 (1U << 4) +#define ST25R3916_REG_MODE_om0 (1U << 3) +#define ST25R3916_REG_MODE_om_bpsk_stream (0xfU << 3) +#define ST25R3916_REG_MODE_om_subcarrier_stream (0xeU << 3) +#define ST25R3916_REG_MODE_om_topaz (0x4U << 3) +#define ST25R3916_REG_MODE_om_felica (0x3U << 3) +#define ST25R3916_REG_MODE_om_iso14443b (0x2U << 3) +#define ST25R3916_REG_MODE_om_iso14443a (0x1U << 3) +#define ST25R3916_REG_MODE_om_targ_nfca (0x1U << 3) +#define ST25R3916_REG_MODE_om_targ_nfcb (0x2U << 3) +#define ST25R3916_REG_MODE_om_targ_nfcf (0x4U << 3) +#define ST25R3916_REG_MODE_om_targ_nfcip (0x7U << 3) +#define ST25R3916_REG_MODE_om_nfc (0x0U << 3) +#define ST25R3916_REG_MODE_om_mask (0xfU << 3) +#define ST25R3916_REG_MODE_om_shift (3U) +#define ST25R3916_REG_MODE_tr_am (1U << 2) +#define ST25R3916_REG_MODE_tr_am_ook (0U << 2) +#define ST25R3916_REG_MODE_tr_am_am (1U << 2) +#define ST25R3916_REG_MODE_nfc_ar1 (1U << 1) +#define ST25R3916_REG_MODE_nfc_ar0 (1U << 0) +#define ST25R3916_REG_MODE_nfc_ar_off (0U << 0) +#define ST25R3916_REG_MODE_nfc_ar_auto_rx (1U << 0) +#define ST25R3916_REG_MODE_nfc_ar_eof (2U << 0) +#define ST25R3916_REG_MODE_nfc_ar_rfu (3U << 0) +#define ST25R3916_REG_MODE_nfc_ar_mask (3U << 0) +#define ST25R3916_REG_MODE_nfc_ar_shift (0U) + +#define ST25R3916_REG_BIT_RATE_txrate_106 (0x0U << 4) +#define ST25R3916_REG_BIT_RATE_txrate_212 (0x1U << 4) +#define ST25R3916_REG_BIT_RATE_txrate_424 (0x2U << 4) +#define ST25R3916_REG_BIT_RATE_txrate_848 (0x3U << 4) +#define ST25R3916_REG_BIT_RATE_txrate_mask (0x3U << 4) +#define ST25R3916_REG_BIT_RATE_txrate_shift (4U) +#define ST25R3916_REG_BIT_RATE_rxrate_106 (0x0U << 0) +#define ST25R3916_REG_BIT_RATE_rxrate_212 (0x1U << 0) +#define ST25R3916_REG_BIT_RATE_rxrate_424 (0x2U << 0) +#define ST25R3916_REG_BIT_RATE_rxrate_848 (0x3U << 0) +#define ST25R3916_REG_BIT_RATE_rxrate_mask (0x3U << 0) +#define ST25R3916_REG_BIT_RATE_rxrate_shift (0U) + +#define ST25R3916_REG_ISO14443A_NFC_no_tx_par (1U << 7) +#define ST25R3916_REG_ISO14443A_NFC_no_tx_par_off (0U << 7) +#define ST25R3916_REG_ISO14443A_NFC_no_rx_par (1U << 6) +#define ST25R3916_REG_ISO14443A_NFC_no_rx_par_off (0U << 6) +#define ST25R3916_REG_ISO14443A_NFC_nfc_f0 (1U << 5) +#define ST25R3916_REG_ISO14443A_NFC_nfc_f0_off (0U << 5) +#define ST25R3916_REG_ISO14443A_NFC_p_len3 (1U << 4) +#define ST25R3916_REG_ISO14443A_NFC_p_len2 (1U << 3) +#define ST25R3916_REG_ISO14443A_NFC_p_len1 (1U << 2) +#define ST25R3916_REG_ISO14443A_NFC_p_len0 (1U << 1) +#define ST25R3916_REG_ISO14443A_NFC_p_len_mask (0xfU << 1) +#define ST25R3916_REG_ISO14443A_NFC_p_len_shift (1U) +#define ST25R3916_REG_ISO14443A_NFC_antcl (1U << 0) + +#define ST25R3916_REG_EMD_SUP_CONF_emd_emv (1U << 7) +#define ST25R3916_REG_EMD_SUP_CONF_emd_emv_on (1U << 7) +#define ST25R3916_REG_EMD_SUP_CONF_emd_emv_off (0U << 7) +#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv (1U << 6) +#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_on (1U << 6) +#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_off (0U << 6) +#define ST25R3916_REG_EMD_SUP_CONF_rfu1 (1U << 5) +#define ST25R3916_REG_EMD_SUP_CONF_rfu0 (1U << 4) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld3 (1U << 3) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld2 (1U << 2) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld1 (1U << 1) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld0 (1U << 0) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld_mask (0xfU << 0) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld_shift (0U) + +#define ST25R3916_REG_SUBC_START_TIME_rfu2 (1U << 7) +#define ST25R3916_REG_SUBC_START_TIME_rfu1 (1U << 6) +#define ST25R3916_REG_SUBC_START_TIME_rfu0 (1U << 5) +#define ST25R3916_REG_SUBC_START_TIME_sst4 (1U << 4) +#define ST25R3916_REG_SUBC_START_TIME_sst3 (1U << 3) +#define ST25R3916_REG_SUBC_START_TIME_sst2 (1U << 2) +#define ST25R3916_REG_SUBC_START_TIME_sst1 (1U << 1) +#define ST25R3916_REG_SUBC_START_TIME_sst0 (1U << 0) +#define ST25R3916_REG_SUBC_START_TIME_sst_mask (0x1fU << 0) +#define ST25R3916_REG_SUBC_START_TIME_sst_shift (0U) + +#define ST25R3916_REG_ISO14443B_1_egt2 (1U << 7) +#define ST25R3916_REG_ISO14443B_1_egt1 (1U << 6) +#define ST25R3916_REG_ISO14443B_1_egt0 (1U << 5) +#define ST25R3916_REG_ISO14443B_1_egt_shift (5U) +#define ST25R3916_REG_ISO14443B_1_egt_mask (7U << 5) +#define ST25R3916_REG_ISO14443B_1_sof_1 (1U << 3) +#define ST25R3916_REG_ISO14443B_1_sof_1_3etu (1U << 3) +#define ST25R3916_REG_ISO14443B_1_sof_1_2etu (0U << 3) +#define ST25R3916_REG_ISO14443B_1_sof_0 (1U << 4) +#define ST25R3916_REG_ISO14443B_1_sof_0_11etu (1U << 4) +#define ST25R3916_REG_ISO14443B_1_sof_0_10etu (0U << 4) +#define ST25R3916_REG_ISO14443B_1_sof_mask (3U << 3) +#define ST25R3916_REG_ISO14443B_1_eof (1U << 2) +#define ST25R3916_REG_ISO14443B_1_eof_11etu (1U << 2) +#define ST25R3916_REG_ISO14443B_1_eof_10etu (0U << 2) +#define ST25R3916_REG_ISO14443B_1_half (1U << 1) +#define ST25R3916_REG_ISO14443B_1_rx_st_om (1U << 0) + +#define ST25R3916_REG_ISO14443B_2_tr1_1 (1U << 7) +#define ST25R3916_REG_ISO14443B_2_tr1_0 (1U << 6) +#define ST25R3916_REG_ISO14443B_2_tr1_64fs32fs (1U << 6) +#define ST25R3916_REG_ISO14443B_2_tr1_80fs80fs (0U << 6) +#define ST25R3916_REG_ISO14443B_2_tr1_mask (3U << 6) +#define ST25R3916_REG_ISO14443B_2_tr1_shift (6U) +#define ST25R3916_REG_ISO14443B_2_no_sof (1U << 5) +#define ST25R3916_REG_ISO14443B_2_no_eof (1U << 4) +#define ST25R3916_REG_ISO14443B_rfu1 (1U << 3) +#define ST25R3916_REG_ISO14443B_rfu0 (1U << 2) +#define ST25R3916_REG_ISO14443B_2_f_p1 (1U << 1) +#define ST25R3916_REG_ISO14443B_2_f_p0 (1U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_96 (3U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_80 (2U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_64 (1U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_48 (0U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_mask (3U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_shift (0U) + +#define ST25R3916_REG_PASSIVE_TARGET_fdel_3 (1U << 7) +#define ST25R3916_REG_PASSIVE_TARGET_fdel_2 (1U << 6) +#define ST25R3916_REG_PASSIVE_TARGET_fdel_1 (1U << 5) +#define ST25R3916_REG_PASSIVE_TARGET_fdel_0 (1U << 4) +#define ST25R3916_REG_PASSIVE_TARGET_fdel_mask (0xfU << 4) +#define ST25R3916_REG_PASSIVE_TARGET_fdel_shift (4U) +#define ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p (1U << 3) +#define ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r (1U << 2) +#define ST25R3916_REG_PASSIVE_TARGET_rfu (1U << 1) +#define ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a (1U << 0) + +#define ST25R3916_REG_STREAM_MODE_rfu (1U << 7) +#define ST25R3916_REG_STREAM_MODE_scf1 (1U << 6) +#define ST25R3916_REG_STREAM_MODE_scf0 (1U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_sc212 (0U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_sc424 (1U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_sc848 (2U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_sc1695 (3U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_bpsk848 (0U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_bpsk1695 (1U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_bpsk3390 (2U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_bpsk106 (3U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_mask (3U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_shift (5U) +#define ST25R3916_REG_STREAM_MODE_scp1 (1U << 4) +#define ST25R3916_REG_STREAM_MODE_scp0 (1U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_1pulse (0U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_2pulses (1U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_4pulses (2U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_8pulses (3U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_mask (3U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_shift (3U) +#define ST25R3916_REG_STREAM_MODE_stx2 (1U << 2) +#define ST25R3916_REG_STREAM_MODE_stx1 (1U << 1) +#define ST25R3916_REG_STREAM_MODE_stx0 (1U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_106 (0U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_212 (1U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_424 (2U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_848 (3U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_mask (7U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_shift (0U) + +#define ST25R3916_REG_AUX_no_crc_rx (1U << 7) +#define ST25R3916_REG_AUX_rfu (1U << 6) +#define ST25R3916_REG_AUX_nfc_id1 (1U << 5) +#define ST25R3916_REG_AUX_nfc_id0 (1U << 4) +#define ST25R3916_REG_AUX_nfc_id_7bytes (1U << 4) +#define ST25R3916_REG_AUX_nfc_id_4bytes (0U << 4) +#define ST25R3916_REG_AUX_nfc_id_mask (3U << 4) +#define ST25R3916_REG_AUX_nfc_id_shift (4U) +#define ST25R3916_REG_AUX_mfaz_cl90 (1U << 3) +#define ST25R3916_REG_AUX_dis_corr (1U << 2) +#define ST25R3916_REG_AUX_dis_corr_coherent (1U << 2) +#define ST25R3916_REG_AUX_dis_corr_correlator (0U << 2) +#define ST25R3916_REG_AUX_nfc_n1 (1U << 1) +#define ST25R3916_REG_AUX_nfc_n0 (1U << 0) +#define ST25R3916_REG_AUX_nfc_n_mask (3U << 0) +#define ST25R3916_REG_AUX_nfc_n_shift (0U) + +#define ST25R3916_REG_RX_CONF1_ch_sel (1U << 7) +#define ST25R3916_REG_RX_CONF1_ch_sel_PM (1U << 7) +#define ST25R3916_REG_RX_CONF1_ch_sel_AM (0U << 7) +#define ST25R3916_REG_RX_CONF1_lp2 (1U << 6) +#define ST25R3916_REG_RX_CONF1_lp1 (1U << 5) +#define ST25R3916_REG_RX_CONF1_lp0 (1U << 4) +#define ST25R3916_REG_RX_CONF1_lp_1200khz (0U << 4) +#define ST25R3916_REG_RX_CONF1_lp_600khz (1U << 4) +#define ST25R3916_REG_RX_CONF1_lp_300khz (2U << 4) +#define ST25R3916_REG_RX_CONF1_lp_2000khz (4U << 4) +#define ST25R3916_REG_RX_CONF1_lp_7000khz (5U << 4) +#define ST25R3916_REG_RX_CONF1_lp_mask (7U << 4) +#define ST25R3916_REG_RX_CONF1_lp_shift (4U) +#define ST25R3916_REG_RX_CONF1_z600k (1U << 3) +#define ST25R3916_REG_RX_CONF1_h200 (1U << 2) +#define ST25R3916_REG_RX_CONF1_h80 (1U << 1) +#define ST25R3916_REG_RX_CONF1_z12k (1U << 0) +#define ST25R3916_REG_RX_CONF1_hz_60_400khz (0U << 0) +#define ST25R3916_REG_RX_CONF1_hz_60_200khz (4U << 0) +#define ST25R3916_REG_RX_CONF1_hz_40_80khz (2U << 0) +#define ST25R3916_REG_RX_CONF1_hz_12_200khz (1U << 0) +#define ST25R3916_REG_RX_CONF1_hz_12_80khz (3U << 0) +#define ST25R3916_REG_RX_CONF1_hz_12_200khz_alt (5U << 0) +#define ST25R3916_REG_RX_CONF1_hz_600_400khz (8U << 0) +#define ST25R3916_REG_RX_CONF1_hz_600_200khz (12U << 0) +#define ST25R3916_REG_RX_CONF1_hz_mask (0xfU << 0) +#define ST25R3916_REG_RX_CONF1_hz_shift (0U) + +#define ST25R3916_REG_RX_CONF2_demod_mode (1U << 7) +#define ST25R3916_REG_RX_CONF2_amd_sel (1U << 6) +#define ST25R3916_REG_RX_CONF2_amd_sel_mixer (1U << 6) +#define ST25R3916_REG_RX_CONF2_amd_sel_peak (0U << 6) +#define ST25R3916_REG_RX_CONF2_sqm_dyn (1U << 5) +#define ST25R3916_REG_RX_CONF2_pulz_61 (1U << 4) +#define ST25R3916_REG_RX_CONF2_agc_en (1U << 3) +#define ST25R3916_REG_RX_CONF2_agc_m (1U << 2) +#define ST25R3916_REG_RX_CONF2_agc_alg (1U << 1) +#define ST25R3916_REG_RX_CONF2_agc6_3 (1U << 0) + +#define ST25R3916_REG_RX_CONF3_rg1_am2 (1U << 7) +#define ST25R3916_REG_RX_CONF3_rg1_am1 (1U << 6) +#define ST25R3916_REG_RX_CONF3_rg1_am0 (1U << 5) +#define ST25R3916_REG_RX_CONF3_rg1_am_mask (0x7U << 5) +#define ST25R3916_REG_RX_CONF3_rg1_am_shift (5U) +#define ST25R3916_REG_RX_CONF3_rg1_pm2 (1U << 4) +#define ST25R3916_REG_RX_CONF3_rg1_pm1 (1U << 3) +#define ST25R3916_REG_RX_CONF3_rg1_pm0 (1U << 2) +#define ST25R3916_REG_RX_CONF3_rg1_pm_mask (0x7U << 2) +#define ST25R3916_REG_RX_CONF3_rg1_pm_shift (2U) +#define ST25R3916_REG_RX_CONF3_lf_en (1U << 1) +#define ST25R3916_REG_RX_CONF3_lf_op (1U << 0) + +#define ST25R3916_REG_RX_CONF4_rg2_am3 (1U << 7) +#define ST25R3916_REG_RX_CONF4_rg2_am2 (1U << 6) +#define ST25R3916_REG_RX_CONF4_rg2_am1 (1U << 5) +#define ST25R3916_REG_RX_CONF4_rg2_am0 (1U << 4) +#define ST25R3916_REG_RX_CONF4_rg2_am_mask (0xfU << 4) +#define ST25R3916_REG_RX_CONF4_rg2_am_shift (4U) +#define ST25R3916_REG_RX_CONF4_rg2_pm3 (1U << 3) +#define ST25R3916_REG_RX_CONF4_rg2_pm2 (1U << 2) +#define ST25R3916_REG_RX_CONF4_rg2_pm1 (1U << 1) +#define ST25R3916_REG_RX_CONF4_rg2_pm0 (1U << 0) +#define ST25R3916_REG_RX_CONF4_rg2_pm_mask (0xfU << 0) +#define ST25R3916_REG_RX_CONF4_rg2_pm_shift (0U) + +#define ST25R3916_REG_P2P_RX_CONF_ook_fd (1U << 7) +#define ST25R3916_REG_P2P_RX_CONF_ook_rc1 (1U << 6) +#define ST25R3916_REG_P2P_RX_CONF_ook_rc0 (1U << 5) +#define ST25R3916_REG_P2P_RX_CONF_ook_thd1 (1U << 4) +#define ST25R3916_REG_P2P_RX_CONF_ook_thd0 (1U << 3) +#define ST25R3916_REG_P2P_RX_CONF_ask_rc1 (1U << 2) +#define ST25R3916_REG_P2P_RX_CONF_ask_rc0 (1U << 1) +#define ST25R3916_REG_P2P_RX_CONF_ask_thd (1U << 0) + +#define ST25R3916_REG_CORR_CONF1_corr_s7 (1U << 7) +#define ST25R3916_REG_CORR_CONF1_corr_s6 (1U << 6) +#define ST25R3916_REG_CORR_CONF1_corr_s5 (1U << 5) +#define ST25R3916_REG_CORR_CONF1_corr_s4 (1U << 4) +#define ST25R3916_REG_CORR_CONF1_corr_s3 (1U << 3) +#define ST25R3916_REG_CORR_CONF1_corr_s2 (1U << 2) +#define ST25R3916_REG_CORR_CONF1_corr_s1 (1U << 1) +#define ST25R3916_REG_CORR_CONF1_corr_s0 (1U << 0) + +#define ST25R3916_REG_CORR_CONF2_rfu5 (1U << 7) +#define ST25R3916_REG_CORR_CONF2_rfu4 (1U << 6) +#define ST25R3916_REG_CORR_CONF2_rfu3 (1U << 5) +#define ST25R3916_REG_CORR_CONF2_rfu2 (1U << 4) +#define ST25R3916_REG_CORR_CONF2_rfu1 (1U << 3) +#define ST25R3916_REG_CORR_CONF2_rfu0 (1U << 2) +#define ST25R3916_REG_CORR_CONF2_corr_s9 (1U << 1) +#define ST25R3916_REG_CORR_CONF2_corr_s8 (1U << 0) + +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc2 (1U << 7) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc1 (1U << 6) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc0 (1U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_no_trigger (0U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_erx (1U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_srx (2U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_etx_nfc (3U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_mask (7U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_shift (5U) +#define ST25R3916_REG_TIMER_EMV_CONTROL_rfu (1U << 4) +#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step (1U << 3) +#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step_512 (1U << 3) +#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step_64 (0U << 3) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc (1U << 2) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc_on (1U << 2) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc_off (0U << 2) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv (1U << 1) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv_on (1U << 1) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv_off (0U << 1) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step (1U << 0) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step_64fc (0U << 0) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step_4096_fc (1U << 0) + +#define ST25R3916_REG_FIFO_STATUS2_fifo_b9 (1U << 7) +#define ST25R3916_REG_FIFO_STATUS2_fifo_b8 (1U << 6) +#define ST25R3916_REG_FIFO_STATUS2_fifo_b_mask (3U << 6) +#define ST25R3916_REG_FIFO_STATUS2_fifo_b_shift (6U) +#define ST25R3916_REG_FIFO_STATUS2_fifo_unf (1U << 5) +#define ST25R3916_REG_FIFO_STATUS2_fifo_ovr (1U << 4) +#define ST25R3916_REG_FIFO_STATUS2_fifo_lb2 (1U << 3) +#define ST25R3916_REG_FIFO_STATUS2_fifo_lb1 (1U << 2) +#define ST25R3916_REG_FIFO_STATUS2_fifo_lb0 (1U << 1) +#define ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask (7U << 1) +#define ST25R3916_REG_FIFO_STATUS2_fifo_lb_shift (1U) +#define ST25R3916_REG_FIFO_STATUS2_np_lb (1U << 0) + +#define ST25R3916_REG_COLLISION_STATUS_c_byte3 (1U << 7) +#define ST25R3916_REG_COLLISION_STATUS_c_byte2 (1U << 6) +#define ST25R3916_REG_COLLISION_STATUS_c_byte1 (1U << 5) +#define ST25R3916_REG_COLLISION_STATUS_c_byte0 (1U << 4) +#define ST25R3916_REG_COLLISION_STATUS_c_byte_mask (0xfU << 4) +#define ST25R3916_REG_COLLISION_STATUS_c_byte_shift (4U) +#define ST25R3916_REG_COLLISION_STATUS_c_bit2 (1U << 3) +#define ST25R3916_REG_COLLISION_STATUS_c_bit1 (1U << 2) +#define ST25R3916_REG_COLLISION_STATUS_c_bit0 (1U << 1) +#define ST25R3916_REG_COLLISION_STATUS_c_pb (1U << 0) +#define ST25R3916_REG_COLLISION_STATUS_c_bit_mask (3U << 1) +#define ST25R3916_REG_COLLISION_STATUS_c_bit_shift (1U) + +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu (1U << 7) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu1 (1U << 6) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu2 (1U << 5) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu3 (1U << 4) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state3 (1U << 3) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state2 (1U << 2) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state1 (1U << 1) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state0 (1U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_power_off (0x0U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_idle (0x1U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l1 (0x2U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l2 (0x3U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu4 (0x4U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_active (0x5U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu6 (0x6U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu7 (0x7U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu8 (0x8U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_halt (0x9U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l1_x (0xaU << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l2_x (0xbU << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu12 (0xcU << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_active_x (0xdU << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state_mask (0xfU << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state_shift (0U) + +#define ST25R3916_REG_NUM_TX_BYTES2_ntx4 (1U << 7) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx3 (1U << 6) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx2 (1U << 5) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx1 (1U << 4) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx0 (1U << 3) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx_mask (0x1fU << 3) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx_shift (3U) +#define ST25R3916_REG_NUM_TX_BYTES2_nbtx2 (1U << 2) +#define ST25R3916_REG_NUM_TX_BYTES2_nbtx1 (1U << 1) +#define ST25R3916_REG_NUM_TX_BYTES2_nbtx0 (1U << 0) +#define ST25R3916_REG_NUM_TX_BYTES2_nbtx_mask (7U << 0) +#define ST25R3916_REG_NUM_TX_BYTES2_nbtx_shift (0U) + +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rfu1 (1U << 7) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rfu0 (1U << 6) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate1 (1U << 5) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate0 (1U << 4) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate_mask (0x3U << 4) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate_shift (4U) +#define ST25R3916_REG_NFCIP1_BIT_RATE_ppt2_on (1U << 3) +#define ST25R3916_REG_NFCIP1_BIT_RATE_gpt_on (1U << 2) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nrt_on (1U << 1) +#define ST25R3916_REG_NFCIP1_BIT_RATE_mrt_on (1U << 0) + +#define ST25R3916_REG_TX_DRIVER_am_mod3 (1U << 7) +#define ST25R3916_REG_TX_DRIVER_am_mod2 (1U << 6) +#define ST25R3916_REG_TX_DRIVER_am_mod1 (1U << 5) +#define ST25R3916_REG_TX_DRIVER_am_mod0 (1U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_5percent (0x0U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_6percent (0x1U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_7percent (0x2U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_8percent (0x3U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_9percent (0x4U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_10percent (0x5U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_11percent (0x6U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_12percent (0x7U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_13percent (0x8U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_14percent (0x9U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_15percent (0xaU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_17percent (0xbU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_19percent (0xcU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_22percent (0xdU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_26percent (0xeU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_40percent (0xfU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_mask (0xfU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_shift (4U) +#define ST25R3916_REG_TX_DRIVER_d_res3 (1U << 3) +#define ST25R3916_REG_TX_DRIVER_d_res2 (1U << 2) +#define ST25R3916_REG_TX_DRIVER_d_res1 (1U << 1) +#define ST25R3916_REG_TX_DRIVER_d_res0 (1U << 0) +#define ST25R3916_REG_TX_DRIVER_d_res_mask (0xfU << 0) +#define ST25R3916_REG_TX_DRIVER_d_res_shift (0U) + +#define ST25R3916_REG_PT_MOD_ptm_res3 (1U << 7) +#define ST25R3916_REG_PT_MOD_ptm_res2 (1U << 6) +#define ST25R3916_REG_PT_MOD_ptm_res1 (1U << 5) +#define ST25R3916_REG_PT_MOD_ptm_res0 (1U << 4) +#define ST25R3916_REG_PT_MOD_ptm_res_mask (0xfU << 4) +#define ST25R3916_REG_PT_MOD_ptm_res_shift (4U) +#define ST25R3916_REG_PT_MOD_pt_res3 (1U << 3) +#define ST25R3916_REG_PT_MOD_pt_res2 (1U << 2) +#define ST25R3916_REG_PT_MOD_pt_res1 (1U << 1) +#define ST25R3916_REG_PT_MOD_pt_res0 (1U << 0) +#define ST25R3916_REG_PT_MOD_pt_res_mask (0xfU << 0) +#define ST25R3916_REG_PT_MOD_pt_res_shift (0U) + +#define ST25R3916_REG_AUX_MOD_dis_reg_am (1U << 7) +#define ST25R3916_REG_AUX_MOD_lm_ext_pol (1U << 6) +#define ST25R3916_REG_AUX_MOD_lm_ext (1U << 5) +#define ST25R3916_REG_AUX_MOD_lm_dri (1U << 4) +#define ST25R3916_REG_AUX_MOD_res_am (1U << 3) +#define ST25R3916_REG_AUX_MOD_rfu2 (1U << 2) +#define ST25R3916_REG_AUX_MOD_rfu1 (1U << 1) +#define ST25R3916_REG_AUX_MOD_rfu0 (1U << 0) + +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t3 (1U << 7) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t2 (1U << 6) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t1 (1U << 5) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t0 (1U << 4) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_mask (0xfU << 4) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_shift (4U) +#define ST25R3916_REG_TX_DRIVER_TIMING_rfu (1U << 3) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m2 (1U << 2) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m1 (1U << 1) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m0 (1U << 0) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m_mask (0x7U << 0) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m_shift (0U) + +#define ST25R3916_REG_RES_AM_MOD_fa3_f (1U << 7) +#define ST25R3916_REG_RES_AM_MOD_md_res6 (1U << 6) +#define ST25R3916_REG_RES_AM_MOD_md_res5 (1U << 5) +#define ST25R3916_REG_RES_AM_MOD_md_res4 (1U << 4) +#define ST25R3916_REG_RES_AM_MOD_md_res3 (1U << 3) +#define ST25R3916_REG_RES_AM_MOD_md_res2 (1U << 2) +#define ST25R3916_REG_RES_AM_MOD_md_res1 (1U << 1) +#define ST25R3916_REG_RES_AM_MOD_md_res0 (1U << 0) +#define ST25R3916_REG_RES_AM_MOD_md_res_mask (0x7FU << 0) +#define ST25R3916_REG_RES_AM_MOD_md_res_shift (0U) + +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r3 (1U << 7) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r2 (1U << 6) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r1 (1U << 5) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r0 (1U << 4) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_mask (0xfU << 4) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_shift (4U) +#define ST25R3916_REG_TX_DRIVER_STATUS_rfu (1U << 3) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r2 (1U << 2) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r1 (1U << 1) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r0 (1U << 0) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_mask (0x7U << 0) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_shift (0U) + +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l2a (1U << 6) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l1a (1U << 5) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l0a (1U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_75mV (0x0U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_105mV (0x1U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_150mV (0x2U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_205mV (0x3U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_290mV (0x4U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_400mV (0x5U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_560mV (0x6U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_800mV (0x7U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask (7U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_shift (4U) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t3a (1U << 3) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t2a (1U << 2) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t1a (1U << 1) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t0a (1U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_75mV (0x0U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_105mV (0x1U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_150mV (0x2U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_205mV (0x3U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_290mV (0x4U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_400mV (0x5U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_560mV (0x6U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_800mV (0x7U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_25mV (0x8U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_33mV (0x9U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_47mV (0xAU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_64mV (0xBU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_90mV (0xCU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_125mV (0xDU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_175mV (0xEU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_250mV (0xFU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask (0xfU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_shift (0U) + +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l2d (1U << 6) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l1d (1U << 5) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l0d (1U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_75mV (0x0U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_105mV (0x1U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_150mV (0x2U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_205mV (0x3U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_290mV (0x4U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_400mV (0x5U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_560mV (0x6U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_800mV (0x7U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_mask (7U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_shift (4U) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t3d (1U << 3) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t2d (1U << 2) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t1d (1U << 1) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t0d (1U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_75mV (0x0U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_105mV (0x1U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_150mV (0x2U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_205mV (0x3U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_290mV (0x4U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_400mV (0x5U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_560mV (0x6U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_800mV (0x7U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_25mV (0x8U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_33mV (0x9U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_47mV (0xAU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_64mV (0xBU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_90mV (0xCU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_125mV (0xDU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_175mV (0xEU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_250mV (0xFU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_mask (0xfU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_shift (0U) + +#define ST25R3916_REG_REGULATOR_CONTROL_reg_s (1U << 7) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_3 (1U << 6) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_2 (1U << 5) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_1 (1U << 4) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_0 (1U << 3) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_mask (0xfU << 3) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_shift (3U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv2 (2U << 2) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv1 (1U << 1) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv0 (1U << 0) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd (0U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_a (1U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_d (2U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_rf (3U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_am (4U) +#define ST25R3916_REG_REGULATOR_CONTROL_rfu (5U) +#define ST25R3916_REG_REGULATOR_CONTROL_rfu1 (6U) +#define ST25R3916_REG_REGULATOR_CONTROL_rfu2 (7U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_mask (7U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_shift (0U) + +#define ST25R3916_REG_REGULATOR_RESULT_reg_3 (1U << 7) +#define ST25R3916_REG_REGULATOR_RESULT_reg_2 (1U << 6) +#define ST25R3916_REG_REGULATOR_RESULT_reg_1 (1U << 5) +#define ST25R3916_REG_REGULATOR_RESULT_reg_0 (1U << 4) +#define ST25R3916_REG_REGULATOR_RESULT_reg_mask (0xfU << 4) +#define ST25R3916_REG_REGULATOR_RESULT_reg_shift (4U) +#define ST25R3916_REG_REGULATOR_RESULT_i_lim (1U << 0) + +#define ST25R3916_REG_RSSI_RESULT_rssi_am_3 (1U << 7) +#define ST25R3916_REG_RSSI_RESULT_rssi_am_2 (1U << 6) +#define ST25R3916_REG_RSSI_RESULT_rssi_am_1 (1U << 5) +#define ST25R3916_REG_RSSI_RESULT_rssi_am_0 (1U << 4) +#define ST25R3916_REG_RSSI_RESULT_rssi_am_mask (0xfU << 4) +#define ST25R3916_REG_RSSI_RESULT_rssi_am_shift (4U) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm3 (1U << 3) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm2 (1U << 2) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm1 (1U << 1) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm0 (1U << 0) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm_mask (0xfU << 0) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm_shift (0U) + +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_3 (1U << 7) +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_2 (1U << 6) +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_1 (1U << 5) +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_0 (1U << 4) +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_mask (0xfU << 4) +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_shift (4U) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_3 (1U << 3) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_2 (1U << 2) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_1 (1U << 1) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_0 (1U << 0) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_mask (0xfU << 0) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_shift (0U) + +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal4 (1U << 7) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal3 (1U << 6) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal2 (1U << 5) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal1 (1U << 4) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal0 (1U << 3) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal_mask (0x1fU << 3) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal_shift (3U) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g2 (1U << 2) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g1 (1U << 1) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g0 (1U << 0) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g_mask (7U << 0) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g_shift (0U) + +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal4 (1U << 7) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal3 (1U << 6) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal2 (1U << 5) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal1 (1U << 4) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal0 (1U << 3) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_mask (0x1fU << 3) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_shift (3U) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_end (1U << 2) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_err (1U << 1) + +#define ST25R3916_REG_AUX_DISPLAY_a_cha (1U << 7) +#define ST25R3916_REG_AUX_DISPLAY_efd_o (1U << 6) +#define ST25R3916_REG_AUX_DISPLAY_tx_on (1U << 5) +#define ST25R3916_REG_AUX_DISPLAY_osc_ok (1U << 4) +#define ST25R3916_REG_AUX_DISPLAY_rx_on (1U << 3) +#define ST25R3916_REG_AUX_DISPLAY_rx_act (1U << 2) +#define ST25R3916_REG_AUX_DISPLAY_en_peer (1U << 1) +#define ST25R3916_REG_AUX_DISPLAY_en_ac (1U << 0) + +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_tx_mode1 (1U << 7) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_tx_mode0 (1U << 6) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern13 (1U << 5) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern12 (1U << 4) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern11 (1U << 3) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern10 (1U << 2) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern9 (1U << 1) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern8 (1U << 0) + +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern7 (1U << 7) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern6 (1U << 6) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern5 (1U << 5) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern4 (1U << 4) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern3 (1U << 3) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern2 (1U << 2) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern1 (1U << 1) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern0 (1U << 0) + +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_tx_mode1 (1U << 7) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_tx_mode0 (1U << 6) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern13 (1U << 5) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern12 (1U << 4) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern11 (1U << 3) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern10 (1U << 2) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern9 (1U << 1) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern8 (1U << 0) + +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern7 (1U << 7) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern6 (1U << 6) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern5 (1U << 5) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern4 (1U << 4) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern3 (1U << 3) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern2 (1U << 2) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern1 (1U << 1) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern0 (1U << 0) + +#define ST25R3916_REG_WUP_TIMER_CONTROL_wur (1U << 7) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wut2 (1U << 6) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wut1 (1U << 5) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wut0 (1U << 4) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wut_mask (7U << 4) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wut_shift (4U) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wto (1U << 3) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wam (1U << 2) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wph (1U << 1) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wcap (1U << 0) + +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d3 (1U << 7) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d2 (1U << 6) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d1 (1U << 5) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d0 (1U << 4) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d_mask (0xfU << 4) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d_shift (4U) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aam (1U << 3) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew1 (1U << 2) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew0 (1U << 1) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew_mask (0x3U << 1) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew_shift (1U) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_ae (1U << 0) + +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d3 (1U << 7) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d2 (1U << 6) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d1 (1U << 5) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d0 (1U << 4) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d_mask (0xfU << 4) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d_shift (4U) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aam (1U << 3) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew1 (1U << 2) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew0 (1U << 1) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew_mask (0x3U << 1) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew_shift (1U) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_ae (1U << 0) + +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d3 (1U << 7) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d2 (1U << 6) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d1 (1U << 5) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d0 (1U << 4) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d_mask (0xfU << 4) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d_shift (4U) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aam (1U << 3) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew1 (1U << 2) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew0 (1U << 1) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew_mask (0x3U << 1) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew_shift (1U) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_ae (1U << 0) + +#define ST25R3916_REG_IC_IDENTITY_ic_type4 (1U << 7) +#define ST25R3916_REG_IC_IDENTITY_ic_type3 (1U << 6) +#define ST25R3916_REG_IC_IDENTITY_ic_type2 (1U << 5) +#define ST25R3916_REG_IC_IDENTITY_ic_type1 (1U << 4) +#define ST25R3916_REG_IC_IDENTITY_ic_type0 (1U << 3) +#define ST25R3916_REG_IC_IDENTITY_ic_type_st25r3916 (5U << 3) +#define ST25R3916_REG_IC_IDENTITY_ic_type_mask (0x1fU << 3) +#define ST25R3916_REG_IC_IDENTITY_ic_type_shift (3U) +#define ST25R3916_REG_IC_IDENTITY_ic_rev2 (1U << 2) +#define ST25R3916_REG_IC_IDENTITY_ic_rev1 (1U << 1) +#define ST25R3916_REG_IC_IDENTITY_ic_rev0 (1U << 0) +#define ST25R3916_REG_IC_IDENTITY_ic_rev_v0 (0U << 0) +#define ST25R3916_REG_IC_IDENTITY_ic_rev_mask (7U << 0) +#define ST25R3916_REG_IC_IDENTITY_ic_rev_shift (0U) + +void st25r3916_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val); + +void st25r3916_read_burst_regs( + FuriHalSpiBusHandle* handle, + uint8_t reg_start, + uint8_t* values, + uint8_t length); + +void st25r3916_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val); + +void st25r3916_write_burst_regs( + FuriHalSpiBusHandle* handle, + uint8_t reg_start, + const uint8_t* values, + uint8_t length); + +void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t length); + +void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length); + +void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length); + +void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* values, size_t length); + +void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length); + +void st25r3916_write_pttsn_mem(FuriHalSpiBusHandle* handle, uint8_t* values, size_t length); + +void st25r3916_direct_cmd(FuriHalSpiBusHandle* handle, uint8_t cmd); + +void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val); + +void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val); + +void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask); + +void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask); + +void st25r3916_change_reg_bits( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t mask, + uint8_t value); + +void st25r3916_modify_reg( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t clr_mask, + uint8_t set_mask); + +void st25r3916_change_test_reg_bits( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t mask, + uint8_t value); + +bool st25r3916_check_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val); + +#ifdef __cplusplus +} +#endif diff --git a/lib/flipper_application/api_hashtable/api_hashtable.cpp b/lib/flipper_application/api_hashtable/api_hashtable.cpp index 022792dce63b..6db5fb5fde64 100644 --- a/lib/flipper_application/api_hashtable/api_hashtable.cpp +++ b/lib/flipper_application/api_hashtable/api_hashtable.cpp @@ -7,27 +7,22 @@ bool elf_resolve_from_hashtable( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address) { + bool result = false; const HashtableApiInterface* hashtable_interface = static_cast(interface); - bool result = false; - uint32_t gnu_sym_hash = elf_gnu_hash(name); sym_entry key = { - .hash = gnu_sym_hash, + .hash = hash, .address = 0, }; auto find_res = std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key); - if((find_res == hashtable_interface->table_cend || (find_res->hash != gnu_sym_hash))) { + if((find_res == hashtable_interface->table_cend || (find_res->hash != hash))) { FURI_LOG_W( - TAG, - "Can't find symbol '%s' (hash %lx) @ %p!", - name, - gnu_sym_hash, - hashtable_interface->table_cbegin); + TAG, "Can't find symbol with hash %lx @ %p!", hash, hashtable_interface->table_cbegin); result = false; } else { result = true; @@ -36,3 +31,7 @@ bool elf_resolve_from_hashtable( return result; } + +uint32_t elf_symbolname_hash(const char* s) { + return elf_gnu_hash(s); +} \ No newline at end of file diff --git a/lib/flipper_application/api_hashtable/api_hashtable.h b/lib/flipper_application/api_hashtable/api_hashtable.h index 7e4b4aba1dd3..7ba6aab9727c 100644 --- a/lib/flipper_application/api_hashtable/api_hashtable.h +++ b/lib/flipper_application/api_hashtable/api_hashtable.h @@ -19,15 +19,17 @@ struct sym_entry { /** * @brief Resolver for API entries using a pre-sorted table with hashes * @param interface pointer to HashtableApiInterface - * @param name function name + * @param hash gnu hash of function name * @param address output for function address * @return true if the table contains a function */ bool elf_resolve_from_hashtable( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address); +uint32_t elf_symbolname_hash(const char* s); + #ifdef __cplusplus } @@ -48,8 +50,10 @@ struct HashtableApiInterface : public ElfApiInterface { .hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast(x)) \ } -#define API_VARIABLE(x, var_type) \ - sym_entry { .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), } +#define API_VARIABLE(x, var_type) \ + sym_entry { \ + .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \ + } constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) { return k1.hash < k2.hash; diff --git a/lib/flipper_application/elf/elf_api_interface.h b/lib/flipper_application/elf/elf_api_interface.h index f07df4edb763..facdc44473d6 100644 --- a/lib/flipper_application/elf/elf_api_interface.h +++ b/lib/flipper_application/elf/elf_api_interface.h @@ -11,6 +11,6 @@ typedef struct ElfApiInterface { uint16_t api_version_minor; bool (*resolver_callback)( const struct ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address); } ElfApiInterface; diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 0338144a9a74..fc9dd06ba818 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -2,6 +2,7 @@ #include "elf_file.h" #include "elf_file_i.h" #include "elf_api_interface.h" +#include "../api_hashtable/api_hashtable.h" #define TAG "elf" @@ -9,6 +10,7 @@ #define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr)) #define IS_FLAGS_SET(v, m) (((v) & (m)) == (m)) #define RESOLVER_THREAD_YIELD_STEP 30 +#define FAST_RELOCATION_VERSION 1 // #define ELF_DEBUG_LOG 1 @@ -71,6 +73,7 @@ static ELFSection* elf_file_get_or_put_section(ELFFile* elf, const char* name) { .size = 0, .rel_count = 0, .rel_offset = 0, + .fast_rel = NULL, }); section_p = elf_file_get_section(elf, name); } @@ -168,7 +171,8 @@ static ELFSection* elf_section_of(ELFFile* elf, int index) { static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) { if(sym->st_shndx == SHN_UNDEF) { Elf32_Addr addr = 0; - if(elf->api_interface->resolver_callback(elf->api_interface, sName, &addr)) { + uint32_t hash = elf_symbolname_hash(sName); + if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) { return addr; } } else { @@ -424,6 +428,7 @@ typedef enum { SectionTypeSymTab = 1 << 3, SectionTypeStrTab = 1 << 4, SectionTypeDebugLink = 1 << 5, + SectionTypeFastRelData = 1 << 6, SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab, } SectionType; @@ -505,7 +510,8 @@ static SectionType elf_preload_section( // TODO: how to do it not by name? // .ARM: type 0x70000001, flags SHF_ALLOC | SHF_LINK_ORDER // .rel.ARM: type 0x9, flags SHT_REL - if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.")) { + if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.") || + str_prefix(name, ".fast.rel.ARM.")) { FURI_LOG_D(TAG, "Ignoring ARM section"); return SectionTypeUnused; } @@ -536,11 +542,31 @@ static SectionType elf_preload_section( // Load link info section if(section_header->sh_flags & SHF_INFO_LINK) { - name = name + strlen(".rel"); + if(str_prefix(name, ".rel")) { + name = name + strlen(".rel"); + ELFSection* section_p = elf_file_get_or_put_section(elf, name); + section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel); + section_p->rel_offset = section_header->sh_offset; + return SectionTypeRelData; + } else { + FURI_LOG_E(TAG, "Unknown link info section '%s'", name); + return SectionTypeERROR; + } + } + + // Load fast rel section + if(str_prefix(name, ".fast.rel")) { + name = name + strlen(".fast.rel"); ELFSection* section_p = elf_file_get_or_put_section(elf, name); - section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel); - section_p->rel_offset = section_header->sh_offset; - return SectionTypeRelData; + section_p->fast_rel = malloc(sizeof(ELFSection)); + + if(!elf_load_section_data(elf, section_p->fast_rel, section_header)) { + FURI_LOG_E(TAG, "Error loading section '%s'", name); + return SectionTypeERROR; + } + + FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name); + return SectionTypeFastRelData; } // Load symbol table @@ -571,8 +597,90 @@ static SectionType elf_preload_section( return SectionTypeUnused; } +static Elf32_Addr elf_address_of_by_hash(ELFFile* elf, uint32_t hash) { + Elf32_Addr addr = 0; + if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) { + return addr; + } + return ELF_INVALID_ADDRESS; +} + +static bool elf_relocate_fast(ELFFile* elf, ELFSection* s) { + UNUSED(elf); + const uint8_t* start = s->fast_rel->data; + const uint8_t version = *start; + + if(version != FAST_RELOCATION_VERSION) { + FURI_LOG_E(TAG, "Unsupported fast relocation version %d", version); + return false; + } + start += 1; + + const uint32_t records_count = *((uint32_t*)start); + start += 4; + FURI_LOG_D(TAG, "Fast relocation records count: %ld", records_count); + + for(uint32_t i = 0; i < records_count; i++) { + bool is_section = (*start & (0x1 << 7)) ? true : false; + uint8_t type = *start & 0x7F; + start += 1; + uint32_t hash_or_section_index = *((uint32_t*)start); + start += 4; + + uint32_t section_value = ELF_INVALID_ADDRESS; + if(is_section) { + section_value = *((uint32_t*)start); + start += 4; + } + + const uint32_t offsets_count = *((uint32_t*)start); + start += 4; + + FURI_LOG_D( + TAG, + "Fast relocation record %ld: is_section=%d, type=%d, hash_or_section_index=%lX, offsets_count=%ld", + i, + is_section, + type, + hash_or_section_index, + offsets_count); + + Elf32_Addr address = 0; + if(is_section) { + ELFSection* symSec = elf_section_of(elf, hash_or_section_index); + if(symSec) { + address = ((Elf32_Addr)symSec->data) + section_value; + } + } else { + address = elf_address_of_by_hash(elf, hash_or_section_index); + } + + if(address == ELF_INVALID_ADDRESS) { + FURI_LOG_E(TAG, "Failed to resolve address for hash %lX", hash_or_section_index); + return false; + } + + for(uint32_t j = 0; j < offsets_count; j++) { + uint32_t offset = *((uint32_t*)start) & 0x00FFFFFF; + start += 3; + // FURI_LOG_I(TAG, " Fast relocation offset %ld: %ld", j, offset); + Elf32_Addr relAddr = ((Elf32_Addr)s->data) + offset; + elf_relocate_symbol(elf, relAddr, type, address); + } + } + + aligned_free(s->fast_rel->data); + free(s->fast_rel); + s->fast_rel = NULL; + + return true; +} + static bool elf_relocate_section(ELFFile* elf, ELFSection* section) { - if(section->rel_count) { + if(section->fast_rel) { + FURI_LOG_D(TAG, "Fast relocating section"); + return elf_relocate_fast(elf, section); + } else if(section->rel_count) { FURI_LOG_D(TAG, "Relocating section"); return elf_relocate(elf, section); } else { @@ -630,6 +738,10 @@ void elf_file_free(ELFFile* elf) { if(itref->value.data) { aligned_free(itref->value.data); } + if(itref->value.fast_rel) { + aligned_free(itref->value.fast_rel->data); + free(itref->value.fast_rel); + } free((void*)itref->key); } diff --git a/lib/flipper_application/elf/elf_file_i.h b/lib/flipper_application/elf/elf_file_i.h index af9a1d9b4fbc..39cadfdc6f6d 100644 --- a/lib/flipper_application/elf/elf_file_i.h +++ b/lib/flipper_application/elf/elf_file_i.h @@ -13,14 +13,18 @@ DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST) */ typedef int32_t(entry_t)(void*); -typedef struct { +typedef struct ELFSection ELFSection; + +struct ELFSection { void* data; - uint16_t sec_idx; Elf32_Word size; size_t rel_count; Elf32_Off rel_offset; -} ELFSection; + ELFSection* fast_rel; + + uint16_t sec_idx; +}; DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST) diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index 1b4f5681486c..fbcf2973dbed 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -2,6 +2,7 @@ #include "elf/elf_file.h" #include #include "application_assets.h" +#include #include @@ -81,6 +82,12 @@ void flipper_application_free(FlipperApplication* app) { } elf_file_free(app->elf); + + if(app->ep_thread_args) { + free(app->ep_thread_args); + app->ep_thread_args = NULL; + } + free(app); } @@ -224,10 +231,19 @@ static int32_t flipper_application_thread(void* context) { return ret_code; } -FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { +FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args) { furi_check(app->thread == NULL); furi_check(!flipper_application_is_plugin(app)); - app->ep_thread_args = args; + + if(app->ep_thread_args) { + free(app->ep_thread_args); + } + + if(args) { + app->ep_thread_args = strdup(args); + } else { + app->ep_thread_args = NULL; + } const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); app->thread = furi_thread_alloc_ex( @@ -289,4 +305,32 @@ const FlipperAppPluginDescriptor* lib_descriptor->ep_api_version); return lib_descriptor; +} + +bool flipper_application_load_name_and_icon( + FuriString* path, + Storage* storage, + uint8_t** icon_ptr, + FuriString* item_name) { + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); + + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload_manifest(app, furi_string_get_cstr(path)); + + bool load_success = false; + + if(preload_res == FlipperApplicationPreloadStatusSuccess) { + const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); + if(manifest->has_icon) { + memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); + } + furi_string_set(item_name, manifest->name); + load_success = true; + } else { + FURI_LOG_E(TAG, "Failed to preload %s", furi_string_get_cstr(path)); + load_success = false; + } + + flipper_application_free(app); + return load_success; } \ No newline at end of file diff --git a/lib/flipper_application/flipper_application.h b/lib/flipper_application/flipper_application.h index 519cc397108c..20baae8264f2 100644 --- a/lib/flipper_application/flipper_application.h +++ b/lib/flipper_application/flipper_application.h @@ -106,14 +106,14 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app); /** - * @brief Create application thread at entry point address, using app name and + * @brief Allocate application thread at entry point address, using app name and * stack size from metadata. Returned thread isn't started yet. * Can be only called once for application instance. * @param app Applicaiton pointer - * @param args Object to pass to app's entry point + * @param args Args to pass to app's entry point * @return Created thread */ -FuriThread* flipper_application_spawn(FlipperApplication* app, void* args); +FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args); /** * @brief Check if application is a plugin (not a runnable standalone app) @@ -149,6 +149,21 @@ typedef const FlipperAppPluginDescriptor* (*FlipperApplicationPluginEntryPoint)( const FlipperAppPluginDescriptor* flipper_application_plugin_get_descriptor(FlipperApplication* app); +/** + * @brief Load name and icon from FAP file. + * + * @param path Path to FAP file. + * @param storage Storage instance. + * @param icon_ptr Icon pointer. + * @param item_name Application name. + * @return true if icon and name were loaded successfully. + */ +bool flipper_application_load_name_and_icon( + FuriString* path, + Storage* storage, + uint8_t** icon_ptr, + FuriString* item_name); + #ifdef __cplusplus } #endif diff --git a/lib/flipper_application/plugins/composite_resolver.c b/lib/flipper_application/plugins/composite_resolver.c index 1402c3ad08fa..7cc2b340a96d 100644 --- a/lib/flipper_application/plugins/composite_resolver.c +++ b/lib/flipper_application/plugins/composite_resolver.c @@ -13,12 +13,12 @@ struct CompositeApiResolver { static bool composite_api_resolver_callback( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address) { CompositeApiResolver* resolver = (CompositeApiResolver*)interface; for M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) { - if((*interface)->resolver_callback(*interface, name, address)) { + if((*interface)->resolver_callback(*interface, hash, address)) { return true; } } diff --git a/lib/infrared/encoder_decoder/infrared.c b/lib/infrared/encoder_decoder/infrared.c index fcfc5da2b22a..56f2c3f9ee68 100644 --- a/lib/infrared/encoder_decoder/infrared.c +++ b/lib/infrared/encoder_decoder/infrared.c @@ -11,6 +11,7 @@ #include "rc6/infrared_protocol_rc6.h" #include "sirc/infrared_protocol_sirc.h" #include "kaseikyo/infrared_protocol_kaseikyo.h" +#include "rca/infrared_protocol_rca.h" typedef struct { InfraredAlloc alloc; @@ -127,6 +128,20 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { .free = infrared_encoder_kaseikyo_free}, .get_protocol_variant = infrared_protocol_kaseikyo_get_variant, }, + { + .decoder = + {.alloc = infrared_decoder_rca_alloc, + .decode = infrared_decoder_rca_decode, + .reset = infrared_decoder_rca_reset, + .check_ready = infrared_decoder_rca_check_ready, + .free = infrared_decoder_rca_free}, + .encoder = + {.alloc = infrared_encoder_rca_alloc, + .encode = infrared_encoder_rca_encode, + .reset = infrared_encoder_rca_reset, + .free = infrared_encoder_rca_free}, + .get_protocol_variant = infrared_protocol_rca_get_variant, + }, }; static int infrared_find_index_by_protocol(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/infrared.h b/lib/infrared/encoder_decoder/infrared.h index 3ab46cbbf566..ada449b98317 100644 --- a/lib/infrared/encoder_decoder/infrared.h +++ b/lib/infrared/encoder_decoder/infrared.h @@ -33,6 +33,7 @@ typedef enum { InfraredProtocolSIRC15, InfraredProtocolSIRC20, InfraredProtocolKaseikyo, + InfraredProtocolRCA, InfraredProtocolMAX, } InfraredProtocol; diff --git a/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c b/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c new file mode 100644 index 000000000000..b6d02a38c77f --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c @@ -0,0 +1,45 @@ +#include "infrared_protocol_rca_i.h" +#include + +InfraredMessage* infrared_decoder_rca_check_ready(void* ctx) { + return infrared_common_decoder_check_ready(ctx); +} + +bool infrared_decoder_rca_interpret(InfraredCommonDecoder* decoder) { + furi_assert(decoder); + + uint32_t* data = (void*)&decoder->data; + + uint8_t address = (*data & 0xF); + uint8_t command = (*data >> 4) & 0xFF; + uint8_t address_inverse = (*data >> 12) & 0xF; + uint8_t command_inverse = (*data >> 16) & 0xFF; + uint8_t inverse_address_inverse = (uint8_t)~address_inverse & 0xF; + uint8_t inverse_command_inverse = (uint8_t)~command_inverse; + + if((command == inverse_command_inverse) && (address == inverse_address_inverse)) { + decoder->message.protocol = InfraredProtocolRCA; + decoder->message.address = address; + decoder->message.command = command; + decoder->message.repeat = false; + return true; + } + + return false; +} + +void* infrared_decoder_rca_alloc(void) { + return infrared_common_decoder_alloc(&infrared_protocol_rca); +} + +InfraredMessage* infrared_decoder_rca_decode(void* decoder, bool level, uint32_t duration) { + return infrared_common_decode(decoder, level, duration); +} + +void infrared_decoder_rca_free(void* decoder) { + infrared_common_decoder_free(decoder); +} + +void infrared_decoder_rca_reset(void* decoder) { + infrared_common_decoder_reset(decoder); +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c b/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c new file mode 100644 index 000000000000..f0be4a6a9e21 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c @@ -0,0 +1,37 @@ +#include "infrared_protocol_rca_i.h" + +#include + +void infrared_encoder_rca_reset(void* encoder_ptr, const InfraredMessage* message) { + furi_assert(encoder_ptr); + furi_assert(message); + + InfraredCommonEncoder* encoder = encoder_ptr; + infrared_common_encoder_reset(encoder); + + uint32_t* data = (void*)encoder->data; + + uint8_t address = message->address; + uint8_t address_inverse = ~address; + uint8_t command = message->command; + uint8_t command_inverse = ~command; + + *data = address & 0xF; + *data |= command << 4; + *data |= (address_inverse & 0xF) << 12; + *data |= command_inverse << 16; + + encoder->bits_to_encode = encoder->protocol->databit_len[0]; +} + +void* infrared_encoder_rca_alloc(void) { + return infrared_common_encoder_alloc(&infrared_protocol_rca); +} + +void infrared_encoder_rca_free(void* encoder_ptr) { + infrared_common_encoder_free(encoder_ptr); +} + +InfraredStatus infrared_encoder_rca_encode(void* encoder_ptr, uint32_t* duration, bool* level) { + return infrared_common_encode(encoder_ptr, duration, level); +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c new file mode 100644 index 000000000000..8e1e76dbd37d --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c @@ -0,0 +1,40 @@ +#include "infrared_protocol_rca_i.h" + +const InfraredCommonProtocolSpec infrared_protocol_rca = { + .timings = + { + .preamble_mark = INFRARED_RCA_PREAMBLE_MARK, + .preamble_space = INFRARED_RCA_PREAMBLE_SPACE, + .bit1_mark = INFRARED_RCA_BIT1_MARK, + .bit1_space = INFRARED_RCA_BIT1_SPACE, + .bit0_mark = INFRARED_RCA_BIT0_MARK, + .bit0_space = INFRARED_RCA_BIT0_SPACE, + .preamble_tolerance = INFRARED_RCA_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_RCA_BIT_TOLERANCE, + .silence_time = INFRARED_RCA_SILENCE, + .min_split_time = INFRARED_RCA_MIN_SPLIT_TIME, + }, + .databit_len[0] = 24, + .no_stop_bit = false, + .decode = infrared_common_decode_pdwm, + .encode = infrared_common_encode_pdwm, + .interpret = infrared_decoder_rca_interpret, + .decode_repeat = NULL, + .encode_repeat = NULL, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_rca = { + .name = "RCA", + .address_length = 4, + .command_length = 8, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, + .repeat_count = INFRARED_RCA_REPEAT_COUNT_MIN, +}; + +const InfraredProtocolVariant* infrared_protocol_rca_get_variant(InfraredProtocol protocol) { + if(protocol == InfraredProtocolRCA) + return &infrared_protocol_variant_rca; + else + return NULL; +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h new file mode 100644 index 000000000000..d9cae48e48d9 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../infrared_i.h" + +/*************************************************************************************************** +* RCA protocol description +* https://www.sbprojects.net/knowledge/ir/rca.php +**************************************************************************************************** +* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble +* mark space Modulation up to period repeat repeat +* mark space +* +* 4000 4000 24 bit ...8000 4000 4000 +* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________ +* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________ +* +***************************************************************************************************/ + +void* infrared_decoder_rca_alloc(void); +void infrared_decoder_rca_reset(void* decoder); +void infrared_decoder_rca_free(void* decoder); +InfraredMessage* infrared_decoder_rca_check_ready(void* decoder); +InfraredMessage* infrared_decoder_rca_decode(void* decoder, bool level, uint32_t duration); + +void* infrared_encoder_rca_alloc(void); +InfraredStatus infrared_encoder_rca_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void infrared_encoder_rca_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_rca_free(void* encoder_ptr); + +const InfraredProtocolVariant* infrared_protocol_rca_get_variant(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h new file mode 100644 index 000000000000..9ec4fe3b179b --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../common/infrared_common_i.h" + +#define INFRARED_RCA_PREAMBLE_MARK 4000 +#define INFRARED_RCA_PREAMBLE_SPACE 4000 +#define INFRARED_RCA_BIT1_MARK 500 +#define INFRARED_RCA_BIT1_SPACE 2000 +#define INFRARED_RCA_BIT0_MARK 500 +#define INFRARED_RCA_BIT0_SPACE 1000 +#define INFRARED_RCA_REPEAT_PERIOD 8000 +#define INFRARED_RCA_SILENCE INFRARED_RCA_REPEAT_PERIOD + +#define INFRARED_RCA_MIN_SPLIT_TIME INFRARED_RCA_REPEAT_PAUSE_MIN +#define INFRARED_RCA_REPEAT_PAUSE_MIN 4000 +#define INFRARED_RCA_REPEAT_PAUSE_MAX 150000 +#define INFRARED_RCA_REPEAT_COUNT_MIN 1 +#define INFRARED_RCA_REPEAT_MARK INFRARED_RCA_PREAMBLE_MARK +#define INFRARED_RCA_REPEAT_SPACE INFRARED_RCA_PREAMBLE_SPACE +#define INFRARED_RCA_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_RCA_BIT_TOLERANCE 120 // us + +extern const InfraredCommonProtocolSpec infrared_protocol_rca; + +bool infrared_decoder_rca_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_decoder_rca_decode_repeat(InfraredCommonDecoder* decoder); +InfraredStatus infrared_encoder_rca_encode_repeat( + InfraredCommonEncoder* encoder, + uint32_t* duration, + bool* level); diff --git a/lib/infrared/worker/infrared_worker.c b/lib/infrared/worker/infrared_worker.c index 5add1413e908..46effd420dc3 100644 --- a/lib/infrared/worker/infrared_worker.c +++ b/lib/infrared/worker/infrared_worker.c @@ -40,8 +40,12 @@ struct InfraredWorkerSignal { size_t timings_cnt; union { InfraredMessage message; - /* +1 is for pause we add at the beginning */ - uint32_t timings[MAX_TIMINGS_AMOUNT + 1]; + struct { + /* +1 is for pause we add at the beginning */ + uint32_t timings[MAX_TIMINGS_AMOUNT + 1]; + uint32_t frequency; + float duty_cycle; + } raw; }; }; @@ -146,7 +150,7 @@ static void } if(instance->signal.timings_cnt < MAX_TIMINGS_AMOUNT) { - instance->signal.timings[instance->signal.timings_cnt] = duration; + instance->signal.raw.timings[instance->signal.timings_cnt] = duration; ++instance->signal.timings_cnt; } else { uint32_t flags_set = furi_thread_flags_set( @@ -300,7 +304,7 @@ void infrared_worker_get_raw_signal( furi_assert(timings); furi_assert(timings_cnt); - *timings = signal->timings; + *timings = signal->raw.timings; *timings_cnt = signal->timings_cnt; } @@ -390,8 +394,8 @@ static bool infrared_get_new_signal(InfraredWorker* instance) { infrared_get_protocol_duty_cycle(instance->signal.message.protocol); } else { furi_assert(instance->signal.timings_cnt > 1); - new_tx_frequency = INFRARED_COMMON_CARRIER_FREQUENCY; - new_tx_duty_cycle = INFRARED_COMMON_DUTY_CYCLE; + new_tx_frequency = instance->signal.raw.frequency; + new_tx_duty_cycle = instance->signal.raw.duty_cycle; } instance->tx.tx_raw_cnt = 0; @@ -426,7 +430,7 @@ static bool infrared_worker_tx_fill_buffer(InfraredWorker* instance) { if(instance->signal.decoded) { status = infrared_encode(instance->infrared_encoder, &timing.duration, &timing.level); } else { - timing.duration = instance->signal.timings[instance->tx.tx_raw_cnt]; + timing.duration = instance->signal.raw.timings[instance->tx.tx_raw_cnt]; /* raw always starts from Mark, but we fill it with space delay at start */ timing.level = (instance->tx.tx_raw_cnt % 2); ++instance->tx.tx_raw_cnt; @@ -597,15 +601,21 @@ void infrared_worker_set_decoded_signal(InfraredWorker* instance, const Infrared void infrared_worker_set_raw_signal( InfraredWorker* instance, const uint32_t* timings, - size_t timings_cnt) { + size_t timings_cnt, + uint32_t frequency, + float duty_cycle) { furi_assert(instance); furi_assert(timings); furi_assert(timings_cnt > 0); - size_t max_copy_num = COUNT_OF(instance->signal.timings) - 1; + furi_assert((frequency <= INFRARED_MAX_FREQUENCY) && (frequency >= INFRARED_MIN_FREQUENCY)); + furi_assert((duty_cycle < 1.0f) && (duty_cycle > 0.0f)); + size_t max_copy_num = COUNT_OF(instance->signal.raw.timings) - 1; furi_check(timings_cnt <= max_copy_num); - instance->signal.timings[0] = INFRARED_RAW_TX_TIMING_DELAY_US; - memcpy(&instance->signal.timings[1], timings, timings_cnt * sizeof(uint32_t)); + instance->signal.raw.frequency = frequency; + instance->signal.raw.duty_cycle = duty_cycle; + instance->signal.raw.timings[0] = INFRARED_RAW_TX_TIMING_DELAY_US; + memcpy(&instance->signal.raw.timings[1], timings, timings_cnt * sizeof(uint32_t)); instance->signal.decoded = false; instance->signal.timings_cnt = timings_cnt + 1; } diff --git a/lib/infrared/worker/infrared_worker.h b/lib/infrared/worker/infrared_worker.h index 1a8cd9a76c4c..e0e86198309e 100644 --- a/lib/infrared/worker/infrared_worker.h +++ b/lib/infrared/worker/infrared_worker.h @@ -130,9 +130,9 @@ void infrared_worker_tx_set_signal_sent_callback( /** Callback to pass to infrared_worker_tx_set_get_signal_callback() if signal * is steady and will not be changed between infrared_worker start and stop. * Before starting transmission, desired steady signal must be set with - * infrared_worker_make_decoded_signal() or infrared_worker_make_raw_signal(). + * infrared_worker_set_decoded_signal() or infrared_worker_set_raw_signal(). * - * This function should not be implicitly called. + * This function should not be called directly. * * @param[in] context - context * @param[out] instance - InfraredWorker instance @@ -172,11 +172,15 @@ void infrared_worker_set_decoded_signal(InfraredWorker* instance, const Infrared * @param[out] instance - InfraredWorker instance * @param[in] timings - array of raw timings * @param[in] timings_cnt - size of array of raw timings + * @param[in] frequency - carrier frequency in Hertz + * @param[in] duty_cycle - carrier duty cycle (0.0 - 1.0) */ void infrared_worker_set_raw_signal( InfraredWorker* instance, const uint32_t* timings, - size_t timings_cnt); + size_t timings_cnt, + uint32_t frequency, + float duty_cycle); #ifdef __cplusplus } diff --git a/lib/lfrfid/lfrfid_raw_worker.c b/lib/lfrfid/lfrfid_raw_worker.c index 22c0bbd02c1f..aa962a47d690 100644 --- a/lib/lfrfid/lfrfid_raw_worker.c +++ b/lib/lfrfid/lfrfid_raw_worker.c @@ -151,9 +151,7 @@ static int32_t lfrfid_raw_read_worker_thread(void* thread_context) { if(file_valid) { // setup carrier - furi_hal_rfid_pins_read(); - furi_hal_rfid_tim_read(worker->frequency, worker->duty_cycle); - furi_hal_rfid_tim_read_start(); + furi_hal_rfid_tim_read_start(worker->frequency, worker->duty_cycle); // stabilize detector furi_delay_ms(1500); diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index 9b6f16eb14c9..8a26677746d1 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -100,24 +100,21 @@ static LFRFIDWorkerReadState lfrfid_worker_read_internal( uint32_t timeout, ProtocolId* result_protocol) { LFRFIDWorkerReadState state = LFRFIDWorkerReadTimeout; - furi_hal_rfid_pins_read(); if(feature & LFRFIDFeatureASK) { - furi_hal_rfid_tim_read(125000, 0.5); + furi_hal_rfid_tim_read_start(125000, 0.5); FURI_LOG_D(TAG, "Start ASK"); if(worker->read_cb) { worker->read_cb(LFRFIDWorkerReadStartASK, PROTOCOL_NO, worker->cb_ctx); } } else { - furi_hal_rfid_tim_read(62500, 0.25); + furi_hal_rfid_tim_read_start(62500, 0.25); FURI_LOG_D(TAG, "Start PSK"); if(worker->read_cb) { worker->read_cb(LFRFIDWorkerReadStartPSK, PROTOCOL_NO, worker->cb_ctx); } } - furi_hal_rfid_tim_read_start(); - // stabilize detector lfrfid_worker_delay(worker, LFRFID_WORKER_READ_STABILIZE_TIME_MS); @@ -205,7 +202,7 @@ static LFRFIDWorkerReadState lfrfid_worker_read_internal( average_index = 0; if(worker->read_cb) { - if(average > 0.2 && average < 0.8) { + if(average > 0.2f && average < 0.8f) { if(!card_detected) { card_detected = true; worker->read_cb( diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index 2c1f0ad97cd2..f07218d7f302 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -16,6 +16,7 @@ #include "protocol_pac_stanley.h" #include "protocol_keri.h" #include "protocol_gallagher.h" +#include "protocol_nexwatch.h" const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, @@ -35,4 +36,5 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolPACStanley] = &protocol_pac_stanley, [LFRFIDProtocolKeri] = &protocol_keri, [LFRFIDProtocolGallagher] = &protocol_gallagher, -}; \ No newline at end of file + [LFRFIDProtocolNexwatch] = &protocol_nexwatch, +}; diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 848f003a31e7..0cb7cbc84405 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -25,6 +25,7 @@ typedef enum { LFRFIDProtocolPACStanley, LFRFIDProtocolKeri, LFRFIDProtocolGallagher, + LFRFIDProtocolNexwatch, LFRFIDProtocolMax, } LFRFIDProtocol; @@ -39,4 +40,4 @@ typedef struct { union { LFRFIDT5577 t5577; }; -} LFRFIDWriteRequest; \ No newline at end of file +} LFRFIDWriteRequest; diff --git a/lib/lfrfid/protocols/protocol_nexwatch.c b/lib/lfrfid/protocols/protocol_nexwatch.c new file mode 100644 index 000000000000..3bbbb42f503c --- /dev/null +++ b/lib/lfrfid/protocols/protocol_nexwatch.c @@ -0,0 +1,323 @@ +#include +#include +#include +#include "lfrfid_protocols.h" + +#define NEXWATCH_PREAMBLE_BIT_SIZE (8) +#define NEXWATCH_PREAMBLE_DATA_SIZE (1) + +#define NEXWATCH_ENCODED_BIT_SIZE (96) +#define NEXWATCH_ENCODED_DATA_SIZE ((NEXWATCH_ENCODED_BIT_SIZE) / 8) + +#define NEXWATCH_DECODED_BIT_SIZE (NEXWATCH_DECODED_DATA_SIZE * 8) +#define NEXWATCH_DECODED_DATA_SIZE (8) + +#define NEXWATCH_US_PER_BIT (255) +#define NEXWATCH_ENCODER_PULSES_PER_BIT (16) + +typedef struct { + uint8_t magic; + char desc[13]; + uint8_t chk; +} ProtocolNexwatchMagic; + +ProtocolNexwatchMagic magic_items[] = { + {0xBE, "Quadrakey", 0}, + {0x88, "Nexkey", 0}, + {0x86, "Honeywell", 0}}; + +typedef struct { + uint8_t data_index; + uint8_t bit_clock_index; + bool last_bit; + bool current_polarity; + bool pulse_phase; +} ProtocolNexwatchEncoder; + +typedef struct { + uint8_t encoded_data[NEXWATCH_ENCODED_DATA_SIZE]; + uint8_t negative_encoded_data[NEXWATCH_ENCODED_DATA_SIZE]; + uint8_t corrupted_encoded_data[NEXWATCH_ENCODED_DATA_SIZE]; + uint8_t corrupted_negative_encoded_data[NEXWATCH_ENCODED_DATA_SIZE]; + + uint8_t data[NEXWATCH_DECODED_DATA_SIZE]; + ProtocolNexwatchEncoder encoder; +} ProtocolNexwatch; + +ProtocolNexwatch* protocol_nexwatch_alloc(void) { + ProtocolNexwatch* protocol = malloc(sizeof(ProtocolNexwatch)); + return protocol; +}; + +void protocol_nexwatch_free(ProtocolNexwatch* protocol) { + free(protocol); +}; + +uint8_t* protocol_nexwatch_get_data(ProtocolNexwatch* protocol) { + return protocol->data; +}; + +void protocol_nexwatch_decoder_start(ProtocolNexwatch* protocol) { + memset(protocol->encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE); + memset(protocol->negative_encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE); + memset(protocol->corrupted_encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE); + memset(protocol->corrupted_negative_encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE); +}; + +static bool protocol_nexwatch_check_preamble(uint8_t* data, size_t bit_index) { + // 01010110 + if(bit_lib_get_bits(data, bit_index, 8) != 0b01010110) return false; + return true; +} + +static uint8_t protocol_nexwatch_parity_swap(uint8_t parity) { + uint8_t a = (((parity >> 3) & 1)); + a |= (((parity >> 1) & 1) << 1); + a |= (((parity >> 2) & 1) << 2); + a |= ((parity & 1) << 3); + return a; +} + +static uint8_t protocol_nexwatch_parity(const uint8_t hexid[5]) { + uint8_t p = 0; + for(uint8_t i = 0; i < 5; i++) { + p ^= ((hexid[i]) & 0xF0) >> 4; + p ^= ((hexid[i]) & 0x0F); + } + return protocol_nexwatch_parity_swap(p); +} + +static uint8_t protocol_nexwatch_checksum(uint8_t magic, uint32_t id, uint8_t parity) { + uint8_t a = ((id >> 24) & 0xFF); + a -= ((id >> 16) & 0xFF); + a -= ((id >> 8) & 0xFF); + a -= (id & 0xFF); + a -= magic; + a -= (bit_lib_reverse_8_fast(parity) >> 4); + return bit_lib_reverse_8_fast(a); +} + +static bool protocol_nexwatch_can_be_decoded(uint8_t* data) { + if(!protocol_nexwatch_check_preamble(data, 0)) return false; + + // Check for reserved word (32-bit) + if(bit_lib_get_bits_32(data, 8, 32) != 0) { + return false; + } + + uint8_t parity = bit_lib_get_bits(data, 76, 4); + + // parity check + // from 32b hex id, 4b mode + uint8_t hex[5] = {0}; + for(uint8_t i = 0; i < 5; i++) { + hex[i] = bit_lib_get_bits(data, 40 + (i * 8), 8); + } + //mode is only 4 bits. + hex[4] &= 0xf0; + uint8_t calc_parity = protocol_nexwatch_parity(hex); + + if(calc_parity != parity) { + return false; + } + + return true; +} + +static bool protocol_nexwatch_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) { + time += (NEXWATCH_US_PER_BIT / 2); + + size_t bit_count = (time / NEXWATCH_US_PER_BIT); + bool result = false; + + if(bit_count < NEXWATCH_ENCODED_BIT_SIZE) { + for(size_t i = 0; i < bit_count; i++) { + bit_lib_push_bit(data, NEXWATCH_ENCODED_DATA_SIZE, polarity); + if(protocol_nexwatch_can_be_decoded(data)) { + result = true; + break; + } + } + } + + return result; +} + +static void protocol_nexwatch_descramble(uint32_t* id, uint32_t* scrambled) { + // 255 = Not used/Unknown other values are the bit offset in the ID/FC values + const uint8_t hex_2_id[] = {31, 27, 23, 19, 15, 11, 7, 3, 30, 26, 22, 18, 14, 10, 6, 2, + 29, 25, 21, 17, 13, 9, 5, 1, 28, 24, 20, 16, 12, 8, 4, 0}; + + *id = 0; + for(uint8_t idx = 0; idx < 32; idx++) { + bool bit_state = (*scrambled >> hex_2_id[idx]) & 1; + *id |= (bit_state << (31 - idx)); + } +} + +static void protocol_nexwatch_decoder_save(uint8_t* data_to, const uint8_t* data_from) { + uint32_t id = bit_lib_get_bits_32(data_from, 40, 32); + data_to[4] = (uint8_t)id; + data_to[3] = (uint8_t)(id >>= 8); + data_to[2] = (uint8_t)(id >>= 8); + data_to[1] = (uint8_t)(id >>= 8); + data_to[0] = (uint8_t)(id >>= 8); + uint32_t check = bit_lib_get_bits_32(data_from, 72, 24); + data_to[7] = (uint8_t)check; + data_to[6] = (uint8_t)(check >>= 8); + data_to[5] = (uint8_t)(check >>= 8); +} + +bool protocol_nexwatch_decoder_feed(ProtocolNexwatch* protocol, bool level, uint32_t duration) { + bool result = false; + + if(duration > (NEXWATCH_US_PER_BIT / 2)) { + if(protocol_nexwatch_decoder_feed_internal(level, duration, protocol->encoded_data)) { + protocol_nexwatch_decoder_save(protocol->data, protocol->encoded_data); + result = true; + return result; + } + + if(protocol_nexwatch_decoder_feed_internal( + !level, duration, protocol->negative_encoded_data)) { + protocol_nexwatch_decoder_save(protocol->data, protocol->negative_encoded_data); + result = true; + return result; + } + } + + if(duration > (NEXWATCH_US_PER_BIT / 4)) { + // Try to decode wrong phase synced data + if(level) { + duration += 120; + } else { + if(duration > 120) { + duration -= 120; + } + } + + if(protocol_nexwatch_decoder_feed_internal( + level, duration, protocol->corrupted_encoded_data)) { + protocol_nexwatch_decoder_save(protocol->data, protocol->corrupted_encoded_data); + + result = true; + return result; + } + + if(protocol_nexwatch_decoder_feed_internal( + !level, duration, protocol->corrupted_negative_encoded_data)) { + protocol_nexwatch_decoder_save( + protocol->data, protocol->corrupted_negative_encoded_data); + + result = true; + return result; + } + } + + return result; +}; + +bool protocol_nexwatch_encoder_start(ProtocolNexwatch* protocol) { + memset(protocol->encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE); + *(uint32_t*)&protocol->encoded_data[0] = 0b00000000000000000000000001010110; + bit_lib_copy_bits(protocol->encoded_data, 32, 32, protocol->data, 0); + bit_lib_copy_bits(protocol->encoded_data, 64, 32, protocol->data, 32); + + protocol->encoder.last_bit = + bit_lib_get_bit(protocol->encoded_data, NEXWATCH_ENCODED_BIT_SIZE - 1); + protocol->encoder.data_index = 0; + protocol->encoder.current_polarity = true; + protocol->encoder.pulse_phase = true; + protocol->encoder.bit_clock_index = 0; + + return true; +}; + +LevelDuration protocol_nexwatch_encoder_yield(ProtocolNexwatch* protocol) { + LevelDuration level_duration; + ProtocolNexwatchEncoder* encoder = &protocol->encoder; + + if(encoder->pulse_phase) { + level_duration = level_duration_make(encoder->current_polarity, 1); + encoder->pulse_phase = false; + } else { + level_duration = level_duration_make(!encoder->current_polarity, 1); + encoder->pulse_phase = true; + + encoder->bit_clock_index++; + if(encoder->bit_clock_index >= NEXWATCH_ENCODER_PULSES_PER_BIT) { + encoder->bit_clock_index = 0; + + bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index); + + if(current_bit != encoder->last_bit) { + encoder->current_polarity = !encoder->current_polarity; + } + + encoder->last_bit = current_bit; + + bit_lib_increment_index(encoder->data_index, NEXWATCH_ENCODED_BIT_SIZE); + } + } + + return level_duration; +}; + +void protocol_nexwatch_render_data(ProtocolNexwatch* protocol, FuriString* result) { + uint32_t id = 0; + uint32_t scrambled = bit_lib_get_bits_32(protocol->data, 8, 32); + protocol_nexwatch_descramble(&id, &scrambled); + + uint8_t m_idx; + uint8_t mode = bit_lib_get_bits(protocol->data, 40, 4); + uint8_t parity = bit_lib_get_bits(protocol->data, 44, 4); + uint8_t chk = bit_lib_get_bits(protocol->data, 48, 8); + for(m_idx = 0; m_idx < 3; m_idx++) { + magic_items[m_idx].chk = protocol_nexwatch_checksum(magic_items[m_idx].magic, id, parity); + if(magic_items[m_idx].chk == chk) { + break; + } + } + furi_string_printf(result, "ID: %lu, M:%u\r\nType: %s\r\n", id, mode, magic_items[m_idx].desc); +} + +bool protocol_nexwatch_write_data(ProtocolNexwatch* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_nexwatch_encoder_start(protocol); + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_PSK1 | LFRFID_T5577_BITRATE_RF_32 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +const ProtocolBase protocol_nexwatch = { + .name = "Nexwatch", + .manufacturer = "Honeywell", + .data_size = NEXWATCH_DECODED_DATA_SIZE, + .features = LFRFIDFeaturePSK, + .validate_count = 6, + .alloc = (ProtocolAlloc)protocol_nexwatch_alloc, + .free = (ProtocolFree)protocol_nexwatch_free, + .get_data = (ProtocolGetData)protocol_nexwatch_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_nexwatch_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_nexwatch_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_nexwatch_encoder_start, + .yield = (ProtocolEncoderYield)protocol_nexwatch_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_nexwatch_render_data, + .render_brief_data = (ProtocolRenderData)protocol_nexwatch_render_data, + .write_data = (ProtocolWriteData)protocol_nexwatch_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_nexwatch.h b/lib/lfrfid/protocols/protocol_nexwatch.h new file mode 100644 index 000000000000..0872ca7dcdde --- /dev/null +++ b/lib/lfrfid/protocols/protocol_nexwatch.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_nexwatch; diff --git a/lib/lfrfid/tools/t5577.c b/lib/lfrfid/tools/t5577.c index 3444afea3e41..00c9ddfb6272 100644 --- a/lib/lfrfid/tools/t5577.c +++ b/lib/lfrfid/tools/t5577.c @@ -14,9 +14,7 @@ #define T5577_OPCODE_RESET 0b00 static void t5577_start() { - furi_hal_rfid_tim_read(125000, 0.5); - furi_hal_rfid_pins_read(); - furi_hal_rfid_tim_read_start(); + furi_hal_rfid_tim_read_start(125000, 0.5); // do not ground the antenna furi_hal_rfid_pin_pull_release(); @@ -24,14 +22,13 @@ static void t5577_start() { static void t5577_stop() { furi_hal_rfid_tim_read_stop(); - furi_hal_rfid_tim_reset(); furi_hal_rfid_pins_reset(); } static void t5577_write_gap(uint32_t gap_time) { - furi_hal_rfid_tim_read_stop(); + furi_hal_rfid_tim_read_pause(); furi_delay_us(gap_time * 8); - furi_hal_rfid_tim_read_start(); + furi_hal_rfid_tim_read_continue(); } static void t5577_write_bit(bool value) { diff --git a/lib/misc.scons b/lib/misc.scons index 1ff6e2fb0ce3..92fa5a106ff1 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -4,7 +4,6 @@ Import("env") env.Append( CPPPATH=[ - "#/lib/digital_signal", "#/lib/fnv1a_hash", "#/lib/heatshrink", "#/lib/micro-ecc", @@ -16,6 +15,9 @@ env.Append( ], SDK_HEADERS=[ File("micro-ecc/uECC.h"), + File("nanopb/pb.h"), + File("nanopb/pb_decode.h"), + File("nanopb/pb_encode.h"), ], ) @@ -26,7 +28,6 @@ libenv.ApplyLibFlags() sources = [] libs_recurse = [ - "digital_signal", "micro-ecc", "u8g2", "update_util", diff --git a/lib/music_worker/SConscript b/lib/music_worker/SConscript new file mode 100644 index 000000000000..36d01d8596ab --- /dev/null +++ b/lib/music_worker/SConscript @@ -0,0 +1,27 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/music_worker", + ], + SDK_HEADERS=[ + File("music_worker.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="music_worker") +libenv.ApplyLibFlags() + +libenv.AppendUnique( + CCFLAGS=[ + # Required for lib to be linkable with .faps + "-mword-relocations", + "-mlong-calls", + ], +) + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/applications/external/music_player/music_player_worker.c b/lib/music_worker/music_worker.c similarity index 85% rename from applications/external/music_player/music_player_worker.c rename to lib/music_worker/music_worker.c index ee350ee80455..61fc838f2e36 100644 --- a/applications/external/music_player/music_player_worker.c +++ b/lib/music_worker/music_worker.c @@ -1,4 +1,4 @@ -#include "music_player_worker.h" +#include "music_worker.h" #include #include @@ -9,7 +9,7 @@ #include #include -#define TAG "MusicPlayerWorker" +#define TAG "MusicWorker" #define MUSIC_PLAYER_FILETYPE "Flipper Music Format" #define MUSIC_PLAYER_VERSION 0 @@ -28,11 +28,11 @@ typedef struct { ARRAY_DEF(NoteBlockArray, NoteBlock, M_POD_OPLIST); -struct MusicPlayerWorker { +struct MusicWorker { FuriThread* thread; bool should_work; - MusicPlayerWorkerCallback callback; + MusicWorkerCallback callback; void* callback_context; float volume; @@ -42,9 +42,9 @@ struct MusicPlayerWorker { NoteBlockArray_t notes; }; -static int32_t music_player_worker_thread_callback(void* context) { +static int32_t music_worker_thread_callback(void* context) { furi_assert(context); - MusicPlayerWorker* instance = context; + MusicWorker* instance = context; NoteBlockArray_it_t it; NoteBlockArray_it(it, instance->notes); @@ -97,24 +97,24 @@ static int32_t music_player_worker_thread_callback(void* context) { return 0; } -MusicPlayerWorker* music_player_worker_alloc() { - MusicPlayerWorker* instance = malloc(sizeof(MusicPlayerWorker)); +MusicWorker* music_worker_alloc() { + MusicWorker* instance = malloc(sizeof(MusicWorker)); NoteBlockArray_init(instance->notes); - instance->thread = furi_thread_alloc_ex( - "MusicPlayerWorker", 1024, music_player_worker_thread_callback, instance); + instance->thread = + furi_thread_alloc_ex("MusicWorker", 1024, music_worker_thread_callback, instance); instance->volume = 1.0f; return instance; } -void music_player_worker_clear(MusicPlayerWorker* instance) { +void music_worker_clear(MusicWorker* instance) { NoteBlockArray_reset(instance->notes); } -void music_player_worker_free(MusicPlayerWorker* instance) { +void music_worker_free(MusicWorker* instance) { furi_assert(instance); furi_thread_free(instance->thread); NoteBlockArray_clear(instance->notes); @@ -186,11 +186,8 @@ static size_t skip_till(const char* string, const char symbol) { return ret; } -static bool music_player_worker_add_note( - MusicPlayerWorker* instance, - uint8_t semitone, - uint8_t duration, - uint8_t dots) { +static bool + music_worker_add_note(MusicWorker* instance, uint8_t semitone, uint8_t duration, uint8_t dots) { NoteBlock note_block; note_block.semitone = semitone; @@ -228,7 +225,7 @@ static int8_t note_to_semitone(const char note) { } } -static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const char* string) { +static bool music_worker_parse_notes(MusicWorker* instance, const char* string) { const char* cursor = string; bool result = true; @@ -286,7 +283,7 @@ static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const c semitone += sharp_char == '#' ? 1 : 0; } - if(music_player_worker_add_note(instance, semitone, duration, dots)) { + if(music_worker_add_note(instance, semitone, duration, dots)) { FURI_LOG_D( TAG, "Added note: %c%c%lu.%lu = %u %lu", @@ -316,20 +313,20 @@ static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const c return result; } -bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path) { +bool music_worker_load(MusicWorker* instance, const char* file_path) { furi_assert(instance); furi_assert(file_path); bool ret = false; if(strcasestr(file_path, ".fmf")) { - ret = music_player_worker_load_fmf_from_file(instance, file_path); + ret = music_worker_load_fmf_from_file(instance, file_path); } else { - ret = music_player_worker_load_rtttl_from_file(instance, file_path); + ret = music_worker_load_rtttl_from_file(instance, file_path); } return ret; } -bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path) { +bool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path) { furi_assert(instance); furi_assert(file_path); @@ -369,7 +366,7 @@ bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const c break; } - if(!music_player_worker_parse_notes(instance, furi_string_get_cstr(temp_str))) { + if(!music_worker_parse_notes(instance, furi_string_get_cstr(temp_str))) { break; } @@ -383,7 +380,7 @@ bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const c return result; } -bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path) { +bool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path) { furi_assert(instance); furi_assert(file_path); @@ -414,7 +411,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const break; } - if(!music_player_worker_load_rtttl_from_string(instance, furi_string_get_cstr(content))) { + if(!music_worker_load_rtttl_from_string(instance, furi_string_get_cstr(content))) { FURI_LOG_E(TAG, "Invalid file content"); break; } @@ -429,7 +426,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const return result; } -bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string) { +bool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string) { furi_assert(instance); const char* cursor = string; @@ -470,28 +467,25 @@ bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, con return false; } cursor++; - if(!music_player_worker_parse_notes(instance, cursor)) { + if(!music_worker_parse_notes(instance, cursor)) { return false; } return true; } -void music_player_worker_set_callback( - MusicPlayerWorker* instance, - MusicPlayerWorkerCallback callback, - void* context) { +void music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context) { furi_assert(instance); instance->callback = callback; instance->callback_context = context; } -void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume) { +void music_worker_set_volume(MusicWorker* instance, float volume) { furi_assert(instance); instance->volume = volume; } -void music_player_worker_start(MusicPlayerWorker* instance) { +void music_worker_start(MusicWorker* instance) { furi_assert(instance); furi_assert(instance->should_work == false); @@ -499,10 +493,15 @@ void music_player_worker_start(MusicPlayerWorker* instance) { furi_thread_start(instance->thread); } -void music_player_worker_stop(MusicPlayerWorker* instance) { +void music_worker_stop(MusicWorker* instance) { furi_assert(instance); furi_assert(instance->should_work == true); instance->should_work = false; furi_thread_join(instance->thread); } + +bool music_worker_is_playing(MusicWorker* instance) { + furi_assert(instance); + return instance->should_work; +} diff --git a/lib/music_worker/music_worker.h b/lib/music_worker/music_worker.h new file mode 100644 index 000000000000..5a7cb4936a9f --- /dev/null +++ b/lib/music_worker/music_worker.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +typedef void (*MusicWorkerCallback)( + uint8_t semitone, + uint8_t dots, + uint8_t duration, + float position, + void* context); + +typedef struct MusicWorker MusicWorker; + +MusicWorker* music_worker_alloc(); + +void music_worker_clear(MusicWorker* instance); + +void music_worker_free(MusicWorker* instance); + +bool music_worker_load(MusicWorker* instance, const char* file_path); + +bool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path); + +bool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path); + +bool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string); + +void music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context); + +void music_worker_set_volume(MusicWorker* instance, float volume); + +void music_worker_start(MusicWorker* instance); + +void music_worker_stop(MusicWorker* instance); + +bool music_worker_is_playing(MusicWorker* instance); diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index b086298de2aa..4702c7b96f08 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -5,14 +5,45 @@ env.Append( "#/lib/nfc", ], SDK_HEADERS=[ + # Main + File("nfc.h"), File("nfc_device.h"), + File("nfc_listener.h"), + File("nfc_poller.h"), + File("nfc_scanner.h"), + # Protocols + File("protocols/iso14443_3a/iso14443_3a.h"), + File("protocols/iso14443_3b/iso14443_3b.h"), + File("protocols/iso14443_4a/iso14443_4a.h"), + File("protocols/mf_ultralight/mf_ultralight.h"), + File("protocols/mf_classic/mf_classic.h"), + File("protocols/mf_desfire/mf_desfire.h"), + # Pollers + File("protocols/iso14443_3a/iso14443_3a_poller.h"), + File("protocols/iso14443_3b/iso14443_3b_poller.h"), + File("protocols/iso14443_4a/iso14443_4a_poller.h"), + File("protocols/mf_ultralight/mf_ultralight_poller.h"), + File("protocols/mf_classic/mf_classic_poller.h"), + File("protocols/mf_desfire/mf_desfire_poller.h"), + # Listeners + File("protocols/iso14443_3a/iso14443_3a_listener.h"), + File("protocols/iso14443_4a/iso14443_4a_listener.h"), + File("protocols/mf_ultralight/mf_ultralight_listener.h"), + File("protocols/mf_classic/mf_classic_listener.h"), + # Sync API + File("protocols/iso14443_3a/iso14443_3a_poller_sync_api.h"), + File("protocols/mf_ultralight/mf_ultralight_poller_sync_api.h"), + File("protocols/mf_classic/mf_classic_poller_sync_api.h"), + # Misc + File("helpers/nfc_util.h"), + File("helpers/iso14443_crc.h"), ], ) libenv = env.Clone(FW_LIB_NAME="nfc") libenv.ApplyLibFlags() -sources = libenv.GlobRecursive("*.c*") +sources = libenv.GlobRecursive("*.c*", exclude="deprecated/*c") lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/lib/nfc/helpers/mf_classic_dict.c b/lib/nfc/deprecated/helpers/mf_classic_dict.c similarity index 100% rename from lib/nfc/helpers/mf_classic_dict.c rename to lib/nfc/deprecated/helpers/mf_classic_dict.c diff --git a/lib/nfc/helpers/mf_classic_dict.h b/lib/nfc/deprecated/helpers/mf_classic_dict.h similarity index 100% rename from lib/nfc/helpers/mf_classic_dict.h rename to lib/nfc/deprecated/helpers/mf_classic_dict.h diff --git a/lib/nfc/helpers/mfkey32.c b/lib/nfc/deprecated/helpers/mfkey32.c similarity index 97% rename from lib/nfc/helpers/mfkey32.c rename to lib/nfc/deprecated/helpers/mfkey32.c index 47e7e9f6b39b..e9701212ff3c 100644 --- a/lib/nfc/helpers/mfkey32.c +++ b/lib/nfc/deprecated/helpers/mfkey32.c @@ -6,8 +6,7 @@ #include #include -#include -#include +#include #define TAG "Mfkey32" @@ -169,7 +168,7 @@ void mfkey32_process_data( if(reader_to_tag) { if((data[0] == 0x60) || (data[0] == 0x61)) { nonce->key = data[0] == 0x60 ? MfClassicKeyA : MfClassicKeyB; - nonce->sector = mf_classic_get_sector_by_block(data[1]); + nonce->sector = mifare_classic_get_sector_by_block(data[1]); instance->state = Mfkey32StateAuthReceived; data_processed = true; } diff --git a/lib/nfc/helpers/mfkey32.h b/lib/nfc/deprecated/helpers/mfkey32.h similarity index 90% rename from lib/nfc/helpers/mfkey32.h rename to lib/nfc/deprecated/helpers/mfkey32.h index e1f472e50c81..9648f3e7a8fd 100644 --- a/lib/nfc/helpers/mfkey32.h +++ b/lib/nfc/deprecated/helpers/mfkey32.h @@ -1,6 +1,6 @@ #pragma once -#include +#include typedef struct Mfkey32 Mfkey32; diff --git a/lib/nfc/helpers/nfc_debug_log.c b/lib/nfc/deprecated/helpers/nfc_debug_log.c similarity index 100% rename from lib/nfc/helpers/nfc_debug_log.c rename to lib/nfc/deprecated/helpers/nfc_debug_log.c diff --git a/lib/nfc/helpers/nfc_debug_log.h b/lib/nfc/deprecated/helpers/nfc_debug_log.h similarity index 100% rename from lib/nfc/helpers/nfc_debug_log.h rename to lib/nfc/deprecated/helpers/nfc_debug_log.h diff --git a/lib/nfc/helpers/nfc_debug_pcap.c b/lib/nfc/deprecated/helpers/nfc_debug_pcap.c similarity index 100% rename from lib/nfc/helpers/nfc_debug_pcap.c rename to lib/nfc/deprecated/helpers/nfc_debug_pcap.c diff --git a/lib/nfc/helpers/nfc_debug_pcap.h b/lib/nfc/deprecated/helpers/nfc_debug_pcap.h similarity index 100% rename from lib/nfc/helpers/nfc_debug_pcap.h rename to lib/nfc/deprecated/helpers/nfc_debug_pcap.h diff --git a/lib/nfc/helpers/nfc_generators.c b/lib/nfc/deprecated/helpers/nfc_generators.c similarity index 84% rename from lib/nfc/helpers/nfc_generators.c rename to lib/nfc/deprecated/helpers/nfc_generators.c index 50c89aba85c9..7321183c819a 100644 --- a/lib/nfc/helpers/nfc_generators.c +++ b/lib/nfc/deprecated/helpers/nfc_generators.c @@ -13,8 +13,8 @@ static const uint8_t default_data_ntag215_216[] = {0x03, 0x00, 0xFE}; static const uint8_t default_data_ntag_i2c[] = {0xE1, 0x10, 0x00, 0x00, 0x03, 0x00, 0xFE}; static const uint8_t default_config_ntag_i2c[] = {0x01, 0x00, 0xF8, 0x48, 0x08, 0x01, 0x00, 0x00}; -static void nfc_generate_common_start(NfcDeviceData* data) { - nfc_device_data_clear(data); +static void nfc_generate_common_start(NfcDeviceOldDataOld* data) { + nfc_device_old_data_clear(data); } static void nfc_generate_mf_ul_uid(uint8_t* uid) { @@ -71,14 +71,14 @@ static void nfc_generate_mf_classic_sector_trailer(MfClassicData* data, uint8_t memset(sec_tr->key_a, 0xff, sizeof(sec_tr->key_a)); memset(sec_tr->key_b, 0xff, sizeof(sec_tr->key_b)); - mf_classic_set_block_read(data, block, &data->block[block]); - mf_classic_set_key_found( - data, mf_classic_get_sector_by_block(block), MfClassicKeyA, 0xFFFFFFFFFFFF); - mf_classic_set_key_found( - data, mf_classic_get_sector_by_block(block), MfClassicKeyB, 0xFFFFFFFFFFFF); + mifare_classic_set_block_read(data, block, &data->block[block]); + mifare_classic_set_key_found( + data, mifare_classic_get_sector_by_block(block), MfClassicKeyA, 0xFFFFFFFFFFFF); + mifare_classic_set_key_found( + data, mifare_classic_get_sector_by_block(block), MfClassicKeyB, 0xFFFFFFFFFFFF); } -static void nfc_generate_mf_ul_common(NfcDeviceData* data) { +static void nfc_generate_mf_ul_common(NfcDeviceOldDataOld* data) { data->nfc_data.type = FuriHalNfcTypeA; data->nfc_data.interface = FuriHalNfcInterfaceRf; data->nfc_data.uid_len = 7; @@ -86,18 +86,18 @@ static void nfc_generate_mf_ul_common(NfcDeviceData* data) { data->nfc_data.atqa[0] = 0x44; data->nfc_data.atqa[1] = 0x00; data->nfc_data.sak = 0x00; - data->protocol = NfcDeviceProtocolMifareUl; + data->protocol = NfcDeviceOldProtocolMifareUl; } static void - nfc_generate_mf_classic_common(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { + nfc_generate_mf_classic_common(NfcDeviceOldDataOld* data, uint8_t uid_len, MfClassicType type) { data->nfc_data.type = FuriHalNfcTypeA; data->nfc_data.interface = FuriHalNfcInterfaceRf; data->nfc_data.uid_len = uid_len; data->nfc_data.atqa[0] = 0x44; data->nfc_data.atqa[1] = 0x00; data->nfc_data.sak = 0x08; - data->protocol = NfcDeviceProtocolMifareClassic; + data->protocol = NfcDeviceOldProtocolMifareClassic; data->mf_classic_data.type = type; } @@ -106,14 +106,14 @@ static void nfc_generate_calc_bcc(uint8_t* uid, uint8_t* bcc0, uint8_t* bcc1) { *bcc1 = uid[3] ^ uid[4] ^ uid[5] ^ uid[6]; } -static void nfc_generate_mf_ul_copy_uid_with_bcc(NfcDeviceData* data) { +static void nfc_generate_mf_ul_copy_uid_with_bcc(NfcDeviceOldDataOld* data) { MfUltralightData* mful = &data->mf_ul_data; memcpy(mful->data, data->nfc_data.uid, 3); memcpy(&mful->data[4], &data->nfc_data.uid[3], 4); nfc_generate_calc_bcc(data->nfc_data.uid, &mful->data[3], &mful->data[8]); } -static void nfc_generate_mf_ul_orig(NfcDeviceData* data) { +static void nfc_generate_mf_ul_orig(NfcDeviceOldDataOld* data) { nfc_generate_common_start(data); nfc_generate_mf_ul_common(data); @@ -126,7 +126,7 @@ static void nfc_generate_mf_ul_orig(NfcDeviceData* data) { memset(&mful->data[4 * 4], 0xFF, 4); } -static void nfc_generate_mf_ul_ntag203(NfcDeviceData* data) { +static void nfc_generate_mf_ul_ntag203(NfcDeviceOldDataOld* data) { nfc_generate_common_start(data); nfc_generate_mf_ul_common(data); @@ -139,7 +139,7 @@ static void nfc_generate_mf_ul_ntag203(NfcDeviceData* data) { memcpy(&mful->data[3 * 4], default_data_ntag203, sizeof(default_data_ntag203)); } -static void nfc_generate_mf_ul_with_config_common(NfcDeviceData* data, uint8_t num_pages) { +static void nfc_generate_mf_ul_with_config_common(NfcDeviceOldDataOld* data, uint8_t num_pages) { nfc_generate_common_start(data); nfc_generate_mf_ul_common(data); @@ -155,7 +155,7 @@ static void nfc_generate_mf_ul_with_config_common(NfcDeviceData* data, uint8_t n if(num_pages > 20) mful->data[config_index - 1] = MF_UL_TEARING_FLAG_DEFAULT; } -static void nfc_generate_mf_ul_ev1_common(NfcDeviceData* data, uint8_t num_pages) { +static void nfc_generate_mf_ul_ev1_common(NfcDeviceOldDataOld* data, uint8_t num_pages) { nfc_generate_mf_ul_with_config_common(data, num_pages); MfUltralightData* mful = &data->mf_ul_data; memcpy(&mful->version, version_bytes_mf0ulx1, sizeof(version_bytes_mf0ulx1)); @@ -165,7 +165,7 @@ static void nfc_generate_mf_ul_ev1_common(NfcDeviceData* data, uint8_t num_pages // TODO: what's internal byte on page 2? } -static void nfc_generate_mf_ul_11(NfcDeviceData* data) { +static void nfc_generate_mf_ul_11(NfcDeviceOldDataOld* data) { nfc_generate_mf_ul_ev1_common(data, 20); MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeUL11; @@ -174,7 +174,7 @@ static void nfc_generate_mf_ul_11(NfcDeviceData* data) { mful->data[16 * 4] = 0x00; // Low capacitance version does not have STRG_MOD_EN } -static void nfc_generate_mf_ul_h11(NfcDeviceData* data) { +static void nfc_generate_mf_ul_h11(NfcDeviceOldDataOld* data) { nfc_generate_mf_ul_ev1_common(data, 20); MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeUL11; @@ -182,7 +182,7 @@ static void nfc_generate_mf_ul_h11(NfcDeviceData* data) { mful->version.storage_size = 0x0B; } -static void nfc_generate_mf_ul_21(NfcDeviceData* data) { +static void nfc_generate_mf_ul_21(NfcDeviceOldDataOld* data) { nfc_generate_mf_ul_ev1_common(data, 41); MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeUL21; @@ -191,7 +191,7 @@ static void nfc_generate_mf_ul_21(NfcDeviceData* data) { mful->data[37 * 4] = 0x00; // Low capacitance version does not have STRG_MOD_EN } -static void nfc_generate_mf_ul_h21(NfcDeviceData* data) { +static void nfc_generate_mf_ul_h21(NfcDeviceOldDataOld* data) { nfc_generate_mf_ul_ev1_common(data, 41); MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeUL21; @@ -199,7 +199,7 @@ static void nfc_generate_mf_ul_h21(NfcDeviceData* data) { mful->version.storage_size = 0x0E; } -static void nfc_generate_ntag21x_common(NfcDeviceData* data, uint8_t num_pages) { +static void nfc_generate_ntag21x_common(NfcDeviceOldDataOld* data, uint8_t num_pages) { nfc_generate_mf_ul_with_config_common(data, num_pages); MfUltralightData* mful = &data->mf_ul_data; memcpy(&mful->version, version_bytes_ntag21x, sizeof(version_bytes_mf0ulx1)); @@ -209,7 +209,7 @@ static void nfc_generate_ntag21x_common(NfcDeviceData* data, uint8_t num_pages) mful->data[13] = 0x10; } -static void nfc_generate_ntag213(NfcDeviceData* data) { +static void nfc_generate_ntag213(NfcDeviceOldDataOld* data) { nfc_generate_ntag21x_common(data, 45); MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeNTAG213; @@ -219,7 +219,7 @@ static void nfc_generate_ntag213(NfcDeviceData* data) { memcpy(&mful->data[16], default_data_ntag213, sizeof(default_data_ntag213)); } -static void nfc_generate_ntag215(NfcDeviceData* data) { +static void nfc_generate_ntag215(NfcDeviceOldDataOld* data) { nfc_generate_ntag21x_common(data, 135); MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeNTAG215; @@ -229,7 +229,7 @@ static void nfc_generate_ntag215(NfcDeviceData* data) { memcpy(&mful->data[16], default_data_ntag215_216, sizeof(default_data_ntag215_216)); } -static void nfc_generate_ntag216(NfcDeviceData* data) { +static void nfc_generate_ntag216(NfcDeviceOldDataOld* data) { nfc_generate_ntag21x_common(data, 231); MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeNTAG216; @@ -239,8 +239,10 @@ static void nfc_generate_ntag216(NfcDeviceData* data) { memcpy(&mful->data[16], default_data_ntag215_216, sizeof(default_data_ntag215_216)); } -static void - nfc_generate_ntag_i2c_common(NfcDeviceData* data, MfUltralightType type, uint16_t num_pages) { +static void nfc_generate_ntag_i2c_common( + NfcDeviceOldDataOld* data, + MfUltralightType type, + uint16_t num_pages) { nfc_generate_common_start(data); nfc_generate_mf_ul_common(data); @@ -287,7 +289,7 @@ static void sizeof(default_config_ntag_i2c)); } -static void nfc_generate_ntag_i2c_1k(NfcDeviceData* data) { +static void nfc_generate_ntag_i2c_1k(NfcDeviceOldDataOld* data) { nfc_generate_ntag_i2c_common(data, MfUltralightTypeNTAGI2C1K, 231); MfUltralightData* mful = &data->mf_ul_data; mful->version.prod_ver_minor = 0x01; @@ -297,7 +299,7 @@ static void nfc_generate_ntag_i2c_1k(NfcDeviceData* data) { mful->data[14] = 0x6D; // Size of tag in CC } -static void nfc_generate_ntag_i2c_2k(NfcDeviceData* data) { +static void nfc_generate_ntag_i2c_2k(NfcDeviceOldDataOld* data) { nfc_generate_ntag_i2c_common(data, MfUltralightTypeNTAGI2C2K, 485); MfUltralightData* mful = &data->mf_ul_data; mful->version.prod_ver_minor = 0x01; @@ -308,7 +310,7 @@ static void nfc_generate_ntag_i2c_2k(NfcDeviceData* data) { } static void nfc_generate_ntag_i2c_plus_common( - NfcDeviceData* data, + NfcDeviceOldDataOld* data, MfUltralightType type, uint16_t num_pages) { nfc_generate_ntag_i2c_common(data, type, num_pages); @@ -319,21 +321,21 @@ static void nfc_generate_ntag_i2c_plus_common( memset(&mful->data[config_index + 8], 0xFF, 4); // Default PWD } -static void nfc_generate_ntag_i2c_plus_1k(NfcDeviceData* data) { +static void nfc_generate_ntag_i2c_plus_1k(NfcDeviceOldDataOld* data) { nfc_generate_ntag_i2c_plus_common(data, MfUltralightTypeNTAGI2CPlus1K, 236); MfUltralightData* mful = &data->mf_ul_data; mful->version.prod_ver_minor = 0x02; mful->version.storage_size = 0x13; } -static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) { +static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceOldDataOld* data) { nfc_generate_ntag_i2c_plus_common(data, MfUltralightTypeNTAGI2CPlus2K, 492); MfUltralightData* mful = &data->mf_ul_data; mful->version.prod_ver_minor = 0x02; mful->version.storage_size = 0x15; } -void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { +void nfc_generate_mf_classic(NfcDeviceOldDataOld* data, uint8_t uid_len, MfClassicType type) { nfc_generate_common_start(data); nfc_generate_mf_classic_uid(data->mf_classic_data.block[0].value, uid_len); nfc_generate_mf_classic_common(data, uid_len, type); @@ -345,41 +347,41 @@ void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType } MfClassicData* mfc = &data->mf_classic_data; - mf_classic_set_block_read(mfc, 0, &mfc->block[0]); + mifare_classic_set_block_read(mfc, 0, &mfc->block[0]); if(type == MfClassicType4k) { // Set every block to 0xFF for(uint16_t i = 1; i < 256; i += 1) { - if(mf_classic_is_sector_trailer(i)) { + if(mifare_classic_is_sector_trailer(i)) { nfc_generate_mf_classic_sector_trailer(mfc, i); } else { memset(&mfc->block[i].value, 0xFF, 16); } - mf_classic_set_block_read(mfc, i, &mfc->block[i]); + mifare_classic_set_block_read(mfc, i, &mfc->block[i]); } // Set SAK to 18 data->nfc_data.sak = 0x18; } else if(type == MfClassicType1k) { // Set every block to 0xFF for(uint16_t i = 1; i < MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4; i += 1) { - if(mf_classic_is_sector_trailer(i)) { + if(mifare_classic_is_sector_trailer(i)) { nfc_generate_mf_classic_sector_trailer(mfc, i); } else { memset(&mfc->block[i].value, 0xFF, 16); } - mf_classic_set_block_read(mfc, i, &mfc->block[i]); + mifare_classic_set_block_read(mfc, i, &mfc->block[i]); } // Set SAK to 08 data->nfc_data.sak = 0x08; } else if(type == MfClassicTypeMini) { // Set every block to 0xFF for(uint16_t i = 1; i < MF_MINI_TOTAL_SECTORS_NUM * 4; i += 1) { - if(mf_classic_is_sector_trailer(i)) { + if(mifare_classic_is_sector_trailer(i)) { nfc_generate_mf_classic_sector_trailer(mfc, i); } else { memset(&mfc->block[i].value, 0xFF, 16); } - mf_classic_set_block_read(mfc, i, &mfc->block[i]); + mifare_classic_set_block_read(mfc, i, &mfc->block[i]); } // Set SAK to 09 data->nfc_data.sak = 0x09; @@ -395,23 +397,23 @@ void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType mfc->type = type; } -static void nfc_generate_mf_mini(NfcDeviceData* data) { +static void nfc_generate_mf_mini(NfcDeviceOldDataOld* data) { nfc_generate_mf_classic(data, 4, MfClassicTypeMini); } -static void nfc_generate_mf_classic_1k_4b_uid(NfcDeviceData* data) { +static void nfc_generate_mf_classic_1k_4b_uid(NfcDeviceOldDataOld* data) { nfc_generate_mf_classic(data, 4, MfClassicType1k); } -static void nfc_generate_mf_classic_1k_7b_uid(NfcDeviceData* data) { +static void nfc_generate_mf_classic_1k_7b_uid(NfcDeviceOldDataOld* data) { nfc_generate_mf_classic(data, 7, MfClassicType1k); } -static void nfc_generate_mf_classic_4k_4b_uid(NfcDeviceData* data) { +static void nfc_generate_mf_classic_4k_4b_uid(NfcDeviceOldDataOld* data) { nfc_generate_mf_classic(data, 4, MfClassicType4k); } -static void nfc_generate_mf_classic_4k_7b_uid(NfcDeviceData* data) { +static void nfc_generate_mf_classic_4k_7b_uid(NfcDeviceOldDataOld* data) { nfc_generate_mf_classic(data, 7, MfClassicType4k); } diff --git a/lib/nfc/deprecated/helpers/nfc_generators.h b/lib/nfc/deprecated/helpers/nfc_generators.h new file mode 100644 index 000000000000..289f3f3e6479 --- /dev/null +++ b/lib/nfc/deprecated/helpers/nfc_generators.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../nfc_device_old.h" + +typedef void (*NfcGeneratorFunc)(NfcDeviceOldDataOld* data); + +typedef struct { + const char* name; + NfcGeneratorFunc generator_func; +} NfcGenerator; + +extern const NfcGenerator* const nfc_generators[]; + +void nfc_generate_mf_classic(NfcDeviceOldDataOld* data, uint8_t uid_len, MfClassicType type); diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/deprecated/helpers/reader_analyzer.c similarity index 97% rename from lib/nfc/helpers/reader_analyzer.c rename to lib/nfc/deprecated/helpers/reader_analyzer.c index 9bf37a60d296..10c576ac8353 100644 --- a/lib/nfc/helpers/reader_analyzer.c +++ b/lib/nfc/deprecated/helpers/reader_analyzer.c @@ -1,6 +1,6 @@ #include "reader_analyzer.h" -#include -#include +#include +#include #include #include "mfkey32.h" @@ -183,15 +183,15 @@ void reader_analyzer_set_callback( instance->context = context; } -NfcProtocol +NfcProtocolOld reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len) { furi_assert(instance); furi_assert(buff_rx); UNUSED(len); - NfcProtocol protocol = NfcDeviceProtocolUnknown; + NfcProtocolOld protocol = NfcDeviceOldProtocolUnknown; if((buff_rx[0] == 0x60) || (buff_rx[0] == 0x61)) { - protocol = NfcDeviceProtocolMifareClassic; + protocol = NfcDeviceOldProtocolMifareClassic; } return protocol; diff --git a/lib/nfc/helpers/reader_analyzer.h b/lib/nfc/deprecated/helpers/reader_analyzer.h similarity index 94% rename from lib/nfc/helpers/reader_analyzer.h rename to lib/nfc/deprecated/helpers/reader_analyzer.h index 13bf4d77ccf9..5170714608ed 100644 --- a/lib/nfc/helpers/reader_analyzer.h +++ b/lib/nfc/deprecated/helpers/reader_analyzer.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include typedef enum { ReaderAnalyzerModeDebugLog = 0x01, @@ -30,7 +30,7 @@ void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode); void reader_analyzer_stop(ReaderAnalyzer* instance); -NfcProtocol +NfcProtocolOld reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len); FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance); diff --git a/lib/nfc/deprecated/nfc_device_old.c b/lib/nfc/deprecated/nfc_device_old.c new file mode 100644 index 000000000000..40b548dbd1a2 --- /dev/null +++ b/lib/nfc/deprecated/nfc_device_old.c @@ -0,0 +1,1725 @@ +#include "nfc_device_old.h" +#include "assets_icons.h" +#include "nfc_types.h" + +#include +#include +#include +#include + +#define TAG "NfcDeviceOld" +#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/.cache") +#define NFC_DEVICE_KEYS_EXTENSION ".keys" + +static const char* nfc_file_header = "Flipper NFC device"; +static const uint32_t nfc_file_version = 3; + +static const char* nfc_keys_file_header = "Flipper NFC keys"; +static const uint32_t nfc_keys_file_version = 1; + +// Protocols format versions +static const uint32_t nfc_mifare_classic_data_format_version = 2; +static const uint32_t nfc_mifare_ultralight_data_format_version = 1; + +NfcDeviceOld* nfc_device_old_alloc() { + NfcDeviceOld* nfc_dev = malloc(sizeof(NfcDeviceOld)); + nfc_dev->storage = furi_record_open(RECORD_STORAGE); + nfc_dev->dialogs = furi_record_open(RECORD_DIALOGS); + nfc_dev->load_path = furi_string_alloc(); + nfc_dev->dev_data.parsed_data = furi_string_alloc(); + nfc_dev->folder = furi_string_alloc(); + + // Rename cache folder name for backward compatibility + if(storage_common_stat(nfc_dev->storage, "/ext/nfc/cache", NULL) == FSE_OK) { + storage_common_rename(nfc_dev->storage, "/ext/nfc/cache", NFC_DEVICE_KEYS_FOLDER); + } + return nfc_dev; +} + +void nfc_device_old_free(NfcDeviceOld* nfc_dev) { + furi_assert(nfc_dev); + nfc_device_old_clear(nfc_dev); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_DIALOGS); + furi_string_free(nfc_dev->load_path); + furi_string_free(nfc_dev->dev_data.parsed_data); + furi_string_free(nfc_dev->folder); + free(nfc_dev); +} + +static void nfc_device_old_prepare_format_string(NfcDeviceOld* dev, FuriString* format_string) { + if(dev->format == NfcDeviceOldSaveFormatUid) { + furi_string_set(format_string, "UID"); + } else if(dev->format == NfcDeviceOldSaveFormatBankCard) { + furi_string_set(format_string, "Bank card"); + } else if(dev->format == NfcDeviceOldSaveFormatMifareUl) { + furi_string_set(format_string, nfc_mf_ul_type(dev->dev_data.mf_ul_data.type, true)); + } else if(dev->format == NfcDeviceOldSaveFormatMifareClassic) { + furi_string_set(format_string, "Mifare Classic"); + } else if(dev->format == NfcDeviceOldSaveFormatMifareDesfire) { + furi_string_set(format_string, "Mifare DESFire"); + } else if(dev->format == NfcDeviceOldSaveFormatNfcV) { + furi_string_set(format_string, "ISO15693"); + } else { + furi_string_set(format_string, "Unknown"); + } +} + +static bool nfc_device_old_parse_format_string(NfcDeviceOld* dev, FuriString* format_string) { + if(furi_string_start_with_str(format_string, "UID")) { + dev->format = NfcDeviceOldSaveFormatUid; + dev->dev_data.protocol = NfcDeviceOldProtocolUnknown; + return true; + } + if(furi_string_start_with_str(format_string, "Bank card")) { + dev->format = NfcDeviceOldSaveFormatBankCard; + dev->dev_data.protocol = NfcDeviceOldProtocolEMV; + return true; + } + // Check Mifare Ultralight types + for(MfUltralightType type = MfUltralightTypeUnknown; type < MfUltralightTypeNum; type++) { + if(furi_string_equal(format_string, nfc_mf_ul_type(type, true))) { + dev->format = NfcDeviceOldSaveFormatMifareUl; + dev->dev_data.protocol = NfcDeviceOldProtocolMifareUl; + dev->dev_data.mf_ul_data.type = type; + return true; + } + } + if(furi_string_start_with_str(format_string, "Mifare Classic")) { + dev->format = NfcDeviceOldSaveFormatMifareClassic; + dev->dev_data.protocol = NfcDeviceOldProtocolMifareClassic; + return true; + } + if(furi_string_start_with_str(format_string, "Mifare DESFire")) { + dev->format = NfcDeviceOldSaveFormatMifareDesfire; + dev->dev_data.protocol = NfcDeviceOldProtocolMifareDesfire; + return true; + } + if(furi_string_start_with_str(format_string, "ISO15693")) { + dev->format = NfcDeviceOldSaveFormatNfcV; + dev->dev_data.protocol = NfcDeviceOldProtocolNfcV; + return true; + } + return false; +} + +static bool nfc_device_old_save_mifare_ul_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool saved = false; + MfUltralightData* data = &dev->dev_data.mf_ul_data; + FuriString* temp_str; + temp_str = furi_string_alloc(); + + // Save Mifare Ultralight specific data + do { + if(!flipper_format_write_comment_cstr(file, "Mifare Ultralight specific data")) break; + if(!flipper_format_write_uint32( + file, "Data format version", &nfc_mifare_ultralight_data_format_version, 1)) + break; + if(!flipper_format_write_hex(file, "Signature", data->signature, sizeof(data->signature))) + break; + if(!flipper_format_write_hex( + file, "Mifare version", (uint8_t*)&data->version, sizeof(data->version))) + break; + // Write conters and tearing flags data + bool counters_saved = true; + for(uint8_t i = 0; i < 3; i++) { + furi_string_printf(temp_str, "Counter %d", i); + if(!flipper_format_write_uint32( + file, furi_string_get_cstr(temp_str), &data->counter[i], 1)) { + counters_saved = false; + break; + } + furi_string_printf(temp_str, "Tearing %d", i); + if(!flipper_format_write_hex( + file, furi_string_get_cstr(temp_str), &data->tearing[i], 1)) { + counters_saved = false; + break; + } + } + if(!counters_saved) break; + // Write pages data + uint32_t pages_total = data->data_size / 4; + if(!flipper_format_write_uint32(file, "Pages total", &pages_total, 1)) break; + uint32_t pages_read = data->data_read / 4; + if(!flipper_format_write_uint32(file, "Pages read", &pages_read, 1)) break; + bool pages_saved = true; + for(uint16_t i = 0; i < data->data_size; i += 4) { + furi_string_printf(temp_str, "Page %d", i / 4); + if(!flipper_format_write_hex(file, furi_string_get_cstr(temp_str), &data->data[i], 4)) { + pages_saved = false; + break; + } + } + if(!pages_saved) break; + + // Write authentication counter + uint32_t auth_counter = data->curr_authlim; + if(!flipper_format_write_uint32(file, "Failed authentication attempts", &auth_counter, 1)) + break; + + saved = true; + } while(false); + + furi_string_free(temp_str); + return saved; +} + +bool nfc_device_old_load_mifare_ul_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool parsed = false; + MfUltralightData* data = &dev->dev_data.mf_ul_data; + FuriString* temp_str; + temp_str = furi_string_alloc(); + uint32_t data_format_version = 0; + + do { + // Read Mifare Ultralight format version + if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { + if(!flipper_format_rewind(file)) break; + } + + // Read signature + if(!flipper_format_read_hex(file, "Signature", data->signature, sizeof(data->signature))) + break; + // Read Mifare version + if(!flipper_format_read_hex( + file, "Mifare version", (uint8_t*)&data->version, sizeof(data->version))) + break; + // Read counters and tearing flags + bool counters_parsed = true; + for(uint8_t i = 0; i < 3; i++) { + furi_string_printf(temp_str, "Counter %d", i); + if(!flipper_format_read_uint32( + file, furi_string_get_cstr(temp_str), &data->counter[i], 1)) { + counters_parsed = false; + break; + } + furi_string_printf(temp_str, "Tearing %d", i); + if(!flipper_format_read_hex( + file, furi_string_get_cstr(temp_str), &data->tearing[i], 1)) { + counters_parsed = false; + break; + } + } + if(!counters_parsed) break; + // Read pages + uint32_t pages_total = 0; + if(!flipper_format_read_uint32(file, "Pages total", &pages_total, 1)) break; + uint32_t pages_read = 0; + if(data_format_version < nfc_mifare_ultralight_data_format_version) { + pages_read = pages_total; + } else { + if(!flipper_format_read_uint32(file, "Pages read", &pages_read, 1)) break; + } + data->data_size = pages_total * 4; + data->data_read = pages_read * 4; + if(data->data_size > MF_UL_MAX_DUMP_SIZE || data->data_read > MF_UL_MAX_DUMP_SIZE) break; + bool pages_parsed = true; + for(uint16_t i = 0; i < pages_total; i++) { + furi_string_printf(temp_str, "Page %d", i); + if(!flipper_format_read_hex( + file, furi_string_get_cstr(temp_str), &data->data[i * 4], 4)) { + pages_parsed = false; + break; + } + } + if(!pages_parsed) break; + + // Read authentication counter + uint32_t auth_counter; + if(!flipper_format_read_uint32(file, "Failed authentication attempts", &auth_counter, 1)) + auth_counter = 0; + data->curr_authlim = auth_counter; + + data->auth_success = mf_ul_is_full_capture(data); + + parsed = true; + } while(false); + + furi_string_free(temp_str); + return parsed; +} + +static bool nfc_device_old_save_mifare_df_key_settings( + FlipperFormat* file, + MifareDesfireKeySettings* ks, + const char* prefix) { + bool saved = false; + FuriString* key; + key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s Change Key ID", prefix); + if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &ks->change_key_id, 1)) + break; + furi_string_printf(key, "%s Config Changeable", prefix); + if(!flipper_format_write_bool(file, furi_string_get_cstr(key), &ks->config_changeable, 1)) + break; + furi_string_printf(key, "%s Free Create Delete", prefix); + if(!flipper_format_write_bool(file, furi_string_get_cstr(key), &ks->free_create_delete, 1)) + break; + furi_string_printf(key, "%s Free Directory List", prefix); + if(!flipper_format_write_bool(file, furi_string_get_cstr(key), &ks->free_directory_list, 1)) + break; + furi_string_printf(key, "%s Key Changeable", prefix); + if(!flipper_format_write_bool( + file, furi_string_get_cstr(key), &ks->master_key_changeable, 1)) + break; + if(ks->flags) { + furi_string_printf(key, "%s Flags", prefix); + if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &ks->flags, 1)) break; + } + furi_string_printf(key, "%s Max Keys", prefix); + if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &ks->max_keys, 1)) break; + for(MifareDesfireKeyVersion* kv = ks->key_version_head; kv; kv = kv->next) { + furi_string_printf(key, "%s Key %d Version", prefix, kv->id); + if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &kv->version, 1)) break; + } + saved = true; + } while(false); + + furi_string_free(key); + return saved; +} + +bool nfc_device_old_load_mifare_df_key_settings( + FlipperFormat* file, + MifareDesfireKeySettings* ks, + const char* prefix) { + bool parsed = false; + FuriString* key; + key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s Change Key ID", prefix); + if(!flipper_format_read_hex(file, furi_string_get_cstr(key), &ks->change_key_id, 1)) break; + furi_string_printf(key, "%s Config Changeable", prefix); + if(!flipper_format_read_bool(file, furi_string_get_cstr(key), &ks->config_changeable, 1)) + break; + furi_string_printf(key, "%s Free Create Delete", prefix); + if(!flipper_format_read_bool(file, furi_string_get_cstr(key), &ks->free_create_delete, 1)) + break; + furi_string_printf(key, "%s Free Directory List", prefix); + if(!flipper_format_read_bool(file, furi_string_get_cstr(key), &ks->free_directory_list, 1)) + break; + furi_string_printf(key, "%s Key Changeable", prefix); + if(!flipper_format_read_bool( + file, furi_string_get_cstr(key), &ks->master_key_changeable, 1)) + break; + furi_string_printf(key, "%s Flags", prefix); + if(flipper_format_key_exist(file, furi_string_get_cstr(key))) { + if(!flipper_format_read_hex(file, furi_string_get_cstr(key), &ks->flags, 1)) break; + } + furi_string_printf(key, "%s Max Keys", prefix); + if(!flipper_format_read_hex(file, furi_string_get_cstr(key), &ks->max_keys, 1)) break; + ks->flags |= ks->max_keys >> 4; + ks->max_keys &= 0xF; + MifareDesfireKeyVersion** kv_head = &ks->key_version_head; + for(int key_id = 0; key_id < ks->max_keys; key_id++) { + furi_string_printf(key, "%s Key %d Version", prefix, key_id); + uint8_t version; + if(flipper_format_read_hex(file, furi_string_get_cstr(key), &version, 1)) { + MifareDesfireKeyVersion* kv = malloc(sizeof(MifareDesfireKeyVersion)); + memset(kv, 0, sizeof(MifareDesfireKeyVersion)); + kv->id = key_id; + kv->version = version; + *kv_head = kv; + kv_head = &kv->next; + } + } + parsed = true; + } while(false); + + furi_string_free(key); + return parsed; +} + +static bool nfc_device_old_save_mifare_df_app(FlipperFormat* file, MifareDesfireApplication* app) { + bool saved = false; + FuriString *prefix, *key; + prefix = + furi_string_alloc_printf("Application %02x%02x%02x", app->id[0], app->id[1], app->id[2]); + key = furi_string_alloc(); + uint8_t* tmp = NULL; + + do { + if(app->key_settings) { + if(!nfc_device_old_save_mifare_df_key_settings( + file, app->key_settings, furi_string_get_cstr(prefix))) + break; + } + if(!app->file_head) break; + uint32_t n_files = 0; + for(MifareDesfireFile* f = app->file_head; f; f = f->next) { + n_files++; + } + tmp = malloc(n_files); + int i = 0; + for(MifareDesfireFile* f = app->file_head; f; f = f->next) { + tmp[i++] = f->id; + } + furi_string_printf(key, "%s File IDs", furi_string_get_cstr(prefix)); + if(!flipper_format_write_hex(file, furi_string_get_cstr(key), tmp, n_files)) break; + bool saved_files = true; + for(MifareDesfireFile* f = app->file_head; f; f = f->next) { + saved_files = false; + furi_string_printf(key, "%s File %d Type", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &f->type, 1)) break; + furi_string_printf( + key, "%s File %d Communication Settings", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &f->comm, 1)) break; + furi_string_printf( + key, "%s File %d Access Rights", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_hex( + file, furi_string_get_cstr(key), (uint8_t*)&f->access_rights, 2)) + break; + uint16_t size = 0; + if(f->type == MifareDesfireFileTypeStandard || + f->type == MifareDesfireFileTypeBackup) { + size = f->settings.data.size; + furi_string_printf(key, "%s File %d Size", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_uint32( + file, furi_string_get_cstr(key), &f->settings.data.size, 1)) + break; + } else if(f->type == MifareDesfireFileTypeValue) { + furi_string_printf( + key, "%s File %d Hi Limit", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_uint32( + file, furi_string_get_cstr(key), &f->settings.value.hi_limit, 1)) + break; + furi_string_printf( + key, "%s File %d Lo Limit", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_uint32( + file, furi_string_get_cstr(key), &f->settings.value.lo_limit, 1)) + break; + furi_string_printf( + key, "%s File %d Limited Credit Value", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_uint32( + file, furi_string_get_cstr(key), &f->settings.value.limited_credit_value, 1)) + break; + furi_string_printf( + key, "%s File %d Limited Credit Enabled", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_bool( + file, + furi_string_get_cstr(key), + &f->settings.value.limited_credit_enabled, + 1)) + break; + size = 4; + } else if( + f->type == MifareDesfireFileTypeLinearRecord || + f->type == MifareDesfireFileTypeCyclicRecord) { + furi_string_printf(key, "%s File %d Size", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_uint32( + file, furi_string_get_cstr(key), &f->settings.record.size, 1)) + break; + furi_string_printf(key, "%s File %d Max", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_uint32( + file, furi_string_get_cstr(key), &f->settings.record.max, 1)) + break; + furi_string_printf(key, "%s File %d Cur", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_uint32( + file, furi_string_get_cstr(key), &f->settings.record.cur, 1)) + break; + size = f->settings.record.size * f->settings.record.cur; + } + if(f->contents) { + furi_string_printf(key, "%s File %d", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_write_hex(file, furi_string_get_cstr(key), f->contents, size)) + break; + } + saved_files = true; + } + if(!saved_files) { + break; + } + saved = true; + } while(false); + + free(tmp); + furi_string_free(prefix); + furi_string_free(key); + return saved; +} + +bool nfc_device_old_load_mifare_df_app(FlipperFormat* file, MifareDesfireApplication* app) { + bool parsed = false; + FuriString *prefix, *key; + prefix = + furi_string_alloc_printf("Application %02x%02x%02x", app->id[0], app->id[1], app->id[2]); + key = furi_string_alloc(); + uint8_t* tmp = NULL; + MifareDesfireFile* f = NULL; + + do { + app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); + memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); + if(!nfc_device_old_load_mifare_df_key_settings( + file, app->key_settings, furi_string_get_cstr(prefix))) { + free(app->key_settings); + app->key_settings = NULL; + break; + } + furi_string_printf(key, "%s File IDs", furi_string_get_cstr(prefix)); + uint32_t n_files; + if(!flipper_format_get_value_count(file, furi_string_get_cstr(key), &n_files)) break; + tmp = malloc(n_files); + if(!flipper_format_read_hex(file, furi_string_get_cstr(key), tmp, n_files)) break; + MifareDesfireFile** file_head = &app->file_head; + bool parsed_files = true; + for(uint32_t i = 0; i < n_files; i++) { + parsed_files = false; + f = malloc(sizeof(MifareDesfireFile)); + memset(f, 0, sizeof(MifareDesfireFile)); + f->id = tmp[i]; + furi_string_printf(key, "%s File %d Type", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_read_hex(file, furi_string_get_cstr(key), &f->type, 1)) break; + furi_string_printf( + key, "%s File %d Communication Settings", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_read_hex(file, furi_string_get_cstr(key), &f->comm, 1)) break; + furi_string_printf( + key, "%s File %d Access Rights", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_read_hex( + file, furi_string_get_cstr(key), (uint8_t*)&f->access_rights, 2)) + break; + if(f->type == MifareDesfireFileTypeStandard || + f->type == MifareDesfireFileTypeBackup) { + furi_string_printf(key, "%s File %d Size", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_read_uint32( + file, furi_string_get_cstr(key), &f->settings.data.size, 1)) + break; + } else if(f->type == MifareDesfireFileTypeValue) { + furi_string_printf( + key, "%s File %d Hi Limit", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_read_uint32( + file, furi_string_get_cstr(key), &f->settings.value.hi_limit, 1)) + break; + furi_string_printf( + key, "%s File %d Lo Limit", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_read_uint32( + file, furi_string_get_cstr(key), &f->settings.value.lo_limit, 1)) + break; + furi_string_printf( + key, "%s File %d Limited Credit Value", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_read_uint32( + file, furi_string_get_cstr(key), &f->settings.value.limited_credit_value, 1)) + break; + furi_string_printf( + key, "%s File %d Limited Credit Enabled", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_read_bool( + file, + furi_string_get_cstr(key), + &f->settings.value.limited_credit_enabled, + 1)) + break; + } else if( + f->type == MifareDesfireFileTypeLinearRecord || + f->type == MifareDesfireFileTypeCyclicRecord) { + furi_string_printf(key, "%s File %d Size", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_read_uint32( + file, furi_string_get_cstr(key), &f->settings.record.size, 1)) + break; + furi_string_printf(key, "%s File %d Max", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_read_uint32( + file, furi_string_get_cstr(key), &f->settings.record.max, 1)) + break; + furi_string_printf(key, "%s File %d Cur", furi_string_get_cstr(prefix), f->id); + if(!flipper_format_read_uint32( + file, furi_string_get_cstr(key), &f->settings.record.cur, 1)) + break; + } + furi_string_printf(key, "%s File %d", furi_string_get_cstr(prefix), f->id); + if(flipper_format_key_exist(file, furi_string_get_cstr(key))) { + uint32_t size; + if(!flipper_format_get_value_count(file, furi_string_get_cstr(key), &size)) break; + f->contents = malloc(size); + if(!flipper_format_read_hex(file, furi_string_get_cstr(key), f->contents, size)) + break; + } + *file_head = f; + file_head = &f->next; + f = NULL; + parsed_files = true; + } + if(!parsed_files) { + break; + } + parsed = true; + } while(false); + + if(f) { + free(f->contents); + free(f); + } + free(tmp); + furi_string_free(prefix); + furi_string_free(key); + return parsed; +} + +static bool nfc_device_old_save_mifare_df_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool saved = false; + MifareDesfireData* data = &dev->dev_data.mf_df_data; + uint8_t* tmp = NULL; + + do { + if(!flipper_format_write_comment_cstr(file, "Mifare DESFire specific data")) break; + if(!flipper_format_write_hex( + file, "PICC Version", (uint8_t*)&data->version, sizeof(data->version))) + break; + if(data->free_memory) { + if(!flipper_format_write_uint32(file, "PICC Free Memory", &data->free_memory->bytes, 1)) + break; + } + if(data->master_key_settings) { + if(!nfc_device_old_save_mifare_df_key_settings(file, data->master_key_settings, "PICC")) + break; + } + uint32_t n_apps = 0; + for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { + n_apps++; + } + if(!flipper_format_write_uint32(file, "Application Count", &n_apps, 1)) break; + if(n_apps) { + tmp = malloc(n_apps * 3); + int i = 0; + for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { + memcpy(tmp + i, app->id, 3); //-V769 + i += 3; + } + if(!flipper_format_write_hex(file, "Application IDs", tmp, n_apps * 3)) break; + for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { + if(!nfc_device_old_save_mifare_df_app(file, app)) break; + } + } + saved = true; + } while(false); + + free(tmp); + return saved; +} + +bool nfc_device_old_load_mifare_df_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool parsed = false; + MifareDesfireData* data = &dev->dev_data.mf_df_data; + memset(data, 0, sizeof(MifareDesfireData)); + uint8_t* tmp = NULL; + + do { + if(!flipper_format_read_hex( + file, "PICC Version", (uint8_t*)&data->version, sizeof(data->version))) + break; + if(flipper_format_key_exist(file, "PICC Free Memory")) { + data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); + memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory)); + if(!flipper_format_read_uint32( + file, "PICC Free Memory", &data->free_memory->bytes, 1)) { + free(data->free_memory); + break; + } + } + if(flipper_format_key_exist(file, "PICC Change Key ID")) { + data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); + memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings)); + if(!nfc_device_old_load_mifare_df_key_settings( + file, data->master_key_settings, "PICC")) { + free(data->master_key_settings); + data->master_key_settings = NULL; + break; + } + } + uint32_t n_apps; + if(!flipper_format_read_uint32(file, "Application Count", &n_apps, 1)) break; + if(n_apps) { + tmp = malloc(n_apps * 3); + if(!flipper_format_read_hex(file, "Application IDs", tmp, n_apps * 3)) break; + bool parsed_apps = true; + MifareDesfireApplication** app_head = &data->app_head; + for(uint32_t i = 0; i < n_apps; i++) { + MifareDesfireApplication* app = malloc(sizeof(MifareDesfireApplication)); + memset(app, 0, sizeof(MifareDesfireApplication)); + memcpy(app->id, &tmp[i * 3], 3); + if(!nfc_device_old_load_mifare_df_app(file, app)) { + free(app); + parsed_apps = false; + break; + } + *app_head = app; + app_head = &app->next; + } + if(!parsed_apps) { + // accept non-parsed apps, just log a warning: + FURI_LOG_W(TAG, "Non-parsed apps found!"); + } + } + parsed = true; + } while(false); + + free(tmp); + return parsed; +} + +static bool nfc_device_old_save_slix_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_old_load_slix_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_old_save_slix_s_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_old_load_slix_s_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_old_save_slix_l_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_old_load_slix_l_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_old_save_slix2_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_old_load_slix2_data(FlipperFormat* file, NfcDeviceOld* dev) { // -V524 + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_old_save_nfcv_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool saved = false; + NfcVData* data = &dev->dev_data.nfcv_data; + + do { + uint32_t temp_uint32 = 0; + uint8_t temp_uint8 = 0; + + if(!flipper_format_write_comment_cstr(file, "Data Storage Format Identifier")) break; + if(!flipper_format_write_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_write_comment_cstr(file, "Application Family Identifier")) break; + if(!flipper_format_write_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_write_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + temp_uint32 = data->block_num; + if(!flipper_format_write_comment_cstr(file, "Number of memory blocks, usually 0 to 256")) + break; + if(!flipper_format_write_uint32(file, "Block Count", &temp_uint32, 1)) break; + if(!flipper_format_write_comment_cstr(file, "Size of a single memory block, usually 4")) + break; + if(!flipper_format_write_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_write_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) + break; + if(!flipper_format_write_comment_cstr( + file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info")) + break; + if(!flipper_format_write_hex( + file, "Security Status", data->security_status, 1 + data->block_num)) + break; + if(!flipper_format_write_comment_cstr( + file, + "Subtype of this card (0 = ISO15693, 1 = SLIX, 2 = SLIX-S, 3 = SLIX-L, 4 = SLIX2)")) + break; + temp_uint8 = (uint8_t)data->sub_type; + if(!flipper_format_write_hex(file, "Subtype", &temp_uint8, 1)) break; + + switch(data->sub_type) { + case NfcVTypePlain: + if(!flipper_format_write_comment_cstr(file, "End of ISO15693 parameters")) break; + saved = true; + break; + case NfcVTypeSlix: + saved = nfc_device_old_save_slix_data(file, dev); + break; + case NfcVTypeSlixS: + saved = nfc_device_old_save_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + saved = nfc_device_old_save_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + saved = nfc_device_old_save_slix2_data(file, dev); + break; + default: + break; + } + } while(false); + + return saved; +} + +bool nfc_device_old_load_nfcv_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool parsed = false; + NfcVData* data = &dev->dev_data.nfcv_data; + + memset(data, 0x00, sizeof(NfcVData)); + + do { + uint32_t temp_uint32 = 0; + uint8_t temp_value = 0; + + if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break; + data->block_num = temp_uint32; + if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_read_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) + break; + + /* optional, as added later */ + if(flipper_format_key_exist(file, "Security Status")) { + if(!flipper_format_read_hex( + file, "Security Status", data->security_status, 1 + data->block_num)) + break; + } + if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; + data->sub_type = temp_value; + + switch(data->sub_type) { + case NfcVTypePlain: + parsed = true; + break; + case NfcVTypeSlix: + parsed = nfc_device_old_load_slix_data(file, dev); + break; + case NfcVTypeSlixS: + parsed = nfc_device_old_load_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + parsed = nfc_device_old_load_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + parsed = nfc_device_old_load_slix2_data(file, dev); + break; + default: + break; + } + } while(false); + + return parsed; +} + +static bool nfc_device_old_save_bank_card_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool saved = false; + EmvData* data = &dev->dev_data.emv_data; + uint32_t data_temp = 0; + + do { + // Write Bank card specific data + if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break; + if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break; + if(!flipper_format_write_string_cstr(file, "Name", data->name)) break; + if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break; + if(data->exp_mon) { + uint8_t exp_data[2] = {data->exp_mon, data->exp_year}; + if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break; + } + if(data->country_code) { + data_temp = data->country_code; + if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break; + } + if(data->currency_code) { + data_temp = data->currency_code; + if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break; + } + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_old_load_bank_card_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool parsed = false; + EmvData* data = &dev->dev_data.emv_data; + memset(data, 0, sizeof(EmvData)); + uint32_t data_cnt = 0; + FuriString* temp_str; + temp_str = furi_string_alloc(); + + do { + // Load essential data + if(!flipper_format_get_value_count(file, "AID", &data_cnt)) break; + data->aid_len = data_cnt; + if(!flipper_format_read_hex(file, "AID", data->aid, data->aid_len)) break; + if(!flipper_format_read_string(file, "Name", temp_str)) break; + strlcpy(data->name, furi_string_get_cstr(temp_str), sizeof(data->name)); + if(!flipper_format_get_value_count(file, "Number", &data_cnt)) break; + data->number_len = data_cnt; + if(!flipper_format_read_hex(file, "Number", data->number, data->number_len)) break; + parsed = true; + // Load optional data + uint8_t exp_data[2] = {}; + if(flipper_format_read_hex(file, "Exp data", exp_data, 2)) { + data->exp_mon = exp_data[0]; + data->exp_year = exp_data[1]; + } + if(flipper_format_read_uint32(file, "Country code", &data_cnt, 1)) { + data->country_code = data_cnt; + } + if(flipper_format_read_uint32(file, "Currency code", &data_cnt, 1)) { + data->currency_code = data_cnt; + } + } while(false); + + furi_string_free(temp_str); + return parsed; +} + +static void nfc_device_old_write_mifare_classic_block( + FuriString* block_str, + MfClassicData* data, + uint8_t block_num) { + furi_string_reset(block_str); + bool is_sec_trailer = mifare_classic_is_sector_trailer(block_num); + if(is_sec_trailer) { + uint8_t sector_num = mifare_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = + mifare_classic_get_sector_trailer_by_sector(data, sector_num); + // Write key A + for(size_t i = 0; i < sizeof(sec_tr->key_a); i++) { + if(mifare_classic_is_key_found(data, sector_num, MfClassicKeyA)) { + furi_string_cat_printf(block_str, "%02X ", sec_tr->key_a[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + // Write Access bytes + for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i++) { + if(mifare_classic_is_block_read(data, block_num)) { + furi_string_cat_printf(block_str, "%02X ", sec_tr->access_bits[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + // Write key B + for(size_t i = 0; i < sizeof(sec_tr->key_b); i++) { + if(mifare_classic_is_key_found(data, sector_num, MfClassicKeyB)) { + furi_string_cat_printf(block_str, "%02X ", sec_tr->key_b[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + } else { + // Write data block + for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { + if(mifare_classic_is_block_read(data, block_num)) { + furi_string_cat_printf(block_str, "%02X ", data->block[block_num].value[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + } + furi_string_trim(block_str); +} + +static bool nfc_device_old_save_mifare_classic_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool saved = false; + MfClassicData* data = &dev->dev_data.mf_classic_data; + FuriString* temp_str; + temp_str = furi_string_alloc(); + uint16_t blocks = 0; + + // Save Mifare Classic specific data + do { + if(!flipper_format_write_comment_cstr(file, "Mifare Classic specific data")) break; + + if(data->type == MfClassicTypeMini) { + if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "MINI")) break; + blocks = 20; + } else if(data->type == MfClassicType1k) { + if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; + blocks = 64; + } else if(data->type == MfClassicType4k) { + if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break; + blocks = 256; + } + if(!flipper_format_write_uint32( + file, "Data format version", &nfc_mifare_classic_data_format_version, 1)) + break; + if(!flipper_format_write_comment_cstr( + file, "Mifare Classic blocks, \'??\' means unknown data")) + break; + bool block_saved = true; + FuriString* block_str; + block_str = furi_string_alloc(); + for(size_t i = 0; i < blocks; i++) { + furi_string_printf(temp_str, "Block %d", i); + nfc_device_old_write_mifare_classic_block(block_str, data, i); + if(!flipper_format_write_string(file, furi_string_get_cstr(temp_str), block_str)) { + block_saved = false; + break; + } + } + furi_string_free(block_str); + if(!block_saved) break; + saved = true; + } while(false); + + furi_string_free(temp_str); + return saved; +} + +static void nfc_device_old_load_mifare_classic_block( + FuriString* block_str, + MfClassicData* data, + uint8_t block_num) { + furi_string_trim(block_str); + MfClassicBlock block_tmp = {}; + bool is_sector_trailer = mifare_classic_is_sector_trailer(block_num); + uint8_t sector_num = mifare_classic_get_sector_by_block(block_num); + uint16_t block_unknown_bytes_mask = 0; + + furi_string_trim(block_str); + for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { + char hi = furi_string_get_char(block_str, 3 * i); + char low = furi_string_get_char(block_str, 3 * i + 1); + uint8_t byte = 0; + if(hex_char_to_uint8(hi, low, &byte)) { + block_tmp.value[i] = byte; + } else { + FURI_BIT_SET(block_unknown_bytes_mask, i); + } + } + + if(block_unknown_bytes_mask != 0xffff) { + // All data is unknown, exit + return; + } + + if(is_sector_trailer) { + MfClassicSectorTrailer* sec_tr_tmp = (MfClassicSectorTrailer*)&block_tmp; + // Load Key A + // Key A mask 0b0000000000111111 = 0x003f + if((block_unknown_bytes_mask & 0x003f) == 0) { + uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_a, sizeof(sec_tr_tmp->key_a)); + mifare_classic_set_key_found(data, sector_num, MfClassicKeyA, key); + } + // Load Access Bits + // Access bits mask 0b0000001111000000 = 0x03c0 + if((block_unknown_bytes_mask & 0x03c0) == 0) { + mifare_classic_set_block_read(data, block_num, &block_tmp); + } + // Load Key B + // Key B mask 0b1111110000000000 = 0xfc00 + if((block_unknown_bytes_mask & 0xfc00) == 0) { + uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_b, sizeof(sec_tr_tmp->key_b)); + mifare_classic_set_key_found(data, sector_num, MfClassicKeyB, key); + } + } else { + if(block_unknown_bytes_mask == 0) { + mifare_classic_set_block_read(data, block_num, &block_tmp); + } + } +} + +static bool nfc_device_old_load_mifare_classic_data(FlipperFormat* file, NfcDeviceOld* dev) { + bool parsed = false; + MfClassicData* data = &dev->dev_data.mf_classic_data; + FuriString* temp_str; + uint32_t data_format_version = 0; + temp_str = furi_string_alloc(); + uint16_t data_blocks = 0; + memset(data, 0, sizeof(MfClassicData)); + + do { + // Read Mifare Classic type + if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break; + if(!furi_string_cmp(temp_str, "MINI")) { + data->type = MfClassicTypeMini; + data_blocks = 20; + } else if(!furi_string_cmp(temp_str, "1K")) { + data->type = MfClassicType1k; + data_blocks = 64; + } else if(!furi_string_cmp(temp_str, "4K")) { + data->type = MfClassicType4k; + data_blocks = 256; + } else { + break; + } + + bool old_format = false; + // Read Mifare Classic format version + if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { + // Load unread sectors with zero keys access for backward compatibility + if(!flipper_format_rewind(file)) break; + old_format = true; + } else { + if(data_format_version < nfc_mifare_classic_data_format_version) { + old_format = true; + } + } + + // Read Mifare Classic blocks + bool block_read = true; + FuriString* block_str; + block_str = furi_string_alloc(); + for(size_t i = 0; i < data_blocks; i++) { + furi_string_printf(temp_str, "Block %d", i); + if(!flipper_format_read_string(file, furi_string_get_cstr(temp_str), block_str)) { + block_read = false; + break; + } + nfc_device_old_load_mifare_classic_block(block_str, data, i); + } + furi_string_free(block_str); + if(!block_read) break; + + // Set keys and blocks as unknown for backward compatibility + if(old_format) { + data->key_a_mask = 0ULL; + data->key_b_mask = 0ULL; + memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); + } + + parsed = true; + } while(false); + + furi_string_free(temp_str); + return parsed; +} + +static void nfc_device_old_get_key_cache_file_path(NfcDeviceOld* dev, FuriString* file_path) { + uint8_t* uid = dev->dev_data.nfc_data.uid; + uint8_t uid_len = dev->dev_data.nfc_data.uid_len; + furi_string_set(file_path, NFC_DEVICE_KEYS_FOLDER "/"); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(file_path, "%02X", uid[i]); + } + furi_string_cat_printf(file_path, NFC_DEVICE_KEYS_EXTENSION); +} + +static bool nfc_device_old_save_mifare_classic_keys(NfcDeviceOld* dev) { + FlipperFormat* file = flipper_format_file_alloc(dev->storage); + MfClassicData* data = &dev->dev_data.mf_classic_data; + FuriString* temp_str; + temp_str = furi_string_alloc(); + + nfc_device_old_get_key_cache_file_path(dev, temp_str); + bool save_success = false; + do { + if(!storage_simply_mkdir(dev->storage, NFC_DEVICE_KEYS_FOLDER)) break; + if(!storage_simply_remove(dev->storage, furi_string_get_cstr(temp_str))) break; + if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; + if(!flipper_format_write_header_cstr(file, nfc_keys_file_header, nfc_keys_file_version)) + break; + if(data->type == MfClassicTypeMini) { + if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "MINI")) break; + } else if(data->type == MfClassicType1k) { + if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; + } else if(data->type == MfClassicType4k) { + if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break; + } + if(!flipper_format_write_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; + if(!flipper_format_write_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; + uint8_t sector_num = mifare_classic_get_total_sectors_num(data->type); + bool key_save_success = true; + for(size_t i = 0; (i < sector_num) && (key_save_success); i++) { + MfClassicSectorTrailer* sec_tr = mifare_classic_get_sector_trailer_by_sector(data, i); + if(FURI_BIT(data->key_a_mask, i)) { + furi_string_printf(temp_str, "Key A sector %d", i); + key_save_success = flipper_format_write_hex( + file, furi_string_get_cstr(temp_str), sec_tr->key_a, 6); + } + if(!key_save_success) break; + if(FURI_BIT(data->key_b_mask, i)) { + furi_string_printf(temp_str, "Key B sector %d", i); + key_save_success = flipper_format_write_hex( + file, furi_string_get_cstr(temp_str), sec_tr->key_b, 6); + } + } + save_success = key_save_success; + } while(false); + + flipper_format_free(file); + furi_string_free(temp_str); + return save_success; +} + +bool nfc_device_old_load_key_cache(NfcDeviceOld* dev) { + furi_assert(dev); + FuriString* temp_str; + temp_str = furi_string_alloc(); + + MfClassicData* data = &dev->dev_data.mf_classic_data; + nfc_device_old_get_key_cache_file_path(dev, temp_str); + FlipperFormat* file = flipper_format_file_alloc(dev->storage); + + bool load_success = false; + do { + if(storage_common_stat(dev->storage, furi_string_get_cstr(temp_str), NULL) != FSE_OK) + break; + if(!flipper_format_file_open_existing(file, furi_string_get_cstr(temp_str))) break; + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, nfc_keys_file_header)) break; + if(version != nfc_keys_file_version) break; + if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break; + if(!furi_string_cmp(temp_str, "MINI")) { + data->type = MfClassicTypeMini; + } else if(!furi_string_cmp(temp_str, "1K")) { + data->type = MfClassicType1k; + } else if(!furi_string_cmp(temp_str, "4K")) { + data->type = MfClassicType4k; + } else { + break; + } + if(!flipper_format_read_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; + if(!flipper_format_read_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; + uint8_t sectors = mifare_classic_get_total_sectors_num(data->type); + bool key_read_success = true; + for(size_t i = 0; (i < sectors) && (key_read_success); i++) { + MfClassicSectorTrailer* sec_tr = mifare_classic_get_sector_trailer_by_sector(data, i); + if(FURI_BIT(data->key_a_mask, i)) { + furi_string_printf(temp_str, "Key A sector %d", i); + key_read_success = flipper_format_read_hex( + file, furi_string_get_cstr(temp_str), sec_tr->key_a, 6); + } + if(!key_read_success) break; + if(FURI_BIT(data->key_b_mask, i)) { + furi_string_printf(temp_str, "Key B sector %d", i); + key_read_success = flipper_format_read_hex( + file, furi_string_get_cstr(temp_str), sec_tr->key_b, 6); + } + } + load_success = key_read_success; + } while(false); + + furi_string_free(temp_str); + flipper_format_free(file); + + return load_success; +} + +void nfc_device_old_set_name(NfcDeviceOld* dev, const char* name) { + furi_assert(dev); + + strlcpy(dev->dev_name, name, NFC_DEV_NAME_MAX_LEN); +} + +static void nfc_device_old_get_path_without_ext(FuriString* orig_path, FuriString* shadow_path) { + // TODO: this won't work if there is ".nfc" anywhere in the path other than + // at the end + size_t ext_start = furi_string_search(orig_path, NFC_APP_EXTENSION); + furi_string_set_n(shadow_path, orig_path, 0, ext_start); +} + +static void nfc_device_old_get_shadow_path(FuriString* orig_path, FuriString* shadow_path) { + nfc_device_old_get_path_without_ext(orig_path, shadow_path); + furi_string_cat_printf(shadow_path, "%s", NFC_APP_SHADOW_EXTENSION); +} + +static void nfc_device_old_get_folder_from_path(FuriString* path, FuriString* folder) { + size_t last_slash = furi_string_search_rchar(path, '/'); + if(last_slash == FURI_STRING_FAILURE) { + // No slashes in the path, treat the whole path as a folder + furi_string_set(folder, path); + } else { + furi_string_set_n(folder, path, 0, last_slash); + } +} + +bool nfc_device_old_save(NfcDeviceOld* dev, const char* dev_name) { + furi_assert(dev); + + bool saved = false; + FlipperFormat* file = flipper_format_file_alloc(dev->storage); + FuriHalNfcDevData* data = &dev->dev_data.nfc_data; + FuriString* temp_str; + temp_str = furi_string_alloc(); + + do { + // Create directory if necessary + FuriString* folder = furi_string_alloc(); + // Get folder from filename (filename is in the form of "folder/filename.nfc", so the folder is "folder/") + furi_string_set(temp_str, dev_name); + // Get folder from filename + nfc_device_old_get_folder_from_path(temp_str, folder); + FURI_LOG_I("Nfc", "Saving to folder %s", furi_string_get_cstr(folder)); + if(!storage_simply_mkdir(dev->storage, furi_string_get_cstr(folder))) { + FURI_LOG_E("Nfc", "Failed to create folder %s", furi_string_get_cstr(folder)); + break; + } + furi_string_free(folder); + // First remove nfc device file if it was saved + // Open file + if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; + // Write header + if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; + // Write nfc device type + if(!flipper_format_write_comment_cstr( + file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693")) + break; + nfc_device_old_prepare_format_string(dev, temp_str); + if(!flipper_format_write_string(file, "Device type", temp_str)) break; + // Write UID + if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break; + if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; + + if(dev->format != NfcDeviceOldSaveFormatNfcV) { + // Write ATQA, SAK + if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break; + // Save ATQA in MSB order for correct companion apps display + uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; + if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; + if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + } + + // Save more data if necessary + if(dev->format == NfcDeviceOldSaveFormatMifareUl) { + if(!nfc_device_old_save_mifare_ul_data(file, dev)) break; + } else if(dev->format == NfcDeviceOldSaveFormatMifareDesfire) { + if(!nfc_device_old_save_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceOldSaveFormatNfcV) { + if(!nfc_device_old_save_nfcv_data(file, dev)) break; + } else if(dev->format == NfcDeviceOldSaveFormatBankCard) { + if(!nfc_device_old_save_bank_card_data(file, dev)) break; + } else if(dev->format == NfcDeviceOldSaveFormatMifareClassic) { + // Save data + if(!nfc_device_old_save_mifare_classic_data(file, dev)) break; + // Save keys cache + if(!nfc_device_old_save_mifare_classic_keys(dev)) break; + } + saved = true; + } while(0); + + if(!saved) { //-V547 + dialog_message_show_storage_error(dev->dialogs, "Can not save\nkey file"); + } + furi_string_free(temp_str); + flipper_format_free(file); + return saved; +} + +bool nfc_device_old_save_shadow(NfcDeviceOld* dev, const char* path) { + dev->shadow_file_exist = true; + // Replace extension from .nfc to .shd if necessary + FuriString* orig_path = furi_string_alloc(); + furi_string_set_str(orig_path, path); + FuriString* shadow_path = furi_string_alloc(); + nfc_device_old_get_shadow_path(orig_path, shadow_path); + + bool file_saved = nfc_device_old_save(dev, furi_string_get_cstr(shadow_path)); + furi_string_free(orig_path); + furi_string_free(shadow_path); + + return file_saved; +} + +static bool nfc_device_old_load_data(NfcDeviceOld* dev, FuriString* path, bool show_dialog) { + bool parsed = false; + FlipperFormat* file = flipper_format_file_alloc(dev->storage); + FuriHalNfcDevData* data = &dev->dev_data.nfc_data; + uint32_t data_cnt = 0; + FuriString* temp_str; + temp_str = furi_string_alloc(); + bool deprecated_version = false; + + // Version 2 of file format had ATQA bytes swapped + uint32_t version_with_lsb_atqa = 2; + + if(dev->loading_cb) { + dev->loading_cb(dev->loading_cb_ctx, true); + } + + do { + // Check existence of shadow file + nfc_device_old_get_shadow_path(path, temp_str); + dev->shadow_file_exist = + storage_common_stat(dev->storage, furi_string_get_cstr(temp_str), NULL) == FSE_OK; + // Open shadow file if it exists. If not - open original + if(dev->shadow_file_exist) { + if(!flipper_format_file_open_existing(file, furi_string_get_cstr(temp_str))) break; + } else { + if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break; + } + // Read and verify file header + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, nfc_file_header)) break; + if(version != nfc_file_version) { + if(version < version_with_lsb_atqa) { + deprecated_version = true; + break; + } + } + // Read Nfc device type + if(!flipper_format_read_string(file, "Device type", temp_str)) break; + if(!nfc_device_old_parse_format_string(dev, temp_str)) break; + // Read and parse UID, ATQA and SAK + if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break; + if(!(data_cnt == 4 || data_cnt == 7 || data_cnt == 8)) break; + data->uid_len = data_cnt; + if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; + if(dev->format != NfcDeviceOldSaveFormatNfcV) { + if(version == version_with_lsb_atqa) { + if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; + } else { + uint8_t atqa[2] = {}; + if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; + data->atqa[0] = atqa[1]; + data->atqa[1] = atqa[0]; + } + if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; + } + // Load CUID + uint8_t* cuid_start = data->uid; + if(data->uid_len == 7) { + cuid_start = &data->uid[3]; + } + data->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | + (cuid_start[3]); + // Parse other data + if(dev->format == NfcDeviceOldSaveFormatMifareUl) { + if(!nfc_device_old_load_mifare_ul_data(file, dev)) break; + } else if(dev->format == NfcDeviceOldSaveFormatMifareClassic) { + if(!nfc_device_old_load_mifare_classic_data(file, dev)) break; + } else if(dev->format == NfcDeviceOldSaveFormatMifareDesfire) { + if(!nfc_device_old_load_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceOldSaveFormatNfcV) { + if(!nfc_device_old_load_nfcv_data(file, dev)) break; + } else if(dev->format == NfcDeviceOldSaveFormatBankCard) { + if(!nfc_device_old_load_bank_card_data(file, dev)) break; + } + parsed = true; + } while(false); + + if(dev->loading_cb) { + dev->loading_cb(dev->loading_cb_ctx, false); + } + + if((!parsed) && (show_dialog)) { + if(deprecated_version) { + dialog_message_show_storage_error(dev->dialogs, "File format deprecated"); + } else { + dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile"); + } + } + + furi_string_free(temp_str); + flipper_format_free(file); + return parsed; +} + +bool nfc_device_old_load(NfcDeviceOld* dev, const char* file_path, bool show_dialog) { + furi_assert(dev); + furi_assert(file_path); + + // Load device data + furi_string_set(dev->load_path, file_path); + bool dev_load = nfc_device_old_load_data(dev, dev->load_path, show_dialog); + if(dev_load) { + // Set device name + FuriString* filename; + filename = furi_string_alloc(); + path_extract_filename_no_ext(file_path, filename); + nfc_device_old_set_name(dev, furi_string_get_cstr(filename)); + furi_string_free(filename); + } + + return dev_load; +} + +bool nfc_file_select(NfcDeviceOld* dev) { + furi_assert(dev); + const char* folder = furi_string_get_cstr(dev->folder); + + // Input events and views are managed by file_browser + + const DialogsFileBrowserOptions browser_options = { + .extension = NFC_APP_EXTENSION, + .skip_assets = true, + .hide_dot_files = true, + .icon = &I_Nfc_10px, + .hide_ext = true, + .item_loader_callback = NULL, + .item_loader_context = NULL, + .base_path = folder, + }; + + bool res = + dialog_file_browser_show(dev->dialogs, dev->load_path, dev->load_path, &browser_options); + + if(res) { + FuriString* filename; + filename = furi_string_alloc(); + path_extract_filename(dev->load_path, filename, true); + strncpy(dev->dev_name, furi_string_get_cstr(filename), NFC_DEV_NAME_MAX_LEN); + res = nfc_device_old_load_data(dev, dev->load_path, true); + if(res) { + nfc_device_old_set_name(dev, dev->dev_name); + } + furi_string_free(filename); + } + + return res; +} + +void nfc_device_old_data_clear(NfcDeviceOldDataOld* dev_data) { + if(dev_data->protocol == NfcDeviceOldProtocolMifareDesfire) { + mf_df_clear(&dev_data->mf_df_data); + } else if(dev_data->protocol == NfcDeviceOldProtocolMifareClassic) { + memset(&dev_data->mf_classic_data, 0, sizeof(MfClassicData)); + } else if(dev_data->protocol == NfcDeviceOldProtocolMifareUl) { + mf_ul_reset(&dev_data->mf_ul_data); + } else if(dev_data->protocol == NfcDeviceOldProtocolEMV) { + memset(&dev_data->emv_data, 0, sizeof(EmvData)); + } + memset(&dev_data->nfc_data, 0, sizeof(FuriHalNfcDevData)); + dev_data->protocol = NfcDeviceOldProtocolUnknown; + furi_string_reset(dev_data->parsed_data); +} + +void nfc_device_old_clear(NfcDeviceOld* dev) { + furi_assert(dev); + + nfc_device_old_set_name(dev, ""); + nfc_device_old_data_clear(&dev->dev_data); + dev->format = NfcDeviceOldSaveFormatUid; + furi_string_reset(dev->load_path); +} + +bool nfc_device_old_delete(NfcDeviceOld* dev, bool use_load_path) { + furi_assert(dev); + + bool deleted = false; + FuriString* file_path; + file_path = furi_string_alloc(); + + do { + // Delete original file + if(use_load_path && !furi_string_empty(dev->load_path)) { + furi_string_set(file_path, dev->load_path); + } else { + furi_string_printf( + file_path, + "%s/%s%s", + furi_string_get_cstr(dev->folder), + dev->dev_name, + NFC_APP_EXTENSION); + } + if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break; + // Delete shadow file if it exists + if(dev->shadow_file_exist) { + if(use_load_path && !furi_string_empty(dev->load_path)) { + nfc_device_old_get_shadow_path(dev->load_path, file_path); + } else { + furi_string_printf( + file_path, + "%s/%s%s", + furi_string_get_cstr(dev->folder), + dev->dev_name, + NFC_APP_SHADOW_EXTENSION); + } + if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break; + } + deleted = true; + } while(0); + + if(!deleted) { + dialog_message_show_storage_error(dev->dialogs, "Can not remove file"); + } + + furi_string_free(file_path); + return deleted; +} + +bool nfc_device_old_restore(NfcDeviceOld* dev, bool use_load_path) { + furi_assert(dev); + furi_assert(dev->shadow_file_exist); + + bool restored = false; + FuriString* path; + + path = furi_string_alloc(); + + do { + if(use_load_path && !furi_string_empty(dev->load_path)) { + nfc_device_old_get_shadow_path(dev->load_path, path); + } else { + furi_string_printf( + path, + "%s/%s%s", + furi_string_get_cstr(dev->folder), + dev->dev_name, + NFC_APP_SHADOW_EXTENSION); + } + if(!storage_simply_remove(dev->storage, furi_string_get_cstr(path))) break; + dev->shadow_file_exist = false; + if(use_load_path && !furi_string_empty(dev->load_path)) { + furi_string_set(path, dev->load_path); + } else { + furi_string_printf( + path, + "%s/%s%s", + furi_string_get_cstr(dev->folder), + dev->dev_name, + NFC_APP_EXTENSION); + } + if(!nfc_device_old_load_data(dev, path, true)) break; + restored = true; + } while(0); + + furi_string_free(path); + return restored; +} + +void nfc_device_old_set_loading_callback( + NfcDeviceOld* dev, + NfcLoadingCallback callback, + void* context) { + furi_assert(dev); + + dev->loading_cb = callback; + dev->loading_cb_ctx = context; +} diff --git a/lib/nfc/deprecated/nfc_device_old.h b/lib/nfc/deprecated/nfc_device_old.h new file mode 100644 index 000000000000..03424a60bf59 --- /dev/null +++ b/lib/nfc/deprecated/nfc_device_old.h @@ -0,0 +1,129 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFC_DEV_NAME_MAX_LEN 22 +#define NFC_READER_DATA_MAX_SIZE 64 +#define NFC_DICT_KEY_BATCH_SIZE 10 + +#define NFC_APP_EXTENSION ".nfc" +#define NFC_APP_SHADOW_EXTENSION ".shd" + +typedef void (*NfcLoadingCallback)(void* context, bool state); + +typedef enum { + NfcDeviceOldProtocolUnknown, + NfcDeviceOldProtocolEMV, + NfcDeviceOldProtocolMifareUl, + NfcDeviceOldProtocolMifareClassic, + NfcDeviceOldProtocolMifareDesfire, + NfcDeviceOldProtocolNfcV +} NfcProtocolOld; + +typedef enum { + NfcDeviceOldSaveFormatUid, + NfcDeviceOldSaveFormatBankCard, + NfcDeviceOldSaveFormatMifareUl, + NfcDeviceOldSaveFormatMifareClassic, + NfcDeviceOldSaveFormatMifareDesfire, + NfcDeviceOldSaveFormatNfcV, +} NfcDeviceOldSaveFormat; + +typedef struct { + uint8_t data[NFC_READER_DATA_MAX_SIZE]; + uint16_t size; +} NfcReaderRequestData; + +typedef struct { + MfClassicDict* dict; + uint8_t current_sector; +} NfcMfClassicDictAttackData; + +typedef enum { + NfcReadModeAuto, + NfcReadModeMfClassic, + NfcReadModeMfUltralight, + NfcReadModeMfDesfire, + NfcReadModeNFCA, +} NfcReadMode; + +typedef struct { + FuriHalNfcDevData nfc_data; + NfcProtocolOld protocol; + NfcReadMode read_mode; + union { + NfcReaderRequestData reader_data; + NfcMfClassicDictAttackData mf_classic_dict_attack_data; + MfUltralightAuth mf_ul_auth; + }; + union { + EmvData emv_data; + MfUltralightData mf_ul_data; + MfClassicData mf_classic_data; + MifareDesfireData mf_df_data; + NfcVData nfcv_data; + }; + FuriString* parsed_data; +} NfcDeviceOldDataOld; + +typedef struct { + Storage* storage; + DialogsApp* dialogs; + NfcDeviceOldDataOld dev_data; + char dev_name[NFC_DEV_NAME_MAX_LEN + 1]; + FuriString* load_path; + FuriString* folder; + NfcDeviceOldSaveFormat format; + bool shadow_file_exist; + + NfcLoadingCallback loading_cb; + void* loading_cb_ctx; +} NfcDeviceOld; + +NfcDeviceOld* nfc_device_old_alloc(); + +void nfc_device_old_free(NfcDeviceOld* nfc_dev); + +void nfc_device_old_set_name(NfcDeviceOld* dev, const char* name); + +bool nfc_device_old_save(NfcDeviceOld* dev, const char* dev_name); + +bool nfc_device_old_save_shadow(NfcDeviceOld* dev, const char* dev_name); + +bool nfc_device_old_load(NfcDeviceOld* dev, const char* file_path, bool show_dialog); + +bool nfc_device_old_load_key_cache(NfcDeviceOld* dev); + +bool nfc_file_select(NfcDeviceOld* dev); + +void nfc_device_old_data_clear(NfcDeviceOldDataOld* dev); + +void nfc_device_old_clear(NfcDeviceOld* dev); + +bool nfc_device_old_delete(NfcDeviceOld* dev, bool use_load_path); + +bool nfc_device_old_restore(NfcDeviceOld* dev, bool use_load_path); + +void nfc_device_old_set_loading_callback( + NfcDeviceOld* dev, + NfcLoadingCallback callback, + void* context); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_types.c b/lib/nfc/deprecated/nfc_types.c similarity index 83% rename from lib/nfc/nfc_types.c rename to lib/nfc/deprecated/nfc_types.c index 02ca85580dbc..3a0de7273993 100644 --- a/lib/nfc/nfc_types.c +++ b/lib/nfc/deprecated/nfc_types.c @@ -14,14 +14,14 @@ const char* nfc_get_dev_type(FuriHalNfcType type) { } } -const char* nfc_guess_protocol(NfcProtocol protocol) { - if(protocol == NfcDeviceProtocolEMV) { +const char* nfc_guess_protocol(NfcProtocolOld protocol) { + if(protocol == NfcDeviceOldProtocolEMV) { return "EMV bank card"; - } else if(protocol == NfcDeviceProtocolMifareUl) { + } else if(protocol == NfcDeviceOldProtocolMifareUl) { return "Mifare Ultral/NTAG"; - } else if(protocol == NfcDeviceProtocolMifareClassic) { + } else if(protocol == NfcDeviceOldProtocolMifareClassic) { return "Mifare Classic"; - } else if(protocol == NfcDeviceProtocolMifareDesfire) { + } else if(protocol == NfcDeviceOldProtocolMifareDesfire) { return "Mifare DESFire"; } else { return "Unrecognized"; @@ -45,6 +45,8 @@ const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) { return "NTAG I2C Plus 2K"; } else if(type == MfUltralightTypeNTAG203) { return "NTAG203"; + } else if(type == MfUltralightTypeULC) { + return "Mifare Ultralight C"; } else if(type == MfUltralightTypeUL11 && full_name) { return "Mifare Ultralight 11"; } else if(type == MfUltralightTypeUL21 && full_name) { diff --git a/lib/nfc/nfc_types.h b/lib/nfc/deprecated/nfc_types.h similarity index 68% rename from lib/nfc/nfc_types.h rename to lib/nfc/deprecated/nfc_types.h index fb53ce7c25be..bfe7a4a4ffda 100644 --- a/lib/nfc/nfc_types.h +++ b/lib/nfc/deprecated/nfc_types.h @@ -1,10 +1,10 @@ #pragma once -#include "nfc_device.h" +#include "nfc_device_old.h" const char* nfc_get_dev_type(FuriHalNfcType type); -const char* nfc_guess_protocol(NfcProtocol protocol); +const char* nfc_guess_protocol(NfcProtocolOld protocol); const char* nfc_mf_ul_type(MfUltralightType type, bool full_name); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/deprecated/nfc_worker.c similarity index 68% rename from lib/nfc/nfc_worker.c rename to lib/nfc/deprecated/nfc_worker.c index 28a1f6827977..2923e9eac81d 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/deprecated/nfc_worker.c @@ -48,7 +48,7 @@ NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) { void nfc_worker_start( NfcWorker* nfc_worker, NfcWorkerState state, - NfcDeviceData* dev_data, + NfcDeviceOldDataOld* dev_data, NfcWorkerCallback callback, void* context) { furi_assert(nfc_worker); @@ -111,6 +111,14 @@ int32_t nfc_worker_task(void* context) { nfc_worker_mf_classic_dict_attack(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { nfc_worker_analyze_reader(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + nfc_worker_nfcv_emulate(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVSniff) { + nfc_worker_nfcv_sniff(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlock) { + nfc_worker_nfcv_unlock(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + nfc_worker_nfcv_unlock(nfc_worker); } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); @@ -118,6 +126,236 @@ int32_t nfc_worker_task(void* context) { return 0; } +static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + NfcVReader reader = {}; + + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + furi_hal_nfc_sleep(); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + do { + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; + if(!nfcv_read_card(&reader, nfc_data, nfcv_data)) break; + + read_success = true; + } while(false); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + + return read_success; +} + +void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + nfcv_emu_init(nfc_data, nfcv_data); + while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); + if(nfcv_data->modified) { + nfc_worker->callback(NfcWorkerEventNfcVContentChanged, nfc_worker->context); + nfcv_data->modified = false; + } + } + } + } + nfcv_emu_deinit(nfcv_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + +void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + nfcv_data->sub_type = NfcVTypeSniff; + nfcv_emu_init(nfc_data, nfcv_data); + + while(nfc_worker->state == NfcWorkerStateNfcVSniff) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); + } + } + } + nfcv_emu_deinit(nfcv_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + FuriHalNfcTxRxContext tx_rx = {}; + uint8_t* key_data = nfcv_data->sub_data.slix.key_privacy; + uint32_t key = 0; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + furi_hal_nfc_sleep(); + + while((nfc_worker->state == NfcWorkerStateNfcVUnlock) || + (nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) { + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_ll_txrx_on(); + furi_hal_nfc_ll_poll(); + if(furi_hal_nfc_ll_set_mode( + FuriHalNfcModePollNfcv, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48) != + FuriHalNfcReturnOk) { + break; + } + + furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER); + furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCV_POLLER); + furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); + furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCV); + + FURI_LOG_D(TAG, "Detect presence"); + ReturnCode ret = slix_get_random(nfcv_data); + + if(ret == ERR_NONE) { + /* there is some chip, responding with a RAND */ + nfc_worker->dev_data->protocol = NfcDeviceOldProtocolNfcV; + FURI_LOG_D(TAG, " Chip detected. In privacy?"); + ret = nfcv_inventory(NULL); + + if(ret == ERR_NONE) { + /* chip is also visible, so no action required, just save */ + if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + NfcVReader reader = {}; + + if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { + FURI_LOG_D(TAG, " => failed, wait for chip to disappear."); + snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + } else { + FURI_LOG_D(TAG, " => success, wait for chip to disappear."); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + } else { + FURI_LOG_D(TAG, " => success, wait for chip to disappear."); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + + FURI_LOG_D(TAG, " => chip is already visible, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + + key_data[0] = 0; + key_data[1] = 0; + key_data[2] = 0; + key_data[3] = 0; + + } else { + /* chip is invisible, try to unlock */ + FURI_LOG_D(TAG, " chip is invisible, unlocking"); + + if(nfcv_data->auth_method == NfcVAuthMethodManual) { + key |= key_data[0] << 24; + key |= key_data[1] << 16; + key |= key_data[2] << 8; + key |= key_data[3] << 0; + + ret = slix_unlock(nfcv_data, 4); + } else { + key = 0x7FFD6E5B; + key_data[0] = (key >> 24) & 0xFF; + key_data[1] = (key >> 16) & 0xFF; + key_data[2] = (key >> 8) & 0xFF; + key_data[3] = (key >> 0) & 0xFF; + ret = slix_unlock(nfcv_data, 4); + + if(ret != ERR_NONE) { + /* main key failed, trying second one */ + FURI_LOG_D(TAG, " trying second key after resetting"); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + if(slix_get_random(nfcv_data) != ERR_NONE) { + FURI_LOG_D(TAG, " reset failed"); + } + + key = 0x0F0F0F0F; + key_data[0] = (key >> 24) & 0xFF; + key_data[1] = (key >> 16) & 0xFF; + key_data[2] = (key >> 8) & 0xFF; + key_data[3] = (key >> 0) & 0xFF; + ret = slix_unlock(nfcv_data, 4); + } + } + if(ret != ERR_NONE) { + /* unlock failed */ + FURI_LOG_D(TAG, " => failed, wait for chip to disappear."); + snprintf( + nfcv_data->error, sizeof(nfcv_data->error), "Passwords not\naccepted"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + /* wait for disappearing */ + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + } + } + } else { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + } + + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_sleep(); + furi_delay_ms(100); + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MfUltralightReader reader = {}; @@ -132,7 +370,7 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC // Try to read supported card FURI_LOG_I(TAG, "Trying to read a supported card ..."); for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { - if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareUl) { + if(nfc_supported_card[i].protocol == NfcDeviceOldProtocolMifareUl) { if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { read_success = true; @@ -175,7 +413,7 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont // Try to read supported card FURI_LOG_I(TAG, "Trying to read a supported card ..."); for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { - if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareClassic) { + if(nfc_supported_card[i].protocol == NfcDeviceOldProtocolMifareClassic) { if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { read_success = true; @@ -193,11 +431,11 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont if(nfc_worker->callback(NfcWorkerEventReadMfClassicLoadKeyCache, nfc_worker->context)) { FURI_LOG_I(TAG, "Load keys cache success. Start reading"); uint8_t sectors_read = - mf_classic_update_card(tx_rx, &nfc_worker->dev_data->mf_classic_data); + mifare_classic_update_card(tx_rx, &nfc_worker->dev_data->mf_classic_data); uint8_t sectors_total = - mf_classic_get_total_sectors_num(nfc_worker->dev_data->mf_classic_data.type); + mifare_classic_get_total_sectors_num(nfc_worker->dev_data->mf_classic_data.type); FURI_LOG_I(TAG, "Read %d sectors out of %d total", sectors_read, sectors_total); - read_success = mf_classic_is_card_read(&nfc_worker->dev_data->mf_classic_data); + read_success = mifare_classic_is_card_read(&nfc_worker->dev_data->mf_classic_data); } } while(false); @@ -219,6 +457,19 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont do { if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; if(!mf_df_read_card(tx_rx, data)) break; + FURI_LOG_I(TAG, "Trying to parse a supported card ..."); + + // The model for parsing DESFire is a little different to other cards; + // we don't have parsers to provide encryption keys, so we can read the + // data normally, and then pass the read data to a parser. + // + // There are fully-protected DESFire cards, but providing keys for them + // is difficult (and unnessesary for many transit cards). + for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { + if(nfc_supported_card[i].protocol == NfcDeviceOldProtocolMifareDesfire) { + if(nfc_supported_card[i].parse(nfc_worker->dev_data)) break; + } + } read_success = true; } while(false); @@ -236,24 +487,24 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t furi_hal_nfc_sleep(); if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { FURI_LOG_I(TAG, "Mifare Ultralight / NTAG detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareUl; + nfc_worker->dev_data->protocol = NfcDeviceOldProtocolMifareUl; card_read = nfc_worker_read_mf_ultralight(nfc_worker, tx_rx); - } else if(mf_classic_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { + } else if(mifare_classic_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { FURI_LOG_I(TAG, "Mifare Classic detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic; + nfc_worker->dev_data->protocol = NfcDeviceOldProtocolMifareClassic; nfc_worker->dev_data->mf_classic_data.type = - mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + mifare_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); card_read = nfc_worker_read_mf_classic(nfc_worker, tx_rx); } else if(mf_df_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { FURI_LOG_I(TAG, "Mifare DESFire detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareDesfire; + nfc_worker->dev_data->protocol = NfcDeviceOldProtocolMifareDesfire; if(!nfc_worker_read_mf_desfire(nfc_worker, tx_rx)) { FURI_LOG_I(TAG, "Unknown card. Save UID"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + nfc_worker->dev_data->protocol = NfcDeviceOldProtocolUnknown; } card_read = true; } else { - nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + nfc_worker->dev_data->protocol = NfcDeviceOldProtocolUnknown; card_read = true; } @@ -264,8 +515,8 @@ void nfc_worker_read(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); - nfc_device_data_clear(nfc_worker->dev_data); - NfcDeviceData* dev_data = nfc_worker->dev_data; + nfc_device_old_data_clear(nfc_worker->dev_data); + NfcDeviceOldDataOld* dev_data = nfc_worker->dev_data; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; FuriHalNfcTxRxContext tx_rx = {}; NfcWorkerEvent event = 0; @@ -278,21 +529,21 @@ void nfc_worker_read(NfcWorker* nfc_worker) { card_not_detected_notified = false; if(nfc_data->type == FuriHalNfcTypeA) { if(nfc_worker_read_nfca(nfc_worker, &tx_rx)) { - if(dev_data->protocol == NfcDeviceProtocolMifareUl) { + if(dev_data->protocol == NfcDeviceOldProtocolMifareUl) { event = NfcWorkerEventReadMfUltralight; break; - } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { + } else if(dev_data->protocol == NfcDeviceOldProtocolMifareClassic) { event = NfcWorkerEventReadMfClassicDone; break; - } else if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { + } else if(dev_data->protocol == NfcDeviceOldProtocolMifareDesfire) { event = NfcWorkerEventReadMfDesfire; break; - } else if(dev_data->protocol == NfcDeviceProtocolUnknown) { + } else if(dev_data->protocol == NfcDeviceOldProtocolUnknown) { event = NfcWorkerEventReadUidNfcA; break; } } else { - if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { + if(dev_data->protocol == NfcDeviceOldProtocolMifareClassic) { event = NfcWorkerEventReadMfClassicDictAttackRequired; break; } @@ -304,7 +555,12 @@ void nfc_worker_read(NfcWorker* nfc_worker) { event = NfcWorkerEventReadUidNfcF; break; } else if(nfc_data->type == FuriHalNfcTypeV) { - event = NfcWorkerEventReadUidNfcV; + FURI_LOG_I(TAG, "NfcV detected"); + nfc_worker->dev_data->protocol = NfcDeviceOldProtocolNfcV; + if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) { + FURI_LOG_I(TAG, "nfc_worker_read_nfcv success"); + } + event = NfcWorkerEventReadNfcV; break; } } else { @@ -327,8 +583,8 @@ void nfc_worker_read_type(NfcWorker* nfc_worker) { furi_assert(nfc_worker->callback); NfcReadMode read_mode = nfc_worker->dev_data->read_mode; - nfc_device_data_clear(nfc_worker->dev_data); - NfcDeviceData* dev_data = nfc_worker->dev_data; + nfc_device_old_data_clear(nfc_worker->dev_data); + NfcDeviceOldDataOld* dev_data = nfc_worker->dev_data; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; FuriHalNfcTxRxContext tx_rx = {}; NfcWorkerEvent event = 0; @@ -343,35 +599,35 @@ void nfc_worker_read_type(NfcWorker* nfc_worker) { card_not_detected_notified = false; if(nfc_data->type == FuriHalNfcTypeA) { if(read_mode == NfcReadModeMfClassic) { - nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic; - nfc_worker->dev_data->mf_classic_data.type = mf_classic_get_classic_type( + nfc_worker->dev_data->protocol = NfcDeviceOldProtocolMifareClassic; + nfc_worker->dev_data->mf_classic_data.type = mifare_classic_get_classic_type( nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); if(nfc_worker_read_mf_classic(nfc_worker, &tx_rx)) { FURI_LOG_D(TAG, "Card read"); - dev_data->protocol = NfcDeviceProtocolMifareClassic; + dev_data->protocol = NfcDeviceOldProtocolMifareClassic; event = NfcWorkerEventReadMfClassicDone; break; } else { FURI_LOG_D(TAG, "Card read failed"); - dev_data->protocol = NfcDeviceProtocolMifareClassic; + dev_data->protocol = NfcDeviceOldProtocolMifareClassic; event = NfcWorkerEventReadMfClassicDictAttackRequired; break; } } else if(read_mode == NfcReadModeMfUltralight) { FURI_LOG_I(TAG, "Mifare Ultralight / NTAG"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareUl; + nfc_worker->dev_data->protocol = NfcDeviceOldProtocolMifareUl; if(nfc_worker_read_mf_ultralight(nfc_worker, &tx_rx)) { event = NfcWorkerEventReadMfUltralight; break; } } else if(read_mode == NfcReadModeMfDesfire) { - nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareDesfire; + nfc_worker->dev_data->protocol = NfcDeviceOldProtocolMifareDesfire; if(nfc_worker_read_mf_desfire(nfc_worker, &tx_rx)) { event = NfcWorkerEventReadMfDesfire; break; } } else if(read_mode == NfcReadModeNFCA) { - nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + nfc_worker->dev_data->protocol = NfcDeviceOldProtocolUnknown; event = NfcWorkerEventReadUidNfcA; break; } @@ -496,7 +752,7 @@ static bool nfc_worker_mf_get_b_key_from_sector_trailer( uint64_t* found_key) { // Some access conditions allow reading B key via A key - uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + uint8_t block = mifare_classic_get_sector_trailer_block_num_by_sector(sector); Crypto1 crypto = {}; MfClassicBlock block_tmp = {}; @@ -504,8 +760,8 @@ static bool nfc_worker_mf_get_b_key_from_sector_trailer( furi_hal_nfc_sleep(); - if(mf_classic_auth_attempt(tx_rx, &crypto, &auth_context, key)) { - if(mf_classic_read_block(tx_rx, &crypto, block, &block_tmp)) { + if(mifare_classic_auth_attempt(tx_rx, &crypto, &auth_context, key)) { + if(mifare_classic_read_block(tx_rx, &crypto, block, &block_tmp)) { *found_key = nfc_util_bytes2num(&block_tmp.value[10], sizeof(uint8_t) * 6); return *found_key; @@ -529,7 +785,7 @@ static void nfc_worker_mf_classic_key_attack( MfClassicData* data = &nfc_worker->dev_data->mf_classic_data; NfcMfClassicDictAttackData* dict_attack_data = &nfc_worker->dev_data->mf_classic_dict_attack_data; - uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type); + uint32_t total_sectors = mifare_classic_get_total_sectors_num(data->type); furi_assert(start_sector < total_sectors); @@ -547,17 +803,17 @@ static void nfc_worker_mf_classic_key_attack( card_found_notified = true; card_removed_notified = false; } - uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i); - if(mf_classic_is_sector_read(data, i)) continue; - if(!mf_classic_is_key_found(data, i, MfClassicKeyA)) { + uint8_t block_num = mifare_classic_get_sector_trailer_block_num_by_sector(i); + if(mifare_classic_is_sector_read(data, i)) continue; + if(!mifare_classic_is_key_found(data, i, MfClassicKeyA)) { FURI_LOG_D( TAG, "Trying A key for sector %d, key: %04lx%08lx", i, (uint32_t)(key >> 32), (uint32_t)key); - if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyA)) { - mf_classic_set_key_found(data, i, MfClassicKeyA, key); + if(mifare_classic_authenticate(tx_rx, block_num, key, MfClassicKeyA)) { + mifare_classic_set_key_found(data, i, MfClassicKeyA, key); FURI_LOG_D( TAG, "Key A found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); @@ -565,7 +821,7 @@ static void nfc_worker_mf_classic_key_attack( uint64_t found_key; if(nfc_worker_mf_get_b_key_from_sector_trailer(tx_rx, i, key, &found_key)) { FURI_LOG_D(TAG, "Found B key via reading sector %d", i); - mf_classic_set_key_found(data, i, MfClassicKeyB, found_key); + mifare_classic_set_key_found(data, i, MfClassicKeyB, found_key); if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); @@ -573,23 +829,23 @@ static void nfc_worker_mf_classic_key_attack( } } } - if(!mf_classic_is_key_found(data, i, MfClassicKeyB)) { + if(!mifare_classic_is_key_found(data, i, MfClassicKeyB)) { FURI_LOG_D( TAG, "Trying B key for sector %d, key: %04lx%08lx", i, (uint32_t)(key >> 32), (uint32_t)key); - if(mf_classic_authenticate(tx_rx, block_num, key, MfClassicKeyB)) { - mf_classic_set_key_found(data, i, MfClassicKeyB, key); + if(mifare_classic_authenticate(tx_rx, block_num, key, MfClassicKeyB)) { + mifare_classic_set_key_found(data, i, MfClassicKeyB, key); FURI_LOG_D( TAG, "Key B found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); } } - if(mf_classic_is_sector_read(data, i)) continue; - mf_classic_read_sector(tx_rx, data, i); + if(mifare_classic_is_sector_read(data, i)) continue; + mifare_classic_read_sector(tx_rx, data, i); } else { if(!card_removed_notified) { nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); @@ -609,7 +865,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { MfClassicData* data = &nfc_worker->dev_data->mf_classic_data; NfcMfClassicDictAttackData* dict_attack_data = &nfc_worker->dev_data->mf_classic_dict_attack_data; - uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type); + uint32_t total_sectors = mifare_classic_get_total_sectors_num(data->type); uint64_t key = 0; uint64_t prev_key = 0; FuriHalNfcTxRxContext tx_rx = {}; @@ -629,10 +885,10 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { for(size_t i = 0; i < total_sectors; i++) { FURI_LOG_I(TAG, "Sector %d", i); nfc_worker->callback(NfcWorkerEventNewSector, nfc_worker->context); - uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i); - if(mf_classic_is_sector_read(data, i)) continue; - bool is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA); - bool is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); + uint8_t block_num = mifare_classic_get_sector_trailer_block_num_by_sector(i); + if(mifare_classic_is_sector_read(data, i)) continue; + bool is_key_a_found = mifare_classic_is_key_found(data, i, MfClassicKeyA); + bool is_key_b_found = mifare_classic_is_key_found(data, i, MfClassicKeyB); uint16_t key_index = 0; while(mf_classic_dict_get_next_key(dict, &key)) { FURI_LOG_T(TAG, "Key %d", key_index); @@ -657,18 +913,19 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { (uint32_t)(key >> 32), (uint32_t)key); if(!is_key_a_found) { - is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA); - if(mf_classic_authenticate_skip_activate( + is_key_a_found = mifare_classic_is_key_found(data, i, MfClassicKeyA); + if(mifare_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyA, !deactivated, cuid)) { - mf_classic_set_key_found(data, i, MfClassicKeyA, key); - FURI_LOG_D(TAG, "Key A found"); + mifare_classic_set_key_found(data, i, MfClassicKeyA, key); + FURI_LOG_D( + TAG, "Key A found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); uint64_t found_key; if(nfc_worker_mf_get_b_key_from_sector_trailer( &tx_rx, i, key, &found_key)) { FURI_LOG_D(TAG, "Found B key via reading sector %d", i); - mf_classic_set_key_found(data, i, MfClassicKeyB, found_key); + mifare_classic_set_key_found(data, i, MfClassicKeyB, found_key); if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); @@ -683,28 +940,41 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key A is marked as found and matches the searching key, invalidate it - if(mf_classic_is_key_found(data, i, MfClassicKeyA) && - data->block[i].value[0] == key) { - mf_classic_set_key_not_found(data, i, MfClassicKeyA); + uint8_t found_key[6]; + memcpy(found_key, data->block[i].value, 6); + + uint8_t current_key[6]; + memcpy(current_key, &key, 6); + + if(mifare_classic_is_key_found(data, i, MfClassicKeyA) && + memcmp(found_key, current_key, 6) == 0) { + mifare_classic_set_key_not_found(data, i, MfClassicKeyA); is_key_a_found = false; FURI_LOG_D(TAG, "Key %dA not found in attack", i); } } if(!is_key_b_found) { - is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); - if(mf_classic_authenticate_skip_activate( + is_key_b_found = mifare_classic_is_key_found(data, i, MfClassicKeyB); + if(mifare_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) { - FURI_LOG_D(TAG, "Key B found"); - mf_classic_set_key_found(data, i, MfClassicKeyB, key); + FURI_LOG_D( + TAG, "Key B found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); + mifare_classic_set_key_found(data, i, MfClassicKeyB, key); nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1); } deactivated = true; } else { // If the key B is marked as found and matches the searching key, invalidate it - if(mf_classic_is_key_found(data, i, MfClassicKeyB) && - data->block[i].value[10] == key) { - mf_classic_set_key_not_found(data, i, MfClassicKeyB); + uint8_t found_key[6]; + memcpy(found_key, data->block[i].value + 10, 6); + + uint8_t current_key[6]; + memcpy(current_key, &key, 6); + + if(mifare_classic_is_key_found(data, i, MfClassicKeyB) && + memcmp(found_key, current_key, 6) == 0) { + mifare_classic_set_key_not_found(data, i, MfClassicKeyB); is_key_b_found = false; FURI_LOG_D(TAG, "Key %dB not found in attack", i); } @@ -722,7 +992,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { memcpy(&prev_key, &key, sizeof(key)); } if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; - mf_classic_read_sector(&tx_rx, data, i); + mifare_classic_read_sector(&tx_rx, data, i); mf_classic_dict_rewind(dict); } if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { @@ -748,7 +1018,7 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { furi_hal_nfc_listen_start(nfc_data); while(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { //-V1044 if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { - mf_classic_emulator(&emulator, &tx_rx); + mifare_classic_emulator(&emulator, &tx_rx, false); } } if(emulator.data_changed) { @@ -789,7 +1059,7 @@ void nfc_worker_write_mf_classic(NfcWorker* nfc_worker) { FURI_LOG_I(TAG, "Check mf classic type"); MfClassicType type = - mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); + mifare_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); if(type != nfc_worker->dev_data->mf_classic_data.type) { FURI_LOG_E(TAG, "Wrong mf classic type"); nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); @@ -797,22 +1067,22 @@ void nfc_worker_write_mf_classic(NfcWorker* nfc_worker) { } // Set blocks not read - mf_classic_set_sector_data_not_read(&dest_data); + mifare_classic_set_sector_data_not_read(&dest_data); FURI_LOG_I(TAG, "Updating card sectors"); - uint8_t total_sectors = mf_classic_get_total_sectors_num(type); + uint8_t total_sectors = mifare_classic_get_total_sectors_num(type); bool write_success = true; for(uint8_t i = 0; i < total_sectors; i++) { FURI_LOG_I(TAG, "Reading sector %d", i); - mf_classic_read_sector(&tx_rx, &dest_data, i); - bool old_data_read = mf_classic_is_sector_data_read(src_data, i); - bool new_data_read = mf_classic_is_sector_data_read(&dest_data, i); + mifare_classic_read_sector(&tx_rx, &dest_data, i); + bool old_data_read = mifare_classic_is_sector_data_read(src_data, i); + bool new_data_read = mifare_classic_is_sector_data_read(&dest_data, i); if(old_data_read != new_data_read) { FURI_LOG_E(TAG, "Failed to update sector %d", i); write_success = false; break; } if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break; - if(!mf_classic_write_sector(&tx_rx, &dest_data, src_data, i)) { + if(!mifare_classic_write_sector(&tx_rx, &dest_data, src_data, i)) { FURI_LOG_E(TAG, "Failed to write %d sector", i); write_success = false; break; @@ -862,7 +1132,7 @@ void nfc_worker_update_mf_classic(NfcWorker* nfc_worker) { FURI_LOG_I(TAG, "Check MF classic type"); MfClassicType type = - mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); + mifare_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak); if(type != nfc_worker->dev_data->mf_classic_data.type) { FURI_LOG_E(TAG, "MF classic type mismatch"); nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context); @@ -870,15 +1140,15 @@ void nfc_worker_update_mf_classic(NfcWorker* nfc_worker) { } // Set blocks not read - mf_classic_set_sector_data_not_read(&new_data); + mifare_classic_set_sector_data_not_read(&new_data); FURI_LOG_I(TAG, "Updating card sectors"); - uint8_t total_sectors = mf_classic_get_total_sectors_num(type); + uint8_t total_sectors = mifare_classic_get_total_sectors_num(type); bool update_success = true; for(uint8_t i = 0; i < total_sectors; i++) { FURI_LOG_I(TAG, "Reading sector %d", i); - mf_classic_read_sector(&tx_rx, &new_data, i); - bool old_data_read = mf_classic_is_sector_data_read(old_data, i); - bool new_data_read = mf_classic_is_sector_data_read(&new_data, i); + mifare_classic_read_sector(&tx_rx, &new_data, i); + bool old_data_read = mifare_classic_is_sector_data_read(old_data, i); + bool new_data_read = mifare_classic_is_sector_data_read(&new_data, i); if(old_data_read != new_data_read) { FURI_LOG_E(TAG, "Failed to update sector %d", i); update_success = false; @@ -998,7 +1268,7 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer; FuriHalNfcDevData* nfc_data = NULL; - if(nfc_worker->dev_data->protocol == NfcDeviceProtocolMifareClassic) { + if(nfc_worker->dev_data->protocol == NfcDeviceOldProtocolMifareClassic) { nfc_data = &nfc_worker->dev_data->nfc_data; reader_analyzer_set_nfc_data(reader_analyzer, nfc_data); } else { @@ -1032,10 +1302,10 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { } reader_no_data_received_cnt = 0; reader_no_data_notified = false; - NfcProtocol protocol = + NfcProtocolOld protocol = reader_analyzer_guess_protocol(reader_analyzer, tx_rx.rx_data, tx_rx.rx_bits / 8); - if(protocol == NfcDeviceProtocolMifareClassic) { - mf_classic_emulator(&emulator, &tx_rx); + if(protocol == NfcDeviceOldProtocolMifareClassic) { + mifare_classic_emulator(&emulator, &tx_rx, true); } } else { reader_no_data_received_cnt++; diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/deprecated/nfc_worker.h similarity index 82% rename from lib/nfc/nfc_worker.h rename to lib/nfc/deprecated/nfc_worker.h index 8e993fc6aa3c..5243c338b7a9 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/deprecated/nfc_worker.h @@ -1,6 +1,6 @@ #pragma once -#include "nfc_device.h" +#include "nfc_device_old.h" typedef struct NfcWorker NfcWorker; @@ -18,6 +18,10 @@ typedef enum { NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, + NfcWorkerStateNfcVEmulate, + NfcWorkerStateNfcVUnlock, + NfcWorkerStateNfcVUnlockAndSave, + NfcWorkerStateNfcVSniff, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -39,6 +43,7 @@ typedef enum { NfcWorkerEventReadMfClassicDone, NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, + NfcWorkerEventReadNfcV, // Nfc worker common events NfcWorkerEventSuccess, @@ -69,6 +74,9 @@ typedef enum { // Mifare Ultralight events NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command + NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key + NfcWorkerEventNfcVCommandExecuted, + NfcWorkerEventNfcVContentChanged, } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); @@ -82,8 +90,11 @@ void nfc_worker_free(NfcWorker* nfc_worker); void nfc_worker_start( NfcWorker* nfc_worker, NfcWorkerState state, - NfcDeviceData* dev_data, + NfcDeviceOldDataOld* dev_data, NfcWorkerCallback callback, void* context); void nfc_worker_stop(NfcWorker* nfc_worker); +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); +void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker); +void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker); \ No newline at end of file diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/deprecated/nfc_worker_i.h similarity index 71% rename from lib/nfc/nfc_worker_i.h rename to lib/nfc/deprecated/nfc_worker_i.h index 701ecb90c636..2b51f7af8f41 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/deprecated/nfc_worker_i.h @@ -5,20 +5,21 @@ #include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include struct NfcWorker { FuriThread* thread; Storage* storage; Stream* dict_stream; - NfcDeviceData* dev_data; + NfcDeviceOldDataOld* dev_data; NfcWorkerCallback callback; void* context; diff --git a/lib/nfc/parsers/all_in_one.c b/lib/nfc/deprecated/parsers/all_in_one.c similarity index 95% rename from lib/nfc/parsers/all_in_one.c rename to lib/nfc/deprecated/parsers/all_in_one.c index edcc0d0c7caf..17cf6fcf859b 100644 --- a/lib/nfc/parsers/all_in_one.c +++ b/lib/nfc/deprecated/parsers/all_in_one.c @@ -2,7 +2,7 @@ #include "all_in_one.h" #include -#include +#include #include @@ -14,7 +14,7 @@ #define ALL_IN_ONE_LAYOUT_E5 5 #define ALL_IN_ONE_LAYOUT_2 6 -uint8_t all_in_one_get_layout(NfcDeviceData* dev_data) { +uint8_t all_in_one_get_layout(NfcDeviceOldDataOld* dev_data) { // I absolutely hate what's about to happen here. // Switch on the second half of the third byte of page 5 @@ -67,7 +67,7 @@ bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) } } -bool all_in_one_parser_parse(NfcDeviceData* dev_data) { +bool all_in_one_parser_parse(NfcDeviceOldDataOld* dev_data) { if(dev_data->mf_ul_data.data[4 * 4] != 0x45 || dev_data->mf_ul_data.data[4 * 4 + 1] != 0xD9) { FURI_LOG_I("all_in_one", "Pass not verified"); return false; diff --git a/lib/nfc/parsers/all_in_one.h b/lib/nfc/deprecated/parsers/all_in_one.h similarity index 78% rename from lib/nfc/parsers/all_in_one.h rename to lib/nfc/deprecated/parsers/all_in_one.h index 9b646d4dc300..967ccc334044 100644 --- a/lib/nfc/parsers/all_in_one.h +++ b/lib/nfc/deprecated/parsers/all_in_one.h @@ -6,4 +6,4 @@ bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_r bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); -bool all_in_one_parser_parse(NfcDeviceData* dev_data); \ No newline at end of file +bool all_in_one_parser_parse(NfcDeviceOldDataOld* dev_data); \ No newline at end of file diff --git a/lib/nfc/parsers/nfc_supported_card.c b/lib/nfc/deprecated/parsers/nfc_supported_card.c similarity index 66% rename from lib/nfc/parsers/nfc_supported_card.c rename to lib/nfc/deprecated/parsers/nfc_supported_card.c index fc2dc34e0ee1..e8be829bf511 100644 --- a/lib/nfc/parsers/nfc_supported_card.c +++ b/lib/nfc/deprecated/parsers/nfc_supported_card.c @@ -6,53 +6,62 @@ #include "troika_4k_parser.h" #include "two_cities.h" #include "all_in_one.h" +#include "opal.h" NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = { [NfcSupportedCardTypePlantain] = { - .protocol = NfcDeviceProtocolMifareClassic, + .protocol = NfcDeviceOldProtocolMifareClassic, .verify = plantain_parser_verify, .read = plantain_parser_read, .parse = plantain_parser_parse, }, [NfcSupportedCardTypeTroika] = { - .protocol = NfcDeviceProtocolMifareClassic, + .protocol = NfcDeviceOldProtocolMifareClassic, .verify = troika_parser_verify, .read = troika_parser_read, .parse = troika_parser_parse, }, [NfcSupportedCardTypePlantain4K] = { - .protocol = NfcDeviceProtocolMifareClassic, + .protocol = NfcDeviceOldProtocolMifareClassic, .verify = plantain_4k_parser_verify, .read = plantain_4k_parser_read, .parse = plantain_4k_parser_parse, }, [NfcSupportedCardTypeTroika4K] = { - .protocol = NfcDeviceProtocolMifareClassic, + .protocol = NfcDeviceOldProtocolMifareClassic, .verify = troika_4k_parser_verify, .read = troika_4k_parser_read, .parse = troika_4k_parser_parse, }, [NfcSupportedCardTypeTwoCities] = { - .protocol = NfcDeviceProtocolMifareClassic, + .protocol = NfcDeviceOldProtocolMifareClassic, .verify = two_cities_parser_verify, .read = two_cities_parser_read, .parse = two_cities_parser_parse, }, [NfcSupportedCardTypeAllInOne] = { - .protocol = NfcDeviceProtocolMifareUl, + .protocol = NfcDeviceOldProtocolMifareUl, .verify = all_in_one_parser_verify, .read = all_in_one_parser_read, .parse = all_in_one_parser_parse, }, + [NfcSupportedCardTypeOpal] = + { + .protocol = NfcDeviceOldProtocolMifareDesfire, + .verify = stub_parser_verify_read, + .read = stub_parser_verify_read, + .parse = opal_parser_parse, + }, + }; -bool nfc_supported_card_verify_and_parse(NfcDeviceData* dev_data) { +bool nfc_supported_card_verify_and_parse(NfcDeviceOldDataOld* dev_data) { furi_assert(dev_data); bool card_parsed = false; @@ -65,3 +74,9 @@ bool nfc_supported_card_verify_and_parse(NfcDeviceData* dev_data) { return card_parsed; } + +bool stub_parser_verify_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + UNUSED(nfc_worker); + UNUSED(tx_rx); + return false; +} diff --git a/lib/nfc/parsers/nfc_supported_card.h b/lib/nfc/deprecated/parsers/nfc_supported_card.h similarity index 58% rename from lib/nfc/parsers/nfc_supported_card.h rename to lib/nfc/deprecated/parsers/nfc_supported_card.h index 4af59aded635..6c739100d38d 100644 --- a/lib/nfc/parsers/nfc_supported_card.h +++ b/lib/nfc/deprecated/parsers/nfc_supported_card.h @@ -2,7 +2,7 @@ #include #include "../nfc_worker.h" -#include "../nfc_device.h" +#include "../nfc_device_old.h" typedef enum { NfcSupportedCardTypePlantain, @@ -11,6 +11,7 @@ typedef enum { NfcSupportedCardTypeTroika4K, NfcSupportedCardTypeTwoCities, NfcSupportedCardTypeAllInOne, + NfcSupportedCardTypeOpal, NfcSupportedCardTypeEnd, } NfcSupportedCardType; @@ -19,10 +20,10 @@ typedef bool (*NfcSupportedCardVerify)(NfcWorker* nfc_worker, FuriHalNfcTxRxCont typedef bool (*NfcSupportedCardRead)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); -typedef bool (*NfcSupportedCardParse)(NfcDeviceData* dev_data); +typedef bool (*NfcSupportedCardParse)(NfcDeviceOldDataOld* dev_data); typedef struct { - NfcProtocol protocol; + NfcProtocolOld protocol; NfcSupportedCardVerify verify; NfcSupportedCardRead read; NfcSupportedCardParse parse; @@ -30,4 +31,9 @@ typedef struct { extern NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd]; -bool nfc_supported_card_verify_and_parse(NfcDeviceData* dev_data); +bool nfc_supported_card_verify_and_parse(NfcDeviceOldDataOld* dev_data); + +// stub_parser_verify_read does nothing, and always reports that it does not +// support the card. This is needed for DESFire card parsers which can't +// provide keys, and only use NfcSupportedCard->parse. +bool stub_parser_verify_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); diff --git a/lib/nfc/deprecated/parsers/opal.c b/lib/nfc/deprecated/parsers/opal.c new file mode 100644 index 000000000000..ff1cd670c99c --- /dev/null +++ b/lib/nfc/deprecated/parsers/opal.c @@ -0,0 +1,204 @@ +/* + * opal.c - Parser for Opal card (Sydney, Australia). + * + * Copyright 2023 Michael Farrell + * + * This will only read "standard" MIFARE DESFire-based Opal cards. Free travel + * cards (including School Opal cards, veteran, vision-impaired persons and + * TfNSW employees' cards) and single-trip tickets are MIFARE Ultralight C + * cards and not supported. + * + * Reference: https://github.com/metrodroid/metrodroid/wiki/Opal + * + * Note: The card values are all little-endian (like Flipper), but the above + * reference was originally written based on Java APIs, which are big-endian. + * This implementation presumes a little-endian system. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "nfc_supported_card.h" +#include "opal.h" + +#include +#include +#include + +#include + +static const uint8_t opal_aid[3] = {0x31, 0x45, 0x53}; +static const char* opal_modes[5] = + {"Rail / Metro", "Ferry / Light Rail", "Bus", "Unknown mode", "Manly Ferry"}; +static const char* opal_usages[14] = { + "New / Unused", + "Tap on: new journey", + "Tap on: transfer from same mode", + "Tap on: transfer from other mode", + "", // Manly Ferry: new journey + "", // Manly Ferry: transfer from ferry + "", // Manly Ferry: transfer from other + "Tap off: distance fare", + "Tap off: flat fare", + "Automated tap off: failed to tap off", + "Tap off: end of trip without start", + "Tap off: reversal", + "Tap on: rejected", + "Unknown usage", +}; + +// Opal file 0x7 structure. Assumes a little-endian CPU. +typedef struct __attribute__((__packed__)) { + uint32_t serial : 32; + uint8_t check_digit : 4; + bool blocked : 1; + uint16_t txn_number : 16; + int32_t balance : 21; + uint16_t days : 15; + uint16_t minutes : 11; + uint8_t mode : 3; + uint16_t usage : 4; + bool auto_topup : 1; + uint8_t weekly_journeys : 4; + uint16_t checksum : 16; +} OpalFile; + +static_assert(sizeof(OpalFile) == 16); + +// Converts an Opal timestamp to FuriHalRtcDateTime. +// +// Opal measures days since 1980-01-01 and minutes since midnight, and presumes +// all days are 1440 minutes. +void opal_date_time_to_furi(uint16_t days, uint16_t minutes, FuriHalRtcDateTime* out) { + if(!out) return; + uint16_t diy; + out->year = 1980; + out->month = 1; + // 1980-01-01 is a Tuesday + out->weekday = ((days + 1) % 7) + 1; + out->hour = minutes / 60; + out->minute = minutes % 60; + out->second = 0; + + // What year is it? + for(;;) { + diy = furi_hal_rtc_get_days_per_year(out->year); + if(days < diy) break; + days -= diy; + out->year++; + } + + // 1-index the day of the year + days++; + // What month is it? + bool is_leap = furi_hal_rtc_is_leap_year(out->year); + + for(;;) { + uint8_t dim = furi_hal_rtc_get_days_per_month(is_leap, out->month); + if(days <= dim) break; + days -= dim; + out->month++; + } + + out->day = days; +} + +bool opal_parser_parse(NfcDeviceOldDataOld* dev_data) { + if(dev_data->protocol != NfcDeviceOldProtocolMifareDesfire) { + return false; + } + + MifareDesfireApplication* app = mf_df_get_application(&dev_data->mf_df_data, &opal_aid); + if(app == NULL) { + return false; + } + MifareDesfireFile* f = mf_df_get_file(app, 0x07); + if(f == NULL || f->type != MifareDesfireFileTypeStandard || f->settings.data.size != 16 || + !f->contents) { + return false; + } + + OpalFile* o = (OpalFile*)f->contents; + + uint8_t serial2 = o->serial / 10000000; + uint16_t serial3 = (o->serial / 1000) % 10000; + uint16_t serial4 = (o->serial % 1000); + + if(o->check_digit > 9) { + return false; + } + + char* sign = ""; + if(o->balance < 0) { + // Negative balance. Make this a positive value again and record the + // sign separately, because then we can handle balances of -99..-1 + // cents, as the "dollars" division below would result in a positive + // zero value. + o->balance = abs(o->balance); //-V1081 + sign = "-"; + } + uint8_t cents = o->balance % 100; + int32_t dollars = o->balance / 100; + + FuriHalRtcDateTime timestamp; + opal_date_time_to_furi(o->days, o->minutes, ×tamp); + + if(o->mode >= 3) { + // 3..7 are "reserved", but we use 4 to indicate the Manly Ferry. + o->mode = 3; + } + + if(o->usage >= 4 && o->usage <= 6) { + // Usages 4..6 associated with the Manly Ferry, which correspond to + // usages 1..3 for other modes. + o->usage -= 3; + o->mode = 4; + } + + const char* mode_str = (o->mode <= 4 ? opal_modes[o->mode] : opal_modes[3]); //-V547 + const char* usage_str = (o->usage <= 12 ? opal_usages[o->usage] : opal_usages[13]); + + furi_string_printf( + dev_data->parsed_data, + "\e#Opal: $%s%ld.%02hu\n3085 22%02hhu %04hu %03hu%01hhu\n%s, %s\n", + sign, + dollars, + cents, + serial2, + serial3, + serial4, + o->check_digit, + mode_str, + usage_str); + FuriString* timestamp_str = furi_string_alloc(); + locale_format_date(timestamp_str, ×tamp, locale_get_date_format(), "-"); + furi_string_cat(dev_data->parsed_data, timestamp_str); + furi_string_cat_str(dev_data->parsed_data, " at "); + + locale_format_time(timestamp_str, ×tamp, locale_get_time_format(), false); + furi_string_cat(dev_data->parsed_data, timestamp_str); + + furi_string_free(timestamp_str); + furi_string_cat_printf( + dev_data->parsed_data, + "\nWeekly journeys: %hhu, Txn #%hu\n", + o->weekly_journeys, + o->txn_number); + + if(o->auto_topup) { + furi_string_cat_str(dev_data->parsed_data, "Auto-topup enabled\n"); + } + if(o->blocked) { + furi_string_cat_str(dev_data->parsed_data, "Card blocked\n"); + } + return true; +} diff --git a/lib/nfc/deprecated/parsers/opal.h b/lib/nfc/deprecated/parsers/opal.h new file mode 100644 index 000000000000..d48b37aa7647 --- /dev/null +++ b/lib/nfc/deprecated/parsers/opal.h @@ -0,0 +1,5 @@ +#pragma once + +#include "nfc_supported_card.h" + +bool opal_parser_parse(NfcDeviceOldDataOld* dev_data); diff --git a/lib/nfc/parsers/plantain_4k_parser.c b/lib/nfc/deprecated/parsers/plantain_4k_parser.c similarity index 89% rename from lib/nfc/parsers/plantain_4k_parser.c rename to lib/nfc/deprecated/parsers/plantain_4k_parser.c index 19da0b5ebb3c..252f2ef2a27c 100644 --- a/lib/nfc/parsers/plantain_4k_parser.c +++ b/lib/nfc/deprecated/parsers/plantain_4k_parser.c @@ -1,7 +1,7 @@ #include "nfc_supported_card.h" #include -#include +#include #include @@ -57,9 +57,9 @@ bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_ } uint8_t sector = 8; - uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + uint8_t block = mifare_classic_get_sector_trailer_block_num_by_sector(sector); FURI_LOG_D("Plant4K", "Verifying sector %d", sector); - if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) { + if(mifare_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) { FURI_LOG_D("Plant4K", "Sector %d verified", sector); return true; } @@ -71,9 +71,10 @@ bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx MfClassicReader reader = {}; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + reader.type = + mifare_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); for(size_t i = 0; i < COUNT_OF(plantain_keys_4k); i++) { - mf_classic_reader_add_sector( + mifare_classic_reader_add_sector( &reader, plantain_keys_4k[i].sector, plantain_keys_4k[i].key_a, @@ -81,18 +82,19 @@ bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx FURI_LOG_T("plant4k", "Added sector %d", plantain_keys_4k[i].sector); } for(int i = 0; i < 5; i++) { - if(mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40) { + if(mifare_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == + 40) { return true; } } return false; } -bool plantain_4k_parser_parse(NfcDeviceData* dev_data) { +bool plantain_4k_parser_parse(NfcDeviceOldDataOld* dev_data) { MfClassicData* data = &dev_data->mf_classic_data; // Verify key - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); + MfClassicSectorTrailer* sec_tr = mifare_classic_get_sector_trailer_by_sector(data, 8); uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); if(key != plantain_keys_4k[8].key_a) return false; diff --git a/lib/nfc/parsers/plantain_4k_parser.h b/lib/nfc/deprecated/parsers/plantain_4k_parser.h similarity index 77% rename from lib/nfc/parsers/plantain_4k_parser.h rename to lib/nfc/deprecated/parsers/plantain_4k_parser.h index 29998af15ea3..ef1308d151d8 100644 --- a/lib/nfc/parsers/plantain_4k_parser.h +++ b/lib/nfc/deprecated/parsers/plantain_4k_parser.h @@ -6,4 +6,4 @@ bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_ bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); -bool plantain_4k_parser_parse(NfcDeviceData* dev_data); +bool plantain_4k_parser_parse(NfcDeviceOldDataOld* dev_data); diff --git a/lib/nfc/parsers/plantain_parser.c b/lib/nfc/deprecated/parsers/plantain_parser.c similarity index 84% rename from lib/nfc/parsers/plantain_parser.c rename to lib/nfc/deprecated/parsers/plantain_parser.c index 2e4091dda10e..d2e091dde8b3 100644 --- a/lib/nfc/parsers/plantain_parser.c +++ b/lib/nfc/deprecated/parsers/plantain_parser.c @@ -1,7 +1,7 @@ #include "nfc_supported_card.h" #include -#include +#include #include @@ -32,9 +32,9 @@ bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) } uint8_t sector = 8; - uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + uint8_t block = mifare_classic_get_sector_trailer_block_num_by_sector(sector); FURI_LOG_D("Plant", "Verifying sector %d", sector); - if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) { + if(mifare_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) { FURI_LOG_D("Plant", "Sector %d verified", sector); return true; } @@ -46,13 +46,14 @@ bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { MfClassicReader reader = {}; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + reader.type = + mifare_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); for(size_t i = 0; i < COUNT_OF(plantain_keys); i++) { - mf_classic_reader_add_sector( + mifare_classic_reader_add_sector( &reader, plantain_keys[i].sector, plantain_keys[i].key_a, plantain_keys[i].key_b); } - return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; + return mifare_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; } uint8_t plantain_calculate_luhn(uint64_t number) { @@ -61,11 +62,11 @@ uint8_t plantain_calculate_luhn(uint64_t number) { return 0; } -bool plantain_parser_parse(NfcDeviceData* dev_data) { +bool plantain_parser_parse(NfcDeviceOldDataOld* dev_data) { MfClassicData* data = &dev_data->mf_classic_data; // Verify key - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); + MfClassicSectorTrailer* sec_tr = mifare_classic_get_sector_trailer_by_sector(data, 8); uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); if(key != plantain_keys[8].key_a) return false; diff --git a/lib/nfc/parsers/plantain_parser.h b/lib/nfc/deprecated/parsers/plantain_parser.h similarity index 81% rename from lib/nfc/parsers/plantain_parser.h rename to lib/nfc/deprecated/parsers/plantain_parser.h index 1af8c50657aa..a7347aa595c1 100644 --- a/lib/nfc/parsers/plantain_parser.h +++ b/lib/nfc/deprecated/parsers/plantain_parser.h @@ -6,6 +6,6 @@ bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); -bool plantain_parser_parse(NfcDeviceData* dev_data); +bool plantain_parser_parse(NfcDeviceOldDataOld* dev_data); uint8_t plantain_calculate_luhn(uint64_t number); diff --git a/lib/nfc/parsers/troika_4k_parser.c b/lib/nfc/deprecated/parsers/troika_4k_parser.c similarity index 87% rename from lib/nfc/parsers/troika_4k_parser.c rename to lib/nfc/deprecated/parsers/troika_4k_parser.c index d248feb17c35..00e69fcc3803 100644 --- a/lib/nfc/parsers/troika_4k_parser.c +++ b/lib/nfc/deprecated/parsers/troika_4k_parser.c @@ -1,7 +1,7 @@ #include "nfc_supported_card.h" #include -#include +#include static const MfClassicAuthContext troika_4k_keys[] = { {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, @@ -54,9 +54,9 @@ bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx } uint8_t sector = 11; - uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + uint8_t block = mifare_classic_get_sector_trailer_block_num_by_sector(sector); FURI_LOG_D("Troika", "Verifying sector %d", sector); - if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) { + if(mifare_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) { FURI_LOG_D("Troika", "Sector %d verified", sector); return true; } @@ -68,20 +68,21 @@ bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) MfClassicReader reader = {}; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + reader.type = + mifare_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); for(size_t i = 0; i < COUNT_OF(troika_4k_keys); i++) { - mf_classic_reader_add_sector( + mifare_classic_reader_add_sector( &reader, troika_4k_keys[i].sector, troika_4k_keys[i].key_a, troika_4k_keys[i].key_b); } - return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40; + return mifare_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40; } -bool troika_4k_parser_parse(NfcDeviceData* dev_data) { +bool troika_4k_parser_parse(NfcDeviceOldDataOld* dev_data) { MfClassicData* data = &dev_data->mf_classic_data; // Verify key - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); + MfClassicSectorTrailer* sec_tr = mifare_classic_get_sector_trailer_by_sector(data, 4); uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); if(key != troika_4k_keys[4].key_a) return false; diff --git a/lib/nfc/parsers/troika_4k_parser.h b/lib/nfc/deprecated/parsers/troika_4k_parser.h similarity index 78% rename from lib/nfc/parsers/troika_4k_parser.h rename to lib/nfc/deprecated/parsers/troika_4k_parser.h index c1d6f01d3aa9..36e814a7ad88 100644 --- a/lib/nfc/parsers/troika_4k_parser.h +++ b/lib/nfc/deprecated/parsers/troika_4k_parser.h @@ -6,4 +6,4 @@ bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); -bool troika_4k_parser_parse(NfcDeviceData* dev_data); +bool troika_4k_parser_parse(NfcDeviceOldDataOld* dev_data); diff --git a/lib/nfc/parsers/troika_parser.c b/lib/nfc/deprecated/parsers/troika_parser.c similarity index 82% rename from lib/nfc/parsers/troika_parser.c rename to lib/nfc/deprecated/parsers/troika_parser.c index 6d8ae98f3cbc..96b83778d564 100644 --- a/lib/nfc/parsers/troika_parser.c +++ b/lib/nfc/deprecated/parsers/troika_parser.c @@ -1,7 +1,7 @@ #include "nfc_supported_card.h" #include -#include +#include static const MfClassicAuthContext troika_keys[] = { {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, @@ -30,9 +30,9 @@ bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { } uint8_t sector = 11; - uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + uint8_t block = mifare_classic_get_sector_trailer_block_num_by_sector(sector); FURI_LOG_D("Troika", "Verifying sector %d", sector); - if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) { + if(mifare_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) { FURI_LOG_D("Troika", "Sector %d verified", sector); return true; } @@ -44,23 +44,24 @@ bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { MfClassicReader reader = {}; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + reader.type = + mifare_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); for(size_t i = 0; i < COUNT_OF(troika_keys); i++) { - mf_classic_reader_add_sector( + mifare_classic_reader_add_sector( &reader, troika_keys[i].sector, troika_keys[i].key_a, troika_keys[i].key_b); } - return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; + return mifare_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; } -bool troika_parser_parse(NfcDeviceData* dev_data) { +bool troika_parser_parse(NfcDeviceOldDataOld* dev_data) { MfClassicData* data = &dev_data->mf_classic_data; bool troika_parsed = false; do { // Verify key - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); + MfClassicSectorTrailer* sec_tr = mifare_classic_get_sector_trailer_by_sector(data, 8); uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); if(key != troika_keys[8].key_a) break; diff --git a/lib/nfc/parsers/troika_parser.h b/lib/nfc/deprecated/parsers/troika_parser.h similarity index 78% rename from lib/nfc/parsers/troika_parser.h rename to lib/nfc/deprecated/parsers/troika_parser.h index 2aae48d29a5a..f91e3b8f46d7 100644 --- a/lib/nfc/parsers/troika_parser.h +++ b/lib/nfc/deprecated/parsers/troika_parser.h @@ -6,4 +6,4 @@ bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); -bool troika_parser_parse(NfcDeviceData* dev_data); +bool troika_parser_parse(NfcDeviceOldDataOld* dev_data); diff --git a/lib/nfc/parsers/two_cities.c b/lib/nfc/deprecated/parsers/two_cities.c similarity index 90% rename from lib/nfc/parsers/two_cities.c rename to lib/nfc/deprecated/parsers/two_cities.c index d6d4279ddd0f..0491452f442c 100644 --- a/lib/nfc/parsers/two_cities.c +++ b/lib/nfc/deprecated/parsers/two_cities.c @@ -2,7 +2,7 @@ #include "plantain_parser.h" // For plantain-specific stuff #include -#include +#include #include @@ -58,9 +58,9 @@ bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_r } uint8_t sector = 4; - uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); + uint8_t block = mifare_classic_get_sector_trailer_block_num_by_sector(sector); FURI_LOG_D("2cities", "Verifying sector %d", sector); - if(mf_classic_authenticate(tx_rx, block, 0xe56ac127dd45, MfClassicKeyA)) { + if(mifare_classic_authenticate(tx_rx, block, 0xe56ac127dd45, MfClassicKeyA)) { FURI_LOG_D("2cities", "Sector %d verified", sector); return true; } @@ -72,9 +72,10 @@ bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) MfClassicReader reader = {}; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + reader.type = + mifare_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); for(size_t i = 0; i < COUNT_OF(two_cities_keys_4k); i++) { - mf_classic_reader_add_sector( + mifare_classic_reader_add_sector( &reader, two_cities_keys_4k[i].sector, two_cities_keys_4k[i].key_a, @@ -82,14 +83,14 @@ bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) FURI_LOG_T("2cities", "Added sector %d", two_cities_keys_4k[i].sector); } - return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40; + return mifare_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40; } -bool two_cities_parser_parse(NfcDeviceData* dev_data) { +bool two_cities_parser_parse(NfcDeviceOldDataOld* dev_data) { MfClassicData* data = &dev_data->mf_classic_data; // Verify key - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); + MfClassicSectorTrailer* sec_tr = mifare_classic_get_sector_trailer_by_sector(data, 4); uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); if(key != two_cities_keys_4k[4].key_a) return false; diff --git a/lib/nfc/parsers/two_cities.h b/lib/nfc/deprecated/parsers/two_cities.h similarity index 77% rename from lib/nfc/parsers/two_cities.h rename to lib/nfc/deprecated/parsers/two_cities.h index e735bea8e1a2..d7ea9cd249a8 100644 --- a/lib/nfc/parsers/two_cities.h +++ b/lib/nfc/deprecated/parsers/two_cities.h @@ -6,4 +6,4 @@ bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_r bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); -bool two_cities_parser_parse(NfcDeviceData* dev_data); +bool two_cities_parser_parse(NfcDeviceOldDataOld* dev_data); diff --git a/lib/nfc/protocols/crypto1.c b/lib/nfc/deprecated/protocols/crypto1.c similarity index 64% rename from lib/nfc/protocols/crypto1.c rename to lib/nfc/deprecated/protocols/crypto1.c index f59651cf45f6..2df762df49da 100644 --- a/lib/nfc/protocols/crypto1.c +++ b/lib/nfc/deprecated/protocols/crypto1.c @@ -1,5 +1,5 @@ #include "crypto1.h" -#include "nfc_util.h" +#include #include // Algorithm from https://github.com/RfidResearchGroup/proxmark3.git @@ -11,13 +11,13 @@ #define BEBIT(x, n) FURI_BIT(x, (n) ^ 24) -void crypto1_reset(Crypto1* crypto1) { +void old_crypto1_reset(Crypto1* crypto1) { furi_assert(crypto1); crypto1->even = 0; crypto1->odd = 0; } -void crypto1_init(Crypto1* crypto1, uint64_t key) { +void old_crypto1_init(Crypto1* crypto1, uint64_t key) { furi_assert(crypto1); crypto1->even = 0; crypto1->odd = 0; @@ -27,7 +27,7 @@ void crypto1_init(Crypto1* crypto1, uint64_t key) { } } -uint32_t crypto1_filter(uint32_t in) { +uint32_t old_crypto1_filter(uint32_t in) { uint32_t out = 0; out = 0xf22c0 >> (in & 0xf) & 16; out |= 0x6c9c0 >> (in >> 4 & 0xf) & 8; @@ -37,9 +37,9 @@ uint32_t crypto1_filter(uint32_t in) { return FURI_BIT(0xEC57E80A, out); } -uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) { +uint8_t old_crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) { furi_assert(crypto1); - uint8_t out = crypto1_filter(crypto1->odd); + uint8_t out = old_crypto1_filter(crypto1->odd); uint32_t feed = out & (!!is_encrypted); feed ^= !!in; feed ^= LF_POLY_ODD & crypto1->odd; @@ -50,32 +50,32 @@ uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) { return out; } -uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) { +uint8_t old_crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) { furi_assert(crypto1); uint8_t out = 0; for(uint8_t i = 0; i < 8; i++) { - out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i; + out |= old_crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i; } return out; } -uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) { +uint32_t old_crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) { furi_assert(crypto1); uint32_t out = 0; for(uint8_t i = 0; i < 32; i++) { - out |= crypto1_bit(crypto1, BEBIT(in, i), is_encrypted) << (24 ^ i); + out |= old_crypto1_bit(crypto1, BEBIT(in, i), is_encrypted) << (24 ^ i); } return out; } -uint32_t prng_successor(uint32_t x, uint32_t n) { +uint32_t old_prng_successor(uint32_t x, uint32_t n) { SWAPENDIAN(x); while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; return SWAPENDIAN(x); } -void crypto1_decrypt( +void old_crypto1_decrypt( Crypto1* crypto, uint8_t* encrypted_data, uint16_t encrypted_data_bits, @@ -86,19 +86,19 @@ void crypto1_decrypt( if(encrypted_data_bits < 8) { uint8_t decrypted_byte = 0; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3; + decrypted_byte |= (old_crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0; + decrypted_byte |= (old_crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1; + decrypted_byte |= (old_crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2; + decrypted_byte |= (old_crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3; decrypted_data[0] = decrypted_byte; } else { for(size_t i = 0; i < encrypted_data_bits / 8; i++) { - decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; + decrypted_data[i] = old_crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; } } } -void crypto1_encrypt( +void old_crypto1_encrypt( Crypto1* crypto, uint8_t* keystream, uint8_t* plain_data, @@ -113,15 +113,15 @@ void crypto1_encrypt( if(plain_data_bits < 8) { encrypted_data[0] = 0; for(size_t i = 0; i < plain_data_bits; i++) { - encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; + encrypted_data[0] |= (old_crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; } } else { memset(encrypted_parity, 0, plain_data_bits / 8 + 1); for(uint8_t i = 0; i < plain_data_bits / 8; i++) { - encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ + encrypted_data[i] = old_crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ plain_data[i]; encrypted_parity[i / 8] |= - (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01) + (((old_crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01) << (7 - (i & 0x0007))); } } diff --git a/lib/nfc/deprecated/protocols/crypto1.h b/lib/nfc/deprecated/protocols/crypto1.h new file mode 100644 index 000000000000..e152f9b38099 --- /dev/null +++ b/lib/nfc/deprecated/protocols/crypto1.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +typedef struct { + uint32_t odd; + uint32_t even; +} Crypto1; + +void old_crypto1_reset(Crypto1* crypto1); + +void old_crypto1_init(Crypto1* crypto1, uint64_t key); + +uint8_t old_crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted); + +uint8_t old_crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted); + +uint32_t old_crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted); + +uint32_t old_crypto1_filter(uint32_t in); + +uint32_t old_prng_successor(uint32_t x, uint32_t n); + +void old_crypto1_decrypt( + Crypto1* crypto, + uint8_t* encrypted_data, + uint16_t encrypted_data_bits, + uint8_t* decrypted_data); + +void old_crypto1_encrypt( + Crypto1* crypto, + uint8_t* keystream, + uint8_t* plain_data, + uint16_t plain_data_bits, + uint8_t* encrypted_data, + uint8_t* encrypted_parity); diff --git a/lib/nfc/protocols/emv.c b/lib/nfc/deprecated/protocols/emv.c similarity index 100% rename from lib/nfc/protocols/emv.c rename to lib/nfc/deprecated/protocols/emv.c diff --git a/lib/nfc/protocols/emv.h b/lib/nfc/deprecated/protocols/emv.h similarity index 100% rename from lib/nfc/protocols/emv.h rename to lib/nfc/deprecated/protocols/emv.h diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/deprecated/protocols/mifare_classic.c similarity index 76% rename from lib/nfc/protocols/mifare_classic.c rename to lib/nfc/deprecated/protocols/mifare_classic.c index d2d7467dce09..b728f2c4f550 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/deprecated/protocols/mifare_classic.c @@ -1,6 +1,6 @@ #include "mifare_classic.h" -#include "nfca.h" -#include "nfc_util.h" +#include +#include #include // Algorithm from https://github.com/RfidResearchGroup/proxmark3.git @@ -12,14 +12,14 @@ #define MF_CLASSIC_NACK_BUF_INVALID_CMD 0x4U #define MF_CLASSIC_AUTH_KEY_A_CMD 0x60U #define MF_CLASSIC_AUTH_KEY_B_CMD 0x61U -#define MF_CLASSIC_READ_BLOCK_CMD 0x30U -#define MF_CLASSIC_WRITE_BLOCK_CMD 0xA0U -#define MF_CLASSIC_TRANSFER_CMD 0xB0U +#define mifare_classic_read_block_CMD 0x30U +#define mifare_classic_write_block_CMD 0xA0U +#define mifare_classic_transfer_CMD 0xB0U #define MF_CLASSIC_DECREMENT_CMD 0xC0U #define MF_CLASSIC_INCREMENT_CMD 0xC1U #define MF_CLASSIC_RESTORE_CMD 0xC2U -const char* mf_classic_get_type_str(MfClassicType type) { +const char* mifare_classic_get_type_str(MfClassicType type) { if(type == MfClassicTypeMini) { return "MIFARE Mini 0.3K"; } else if(type == MfClassicType1k) { @@ -40,7 +40,7 @@ static uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) { } } -uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector) { +uint8_t mifare_classic_get_sector_trailer_block_num_by_sector(uint8_t sector) { furi_assert(sector < 40); if(sector < 32) { return sector * 4 + 3; @@ -49,7 +49,7 @@ uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector) { } } -uint8_t mf_classic_get_sector_by_block(uint8_t block) { +uint8_t mifare_classic_get_sector_by_block(uint8_t block) { if(block < 128) { return (block | 0x03) / 4; } else { @@ -62,7 +62,7 @@ static uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) { return sector < 32 ? 4 : 16; } -uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) { +uint8_t mifare_classic_get_sector_trailer_num_by_block(uint8_t block) { if(block < 128) { return block | 0x03; } else { @@ -70,18 +70,18 @@ uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) { } } -bool mf_classic_is_sector_trailer(uint8_t block) { - return block == mf_classic_get_sector_trailer_num_by_block(block); +bool mifare_classic_is_sector_trailer(uint8_t block) { + return block == mifare_classic_get_sector_trailer_num_by_block(block); } MfClassicSectorTrailer* - mf_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector) { + mifare_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector) { furi_assert(data); - uint8_t sec_tr_block_num = mf_classic_get_sector_trailer_block_num_by_sector(sector); + uint8_t sec_tr_block_num = mifare_classic_get_sector_trailer_block_num_by_sector(sector); return (MfClassicSectorTrailer*)data->block[sec_tr_block_num].value; } -uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { +uint8_t mifare_classic_get_total_sectors_num(MfClassicType type) { if(type == MfClassicTypeMini) { return MF_MINI_TOTAL_SECTORS_NUM; } else if(type == MfClassicType1k) { @@ -93,7 +93,7 @@ uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { } } -uint16_t mf_classic_get_total_block_num(MfClassicType type) { +uint16_t mifare_classic_get_total_block_num(MfClassicType type) { if(type == MfClassicTypeMini) { return 20; } else if(type == MfClassicType1k) { @@ -105,16 +105,19 @@ uint16_t mf_classic_get_total_block_num(MfClassicType type) { } } -bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num) { +bool mifare_classic_is_block_read(MfClassicData* data, uint8_t block_num) { furi_assert(data); return (FURI_BIT(data->block_read_mask[block_num / 32], block_num % 32) == 1); } -void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data) { +void mifare_classic_set_block_read( + MfClassicData* data, + uint8_t block_num, + MfClassicBlock* block_data) { furi_assert(data); - if(mf_classic_is_sector_trailer(block_num)) { + if(mifare_classic_is_sector_trailer(block_num)) { memcpy(&data->block[block_num].value[6], &block_data->value[6], 4); } else { memcpy(data->block[block_num].value, block_data->value, MF_CLASSIC_BLOCK_SIZE); @@ -122,25 +125,25 @@ void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassic FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32); } -bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num) { +bool mifare_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num) { furi_assert(data); uint8_t first_block = mf_classic_get_first_block_num_of_sector(sector_num); uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num); bool data_read = true; for(size_t i = first_block; i < first_block + total_blocks; i++) { - data_read &= mf_classic_is_block_read(data, i); + data_read &= mifare_classic_is_block_read(data, i); } return data_read; } -void mf_classic_set_sector_data_not_read(MfClassicData* data) { +void mifare_classic_set_sector_data_not_read(MfClassicData* data) { furi_assert(data); memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); } -bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) { +bool mifare_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) { furi_assert(data); bool key_found = false; @@ -153,7 +156,7 @@ bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicK return key_found; } -void mf_classic_set_key_found( +void mifare_classic_set_key_found( MfClassicData* data, uint8_t sector_num, MfClassicKey key_type, @@ -162,7 +165,7 @@ void mf_classic_set_key_found( uint8_t key_arr[6] = {}; MfClassicSectorTrailer* sec_trailer = - mf_classic_get_sector_trailer_by_sector(data, sector_num); + mifare_classic_get_sector_trailer_by_sector(data, sector_num); nfc_util_num2bytes(key, 6, key_arr); if(key_type == MfClassicKeyA) { memcpy(sec_trailer->key_a, key_arr, sizeof(sec_trailer->key_a)); @@ -173,7 +176,10 @@ void mf_classic_set_key_found( } } -void mf_classic_set_key_not_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) { +void mifare_classic_set_key_not_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKey key_type) { furi_assert(data); if(key_type == MfClassicKeyA) { @@ -183,18 +189,18 @@ void mf_classic_set_key_not_found(MfClassicData* data, uint8_t sector_num, MfCla } } -bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) { +bool mifare_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) { furi_assert(data); bool sector_read = false; do { - if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) break; - if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) break; + if(!mifare_classic_is_key_found(data, sector_num, MfClassicKeyA)) break; + if(!mifare_classic_is_key_found(data, sector_num, MfClassicKeyB)) break; uint8_t start_block = mf_classic_get_first_block_num_of_sector(sector_num); uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num); uint8_t block_read = true; for(size_t i = start_block; i < start_block + total_blocks; i++) { - block_read = mf_classic_is_block_read(data, i); + block_read = mifare_classic_is_block_read(data, i); if(!block_read) break; } sector_read = block_read; @@ -203,7 +209,7 @@ bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) { return sector_read; } -void mf_classic_get_read_sectors_and_keys( +void mifare_classic_get_read_sectors_and_keys( MfClassicData* data, uint8_t* sectors_read, uint8_t* keys_found) { @@ -213,19 +219,19 @@ void mf_classic_get_read_sectors_and_keys( *sectors_read = 0; *keys_found = 0; - uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + uint8_t sectors_total = mifare_classic_get_total_sectors_num(data->type); for(size_t i = 0; i < sectors_total; i++) { - if(mf_classic_is_key_found(data, i, MfClassicKeyA)) { + if(mifare_classic_is_key_found(data, i, MfClassicKeyA)) { *keys_found += 1; } - if(mf_classic_is_key_found(data, i, MfClassicKeyB)) { + if(mifare_classic_is_key_found(data, i, MfClassicKeyB)) { *keys_found += 1; } uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i); bool blocks_read = true; for(size_t j = first_block; j < first_block + total_blocks_in_sec; j++) { - blocks_read = mf_classic_is_block_read(data, j); + blocks_read = mifare_classic_is_block_read(data, j); if(!blocks_read) break; } if(blocks_read) { @@ -234,13 +240,13 @@ void mf_classic_get_read_sectors_and_keys( } } -bool mf_classic_is_card_read(MfClassicData* data) { +bool mifare_classic_is_card_read(MfClassicData* data) { furi_assert(data); - uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + uint8_t sectors_total = mifare_classic_get_total_sectors_num(data->type); uint8_t sectors_read = 0; uint8_t keys_found = 0; - mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); + mifare_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); bool card_read = (sectors_read == sectors_total) && (keys_found == sectors_total * 2); return card_read; @@ -283,13 +289,13 @@ bool mf_classic_is_allowed_access_sector_trailer( return true; } -bool mf_classic_is_allowed_access_data_block( +bool mifare_classic_is_allowed_access_data_block( MfClassicData* data, uint8_t block_num, MfClassicKey key, MfClassicAction action) { uint8_t* sector_trailer = - data->block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; + data->block[mifare_classic_get_sector_trailer_num_by_block(block_num)].value; if(block_num == 0 && action == MfClassicActionDataWrite) { return false; @@ -356,25 +362,26 @@ static bool mf_classic_is_allowed_access( uint8_t block_num, MfClassicKey key, MfClassicAction action) { - if(mf_classic_is_sector_trailer(block_num)) { + if(mifare_classic_is_sector_trailer(block_num)) { return mf_classic_is_allowed_access_sector_trailer( &emulator->data, block_num, key, action); } else { - return mf_classic_is_allowed_access_data_block(&emulator->data, block_num, key, action); + return mifare_classic_is_allowed_access_data_block( + &emulator->data, block_num, key, action); } } -bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num) { +bool mifare_classic_is_value_block(MfClassicData* data, uint8_t block_num) { // Check if key A can write, if it can, it's transport configuration, not data block - return !mf_classic_is_allowed_access_data_block( + return !mifare_classic_is_allowed_access_data_block( data, block_num, MfClassicKeyA, MfClassicActionDataWrite) && - (mf_classic_is_allowed_access_data_block( + (mifare_classic_is_allowed_access_data_block( data, block_num, MfClassicKeyB, MfClassicActionDataInc) || - mf_classic_is_allowed_access_data_block( + mifare_classic_is_allowed_access_data_block( data, block_num, MfClassicKeyB, MfClassicActionDataDec)); } -bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { +bool mifare_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { UNUSED(ATQA1); if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { return true; @@ -388,7 +395,7 @@ bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { } } -MfClassicType mf_classic_get_classic_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { +MfClassicType mifare_classic_get_classic_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { UNUSED(ATQA1); if((ATQA0 == 0x44 || ATQA0 == 0x04)) { if((SAK == 0x08 || SAK == 0x88)) { @@ -405,7 +412,7 @@ MfClassicType mf_classic_get_classic_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t return MfClassicType1k; } -void mf_classic_reader_add_sector( +void mifare_classic_reader_add_sector( MfClassicReader* reader, uint8_t sector, uint64_t key_a, @@ -422,7 +429,7 @@ void mf_classic_reader_add_sector( } } -bool mf_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* addr) { +bool mifare_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* addr) { uint32_t v = *(uint32_t*)&block[0]; uint32_t v_inv = *(uint32_t*)&block[4]; uint32_t v1 = *(uint32_t*)&block[8]; @@ -439,7 +446,7 @@ bool mf_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* ad return val_checks; } -void mf_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block) { +void mifare_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block) { uint32_t v_inv = ~((uint32_t)value); memcpy(block, &value, 4); //-V1086 @@ -452,7 +459,7 @@ void mf_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block) { block[15] = ~addr & 0xFF; } -void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector) { +void mifare_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector) { furi_assert(auth_ctx); auth_ctx->sector = sector; auth_ctx->key_a = MF_CLASSIC_NO_KEY; @@ -485,28 +492,29 @@ static bool mf_classic_auth( if(!furi_hal_nfc_tx_rx(tx_rx, 6)) break; uint32_t nt = (uint32_t)nfc_util_bytes2num(tx_rx->rx_data, 4); - crypto1_init(crypto, key); - crypto1_word(crypto, nt ^ cuid, 0); + old_crypto1_init(crypto, key); + old_crypto1_word(crypto, nt ^ cuid, 0); uint8_t nr[4] = {}; - nfc_util_num2bytes(prng_successor(DWT->CYCCNT, 32), 4, nr); + nfc_util_num2bytes(old_prng_successor(DWT->CYCCNT, 32), 4, nr); for(uint8_t i = 0; i < 4; i++) { - tx_rx->tx_data[i] = crypto1_byte(crypto, nr[i], 0) ^ nr[i]; + tx_rx->tx_data[i] = old_crypto1_byte(crypto, nr[i], 0) ^ nr[i]; tx_rx->tx_parity[0] |= - (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nr[i])) & 0x01) << (7 - i)); + (((old_crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nr[i])) & 0x01) + << (7 - i)); } - nt = prng_successor(nt, 32); + nt = old_prng_successor(nt, 32); for(uint8_t i = 4; i < 8; i++) { - nt = prng_successor(nt, 8); - tx_rx->tx_data[i] = crypto1_byte(crypto, 0x00, 0) ^ (nt & 0xff); + nt = old_prng_successor(nt, 8); + tx_rx->tx_data[i] = old_crypto1_byte(crypto, 0x00, 0) ^ (nt & 0xff); tx_rx->tx_parity[0] |= - (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt & 0xff)) & 0x01) + (((old_crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt & 0xff)) & 0x01) << (7 - i)); } tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; tx_rx->tx_bits = 8 * 8; if(!furi_hal_nfc_tx_rx(tx_rx, 6)) break; if(tx_rx->rx_bits == 32) { - crypto1_word(crypto, 0, 0); + old_crypto1_word(crypto, 0, 0); auth_success = true; } } while(false); @@ -514,7 +522,7 @@ static bool mf_classic_auth( return auth_success; } -bool mf_classic_authenticate( +bool mifare_classic_authenticate( FuriHalNfcTxRxContext* tx_rx, uint8_t block_num, uint64_t key, @@ -527,7 +535,7 @@ bool mf_classic_authenticate( return key_found; } -bool mf_classic_authenticate_skip_activate( +bool mifare_classic_authenticate_skip_activate( FuriHalNfcTxRxContext* tx_rx, uint8_t block_num, uint64_t key, @@ -543,7 +551,7 @@ bool mf_classic_authenticate_skip_activate( return key_found; } -bool mf_classic_auth_attempt( +bool mifare_classic_auth_attempt( FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, MfClassicAuthContext* auth_ctx, @@ -558,7 +566,7 @@ bool mf_classic_auth_attempt( // Try AUTH with key A if(mf_classic_auth( tx_rx, - mf_classic_get_sector_trailer_block_num_by_sector(auth_ctx->sector), + mifare_classic_get_sector_trailer_block_num_by_sector(auth_ctx->sector), key, MfClassicKeyA, crypto, @@ -577,7 +585,7 @@ bool mf_classic_auth_attempt( // Try AUTH with key B if(mf_classic_auth( tx_rx, - mf_classic_get_sector_trailer_block_num_by_sector(auth_ctx->sector), + mifare_classic_get_sector_trailer_block_num_by_sector(auth_ctx->sector), key, MfClassicKeyB, crypto, @@ -591,7 +599,7 @@ bool mf_classic_auth_attempt( return found_key; } -bool mf_classic_read_block( +bool mifare_classic_read_block( FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num, @@ -601,10 +609,10 @@ bool mf_classic_read_block( furi_assert(block); bool read_block_success = false; - uint8_t plain_cmd[4] = {MF_CLASSIC_READ_BLOCK_CMD, block_num, 0x00, 0x00}; + uint8_t plain_cmd[4] = {mifare_classic_read_block_CMD, block_num, 0x00, 0x00}; nfca_append_crc16(plain_cmd, 2); - crypto1_encrypt( + old_crypto1_encrypt( crypto, NULL, plain_cmd, sizeof(plain_cmd) * 8, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = sizeof(plain_cmd) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; @@ -612,7 +620,7 @@ bool mf_classic_read_block( if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 8 * (MF_CLASSIC_BLOCK_SIZE + 2)) { uint8_t block_received[MF_CLASSIC_BLOCK_SIZE + 2]; - crypto1_decrypt(crypto, tx_rx->rx_data, tx_rx->rx_bits, block_received); + old_crypto1_decrypt(crypto, tx_rx->rx_data, tx_rx->rx_bits, block_received); uint16_t crc_calc = nfca_get_crc16(block_received, MF_CLASSIC_BLOCK_SIZE); uint16_t crc_received = (block_received[MF_CLASSIC_BLOCK_SIZE + 1] << 8) | block_received[MF_CLASSIC_BLOCK_SIZE]; @@ -632,18 +640,18 @@ bool mf_classic_read_block( return read_block_success; } -void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num) { +void mifare_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num) { furi_assert(tx_rx); furi_assert(data); furi_hal_nfc_sleep(); - bool key_a_found = mf_classic_is_key_found(data, sec_num, MfClassicKeyA); - bool key_b_found = mf_classic_is_key_found(data, sec_num, MfClassicKeyB); + bool key_a_found = mifare_classic_is_key_found(data, sec_num, MfClassicKeyA); + bool key_b_found = mifare_classic_is_key_found(data, sec_num, MfClassicKeyB); uint8_t start_block = mf_classic_get_first_block_num_of_sector(sec_num); uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num); MfClassicBlock block_tmp = {}; uint64_t key = 0; - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sec_num); + MfClassicSectorTrailer* sec_tr = mifare_classic_get_sector_trailer_by_sector(data, sec_num); Crypto1 crypto = {}; uint8_t blocks_read = 0; @@ -652,26 +660,26 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u FURI_LOG_D(TAG, "Try to read blocks with key A"); key = nfc_util_bytes2num(sec_tr->key_a, sizeof(sec_tr->key_a)); if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyA, &crypto, false, 0)) { - mf_classic_set_key_not_found(data, sec_num, MfClassicKeyA); + mifare_classic_set_key_not_found(data, sec_num, MfClassicKeyA); FURI_LOG_D(TAG, "Key %dA not found in read", sec_num); break; } for(size_t i = start_block; i < start_block + total_blocks; i++) { - if(!mf_classic_is_block_read(data, i)) { - if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { - mf_classic_set_block_read(data, i, &block_tmp); + if(!mifare_classic_is_block_read(data, i)) { + if(mifare_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { + mifare_classic_set_block_read(data, i, &block_tmp); blocks_read++; } else if(i > start_block) { // Try to re-auth to read block in case prevous block was protected from read furi_hal_nfc_sleep(); if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyA, &crypto, false, 0)) { - mf_classic_set_key_not_found(data, sec_num, MfClassicKeyA); + mifare_classic_set_key_not_found(data, sec_num, MfClassicKeyA); FURI_LOG_D(TAG, "Key %dA not found in read", sec_num); break; } - if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { - mf_classic_set_block_read(data, i, &block_tmp); + if(mifare_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { + mifare_classic_set_block_read(data, i, &block_tmp); blocks_read++; } } @@ -690,26 +698,26 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u FURI_LOG_D(TAG, "Try to read blocks with key B"); key = nfc_util_bytes2num(sec_tr->key_b, sizeof(sec_tr->key_b)); if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto, false, 0)) { - mf_classic_set_key_not_found(data, sec_num, MfClassicKeyB); + mifare_classic_set_key_not_found(data, sec_num, MfClassicKeyB); FURI_LOG_D(TAG, "Key %dB not found in read", sec_num); break; } for(size_t i = start_block; i < start_block + total_blocks; i++) { - if(!mf_classic_is_block_read(data, i)) { - if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { - mf_classic_set_block_read(data, i, &block_tmp); + if(!mifare_classic_is_block_read(data, i)) { + if(mifare_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { + mifare_classic_set_block_read(data, i, &block_tmp); blocks_read++; } else if(i > start_block) { // Try to re-auth to read block in case prevous block was protected from read furi_hal_nfc_sleep(); if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyB, &crypto, false, 0)) { - mf_classic_set_key_not_found(data, sec_num, MfClassicKeyB); + mifare_classic_set_key_not_found(data, sec_num, MfClassicKeyB); FURI_LOG_D(TAG, "Key %dB not found in read", sec_num); break; } - if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { - mf_classic_set_block_read(data, i, &block_tmp); + if(mifare_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { + mifare_classic_set_block_read(data, i, &block_tmp); blocks_read++; } } @@ -721,7 +729,7 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u } while(false); } -static bool mf_classic_read_sector_with_reader( +static bool mifare_classic_read_sector_with_reader( FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, MfClassicSectorReader* sector_reader, @@ -763,12 +771,13 @@ static bool mf_classic_read_sector_with_reader( // Read blocks for(uint8_t i = 0; i < sector->total_blocks; i++) { - if(mf_classic_read_block(tx_rx, crypto, first_block + i, §or->block[i])) continue; + if(mifare_classic_read_block(tx_rx, crypto, first_block + i, §or->block[i])) + continue; if(i == 0) continue; // Try to auth to read next block in case previous is locked furi_hal_nfc_sleep(); if(!mf_classic_auth(tx_rx, first_block + i, key, key_type, crypto, false, 0)) continue; - mf_classic_read_block(tx_rx, crypto, first_block + i, §or->block[i]); + mifare_classic_read_block(tx_rx, crypto, first_block + i, §or->block[i]); } // Save sector keys in last block if(sector_reader->key_a != MF_CLASSIC_NO_KEY) { @@ -786,7 +795,7 @@ static bool mf_classic_read_sector_with_reader( return sector_read; } -uint8_t mf_classic_read_card( +uint8_t mifare_classic_read_card( FuriHalNfcTxRxContext* tx_rx, MfClassicReader* reader, MfClassicData* data) { @@ -800,22 +809,22 @@ uint8_t mf_classic_read_card( data->key_b_mask = 0; MfClassicSector temp_sector = {}; for(uint8_t i = 0; i < reader->sectors_to_read; i++) { - if(mf_classic_read_sector_with_reader( + if(mifare_classic_read_sector_with_reader( tx_rx, &reader->crypto, &reader->sector_reader[i], &temp_sector)) { uint8_t first_block = mf_classic_get_first_block_num_of_sector(reader->sector_reader[i].sector_num); for(uint8_t j = 0; j < temp_sector.total_blocks; j++) { - mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]); + mifare_classic_set_block_read(data, first_block + j, &temp_sector.block[j]); } if(reader->sector_reader[i].key_a != MF_CLASSIC_NO_KEY) { - mf_classic_set_key_found( + mifare_classic_set_key_found( data, reader->sector_reader[i].sector_num, MfClassicKeyA, reader->sector_reader[i].key_a); } if(reader->sector_reader[i].key_b != MF_CLASSIC_NO_KEY) { - mf_classic_set_key_found( + mifare_classic_set_key_found( data, reader->sector_reader[i].sector_num, MfClassicKeyB, @@ -828,24 +837,27 @@ uint8_t mf_classic_read_card( return sectors_read; } -uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data) { +uint8_t mifare_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data) { furi_assert(tx_rx); furi_assert(data); - uint8_t total_sectors = mf_classic_get_total_sectors_num(data->type); + uint8_t total_sectors = mifare_classic_get_total_sectors_num(data->type); for(size_t i = 0; i < total_sectors; i++) { - mf_classic_read_sector(tx_rx, data, i); + mifare_classic_read_sector(tx_rx, data, i); } uint8_t sectors_read = 0; uint8_t keys_found = 0; - mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); + mifare_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); FURI_LOG_D(TAG, "Read %d sectors and %d keys", sectors_read, keys_found); return sectors_read; } -bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) { +bool mifare_classic_emulator( + MfClassicEmulator* emulator, + FuriHalNfcTxRxContext* tx_rx, + bool is_reader_analyzer) { furi_assert(emulator); furi_assert(tx_rx); bool command_processed = false; @@ -859,7 +871,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Read command while(!command_processed) { //-V654 if(!is_encrypted) { - crypto1_reset(&emulator->crypto); + old_crypto1_reset(&emulator->crypto); memcpy(plain_data, tx_rx->rx_data, tx_rx->rx_bits / 8); } else { if(!furi_hal_nfc_tx_rx(tx_rx, 300)) { @@ -870,12 +882,12 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->rx_bits); break; } - crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + old_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); } // After increment, decrement or restore the only allowed command is transfer uint8_t cmd = plain_data[0]; - if(transfer_buf_valid && cmd != MF_CLASSIC_TRANSFER_CMD) { + if(transfer_buf_valid && cmd != mifare_classic_transfer_CMD) { break; } @@ -888,32 +900,48 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD || cmd == MF_CLASSIC_AUTH_KEY_B_CMD) { uint8_t block = plain_data[1]; uint64_t key = 0; - uint8_t sector_trailer_block = mf_classic_get_sector_trailer_num_by_block(block); + uint8_t sector_trailer_block = mifare_classic_get_sector_trailer_num_by_block(block); MfClassicSectorTrailer* sector_trailer = (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD) { - key = nfc_util_bytes2num(sector_trailer->key_a, 6); - access_key = MfClassicKeyA; + if(mifare_classic_is_key_found( + &emulator->data, mifare_classic_get_sector_by_block(block), MfClassicKeyA) || + is_reader_analyzer) { + key = nfc_util_bytes2num(sector_trailer->key_a, 6); + access_key = MfClassicKeyA; + } else { + FURI_LOG_D(TAG, "Key not known"); + command_processed = true; + break; + } } else { - key = nfc_util_bytes2num(sector_trailer->key_b, 6); - access_key = MfClassicKeyB; + if(mifare_classic_is_key_found( + &emulator->data, mifare_classic_get_sector_by_block(block), MfClassicKeyB) || + is_reader_analyzer) { + key = nfc_util_bytes2num(sector_trailer->key_b, 6); + access_key = MfClassicKeyB; + } else { + FURI_LOG_D(TAG, "Key not known"); + command_processed = true; + break; + } } - uint32_t nonce = prng_successor(DWT->CYCCNT, 32) ^ 0xAA; + uint32_t nonce = old_prng_successor(DWT->CYCCNT, 32) ^ 0xAA; uint8_t nt[4]; uint8_t nt_keystream[4]; nfc_util_num2bytes(nonce, 4, nt); nfc_util_num2bytes(nonce ^ emulator->cuid, 4, nt_keystream); - crypto1_init(&emulator->crypto, key); + old_crypto1_init(&emulator->crypto, key); if(!is_encrypted) { - crypto1_word(&emulator->crypto, emulator->cuid ^ nonce, 0); + old_crypto1_word(&emulator->crypto, emulator->cuid ^ nonce, 0); memcpy(tx_rx->tx_data, nt, sizeof(nt)); tx_rx->tx_parity[0] = 0; nfc_util_odd_parity(tx_rx->tx_data, tx_rx->tx_parity, sizeof(nt)); tx_rx->tx_bits = sizeof(nt) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; } else { - crypto1_encrypt( + old_crypto1_encrypt( &emulator->crypto, nt_keystream, nt, @@ -938,19 +966,20 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint32_t nr = nfc_util_bytes2num(tx_rx->rx_data, 4); uint32_t ar = nfc_util_bytes2num(&tx_rx->rx_data[4], 4); - crypto1_word(&emulator->crypto, nr, 1); - uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0); - if(cardRr != prng_successor(nonce, 64)) { - FURI_LOG_T(TAG, "Wrong AUTH! %08lX != %08lX", cardRr, prng_successor(nonce, 64)); + old_crypto1_word(&emulator->crypto, nr, 1); + uint32_t cardRr = ar ^ old_crypto1_word(&emulator->crypto, 0, 0); + if(cardRr != old_prng_successor(nonce, 64)) { + FURI_LOG_T( + TAG, "Wrong AUTH! %08lX != %08lX", cardRr, old_prng_successor(nonce, 64)); // Don't send NACK, as the tag doesn't send it command_processed = true; break; } - uint32_t ans = prng_successor(nonce, 96); + uint32_t ans = old_prng_successor(nonce, 96); uint8_t response[4] = {}; nfc_util_num2bytes(ans, 4, response); - crypto1_encrypt( + old_crypto1_encrypt( &emulator->crypto, NULL, response, @@ -969,11 +998,11 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ break; } - if(cmd == MF_CLASSIC_READ_BLOCK_CMD) { + if(cmd == mifare_classic_read_block_CMD) { uint8_t block = plain_data[1]; uint8_t block_data[MF_CLASSIC_BLOCK_SIZE + 2] = {}; memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); - if(mf_classic_is_sector_trailer(block)) { + if(mifare_classic_is_sector_trailer(block)) { if(!mf_classic_is_allowed_access( emulator, block, access_key, MfClassicActionKeyARead)) { memset(block_data, 0, 6); //-V1086 @@ -990,7 +1019,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ emulator, block, access_key, MfClassicActionDataRead)) { // Send NACK uint8_t nack = 0x04; - crypto1_encrypt( + old_crypto1_encrypt( &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; @@ -999,7 +1028,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } nfca_append_crc16(block_data, 16); - crypto1_encrypt( + old_crypto1_encrypt( &emulator->crypto, NULL, block_data, @@ -1008,24 +1037,25 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->tx_parity); tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - } else if(cmd == MF_CLASSIC_WRITE_BLOCK_CMD) { + } else if(cmd == mifare_classic_write_block_CMD) { uint8_t block = plain_data[1]; - if(block > mf_classic_get_total_block_num(emulator->data.type)) { + if(block > mifare_classic_get_total_block_num(emulator->data.type)) { break; } // Send ACK uint8_t ack = MF_CLASSIC_ACK_CMD; - crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + old_crypto1_encrypt( + &emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; if(tx_rx->rx_bits != (MF_CLASSIC_BLOCK_SIZE + 2) * 8) break; - crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + old_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); uint8_t block_data[MF_CLASSIC_BLOCK_SIZE] = {}; memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); - if(mf_classic_is_sector_trailer(block)) { + if(mifare_classic_is_sector_trailer(block)) { if(mf_classic_is_allowed_access( emulator, block, access_key, MfClassicActionKeyAWrite)) { memcpy(block_data, plain_data, 6); //-V1086 @@ -1052,7 +1082,8 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } // Send ACK ack = MF_CLASSIC_ACK_CMD; - crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + old_crypto1_encrypt( + &emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; } else if( @@ -1060,7 +1091,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ cmd == MF_CLASSIC_RESTORE_CMD) { uint8_t block = plain_data[1]; - if(block > mf_classic_get_total_block_num(emulator->data.type)) { + if(block > mifare_classic_get_total_block_num(emulator->data.type)) { break; } @@ -1074,20 +1105,22 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ int32_t prev_value; uint8_t addr; - if(!mf_classic_block_to_value(emulator->data.block[block].value, &prev_value, &addr)) { + if(!mifare_classic_block_to_value( + emulator->data.block[block].value, &prev_value, &addr)) { break; } // Send ACK uint8_t ack = MF_CLASSIC_ACK_CMD; - crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + old_crypto1_encrypt( + &emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; if(tx_rx->rx_bits != (sizeof(int32_t) + 2) * 8) break; - crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + old_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); int32_t value = *(int32_t*)&plain_data[0]; if(value < 0) { value = -value; @@ -1098,11 +1131,11 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ value = 0; } - mf_classic_value_to_block(prev_value + value, addr, transfer_buf); + mifare_classic_value_to_block(prev_value + value, addr, transfer_buf); transfer_buf_valid = true; // Commands do not ACK tx_rx->tx_bits = 0; - } else if(cmd == MF_CLASSIC_TRANSFER_CMD) { + } else if(cmd == mifare_classic_transfer_CMD) { uint8_t block = plain_data[1]; if(!mf_classic_is_allowed_access(emulator, block, access_key, MfClassicActionDataDec)) { break; @@ -1115,7 +1148,8 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ transfer_buf_valid = false; uint8_t ack = MF_CLASSIC_ACK_CMD; - crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + old_crypto1_encrypt( + &emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; } else { @@ -1129,7 +1163,8 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint8_t nack = transfer_buf_valid ? MF_CLASSIC_NACK_BUF_VALID_CMD : MF_CLASSIC_NACK_BUF_INVALID_CMD; if(is_encrypted) { - crypto1_encrypt(&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); + old_crypto1_encrypt( + &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); } else { tx_rx->tx_data[0] = nack; } @@ -1141,14 +1176,14 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ return true; } -void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto) { +void mifare_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto) { furi_assert(tx_rx); uint8_t plain_data[4] = {0x50, 0x00, 0x00, 0x00}; nfca_append_crc16(plain_data, 2); if(crypto) { - crypto1_encrypt( + old_crypto1_encrypt( crypto, NULL, plain_data, sizeof(plain_data) * 8, tx_rx->tx_data, tx_rx->tx_parity); } else { memcpy(tx_rx->tx_data, plain_data, sizeof(plain_data)); @@ -1160,7 +1195,7 @@ void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto) { furi_hal_nfc_tx_rx(tx_rx, 50); } -bool mf_classic_write_block( +bool mifare_classic_write_block( FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num, @@ -1175,16 +1210,16 @@ bool mf_classic_write_block( do { // Send write command - plain_data[0] = MF_CLASSIC_WRITE_BLOCK_CMD; + plain_data[0] = mifare_classic_write_block_CMD; plain_data[1] = block_num; nfca_append_crc16(plain_data, 2); - crypto1_encrypt(crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); + old_crypto1_encrypt(crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = 4 * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 4) { - crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + old_crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); if(resp != 0x0A) { FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp); break; @@ -1201,7 +1236,7 @@ bool mf_classic_write_block( // Send data memcpy(plain_data, src_block->value, MF_CLASSIC_BLOCK_SIZE); nfca_append_crc16(plain_data, MF_CLASSIC_BLOCK_SIZE); - crypto1_encrypt( + old_crypto1_encrypt( crypto, NULL, plain_data, @@ -1212,7 +1247,7 @@ bool mf_classic_write_block( tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 4) { - crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + old_crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); if(resp != MF_CLASSIC_ACK_CMD) { FURI_LOG_D(TAG, "NACK received on sending data"); break; @@ -1232,7 +1267,7 @@ bool mf_classic_write_block( return write_success; } -bool mf_classic_auth_write_block( +bool mifare_classic_auth_write_block( FuriHalNfcTxRxContext* tx_rx, MfClassicBlock* src_block, uint8_t block_num, @@ -1251,29 +1286,29 @@ bool mf_classic_auth_write_block( break; } - if(!mf_classic_write_block(tx_rx, &crypto, block_num, src_block)) { + if(!mifare_classic_write_block(tx_rx, &crypto, block_num, src_block)) { FURI_LOG_D(TAG, "Write fail"); break; } write_success = true; - mf_classic_halt(tx_rx, &crypto); + mifare_classic_halt(tx_rx, &crypto); } while(false); return write_success; } -bool mf_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num) { +bool mifare_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num) { furi_assert(tx_rx); furi_assert(crypto); // Send transfer command - uint8_t plain_data[4] = {MF_CLASSIC_TRANSFER_CMD, block_num, 0, 0}; + uint8_t plain_data[4] = {mifare_classic_transfer_CMD, block_num, 0, 0}; uint8_t resp = 0; bool transfer_success = false; nfca_append_crc16(plain_data, 2); - crypto1_encrypt( + old_crypto1_encrypt( crypto, NULL, plain_data, sizeof(plain_data) * 8, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = sizeof(plain_data) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; @@ -1281,7 +1316,7 @@ bool mf_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t do { if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 4) { - crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + old_crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); if(resp != 0x0A) { FURI_LOG_D(TAG, "NACK received on transfer cmd: %02X", resp); break; @@ -1301,7 +1336,7 @@ bool mf_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t return transfer_success; } -bool mf_classic_value_cmd( +bool mifare_classic_value_cmd( FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num, @@ -1323,13 +1358,13 @@ bool mf_classic_value_cmd( plain_data[0] = cmd; plain_data[1] = block_num; nfca_append_crc16(plain_data, 2); - crypto1_encrypt(crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); + old_crypto1_encrypt(crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = 4 * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 4) { - crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + old_crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); if(resp != 0x0A) { FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp); break; @@ -1346,14 +1381,14 @@ bool mf_classic_value_cmd( // Send data memcpy(plain_data, &d_value, sizeof(d_value)); nfca_append_crc16(plain_data, sizeof(d_value)); - crypto1_encrypt( + old_crypto1_encrypt( crypto, NULL, plain_data, (sizeof(d_value) + 2) * 8, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = (sizeof(d_value) + 2) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; // inc, dec, restore do not ACK, but they do NACK if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 4) { - crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + old_crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); if(resp != 0x0A) { FURI_LOG_D(TAG, "NACK received on transfer cmd: %02X", resp); break; @@ -1371,7 +1406,7 @@ bool mf_classic_value_cmd( return success; } -bool mf_classic_value_cmd_full( +bool mifare_classic_value_cmd_full( FuriHalNfcTxRxContext* tx_rx, MfClassicBlock* src_block, uint8_t block_num, @@ -1400,12 +1435,12 @@ bool mf_classic_value_cmd_full( FURI_LOG_D(TAG, "Value cmd auth fail"); break; } - if(!mf_classic_value_cmd(tx_rx, &crypto, block_num, cmd, d_value)) { + if(!mifare_classic_value_cmd(tx_rx, &crypto, block_num, cmd, d_value)) { FURI_LOG_D(TAG, "Value cmd inc/dec/res fail"); break; } - if(!mf_classic_transfer(tx_rx, &crypto, block_num)) { + if(!mifare_classic_transfer(tx_rx, &crypto, block_num)) { FURI_LOG_D(TAG, "Value cmd transfer fail"); break; } @@ -1413,13 +1448,13 @@ bool mf_classic_value_cmd_full( success = true; // Send Halt - mf_classic_halt(tx_rx, &crypto); + mifare_classic_halt(tx_rx, &crypto); } while(false); return success; } -bool mf_classic_write_sector( +bool mifare_classic_write_sector( FuriHalNfcTxRxContext* tx_rx, MfClassicData* dest_data, MfClassicData* src_data, @@ -1430,29 +1465,30 @@ bool mf_classic_write_sector( uint8_t first_block = mf_classic_get_first_block_num_of_sector(sec_num); uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num); - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(dest_data, sec_num); - bool key_a_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyA); - bool key_b_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyB); + MfClassicSectorTrailer* sec_tr = + mifare_classic_get_sector_trailer_by_sector(dest_data, sec_num); + bool key_a_found = mifare_classic_is_key_found(dest_data, sec_num, MfClassicKeyA); + bool key_b_found = mifare_classic_is_key_found(dest_data, sec_num, MfClassicKeyB); bool write_success = true; for(size_t i = first_block; i < first_block + total_blocks; i++) { // Compare blocks if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE) != 0) { - if(mf_classic_is_value_block(dest_data, i)) { - bool key_a_inc_allowed = mf_classic_is_allowed_access_data_block( + if(mifare_classic_is_value_block(dest_data, i)) { + bool key_a_inc_allowed = mifare_classic_is_allowed_access_data_block( dest_data, i, MfClassicKeyA, MfClassicActionDataInc); - bool key_b_inc_allowed = mf_classic_is_allowed_access_data_block( + bool key_b_inc_allowed = mifare_classic_is_allowed_access_data_block( dest_data, i, MfClassicKeyB, MfClassicActionDataInc); - bool key_a_dec_allowed = mf_classic_is_allowed_access_data_block( + bool key_a_dec_allowed = mifare_classic_is_allowed_access_data_block( dest_data, i, MfClassicKeyA, MfClassicActionDataDec); - bool key_b_dec_allowed = mf_classic_is_allowed_access_data_block( + bool key_b_dec_allowed = mifare_classic_is_allowed_access_data_block( dest_data, i, MfClassicKeyB, MfClassicActionDataDec); int32_t src_value, dst_value; - mf_classic_block_to_value(src_data->block[i].value, &src_value, NULL); - mf_classic_block_to_value(dest_data->block[i].value, &dst_value, NULL); + mifare_classic_block_to_value(src_data->block[i].value, &src_value, NULL); + mifare_classic_block_to_value(dest_data->block[i].value, &dst_value, NULL); int32_t diff = src_value - dst_value; @@ -1460,7 +1496,7 @@ bool mf_classic_write_sector( if(key_a_found && key_a_inc_allowed) { FURI_LOG_I(TAG, "Incrementing block %d with key A by %ld", i, diff); uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); - if(!mf_classic_value_cmd_full( + if(!mifare_classic_value_cmd_full( tx_rx, &src_data->block[i], i, MfClassicKeyA, key, diff)) { FURI_LOG_E(TAG, "Failed to increment block %d", i); write_success = false; @@ -1469,7 +1505,7 @@ bool mf_classic_write_sector( } else if(key_b_found && key_b_inc_allowed) { FURI_LOG_I(TAG, "Incrementing block %d with key B by %ld", i, diff); uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); - if(!mf_classic_value_cmd_full( + if(!mifare_classic_value_cmd_full( tx_rx, &src_data->block[i], i, MfClassicKeyB, key, diff)) { FURI_LOG_E(TAG, "Failed to increment block %d", i); write_success = false; @@ -1482,7 +1518,7 @@ bool mf_classic_write_sector( if(key_a_found && key_a_dec_allowed) { FURI_LOG_I(TAG, "Decrementing block %d with key A by %ld", i, -diff); uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); - if(!mf_classic_value_cmd_full( + if(!mifare_classic_value_cmd_full( tx_rx, &src_data->block[i], i, MfClassicKeyA, key, diff)) { FURI_LOG_E(TAG, "Failed to decrement block %d", i); write_success = false; @@ -1491,7 +1527,7 @@ bool mf_classic_write_sector( } else if(key_b_found && key_b_dec_allowed) { FURI_LOG_I(TAG, "Decrementing block %d with key B by %ld", i, diff); uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); - if(!mf_classic_value_cmd_full( + if(!mifare_classic_value_cmd_full( tx_rx, &src_data->block[i], i, MfClassicKeyB, key, diff)) { FURI_LOG_E(TAG, "Failed to decrement block %d", i); write_success = false; @@ -1504,15 +1540,15 @@ bool mf_classic_write_sector( FURI_LOG_E(TAG, "Value block %d address changed, cannot write it", i); } } else { - bool key_a_write_allowed = mf_classic_is_allowed_access_data_block( + bool key_a_write_allowed = mifare_classic_is_allowed_access_data_block( dest_data, i, MfClassicKeyA, MfClassicActionDataWrite); - bool key_b_write_allowed = mf_classic_is_allowed_access_data_block( + bool key_b_write_allowed = mifare_classic_is_allowed_access_data_block( dest_data, i, MfClassicKeyB, MfClassicActionDataWrite); if(key_a_found && key_a_write_allowed) { FURI_LOG_I(TAG, "Writing block %d with key A", i); uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); - if(!mf_classic_auth_write_block( + if(!mifare_classic_auth_write_block( tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) { FURI_LOG_E(TAG, "Failed to write block %d", i); write_success = false; @@ -1521,7 +1557,7 @@ bool mf_classic_write_sector( } else if(key_b_found && key_b_write_allowed) { FURI_LOG_I(TAG, "Writing block %d with key A", i); uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); - if(!mf_classic_auth_write_block( + if(!mifare_classic_auth_write_block( tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) { FURI_LOG_E(TAG, "Failed to write block %d", i); write_success = false; diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/deprecated/protocols/mifare_classic.h similarity index 59% rename from lib/nfc/protocols/mifare_classic.h rename to lib/nfc/deprecated/protocols/mifare_classic.h index c03350f2e938..c02dc1c05603 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/deprecated/protocols/mifare_classic.h @@ -92,21 +92,21 @@ typedef struct { bool data_changed; } MfClassicEmulator; -const char* mf_classic_get_type_str(MfClassicType type); +const char* mifare_classic_get_type_str(MfClassicType type); -bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); +bool mifare_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); -MfClassicType mf_classic_get_classic_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); +MfClassicType mifare_classic_get_classic_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); -uint8_t mf_classic_get_total_sectors_num(MfClassicType type); +uint8_t mifare_classic_get_total_sectors_num(MfClassicType type); -uint16_t mf_classic_get_total_block_num(MfClassicType type); +uint16_t mifare_classic_get_total_block_num(MfClassicType type); -uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector); +uint8_t mifare_classic_get_sector_trailer_block_num_by_sector(uint8_t sector); -bool mf_classic_is_sector_trailer(uint8_t block); +bool mifare_classic_is_sector_trailer(uint8_t block); -uint8_t mf_classic_get_sector_by_block(uint8_t block); +uint8_t mifare_classic_get_sector_by_block(uint8_t block); bool mf_classic_is_allowed_access_sector_trailer( MfClassicData* data, @@ -114,57 +114,63 @@ bool mf_classic_is_allowed_access_sector_trailer( MfClassicKey key, MfClassicAction action); -bool mf_classic_is_allowed_access_data_block( +bool mifare_classic_is_allowed_access_data_block( MfClassicData* data, uint8_t block_num, MfClassicKey key, MfClassicAction action); -bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num); +bool mifare_classic_is_value_block(MfClassicData* data, uint8_t block_num); -bool mf_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* addr); +bool mifare_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* addr); -void mf_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block); +void mifare_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block); -bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); +bool mifare_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); -void mf_classic_set_key_found( +void mifare_classic_set_key_found( MfClassicData* data, uint8_t sector_num, MfClassicKey key_type, uint64_t key); -void mf_classic_set_key_not_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); +void mifare_classic_set_key_not_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKey key_type); -bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num); +bool mifare_classic_is_block_read(MfClassicData* data, uint8_t block_num); -void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data); +void mifare_classic_set_block_read( + MfClassicData* data, + uint8_t block_num, + MfClassicBlock* block_data); -bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num); +bool mifare_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num); -void mf_classic_set_sector_data_not_read(MfClassicData* data); +void mifare_classic_set_sector_data_not_read(MfClassicData* data); -bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num); +bool mifare_classic_is_sector_read(MfClassicData* data, uint8_t sector_num); -bool mf_classic_is_card_read(MfClassicData* data); +bool mifare_classic_is_card_read(MfClassicData* data); -void mf_classic_get_read_sectors_and_keys( +void mifare_classic_get_read_sectors_and_keys( MfClassicData* data, uint8_t* sectors_read, uint8_t* keys_found); MfClassicSectorTrailer* - mf_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector); + mifare_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector); -void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector); +void mifare_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector); -bool mf_classic_authenticate( +bool mifare_classic_authenticate( FuriHalNfcTxRxContext* tx_rx, uint8_t block_num, uint64_t key, MfClassicKey key_type); -bool mf_classic_authenticate_skip_activate( +bool mifare_classic_authenticate_skip_activate( FuriHalNfcTxRxContext* tx_rx, uint8_t block_num, uint64_t key, @@ -172,60 +178,63 @@ bool mf_classic_authenticate_skip_activate( bool skip_activate, uint32_t cuid); -bool mf_classic_auth_attempt( +bool mifare_classic_auth_attempt( FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, MfClassicAuthContext* auth_ctx, uint64_t key); -void mf_classic_reader_add_sector( +void mifare_classic_reader_add_sector( MfClassicReader* reader, uint8_t sector, uint64_t key_a, uint64_t key_b); -bool mf_classic_read_block( +bool mifare_classic_read_block( FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num, MfClassicBlock* block); -void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num); +void mifare_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num); -uint8_t mf_classic_read_card( +uint8_t mifare_classic_read_card( FuriHalNfcTxRxContext* tx_rx, MfClassicReader* reader, MfClassicData* data); -uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data); +uint8_t mifare_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data); -bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); +bool mifare_classic_emulator( + MfClassicEmulator* emulator, + FuriHalNfcTxRxContext* tx_rx, + bool is_reader_analyzer); -void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto); +void mifare_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto); -bool mf_classic_write_block( +bool mifare_classic_write_block( FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num, MfClassicBlock* src_block); -bool mf_classic_auth_write_block( +bool mifare_classic_auth_write_block( FuriHalNfcTxRxContext* tx_rx, MfClassicBlock* src_block, uint8_t block_num, MfClassicKey key_type, uint64_t key); -bool mf_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num); +bool mifare_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num); -bool mf_classic_value_cmd( +bool mifare_classic_value_cmd( FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num, uint8_t cmd, int32_t d_value); -bool mf_classic_value_cmd_full( +bool mifare_classic_value_cmd_full( FuriHalNfcTxRxContext* tx_rx, MfClassicBlock* src_block, uint8_t block_num, @@ -233,7 +242,7 @@ bool mf_classic_value_cmd_full( uint64_t key, int32_t d_value); -bool mf_classic_write_sector( +bool mifare_classic_write_sector( FuriHalNfcTxRxContext* tx_rx, MfClassicData* dest_data, MfClassicData* src_data, diff --git a/lib/nfc/protocols/mifare_common.c b/lib/nfc/deprecated/protocols/mifare_common.c similarity index 100% rename from lib/nfc/protocols/mifare_common.c rename to lib/nfc/deprecated/protocols/mifare_common.c diff --git a/lib/nfc/protocols/mifare_common.h b/lib/nfc/deprecated/protocols/mifare_common.h similarity index 100% rename from lib/nfc/protocols/mifare_common.h rename to lib/nfc/deprecated/protocols/mifare_common.h diff --git a/lib/nfc/protocols/mifare_desfire.c b/lib/nfc/deprecated/protocols/mifare_desfire.c similarity index 97% rename from lib/nfc/protocols/mifare_desfire.c rename to lib/nfc/deprecated/protocols/mifare_desfire.c index 23308ae95e34..e0ead737f87d 100644 --- a/lib/nfc/protocols/mifare_desfire.c +++ b/lib/nfc/deprecated/protocols/mifare_desfire.c @@ -42,6 +42,30 @@ void mf_df_clear(MifareDesfireData* data) { data->app_head = NULL; } +MifareDesfireApplication* mf_df_get_application(MifareDesfireData* data, const uint8_t (*aid)[3]) { + if(!data) { + return NULL; + } + for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { + if(memcmp(aid, app->id, 3) == 0) { + return app; + } + } + return NULL; +} + +MifareDesfireFile* mf_df_get_file(MifareDesfireApplication* app, uint8_t id) { + if(!app) { + return NULL; + } + for(MifareDesfireFile* file = app->file_head; file; file = file->next) { + if(file->id == id) { + return file; + } + } + return NULL; +} + void mf_df_cat_data(MifareDesfireData* data, FuriString* out) { mf_df_cat_card_info(data, out); for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { diff --git a/lib/nfc/protocols/mifare_desfire.h b/lib/nfc/deprecated/protocols/mifare_desfire.h similarity index 96% rename from lib/nfc/protocols/mifare_desfire.h rename to lib/nfc/deprecated/protocols/mifare_desfire.h index 963a18f585b8..3dc7c4c2a7c4 100644 --- a/lib/nfc/protocols/mifare_desfire.h +++ b/lib/nfc/deprecated/protocols/mifare_desfire.h @@ -130,6 +130,9 @@ void mf_df_cat_file(MifareDesfireFile* file, FuriString* out); bool mf_df_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); +MifareDesfireApplication* mf_df_get_application(MifareDesfireData* data, const uint8_t (*aid)[3]); +MifareDesfireFile* mf_df_get_file(MifareDesfireApplication* app, uint8_t id); + uint16_t mf_df_prepare_get_version(uint8_t* dest); bool mf_df_parse_get_version_response(uint8_t* buf, uint16_t len, MifareDesfireVersion* out); diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/deprecated/protocols/mifare_ultralight.c similarity index 98% rename from lib/nfc/protocols/mifare_ultralight.c rename to lib/nfc/deprecated/protocols/mifare_ultralight.c index 0e28c0074f1d..56fbeee4991c 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/deprecated/protocols/mifare_ultralight.c @@ -1,7 +1,7 @@ #include #include #include "mifare_ultralight.h" -#include "nfc_util.h" +#include #include #include @@ -79,6 +79,8 @@ static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) { MfUltralightSupportSectorSelect; case MfUltralightTypeNTAG203: return MfUltralightSupportCompatWrite | MfUltralightSupportCounterInMemory; + case MfUltralightTypeULC: + return MfUltralightSupportCompatWrite | MfUltralightSupport3DesAuth; default: // Assumed original MFUL 512-bit return MfUltralightSupportCompatWrite; @@ -95,6 +97,11 @@ static void mf_ul_set_version_ntag203(MfUltralightReader* reader, MfUltralightDa reader->pages_to_read = 42; } +static void mf_ul_set_version_ulc(MfUltralightReader* reader, MfUltralightData* data) { + data->type = MfUltralightTypeULC; + reader->pages_to_read = 48; +} + bool mf_ultralight_read_version( FuriHalNfcTxRxContext* tx_rx, MfUltralightReader* reader, @@ -175,7 +182,7 @@ bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint do { FURI_LOG_D(TAG, "Authenticating"); - tx_rx->tx_data[0] = MF_UL_AUTH; + tx_rx->tx_data[0] = MF_UL_PWD_AUTH; nfc_util_num2bytes(key, 4, &tx_rx->tx_data[1]); tx_rx->tx_bits = 40; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; @@ -702,7 +709,7 @@ bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralight FURI_LOG_D(TAG, "Reading tearing flags"); for(size_t i = 0; i < 3; i++) { tx_rx->tx_data[0] = MF_UL_CHECK_TEARING; - tx_rx->rx_data[1] = i; + tx_rx->tx_data[1] = i; tx_rx->tx_bits = 16; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { @@ -716,6 +723,21 @@ bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralight return flag_read == 2; } +static bool mf_ul_probe_3des_auth(FuriHalNfcTxRxContext* tx_rx) { + tx_rx->tx_data[0] = MF_UL_AUTHENTICATE_1; + tx_rx->tx_data[1] = 0; + tx_rx->tx_bits = 16; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + bool rc = furi_hal_nfc_tx_rx(tx_rx, 50) && tx_rx->rx_bits == 9 * 8 && + tx_rx->rx_data[0] == 0xAF; + + // Reset just in case, we're not going to finish authenticating and need to if tag doesn't support auth + furi_hal_nfc_sleep(); + furi_hal_nfc_activate_nfca(300, NULL); + + return rc; +} + bool mf_ul_read_card( FuriHalNfcTxRxContext* tx_rx, MfUltralightReader* reader, @@ -733,16 +755,20 @@ bool mf_ul_read_card( mf_ultralight_read_signature(tx_rx, data); } } else { - // No GET_VERSION command, check for NTAG203 by reading last page (41) uint8_t dummy[16]; - if(mf_ultralight_read_pages_direct(tx_rx, 41, dummy)) { + // No GET_VERSION command, check if AUTHENTICATE command available (detect UL C). + if(mf_ul_probe_3des_auth(tx_rx)) { + mf_ul_set_version_ulc(reader, data); + } else if(mf_ultralight_read_pages_direct(tx_rx, 41, dummy)) { + // No AUTHENTICATE, check for NTAG203 by reading last page (41) mf_ul_set_version_ntag203(reader, data); - reader->supported_features = mf_ul_get_features(data->type); } else { // We're really an original Mifare Ultralight, reset tag for safety furi_hal_nfc_sleep(); furi_hal_nfc_activate_nfca(300, NULL); } + + reader->supported_features = mf_ul_get_features(data->type); } card_read = mf_ultralight_read_pages(tx_rx, reader, data); @@ -1228,6 +1254,10 @@ static void mf_ul_emulate_write( emulator->data_changed = true; } +bool mf_ul_emulation_supported(MfUltralightData* data) { + return data->type != MfUltralightTypeULC; +} + void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle) { emulator->comp_write_cmd_started = false; emulator->sector_select_cmd_started = false; @@ -1732,7 +1762,7 @@ bool mf_ul_prepare_emulation_response( } } } - } else if(cmd == MF_UL_AUTH) { + } else if(cmd == MF_UL_PWD_AUTH) { if(emulator->supported_features & MfUltralightSupportAuth) { if(buff_rx_len == (1 + 4) * 8) { // Record password sent by PCD diff --git a/lib/nfc/protocols/mifare_ultralight.h b/lib/nfc/deprecated/protocols/mifare_ultralight.h similarity index 97% rename from lib/nfc/protocols/mifare_ultralight.h rename to lib/nfc/deprecated/protocols/mifare_ultralight.h index 4ab22e89cb8e..d444fa7983e6 100644 --- a/lib/nfc/protocols/mifare_ultralight.h +++ b/lib/nfc/deprecated/protocols/mifare_ultralight.h @@ -16,7 +16,8 @@ #define MF_UL_COMP_WRITE (0xA0) #define MF_UL_READ_CNT (0x39) #define MF_UL_INC_CNT (0xA5) -#define MF_UL_AUTH (0x1B) +#define MF_UL_AUTHENTICATE_1 (0x1A) +#define MF_UL_PWD_AUTH (0x1B) #define MF_UL_READ_SIG (0x3C) #define MF_UL_CHECK_TEARING (0x3E) #define MF_UL_READ_VCSL (0x4B) @@ -41,6 +42,7 @@ typedef enum { typedef enum { MfUltralightTypeUnknown, MfUltralightTypeNTAG203, + MfUltralightTypeULC, // Below have config pages and GET_VERSION support MfUltralightTypeUL11, MfUltralightTypeUL21, @@ -77,6 +79,7 @@ typedef enum { MfUltralightSupportAsciiMirror = 1 << 11, // NTAG203 counter that's in memory rather than through a command MfUltralightSupportCounterInMemory = 1 << 12, + MfUltralightSupport3DesAuth = 1 << 13, } MfUltralightFeatures; typedef enum { @@ -237,6 +240,8 @@ bool mf_ul_read_card( MfUltralightReader* reader, MfUltralightData* data); +bool mf_ul_emulation_supported(MfUltralightData* data); + void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle); void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* data); diff --git a/lib/nfc/protocols/nfca.c b/lib/nfc/deprecated/protocols/nfca_utils.c similarity index 99% rename from lib/nfc/protocols/nfca.c rename to lib/nfc/deprecated/protocols/nfca_utils.c index c401f8cc5872..d5b3fb1a846e 100644 --- a/lib/nfc/protocols/nfca.c +++ b/lib/nfc/deprecated/protocols/nfca_utils.c @@ -1,4 +1,4 @@ -#include "nfca.h" +#include "nfca_utils.h" #include #include #include diff --git a/lib/nfc/protocols/nfca.h b/lib/nfc/deprecated/protocols/nfca_utils.h similarity index 100% rename from lib/nfc/protocols/nfca.h rename to lib/nfc/deprecated/protocols/nfca_utils.h diff --git a/lib/nfc/deprecated/protocols/nfcv.c b/lib/nfc/deprecated/protocols/nfcv.c new file mode 100644 index 000000000000..1ec82afd59fe --- /dev/null +++ b/lib/nfc/deprecated/protocols/nfcv.c @@ -0,0 +1,1395 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nfcv.h" +#include "slix.h" + +#define TAG "NfcV" + +/* macros to map "modulate field" flag to GPIO level */ +#define GPIO_LEVEL_MODULATED NFCV_LOAD_MODULATION_POLARITY +#define GPIO_LEVEL_UNMODULATED (!GPIO_LEVEL_MODULATED) + +/* timing macros */ +#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) +#define DIGITAL_SIGNAL_UNIT_US (100000.0f) + +ReturnCode nfcv_inventory(uint8_t* uid) { + uint16_t received = 0; + rfalNfcvInventoryRes res; + ReturnCode ret = ERR_NONE; + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret == ERR_NONE) { + if(uid != NULL) { + memcpy(uid, res.UID, NFCV_UID_LENGTH); + } + } + + return ret; +} + +ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* nfcv_data) { + UNUSED(reader); + + uint16_t received = 0; + for(size_t block = 0; block < nfcv_data->block_num; block++) { + uint8_t rxBuf[32]; + FURI_LOG_D(TAG, "Reading block %d/%d", block, (nfcv_data->block_num - 1)); + + ReturnCode ret = ERR_NONE; + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + ret = rfalNfcvPollerReadSingleBlock( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block, rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + break; + } + } + if(ret != ERR_NONE) { + FURI_LOG_D(TAG, "failed to read: %d", ret); + return ret; + } + memcpy( + &(nfcv_data->data[block * nfcv_data->block_size]), &rxBuf[1], nfcv_data->block_size); + FURI_LOG_D( + TAG, + " %02X %02X %02X %02X", + nfcv_data->data[block * nfcv_data->block_size + 0], + nfcv_data->data[block * nfcv_data->block_size + 1], + nfcv_data->data[block * nfcv_data->block_size + 2], + nfcv_data->data[block * nfcv_data->block_size + 3]); + } + + return ERR_NONE; +} + +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + uint8_t rxBuf[32]; + uint16_t received = 0; + ReturnCode ret = ERR_NONE; + + FURI_LOG_D(TAG, "Read SYSTEM INFORMATION..."); + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerGetSystemInformation( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret == ERR_NONE) { + nfc_data->type = FuriHalNfcTypeV; + nfc_data->uid_len = NFCV_UID_LENGTH; + /* UID is stored reversed in this response */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + nfc_data->uid[pos] = rxBuf[2 + (NFCV_UID_LENGTH - 1 - pos)]; + } + nfcv_data->dsfid = rxBuf[NFCV_UID_LENGTH + 2]; + nfcv_data->afi = rxBuf[NFCV_UID_LENGTH + 3]; + nfcv_data->block_num = rxBuf[NFCV_UID_LENGTH + 4] + 1; + nfcv_data->block_size = rxBuf[NFCV_UID_LENGTH + 5] + 1; + nfcv_data->ic_ref = rxBuf[NFCV_UID_LENGTH + 6]; + FURI_LOG_D( + TAG, + " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + FURI_LOG_D( + TAG, + " DSFID %d, AFI %d, Blocks %d, Size %d, IC Ref %d", + nfcv_data->dsfid, + nfcv_data->afi, + nfcv_data->block_num, + nfcv_data->block_size, + nfcv_data->ic_ref); + return ret; + } + FURI_LOG_D(TAG, "Failed: %d", ret); + + return ret; +} + +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(reader); + furi_assert(nfc_data); + furi_assert(nfcv_data); + + if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) { + return false; + } + + if(nfcv_read_blocks(reader, nfcv_data) != ERR_NONE) { + return false; + } + + if(slix_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX detected"); + nfcv_data->sub_type = NfcVTypeSlix; + } else if(slix2_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX2 detected"); + nfcv_data->sub_type = NfcVTypeSlix2; + } else if(slix_s_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-S detected"); + nfcv_data->sub_type = NfcVTypeSlixS; + } else if(slix_l_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-L detected"); + nfcv_data->sub_type = NfcVTypeSlixL; + } else { + nfcv_data->sub_type = NfcVTypePlain; + } + + return true; +} + +void nfcv_crc(uint8_t* data, uint32_t length) { + uint32_t reg = 0xFFFF; + + for(size_t i = 0; i < length; i++) { + reg = reg ^ ((uint32_t)data[i]); + for(size_t j = 0; j < 8; j++) { + if(reg & 0x0001) { + reg = (reg >> 1) ^ 0x8408; + } else { + reg = (reg >> 1); + } + } + } + + uint16_t crc = ~(uint16_t)(reg & 0xffff); + + data[length + 0] = crc & 0xFF; + data[length + 1] = crc >> 8; +} + +void nfcv_emu_free_signals(NfcVEmuAirSignals* signals) { + furi_assert(signals); + + if(signals->nfcv_resp_one) { + digital_signal_free(signals->nfcv_resp_one); + } + if(signals->nfcv_resp_zero) { + digital_signal_free(signals->nfcv_resp_zero); + } + if(signals->nfcv_resp_sof) { + digital_signal_free(signals->nfcv_resp_sof); + } + if(signals->nfcv_resp_eof) { + digital_signal_free(signals->nfcv_resp_eof); + } + signals->nfcv_resp_one = NULL; + signals->nfcv_resp_zero = NULL; + signals->nfcv_resp_sof = NULL; + signals->nfcv_resp_eof = NULL; +} + +bool nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_t slowdown) { + furi_assert(air); + furi_assert(signals); + + bool success = true; + + if(!signals->nfcv_resp_one) { + /* logical one: unmodulated then 8 pulses */ + signals->nfcv_resp_one = digital_signal_alloc( + slowdown * (air->nfcv_resp_unmod->edge_cnt + 8 * air->nfcv_resp_pulse->edge_cnt)); + if(!signals->nfcv_resp_one) { + return false; + } + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_unmod); + } + for(size_t i = 0; i < slowdown * 8; i++) { + success &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_pulse); + } + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_zero) { + /* logical zero: 8 pulses then unmodulated */ + signals->nfcv_resp_zero = digital_signal_alloc( + slowdown * (8 * air->nfcv_resp_pulse->edge_cnt + air->nfcv_resp_unmod->edge_cnt)); + if(!signals->nfcv_resp_zero) { + return false; + } + for(size_t i = 0; i < slowdown * 8; i++) { + success &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_pulse); + } + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_unmod); + } + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_sof) { + /* SOF: unmodulated, 24 pulses, logic 1 */ + signals->nfcv_resp_sof = digital_signal_alloc( + slowdown * (3 * air->nfcv_resp_unmod->edge_cnt + 24 * air->nfcv_resp_pulse->edge_cnt) + + signals->nfcv_resp_one->edge_cnt); + if(!signals->nfcv_resp_sof) { + return false; + } + for(size_t i = 0; i < slowdown * 3; i++) { + success &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_unmod); + } + for(size_t i = 0; i < slowdown * 24; i++) { + success &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_pulse); + } + success &= digital_signal_append(signals->nfcv_resp_sof, signals->nfcv_resp_one); + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_eof) { + /* EOF: logic 0, 24 pulses, unmodulated */ + signals->nfcv_resp_eof = digital_signal_alloc( + signals->nfcv_resp_zero->edge_cnt + + slowdown * (24 * air->nfcv_resp_pulse->edge_cnt + 3 * air->nfcv_resp_unmod->edge_cnt) + + air->nfcv_resp_unmod->edge_cnt); + if(!signals->nfcv_resp_eof) { + return false; + } + success &= digital_signal_append(signals->nfcv_resp_eof, signals->nfcv_resp_zero); + for(size_t i = 0; i < slowdown * 23; i++) { + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_pulse); + } + /* we don't want to add the last level as we just want a transition to "unmodulated" again */ + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_half_pulse); + } + } + return success; +} + +bool nfcv_emu_alloc(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + if(!nfcv_data->frame) { + nfcv_data->frame = malloc(NFCV_FRAMESIZE_MAX); + if(!nfcv_data->frame) { + return false; + } + } + + if(!nfcv_data->emu_air.nfcv_signal) { + /* assuming max frame length is 255 bytes */ + nfcv_data->emu_air.nfcv_signal = digital_sequence_alloc(8 * 255 + 2, &gpio_spi_r_mosi); + if(!nfcv_data->emu_air.nfcv_signal) { + return false; + } + } + if(!nfcv_data->emu_air.nfcv_resp_unmod) { + /* unmodulated 256/fc or 1024/fc signal as building block */ + nfcv_data->emu_air.nfcv_resp_unmod = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_unmod) { + return false; + } + nfcv_data->emu_air.nfcv_resp_unmod->start_level = GPIO_LEVEL_UNMODULATED; + nfcv_data->emu_air.nfcv_resp_unmod->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_unmod->edge_cnt = 1; + } + if(!nfcv_data->emu_air.nfcv_resp_pulse) { + /* modulated fc/32 or fc/8 pulse as building block */ + nfcv_data->emu_air.nfcv_resp_pulse = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_pulse) { + return false; + } + nfcv_data->emu_air.nfcv_resp_pulse->start_level = GPIO_LEVEL_MODULATED; + nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[1] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_pulse->edge_cnt = 2; + } + + if(!nfcv_data->emu_air.nfcv_resp_half_pulse) { + /* modulated fc/32 or fc/8 pulse as building block */ + nfcv_data->emu_air.nfcv_resp_half_pulse = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_half_pulse) { + return false; + } + nfcv_data->emu_air.nfcv_resp_half_pulse->start_level = GPIO_LEVEL_MODULATED; + nfcv_data->emu_air.nfcv_resp_half_pulse->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_half_pulse->edge_cnt = 1; + } + + bool success = true; + success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_high, 1); + success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_low, 4); + + if(!success) { + FURI_LOG_E(TAG, "Failed to allocate signals"); + return false; + } + + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_SOF, + nfcv_data->emu_air.signals_high.nfcv_resp_sof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_BIT0, + nfcv_data->emu_air.signals_high.nfcv_resp_zero); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_BIT1, + nfcv_data->emu_air.signals_high.nfcv_resp_one); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_EOF, + nfcv_data->emu_air.signals_high.nfcv_resp_eof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_SOF, + nfcv_data->emu_air.signals_low.nfcv_resp_sof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_BIT0, + nfcv_data->emu_air.signals_low.nfcv_resp_zero); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_BIT1, + nfcv_data->emu_air.signals_low.nfcv_resp_one); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_EOF, + nfcv_data->emu_air.signals_low.nfcv_resp_eof); + + return true; +} + +void nfcv_emu_free(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + if(nfcv_data->frame) { + free(nfcv_data->frame); + } + if(nfcv_data->emu_protocol_ctx) { + free(nfcv_data->emu_protocol_ctx); + } + if(nfcv_data->emu_air.nfcv_resp_unmod) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_unmod); + } + if(nfcv_data->emu_air.nfcv_resp_pulse) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse); + } + if(nfcv_data->emu_air.nfcv_resp_half_pulse) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_half_pulse); + } + if(nfcv_data->emu_air.nfcv_signal) { + digital_sequence_free(nfcv_data->emu_air.nfcv_signal); + } + if(nfcv_data->emu_air.reader_signal) { + // Stop pulse reader and disable bus before free + pulse_reader_stop(nfcv_data->emu_air.reader_signal); + // Free pulse reader + pulse_reader_free(nfcv_data->emu_air.reader_signal); + } + + nfcv_data->frame = NULL; + nfcv_data->emu_air.nfcv_resp_unmod = NULL; + nfcv_data->emu_air.nfcv_resp_pulse = NULL; + nfcv_data->emu_air.nfcv_resp_half_pulse = NULL; + nfcv_data->emu_air.nfcv_signal = NULL; + nfcv_data->emu_air.reader_signal = NULL; + + nfcv_emu_free_signals(&nfcv_data->emu_air.signals_high); + nfcv_emu_free_signals(&nfcv_data->emu_air.signals_low); +} + +void nfcv_emu_send( + FuriHalNfcTxRxContext* tx_rx, + NfcVData* nfcv, + uint8_t* data, + uint8_t length, + NfcVSendFlags flags, + uint32_t send_time) { + furi_assert(tx_rx); + furi_assert(nfcv); + + /* picked default value (0) to match the most common format */ + if(flags == NfcVSendFlagsNormal) { + flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof | + NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate; + } + + if(flags & NfcVSendFlagsCrc) { + nfcv_crc(data, length); + length += 2; + } + + /* depending on the request flags, send with high or low rate */ + uint32_t bit0 = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_BIT0 : NFCV_SIG_LOW_BIT0; + uint32_t bit1 = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_BIT1 : NFCV_SIG_LOW_BIT1; + uint32_t sof = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_SOF : NFCV_SIG_LOW_SOF; + uint32_t eof = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_EOF : NFCV_SIG_LOW_EOF; + + digital_sequence_clear(nfcv->emu_air.nfcv_signal); + + if(flags & NfcVSendFlagsSof) { + digital_sequence_add(nfcv->emu_air.nfcv_signal, sof); + } + + for(int bit_total = 0; bit_total < length * 8; bit_total++) { + uint32_t byte_pos = bit_total / 8; + uint32_t bit_pos = bit_total % 8; + uint8_t bit_val = 0x01 << bit_pos; + + digital_sequence_add(nfcv->emu_air.nfcv_signal, (data[byte_pos] & bit_val) ? bit1 : bit0); + } + + if(flags & NfcVSendFlagsEof) { + digital_sequence_add(nfcv->emu_air.nfcv_signal, eof); + } + + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + digital_sequence_set_sendtime(nfcv->emu_air.nfcv_signal, send_time); + digital_sequence_send(nfcv->emu_air.nfcv_signal); + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + + if(tx_rx->sniff_tx) { + tx_rx->sniff_tx(data, length * 8, false, tx_rx->sniff_context); + } +} + +static void nfcv_revuidcpy(uint8_t* dst, uint8_t* src) { + for(int pos = 0; pos < NFCV_UID_LENGTH; pos++) { + dst[pos] = src[NFCV_UID_LENGTH - 1 - pos]; + } +} + +static int nfcv_revuidcmp(uint8_t* dst, uint8_t* src) { + for(int pos = 0; pos < NFCV_UID_LENGTH; pos++) { + if(dst[pos] != src[NFCV_UID_LENGTH - 1 - pos]) { + return 1; + } + } + return 0; +} + +void nfcv_emu_handle_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + + if(nfcv_data->frame_length < 2) { + return; + } + + if(nfcv_data->echo_mode) { + nfcv_emu_send( + tx_rx, + nfcv_data, + nfcv_data->frame, + nfcv_data->frame_length, + NfcVSendFlagsSof | NfcVSendFlagsHighRate | NfcVSendFlagsEof, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO data"); + return; + } + + /* parse the frame data for the upcoming part 3 handling */ + ctx->flags = nfcv_data->frame[0]; + ctx->command = nfcv_data->frame[1]; + ctx->selected = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && (ctx->flags & NFCV_REQ_FLAG_SELECT); + ctx->addressed = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= NFCV_CMD_ADVANCED); + ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? NFCV_UID_LENGTH : 0); + ctx->response_flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof; + ctx->send_time = nfcv_data->eof_timestamp + NFCV_FDT_FC(4380); + + if(ctx->flags & NFCV_REQ_FLAG_DATA_RATE) { + ctx->response_flags |= NfcVSendFlagsHighRate; + } + if(ctx->flags & NFCV_REQ_FLAG_SUB_CARRIER) { + ctx->response_flags |= NfcVSendFlagsTwoSubcarrier; + } + + if(ctx->payload_offset + 2 > nfcv_data->frame_length) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "command 0x%02X, but packet is too short", ctx->command); +#endif + return; + } + + /* standard behavior is implemented */ + if(ctx->addressed) { + uint8_t* address = &nfcv_data->frame[ctx->address_offset]; + if(nfcv_revuidcmp(address, nfc_data->uid)) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "addressed command 0x%02X, but not for us:", ctx->command); + FURI_LOG_D( + TAG, + " dest: %02X%02X%02X%02X%02X%02X%02X%02X", + address[7], + address[6], + address[5], + address[4], + address[3], + address[2], + address[1], + address[0]); + FURI_LOG_D( + TAG, + " our UID: %02X%02X%02X%02X%02X%02X%02X%02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); +#endif + return; + } + } + + if(ctx->selected && !nfcv_data->selected) { +#ifdef NFCV_VERBOSE + FURI_LOG_D( + TAG, + "selected card shall execute command 0x%02X, but we were not selected", + ctx->command); +#endif + return; + } + + /* then give control to the card subtype specific protocol filter */ + if(ctx->emu_protocol_filter != NULL) { + if(ctx->emu_protocol_filter(tx_rx, nfc_data, nfcv_data)) { + if(strlen(nfcv_data->last_command) > 0) { +#ifdef NFCV_VERBOSE + FURI_LOG_D( + TAG, "Received command %s (handled by filter)", nfcv_data->last_command); +#endif + } + return; + } + } + + switch(ctx->command) { + case NFCV_CMD_INVENTORY: { + bool respond = false; + + if(ctx->flags & NFCV_REQ_FLAG_AFI) { + uint8_t afi = nfcv_data->frame[ctx->payload_offset]; + if(afi == nfcv_data->afi) { + respond = true; + } + } else { + respond = true; + } + + if(!nfcv_data->quiet && respond) { + int buffer_pos = 0; + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + ctx->response_buffer[buffer_pos++] = nfcv_data->dsfid; + nfcv_revuidcpy(&ctx->response_buffer[buffer_pos], nfc_data->uid); + buffer_pos += NFCV_UID_LENGTH; + + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + } else { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY (quiet)"); + } + break; + } + + case NFCV_CMD_STAY_QUIET: { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "STAYQUIET"); + nfcv_data->quiet = true; + break; + } + + case NFCV_CMD_LOCK_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + nfcv_data->security_status[block] |= 0x01; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK BLOCK %d", block); + break; + } + + case NFCV_CMD_WRITE_DSFID: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + + if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { + nfcv_data->dsfid = id; + nfcv_data->modified = true; + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE DSFID %02X", id); + break; + } + + case NFCV_CMD_WRITE_AFI: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + + if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { + nfcv_data->afi = id; + nfcv_data->modified = true; + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE AFI %02X", id); + break; + } + + case NFCV_CMD_LOCK_DSFID: { + if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { + nfcv_data->security_status[0] |= NfcVLockBitDsfid; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK DSFID"); + break; + } + + case NFCV_CMD_LOCK_AFI: { + if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { + nfcv_data->security_status[0] |= NfcVLockBitAfi; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK AFI"); + break; + } + + case NFCV_CMD_SELECT: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->selected = true; + nfcv_data->quiet = false; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SELECT"); + break; + } + + case NFCV_CMD_RESET_TO_READY: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->quiet = false; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "RESET_TO_READY"); + break; + } + + case NFCV_CMD_READ_MULTI_BLOCK: + case NFCV_CMD_READ_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + + if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + } + + if(block + blocks <= nfcv_data->block_num) { + uint8_t buffer_pos = 0; + + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + + for(int block_index = 0; block_index < blocks; block_index++) { + int block_current = block + block_index; + /* prepend security status */ + if(ctx->flags & NFCV_REQ_FLAG_OPTION) { + ctx->response_buffer[buffer_pos++] = + nfcv_data->security_status[1 + block_current]; + } + /* then the data block */ + memcpy( + &ctx->response_buffer[buffer_pos], + &nfcv_data->data[nfcv_data->block_size * block_current], + nfcv_data->block_size); + buffer_pos += nfcv_data->block_size; + } + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + } + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); + + break; + } + + case NFCV_CMD_WRITE_MULTI_BLOCK: + case NFCV_CMD_WRITE_BLOCK: { + uint8_t blocks = 1; + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t data_pos = ctx->payload_offset + 1; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[data_pos] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[data_pos]; + uint32_t data_len = nfcv_data->block_size * blocks; + + if((block + blocks) <= nfcv_data->block_num && + (data_pos + data_len + 2) == nfcv_data->frame_length) { + ctx->response_buffer[0] = NFCV_NOERROR; + memcpy( + &nfcv_data->data[nfcv_data->block_size * block], + &nfcv_data->frame[data_pos], + data_len); + nfcv_data->modified = true; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + } + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE MULTI BLOCK %d, %d blocks", + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE BLOCK %d <- %02X %02X %02X %02X", + block, + data[0], + data[1], + data[2], + data[3]); + } + break; + } + + case NFCV_CMD_GET_SYSTEM_INFO: { + int buffer_pos = 0; + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + ctx->response_buffer[buffer_pos++] = NFCV_SYSINFO_FLAG_DSFID | NFCV_SYSINFO_FLAG_AFI | + NFCV_SYSINFO_FLAG_MEMSIZE | NFCV_SYSINFO_FLAG_ICREF; + nfcv_revuidcpy(&ctx->response_buffer[buffer_pos], nfc_data->uid); + buffer_pos += NFCV_UID_LENGTH; + ctx->response_buffer[buffer_pos++] = nfcv_data->dsfid; /* DSFID */ + ctx->response_buffer[buffer_pos++] = nfcv_data->afi; /* AFI */ + ctx->response_buffer[buffer_pos++] = nfcv_data->block_num - 1; /* number of blocks */ + ctx->response_buffer[buffer_pos++] = nfcv_data->block_size - 1; /* block size */ + ctx->response_buffer[buffer_pos++] = nfcv_data->ic_ref; /* IC reference */ + + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO"); + break; + } + + case NFCV_CMD_CUST_ECHO_MODE: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->echo_mode = true; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO mode"); + break; + } + + case NFCV_CMD_CUST_ECHO_DATA: { + nfcv_emu_send( + tx_rx, + nfcv_data, + &nfcv_data->frame[ctx->payload_offset], + nfcv_data->frame_length - ctx->payload_offset - 2, + NfcVSendFlagsSof | NfcVSendFlagsHighRate | NfcVSendFlagsEof, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO data"); + break; + } + + default: + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "unsupported: %02X", + ctx->command); + break; + } + + if(strlen(nfcv_data->last_command) > 0) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); +#endif + } +} + +void nfcv_emu_sniff_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + + if(nfcv_data->frame_length < 2) { + return; + } + + /* parse the frame data for the upcoming part 3 handling */ + ctx->flags = nfcv_data->frame[0]; + ctx->command = nfcv_data->frame[1]; + ctx->selected = (ctx->flags & NFCV_REQ_FLAG_SELECT); + ctx->addressed = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= NFCV_CMD_ADVANCED); + ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? NFCV_UID_LENGTH : 0); + + char flags_string[5]; + + snprintf( + flags_string, + 5, + "%c%c%c%d", + (ctx->flags & NFCV_REQ_FLAG_INVENTORY) ? + 'I' : + (ctx->addressed ? 'A' : (ctx->selected ? 'S' : '*')), + ctx->advanced ? 'X' : ' ', + (ctx->flags & NFCV_REQ_FLAG_DATA_RATE) ? 'h' : 'l', + (ctx->flags & NFCV_REQ_FLAG_SUB_CARRIER) ? 2 : 1); + + switch(ctx->command) { + case NFCV_CMD_INVENTORY: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s INVENTORY", flags_string); + break; + } + + case NFCV_CMD_STAY_QUIET: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s STAYQUIET", flags_string); + nfcv_data->quiet = true; + break; + } + + case NFCV_CMD_LOCK_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s LOCK %d", + flags_string, + block); + break; + } + + case NFCV_CMD_WRITE_DSFID: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WR DSFID %d", + flags_string, + id); + break; + } + + case NFCV_CMD_WRITE_AFI: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WR AFI %d", + flags_string, + id); + break; + } + + case NFCV_CMD_LOCK_DSFID: { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s LOCK DSFID", + flags_string); + break; + } + + case NFCV_CMD_LOCK_AFI: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s LOCK AFI", flags_string); + break; + } + + case NFCV_CMD_SELECT: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s SELECT", flags_string); + break; + } + + case NFCV_CMD_RESET_TO_READY: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s RESET", flags_string); + break; + } + + case NFCV_CMD_READ_MULTI_BLOCK: + case NFCV_CMD_READ_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + + if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + } + + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s READ %d cnt: %d", + flags_string, + block, + blocks); + + break; + } + + case NFCV_CMD_WRITE_MULTI_BLOCK: + case NFCV_CMD_WRITE_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + uint8_t data_pos = 1; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[ctx->payload_offset + data_pos]; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WRITE %d, cnd %d", + flags_string, + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WRITE %d %02X %02X %02X %02X", + flags_string, + block, + data[0], + data[1], + data[2], + data[3]); + } + break; + } + + case NFCV_CMD_GET_SYSTEM_INFO: { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s SYSTEMINFO", + flags_string); + break; + } + + default: + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s unsupported: %02X", + flags_string, + ctx->command); + break; + } + + if(strlen(nfcv_data->last_command) > 0) { + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); + } +} + +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(nfc_data); + furi_assert(nfcv_data); + + if(!nfcv_emu_alloc(nfcv_data)) { + FURI_LOG_E(TAG, "Failed to allocate structures"); + nfcv_data->ready = false; + return; + } + + strcpy(nfcv_data->last_command, ""); + nfcv_data->quiet = false; + nfcv_data->selected = false; + nfcv_data->modified = false; + + /* everything is initialized */ + nfcv_data->ready = true; + + /* ensure the GPIO is already in unmodulated state */ + furi_hal_gpio_init(&gpio_spi_r_mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + + rfal_platform_spi_acquire(); + /* stop operation to configure for transparent and passive mode */ + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); + /* set enable, rx_enable and field detector enable */ + st25r3916WriteRegister( + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + /* explicitely set the modulation resistor in case system config changes for some reason */ + st25r3916WriteRegister( + ST25R3916_REG_PT_MOD, + (0 << ST25R3916_REG_PT_MOD_ptm_res_shift) | (15 << ST25R3916_REG_PT_MOD_pt_res_shift)); + /* target mode: target, other fields do not have any effect as we use transparent mode */ + st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ); + /* let us modulate the field using MOSI, read ASK modulation using IRQ */ + st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + + /* if not set already, initialize the default protocol handler */ + if(!nfcv_data->emu_protocol_ctx) { + nfcv_data->emu_protocol_ctx = malloc(sizeof(NfcVEmuProtocolCtx)); + if(nfcv_data->sub_type == NfcVTypeSniff) { + nfcv_data->emu_protocol_handler = &nfcv_emu_sniff_packet; + } else { + nfcv_data->emu_protocol_handler = &nfcv_emu_handle_packet; + } + } + + FURI_LOG_D(TAG, "Starting NfcV emulation"); + FURI_LOG_D( + TAG, + " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + + switch(nfcv_data->sub_type) { + case NfcVTypeSlixL: + FURI_LOG_D(TAG, " Card type: SLIX-L"); + slix_l_prepare(nfcv_data); + break; + case NfcVTypeSlixS: + FURI_LOG_D(TAG, " Card type: SLIX-S"); + slix_s_prepare(nfcv_data); + break; + case NfcVTypeSlix2: + FURI_LOG_D(TAG, " Card type: SLIX2"); + slix2_prepare(nfcv_data); + break; + case NfcVTypeSlix: + FURI_LOG_D(TAG, " Card type: SLIX"); + slix_prepare(nfcv_data); + break; + case NfcVTypePlain: + FURI_LOG_D(TAG, " Card type: Plain"); + break; + case NfcVTypeSniff: + FURI_LOG_D(TAG, " Card type: Sniffing"); + break; + } + + /* allocate a 512 edge buffer, more than enough */ + nfcv_data->emu_air.reader_signal = + pulse_reader_alloc(&gpio_nfc_irq_rfid_pull, NFCV_PULSE_BUFFER); + /* timebase shall be 1 ns */ + pulse_reader_set_timebase(nfcv_data->emu_air.reader_signal, PulseReaderUnitNanosecond); + /* and configure to already calculate the number of bits */ + pulse_reader_set_bittime(nfcv_data->emu_air.reader_signal, NFCV_PULSE_DURATION_NS); + /* this IO is fed into the µC via a diode, so we need a pulldown */ + pulse_reader_set_pull(nfcv_data->emu_air.reader_signal, GpioPullDown); + + /* start sampling */ + pulse_reader_start(nfcv_data->emu_air.reader_signal); +} + +void nfcv_emu_deinit(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + nfcv_emu_free(nfcv_data); + + if(nfcv_data->emu_protocol_ctx) { + free(nfcv_data->emu_protocol_ctx); + nfcv_data->emu_protocol_ctx = NULL; + } + + /* set registers back to how we found them */ + st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0x00); + st25r3916WriteRegister(ST25R3916_REG_MODE, 0x08); + rfal_platform_spi_release(); +} + +bool nfcv_emu_loop( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data, + uint32_t timeout_ms) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data); + + bool ret = false; + uint32_t frame_state = NFCV_FRAME_STATE_SOF1; + uint32_t periods_previous = 0; + uint32_t frame_pos = 0; + uint32_t byte_value = 0; + uint32_t bits_received = 0; + uint32_t timeout = timeout_ms * 1000; + bool wait_for_pulse = false; + + if(!nfcv_data->ready) { + return false; + } + +#ifdef NFCV_DIAGNOSTIC_DUMPS + uint8_t period_buffer[NFCV_DIAGNOSTIC_DUMP_SIZE]; + uint32_t period_buffer_pos = 0; +#endif + + while(true) { + uint32_t periods = pulse_reader_receive(nfcv_data->emu_air.reader_signal, timeout); + uint32_t timestamp = DWT->CYCCNT; + + /* when timed out, reset to SOF state */ + if(periods == PULSE_READER_NO_EDGE || periods == PULSE_READER_LOST_EDGE) { + break; + } + +#ifdef NFCV_DIAGNOSTIC_DUMPS + if(period_buffer_pos < sizeof(period_buffer)) { + period_buffer[period_buffer_pos++] = periods; + } +#endif + + /* short helper for detecting a pulse position */ + if(wait_for_pulse) { + wait_for_pulse = false; + if(periods != 1) { + frame_state = NFCV_FRAME_STATE_RESET; + } + continue; + } + + switch(frame_state) { + case NFCV_FRAME_STATE_SOF1: + if(periods == 1) { + frame_state = NFCV_FRAME_STATE_SOF2; + } else { + frame_state = NFCV_FRAME_STATE_SOF1; + break; + } + break; + + case NFCV_FRAME_STATE_SOF2: + /* waiting for the second low period, telling us about coding */ + if(periods == 6) { + frame_state = NFCV_FRAME_STATE_CODING_256; + periods_previous = 0; + wait_for_pulse = true; + } else if(periods == 4) { + frame_state = NFCV_FRAME_STATE_CODING_4; + periods_previous = 2; + wait_for_pulse = true; + } else { + frame_state = NFCV_FRAME_STATE_RESET; + } + break; + + case NFCV_FRAME_STATE_CODING_256: + if(periods_previous > periods) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + + if(periods > 512) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } + + periods_previous = 512 - (periods + 1); + byte_value = (periods - 1) / 2; + if(frame_pos < NFCV_FRAMESIZE_MAX) { + nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; + } + + wait_for_pulse = true; + + break; + + case NFCV_FRAME_STATE_CODING_4: + if(periods_previous > periods) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + periods_previous = 0; + + byte_value >>= 2; + bits_received += 2; + + if(periods == 1) { + byte_value |= 0x00 << 6; // -V684 + periods_previous = 6; + } else if(periods == 3) { + byte_value |= 0x01 << 6; + periods_previous = 4; + } else if(periods == 5) { + byte_value |= 0x02 << 6; + periods_previous = 2; + } else if(periods == 7) { + byte_value |= 0x03 << 6; + periods_previous = 0; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } else { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(bits_received >= 8) { + if(frame_pos < NFCV_FRAMESIZE_MAX) { + nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; + } + bits_received = 0; + } + wait_for_pulse = true; + break; + } + + /* post-state-machine cleanup and reset */ + if(frame_state == NFCV_FRAME_STATE_RESET) { + frame_state = NFCV_FRAME_STATE_SOF1; + } else if(frame_state == NFCV_FRAME_STATE_EOF) { + nfcv_data->frame_length = frame_pos; + nfcv_data->eof_timestamp = timestamp; + break; + } + } + + if(frame_state == NFCV_FRAME_STATE_EOF) { + /* we know that this code uses TIM2, so stop pulse reader */ + pulse_reader_stop(nfcv_data->emu_air.reader_signal); + if(tx_rx->sniff_rx) { + tx_rx->sniff_rx(nfcv_data->frame, frame_pos * 8, false, tx_rx->sniff_context); + } + nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data); + + pulse_reader_start(nfcv_data->emu_air.reader_signal); + ret = true; + + } +#ifdef NFCV_VERBOSE + else { + if(frame_state != NFCV_FRAME_STATE_SOF1) { + FURI_LOG_T(TAG, "leaving while in state: %lu", frame_state); + } + } +#endif + +#ifdef NFCV_DIAGNOSTIC_DUMPS + if(period_buffer_pos) { + FURI_LOG_T(TAG, "pulses:"); + for(uint32_t pos = 0; pos < period_buffer_pos; pos++) { + FURI_LOG_T(TAG, " #%lu: %u", pos, period_buffer[pos]); + } + } +#endif + + return ret; +} diff --git a/lib/nfc/deprecated/protocols/nfcv.h b/lib/nfc/deprecated/protocols/nfcv.h new file mode 100644 index 000000000000..dff92324ac1d --- /dev/null +++ b/lib/nfc/deprecated/protocols/nfcv.h @@ -0,0 +1,290 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* true: modulating releases load, false: modulating adds load resistor to field coil */ +#define NFCV_LOAD_MODULATION_POLARITY (false) + +#define NFCV_FC (13560000.0f) /* MHz */ +#define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC / 32) / 2.0f) /* 1.1799 µs */ +#define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */ +#define NFCV_PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) + +/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum number of blocks is defined as 256 */ +#define NFCV_BLOCKS_MAX 256 +/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum size of blocks is defined as 32 */ +#define NFCV_BLOCKSIZE_MAX 32 +/* the resulting memory size a card can have */ +#define NFCV_MEMSIZE_MAX (NFCV_BLOCKS_MAX * NFCV_BLOCKSIZE_MAX) +/* ISO/IEC 15693-3:2019(E) 7.1b: standard allows up to 8192, the maxium frame length that we are expected to receive/send is less */ +#define NFCV_FRAMESIZE_MAX (1 + NFCV_MEMSIZE_MAX + NFCV_BLOCKS_MAX) + +/* maximum string length for log messages */ +#define NFCV_LOG_STR_LEN 128 +/* maximum of pulses to be buffered by pulse reader */ +#define NFCV_PULSE_BUFFER 512 + +//#define NFCV_DIAGNOSTIC_DUMPS +//#define NFCV_DIAGNOSTIC_DUMP_SIZE 256 +//#define NFCV_VERBOSE + +/* helpers to calculate the send time based on DWT->CYCCNT */ +#define NFCV_FDT_USEC(usec) ((usec)*64) +#define NFCV_FDT_FC(ticks) ((ticks)*6400 / 1356) + +/* state machine when receiving frame bits */ +#define NFCV_FRAME_STATE_SOF1 0 +#define NFCV_FRAME_STATE_SOF2 1 +#define NFCV_FRAME_STATE_CODING_4 2 +#define NFCV_FRAME_STATE_CODING_256 3 +#define NFCV_FRAME_STATE_EOF 4 +#define NFCV_FRAME_STATE_RESET 5 + +/* sequences for every section of a frame */ +#define NFCV_SIG_SOF 0 +#define NFCV_SIG_BIT0 1 +#define NFCV_SIG_BIT1 2 +#define NFCV_SIG_EOF 3 +#define NFCV_SIG_LOW_SOF 4 +#define NFCV_SIG_LOW_BIT0 5 +#define NFCV_SIG_LOW_BIT1 6 +#define NFCV_SIG_LOW_EOF 7 + +/* various constants */ +#define NFCV_COMMAND_RETRIES 5 +#define NFCV_UID_LENGTH 8 + +/* ISO15693 protocol flags */ +typedef enum { + /* ISO15693 protocol flags when INVENTORY is NOT set */ + NFCV_REQ_FLAG_SUB_CARRIER = (1 << 0), + NFCV_REQ_FLAG_DATA_RATE = (1 << 1), + NFCV_REQ_FLAG_INVENTORY = (1 << 2), + NFCV_REQ_FLAG_PROTOCOL_EXT = (1 << 3), + NFCV_REQ_FLAG_SELECT = (1 << 4), + NFCV_REQ_FLAG_ADDRESS = (1 << 5), + NFCV_REQ_FLAG_OPTION = (1 << 6), + /* ISO15693 protocol flags when INVENTORY flag is set */ + NFCV_REQ_FLAG_AFI = (1 << 4), + NFCV_REQ_FLAG_NB_SLOTS = (1 << 5) +} NfcVRequestFlags; + +/* ISO15693 protocol flags */ +typedef enum { + NFCV_RES_FLAG_ERROR = (1 << 0), + NFCV_RES_FLAG_VALIDITY = (1 << 1), + NFCV_RES_FLAG_FINAL = (1 << 2), + NFCV_RES_FLAG_PROTOCOL_EXT = (1 << 3), + NFCV_RES_FLAG_SEC_LEN1 = (1 << 4), + NFCV_RES_FLAG_SEC_LEN2 = (1 << 5), + NFCV_RES_FLAG_WAIT_EXT = (1 << 6), +} NfcVRsponseFlags; + +/* flags for SYSINFO response */ +typedef enum { + NFCV_SYSINFO_FLAG_DSFID = (1 << 0), + NFCV_SYSINFO_FLAG_AFI = (1 << 1), + NFCV_SYSINFO_FLAG_MEMSIZE = (1 << 2), + NFCV_SYSINFO_FLAG_ICREF = (1 << 3) +} NfcVSysinfoFlags; + +/* ISO15693 command codes */ +typedef enum { + /* mandatory command codes */ + NFCV_CMD_INVENTORY = 0x01, + NFCV_CMD_STAY_QUIET = 0x02, + /* optional command codes */ + NFCV_CMD_READ_BLOCK = 0x20, + NFCV_CMD_WRITE_BLOCK = 0x21, + NFCV_CMD_LOCK_BLOCK = 0x22, + NFCV_CMD_READ_MULTI_BLOCK = 0x23, + NFCV_CMD_WRITE_MULTI_BLOCK = 0x24, + NFCV_CMD_SELECT = 0x25, + NFCV_CMD_RESET_TO_READY = 0x26, + NFCV_CMD_WRITE_AFI = 0x27, + NFCV_CMD_LOCK_AFI = 0x28, + NFCV_CMD_WRITE_DSFID = 0x29, + NFCV_CMD_LOCK_DSFID = 0x2A, + NFCV_CMD_GET_SYSTEM_INFO = 0x2B, + NFCV_CMD_READ_MULTI_SECSTATUS = 0x2C, + /* advanced command codes */ + NFCV_CMD_ADVANCED = 0xA0, + /* flipper zero custom command codes */ + NFCV_CMD_CUST_ECHO_MODE = 0xDE, + NFCV_CMD_CUST_ECHO_DATA = 0xDF +} NfcVCommands; + +/* ISO15693 Response error codes */ +typedef enum { + NFCV_NOERROR = 0x00, + NFCV_ERROR_CMD_NOT_SUP = 0x01, // Command not supported + NFCV_ERROR_CMD_NOT_REC = 0x02, // Command not recognized (eg. parameter error) + NFCV_ERROR_CMD_OPTION = 0x03, // Command option not supported + NFCV_ERROR_GENERIC = 0x0F, // No additional Info about this error + NFCV_ERROR_BLOCK_UNAVAILABLE = 0x10, + NFCV_ERROR_BLOCK_LOCKED_ALREADY = 0x11, // cannot lock again + NFCV_ERROR_BLOCK_LOCKED = 0x12, // cannot be changed + NFCV_ERROR_BLOCK_WRITE = 0x13, // Writing was unsuccessful + NFCV_ERROR_BLOCL_WRITELOCK = 0x14 // Locking was unsuccessful +} NfcVErrorcodes; + +typedef enum { + NfcVLockBitDsfid = 1, + NfcVLockBitAfi = 2, +} NfcVLockBits; + +typedef enum { + NfcVAuthMethodManual, + NfcVAuthMethodTonieBox, +} NfcVAuthMethod; + +typedef enum { + NfcVTypePlain = 0, + NfcVTypeSlix = 1, + NfcVTypeSlixS = 2, + NfcVTypeSlixL = 3, + NfcVTypeSlix2 = 4, + NfcVTypeSniff = 255, +} NfcVSubtype; + +typedef enum { + NfcVSendFlagsNormal = 0, + NfcVSendFlagsSof = 1 << 0, + NfcVSendFlagsCrc = 1 << 1, + NfcVSendFlagsEof = 1 << 2, + NfcVSendFlagsOneSubcarrier = 0, + NfcVSendFlagsTwoSubcarrier = 1 << 3, + NfcVSendFlagsLowRate = 0, + NfcVSendFlagsHighRate = 1 << 4 +} NfcVSendFlags; + +typedef struct { + uint8_t key_read[4]; + uint8_t key_write[4]; + uint8_t key_privacy[4]; + uint8_t key_destroy[4]; + uint8_t key_eas[4]; + uint8_t rand[2]; + bool privacy; +} NfcVSlixData; + +typedef union { + NfcVSlixData slix; +} NfcVSubtypeData; + +typedef struct { + DigitalSignal* nfcv_resp_sof; + DigitalSignal* nfcv_resp_one; + DigitalSignal* nfcv_resp_zero; + DigitalSignal* nfcv_resp_eof; +} NfcVEmuAirSignals; + +typedef struct { + PulseReader* reader_signal; + DigitalSignal* nfcv_resp_pulse; /* pulse length, fc/32 */ + DigitalSignal* nfcv_resp_half_pulse; /* half pulse length, fc/32 */ + DigitalSignal* nfcv_resp_unmod; /* unmodulated length 256/fc */ + NfcVEmuAirSignals signals_high; + NfcVEmuAirSignals signals_low; + DigitalSequence* nfcv_signal; +} NfcVEmuAir; + +typedef void (*NfcVEmuProtocolHandler)( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data); +typedef bool (*NfcVEmuProtocolFilter)( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data); + +/* the default ISO15693 handler context */ +typedef struct { + uint8_t flags; /* ISO15693-3 flags of the header as specified */ + uint8_t command; /* ISO15693-3 command at offset 1 as specified */ + bool selected; /* ISO15693-3 flags: selected frame */ + bool addressed; /* ISO15693-3 flags: addressed frame */ + bool advanced; /* ISO15693-3 command: advanced command */ + uint8_t address_offset; /* ISO15693-3 offset of the address in frame, if addressed is set */ + uint8_t payload_offset; /* ISO15693-3 offset of the payload in frame */ + + uint8_t response_buffer[NFCV_FRAMESIZE_MAX]; /* pre-allocated response buffer */ + NfcVSendFlags response_flags; /* flags to use when sending response */ + uint32_t send_time; /* timestamp when to send the response */ + + NfcVEmuProtocolFilter emu_protocol_filter; +} NfcVEmuProtocolCtx; + +typedef struct { + /* common ISO15693 fields, being specified in ISO15693-3 */ + uint8_t dsfid; + uint8_t afi; + uint8_t ic_ref; + uint16_t block_num; + uint8_t block_size; + uint8_t data[NFCV_MEMSIZE_MAX]; + uint8_t security_status[1 + NFCV_BLOCKS_MAX]; + bool selected; + bool quiet; + + bool modified; + bool ready; + bool echo_mode; + + /* specfic variant infos */ + NfcVSubtype sub_type; + NfcVSubtypeData sub_data; + NfcVAuthMethod auth_method; + + /* precalced air level data */ + NfcVEmuAir emu_air; + + uint8_t* frame; /* [NFCV_FRAMESIZE_MAX] ISO15693-2 incoming raw data from air layer */ + uint8_t frame_length; /* ISO15693-2 length of incoming data */ + uint32_t eof_timestamp; /* ISO15693-2 EOF timestamp, read from DWT->CYCCNT */ + + /* handler for the protocol layer as specified in ISO15693-3 */ + NfcVEmuProtocolHandler emu_protocol_handler; + void* emu_protocol_ctx; + /* runtime data */ + char last_command[NFCV_LOG_STR_LEN]; + char error[NFCV_LOG_STR_LEN]; +} NfcVData; + +typedef struct { + uint16_t blocks_to_read; + int16_t blocks_read; +} NfcVReader; + +ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data); +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data); +ReturnCode nfcv_inventory(uint8_t* uid); +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data); + +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); +void nfcv_emu_deinit(NfcVData* nfcv_data); +bool nfcv_emu_loop( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data, + uint32_t timeout_ms); +void nfcv_emu_send( + FuriHalNfcTxRxContext* tx_rx, + NfcVData* nfcv, + uint8_t* data, + uint8_t length, + NfcVSendFlags flags, + uint32_t send_time); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/deprecated/protocols/slix.c b/lib/nfc/deprecated/protocols/slix.c new file mode 100644 index 000000000000..7a30cf4fd274 --- /dev/null +++ b/lib/nfc/deprecated/protocols/slix.c @@ -0,0 +1,409 @@ + +#include +#include "nfcv.h" +#include "slix.h" +#include +#include "furi_hal_nfc.h" +#include + +#define TAG "SLIX" + +static uint32_t slix_read_be(uint8_t* data, uint32_t length) { + uint32_t value = 0; + + for(uint32_t pos = 0; pos < length; pos++) { + value <<= 8; + value |= data[pos]; + } + + return value; +} + +uint8_t slix_get_ti(FuriHalNfcDevData* nfc_data) { + return (nfc_data->uid[3] >> 3) & 3; +} + +bool slix_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) && + slix_get_ti(nfc_data) == 2) { + return true; + } + return false; +} + +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) && + slix_get_ti(nfc_data) == 1) { + return true; + } + return false; +} + +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x02)) { + return true; + } + return false; +} + +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x03)) { + return true; + } + return false; +} + +ReturnCode slix_get_random(NfcVData* data) { + uint16_t received = 0; + uint8_t rxBuf[32]; + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_GET_RANDOM_NUMBER, + RFAL_NFCV_REQ_FLAG_DEFAULT, + NFCV_MANUFACTURER_NXP, + NULL, + NULL, + 0, + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + if(received != 3) { + return ERR_PROTO; + } + if(data != NULL) { + data->sub_data.slix.rand[0] = rxBuf[2]; + data->sub_data.slix.rand[1] = rxBuf[1]; + } + } + + return ret; +} + +ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) { + uint16_t received = 0; + uint8_t rxBuf[32]; + uint8_t cmd_set_pass[] = { + password_id, + data->sub_data.slix.rand[1], + data->sub_data.slix.rand[0], + data->sub_data.slix.rand[1], + data->sub_data.slix.rand[0]}; + uint8_t* password = NULL; + + switch(password_id) { + case SLIX_PASS_READ: + password = data->sub_data.slix.key_read; + break; + case SLIX_PASS_WRITE: + password = data->sub_data.slix.key_write; + break; + case SLIX_PASS_PRIVACY: + password = data->sub_data.slix.key_privacy; + break; + case SLIX_PASS_DESTROY: + password = data->sub_data.slix.key_destroy; + break; + case SLIX_PASS_EASAFI: + password = data->sub_data.slix.key_eas; + break; + default: + break; + } + + if(!password) { + return ERR_NOTSUPP; + } + + for(int pos = 0; pos < 4; pos++) { + cmd_set_pass[1 + pos] ^= password[3 - pos]; + } + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_SET_PASSWORD, + RFAL_NFCV_REQ_FLAG_DATA_RATE, + NFCV_MANUFACTURER_NXP, + NULL, + cmd_set_pass, + sizeof(cmd_set_pass), + rxBuf, + sizeof(rxBuf), + &received); + + return ret; +} + +bool slix_generic_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in, + uint32_t password_supported) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + + if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && + ctx->command != NFCV_CMD_NXP_SET_PASSWORD) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "command 0x%02X ignored, privacy mode", + ctx->command); + FURI_LOG_D(TAG, "%s", nfcv_data->last_command); + return true; + } + + bool handled = false; + + switch(ctx->command) { + case NFCV_CMD_NXP_GET_RANDOM_NUMBER: { + slix->rand[0] = furi_hal_random_get(); + slix->rand[1] = furi_hal_random_get(); + + ctx->response_buffer[0] = NFCV_NOERROR; + ctx->response_buffer[1] = slix->rand[1]; + ctx->response_buffer[2] = slix->rand[0]; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 3, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "GET_RANDOM_NUMBER -> 0x%02X%02X", + slix->rand[0], + slix->rand[1]); + + handled = true; + break; + } + + case NFCV_CMD_NXP_SET_PASSWORD: { + uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; + + if(!(password_id & password_supported)) { + break; + } + + uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; + uint8_t* rand = slix->rand; + uint8_t* password = NULL; + uint8_t password_rcv[4]; + + switch(password_id) { + case SLIX_PASS_READ: + password = slix->key_read; + break; + case SLIX_PASS_WRITE: + password = slix->key_write; + break; + case SLIX_PASS_PRIVACY: + password = slix->key_privacy; + break; + case SLIX_PASS_DESTROY: + password = slix->key_destroy; + break; + case SLIX_PASS_EASAFI: + password = slix->key_eas; + break; + default: + break; + } + + if(!password) { + break; + } + + for(int pos = 0; pos < 4; pos++) { + password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; + } + uint32_t pass_expect = slix_read_be(password, 4); + uint32_t pass_received = slix_read_be(password_rcv, 4); + + /* if the password is all-zeroes, just accept any password*/ + if(!pass_expect || pass_expect == pass_received) { + switch(password_id) { + case SLIX_PASS_READ: + break; + case SLIX_PASS_WRITE: + break; + case SLIX_PASS_PRIVACY: + slix->privacy = false; + nfcv_data->modified = true; + break; + case SLIX_PASS_DESTROY: + FURI_LOG_D(TAG, "Pooof! Got destroyed"); + break; + case SLIX_PASS_EASAFI: + break; + default: + break; + } + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "SET_PASSWORD #%02X 0x%08lX OK", + password_id, + pass_received); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "SET_PASSWORD #%02X 0x%08lX/%08lX FAIL", + password_id, + pass_received, + pass_expect); + } + handled = true; + break; + } + + case NFCV_CMD_NXP_ENABLE_PRIVACY: { + ctx->response_buffer[0] = NFCV_NOERROR; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "NFCV_CMD_NXP_ENABLE_PRIVACY"); + + slix->privacy = true; + handled = true; + break; + } + } + + return handled; +} + +bool slix_l_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter( + tx_rx, + nfc_data, + nfcv_data_in, + SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI)) { + return true; + } + + return handled; +} + +void slix_l_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_l_protocol_filter; +} + +bool slix_s_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) { + return true; + } + + return handled; +} + +void slix_s_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_s_protocol_filter; +} + +bool slix_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_EASAFI)) { + return true; + } + + return handled; +} + +void slix_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_protocol_filter; +} + +bool slix2_protocol_filter( // -V524 + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) { + return true; + } + + return handled; +} + +void slix2_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix2_protocol_filter; +} diff --git a/lib/nfc/deprecated/protocols/slix.h b/lib/nfc/deprecated/protocols/slix.h new file mode 100644 index 000000000000..3ed7d43d70e4 --- /dev/null +++ b/lib/nfc/deprecated/protocols/slix.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#define NFCV_MANUFACTURER_NXP 0x04 + +/* ISO15693-3 CUSTOM NXP COMMANDS */ +#define NFCV_CMD_NXP_SET_EAS 0xA2 +#define NFCV_CMD_NXP_RESET_EAS 0xA3 +#define NFCV_CMD_NXP_LOCK_EAS 0xA4 +#define NFCV_CMD_NXP_EAS_ALARM 0xA5 +#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 +#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7 +#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0 +#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 +#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2 +#define NFCV_CMD_NXP_SET_PASSWORD 0xB3 +#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4 +#define NFCV_CMD_NXP_DESTROY 0xB9 +#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA + +/* available passwords */ +#define SLIX_PASS_READ 0x01 +#define SLIX_PASS_WRITE 0x02 +#define SLIX_PASS_PRIVACY 0x04 +#define SLIX_PASS_DESTROY 0x08 +#define SLIX_PASS_EASAFI 0x10 + +#define SLIX_PASS_ALL \ + (SLIX_PASS_READ | SLIX_PASS_WRITE | SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI) + +bool slix_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); + +ReturnCode slix_get_random(NfcVData* data); +ReturnCode slix_unlock(NfcVData* data, uint32_t password_id); + +void slix_prepare(NfcVData* nfcv_data); +void slix_s_prepare(NfcVData* nfcv_data); +void slix_l_prepare(NfcVData* nfcv_data); +void slix2_prepare(NfcVData* nfcv_data); diff --git a/lib/nfc/helpers/felica_crc.c b/lib/nfc/helpers/felica_crc.c new file mode 100644 index 000000000000..d5e0853a1095 --- /dev/null +++ b/lib/nfc/helpers/felica_crc.c @@ -0,0 +1,52 @@ +#include "felica_crc.h" + +#include + +#define FELICA_CRC_POLY (0x1021U) // Polynomial: x^16 + x^12 + x^5 + 1 +#define FELICA_CRC_INIT (0x0000U) + +uint16_t felica_crc_calculate(const uint8_t* data, size_t length) { + uint16_t crc = FELICA_CRC_INIT; + + for(size_t i = 0; i < length; i++) { + crc ^= ((uint16_t)data[i] << 8); + for(size_t j = 0; j < 8; j++) { + if(crc & 0x8000) { + crc <<= 1; + crc ^= FELICA_CRC_POLY; + } else { + crc <<= 1; + } + } + } + + return (crc << 8) | (crc >> 8); +} + +void felica_crc_append(BitBuffer* buf) { + const uint8_t* data = bit_buffer_get_data(buf); + const size_t data_size = bit_buffer_get_size_bytes(buf); + + const uint16_t crc = felica_crc_calculate(data, data_size); + bit_buffer_append_bytes(buf, (const uint8_t*)&crc, FELICA_CRC_SIZE); +} + +bool felica_crc_check(const BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + if(data_size <= FELICA_CRC_SIZE) return false; + + uint16_t crc_received; + bit_buffer_write_bytes_mid(buf, &crc_received, data_size - FELICA_CRC_SIZE, FELICA_CRC_SIZE); + + const uint8_t* data = bit_buffer_get_data(buf); + const uint16_t crc_calc = felica_crc_calculate(data, data_size - FELICA_CRC_SIZE); + + return (crc_calc == crc_received); +} + +void felica_crc_trim(BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + furi_assert(data_size > FELICA_CRC_SIZE); + + bit_buffer_set_size_bytes(buf, data_size - FELICA_CRC_SIZE); +} diff --git a/lib/nfc/helpers/felica_crc.h b/lib/nfc/helpers/felica_crc.h new file mode 100644 index 000000000000..d1dc29e74db7 --- /dev/null +++ b/lib/nfc/helpers/felica_crc.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#include "bit_buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FELICA_CRC_SIZE sizeof(uint16_t) + +void felica_crc_append(BitBuffer* buf); + +bool felica_crc_check(const BitBuffer* buf); + +void felica_crc_trim(BitBuffer* buf); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/iso13239_crc.c b/lib/nfc/helpers/iso13239_crc.c new file mode 100644 index 000000000000..c54fbcfb21cc --- /dev/null +++ b/lib/nfc/helpers/iso13239_crc.c @@ -0,0 +1,62 @@ +#include "iso13239_crc.h" + +#include + +#define ISO13239_CRC_INIT_DEFAULT (0xFFFFU) +#define ISO13239_CRC_INIT_PICOPASS (0xE012U) +#define ISO13239_CRC_POLY (0x8408U) + +static uint16_t + iso13239_crc_calculate(Iso13239CrcType type, const uint8_t* data, size_t data_size) { + uint16_t crc; + + if(type == Iso13239CrcTypeDefault) { + crc = ISO13239_CRC_INIT_DEFAULT; + } else if(type == Iso13239CrcTypePicopass) { + crc = ISO13239_CRC_INIT_PICOPASS; + } else { + furi_crash("Wrong ISO13239 CRC type"); + } + + for(size_t i = 0; i < data_size; ++i) { + crc ^= (uint16_t)data[i]; + for(size_t j = 0; j < 8; ++j) { + if(crc & 1U) { + crc = (crc >> 1) ^ ISO13239_CRC_POLY; + } else { + crc >>= 1; + } + } + } + + return type == Iso13239CrcTypePicopass ? crc : ~crc; +} + +void iso13239_crc_append(Iso13239CrcType type, BitBuffer* buf) { + const uint8_t* data = bit_buffer_get_data(buf); + const size_t data_size = bit_buffer_get_size_bytes(buf); + + const uint16_t crc = iso13239_crc_calculate(type, data, data_size); + bit_buffer_append_bytes(buf, (const uint8_t*)&crc, ISO13239_CRC_SIZE); +} + +bool iso13239_crc_check(Iso13239CrcType type, const BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + if(data_size <= ISO13239_CRC_SIZE) return false; + + uint16_t crc_received; + bit_buffer_write_bytes_mid( + buf, &crc_received, data_size - ISO13239_CRC_SIZE, ISO13239_CRC_SIZE); + + const uint8_t* data = bit_buffer_get_data(buf); + const uint16_t crc_calc = iso13239_crc_calculate(type, data, data_size - ISO13239_CRC_SIZE); + + return (crc_calc == crc_received); +} + +void iso13239_crc_trim(BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + furi_assert(data_size > ISO13239_CRC_SIZE); + + bit_buffer_set_size_bytes(buf, data_size - ISO13239_CRC_SIZE); +} diff --git a/lib/nfc/helpers/iso13239_crc.h b/lib/nfc/helpers/iso13239_crc.h new file mode 100644 index 000000000000..c71ec6befe49 --- /dev/null +++ b/lib/nfc/helpers/iso13239_crc.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO13239_CRC_SIZE sizeof(uint16_t) + +typedef enum { + Iso13239CrcTypeDefault, + Iso13239CrcTypePicopass, +} Iso13239CrcType; + +void iso13239_crc_append(Iso13239CrcType type, BitBuffer* buf); + +bool iso13239_crc_check(Iso13239CrcType type, const BitBuffer* buf); + +void iso13239_crc_trim(BitBuffer* buf); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/iso14443_crc.c b/lib/nfc/helpers/iso14443_crc.c new file mode 100644 index 000000000000..fda9871aa992 --- /dev/null +++ b/lib/nfc/helpers/iso14443_crc.c @@ -0,0 +1,57 @@ +#include "iso14443_crc.h" + +#include + +#define ISO14443_3A_CRC_INIT (0x6363U) +#define ISO14443_3B_CRC_INIT (0xFFFFU) + +static uint16_t + iso14443_crc_calculate(Iso14443CrcType type, const uint8_t* data, size_t data_size) { + uint16_t crc; + + if(type == Iso14443CrcTypeA) { + crc = ISO14443_3A_CRC_INIT; + } else if(type == Iso14443CrcTypeB) { + crc = ISO14443_3B_CRC_INIT; + } else { + furi_crash("Wrong ISO14443 CRC type"); + } + + for(size_t i = 0; i < data_size; i++) { + uint8_t byte = data[i]; + byte ^= (uint8_t)(crc & 0xff); + byte ^= byte << 4; + crc = (crc >> 8) ^ (((uint16_t)byte) << 8) ^ (((uint16_t)byte) << 3) ^ (byte >> 4); + } + + return type == Iso14443CrcTypeA ? crc : ~crc; +} + +void iso14443_crc_append(Iso14443CrcType type, BitBuffer* buf) { + const uint8_t* data = bit_buffer_get_data(buf); + const size_t data_size = bit_buffer_get_size_bytes(buf); + + const uint16_t crc = iso14443_crc_calculate(type, data, data_size); + bit_buffer_append_bytes(buf, (const uint8_t*)&crc, ISO14443_CRC_SIZE); +} + +bool iso14443_crc_check(Iso14443CrcType type, const BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + if(data_size <= ISO14443_CRC_SIZE) return false; + + uint16_t crc_received; + bit_buffer_write_bytes_mid( + buf, &crc_received, data_size - ISO14443_CRC_SIZE, ISO14443_CRC_SIZE); + + const uint8_t* data = bit_buffer_get_data(buf); + const uint16_t crc_calc = iso14443_crc_calculate(type, data, data_size - ISO14443_CRC_SIZE); + + return (crc_calc == crc_received); +} + +void iso14443_crc_trim(BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + furi_assert(data_size > ISO14443_CRC_SIZE); + + bit_buffer_set_size_bytes(buf, data_size - ISO14443_CRC_SIZE); +} diff --git a/lib/nfc/helpers/iso14443_crc.h b/lib/nfc/helpers/iso14443_crc.h new file mode 100644 index 000000000000..14a63841e71f --- /dev/null +++ b/lib/nfc/helpers/iso14443_crc.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO14443_CRC_SIZE sizeof(uint16_t) + +typedef enum { + Iso14443CrcTypeA, + Iso14443CrcTypeB, +} Iso14443CrcType; + +void iso14443_crc_append(Iso14443CrcType type, BitBuffer* buf); + +bool iso14443_crc_check(Iso14443CrcType type, const BitBuffer* buf); + +void iso14443_crc_trim(BitBuffer* buf); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/nfc_data_generator.c b/lib/nfc/helpers/nfc_data_generator.c new file mode 100644 index 000000000000..d717e402dd1f --- /dev/null +++ b/lib/nfc/helpers/nfc_data_generator.c @@ -0,0 +1,568 @@ +#include "nfc_data_generator.h" + +#include +#include +#include +#include +#include + +#define NXP_MANUFACTURER_ID (0x04) + +typedef void (*NfcDataGeneratorHandler)(NfcDevice* nfc_device); + +typedef struct { + const char* name; + NfcDataGeneratorHandler handler; +} NfcDataGenerator; + +static const uint8_t version_bytes_mf0ulx1[] = {0x00, 0x04, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03}; +static const uint8_t version_bytes_ntag21x[] = {0x00, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x03}; +static const uint8_t version_bytes_ntag_i2c[] = {0x00, 0x04, 0x04, 0x05, 0x02, 0x00, 0x00, 0x03}; +static const uint8_t default_data_ntag203[] = + {0xE1, 0x10, 0x12, 0x00, 0x01, 0x03, 0xA0, 0x10, 0x44, 0x03, 0x00, 0xFE}; +static const uint8_t default_data_ntag213[] = {0x01, 0x03, 0xA0, 0x0C, 0x34, 0x03, 0x00, 0xFE}; +static const uint8_t default_data_ntag215_216[] = {0x03, 0x00, 0xFE}; +static const uint8_t default_data_ntag_i2c[] = {0xE1, 0x10, 0x00, 0x00, 0x03, 0x00, 0xFE}; +static const uint8_t default_config_ntag_i2c[] = {0x01, 0x00, 0xF8, 0x48, 0x08, 0x01, 0x00, 0x00}; + +static void nfc_generate_mf_ul_uid(uint8_t* uid) { + uid[0] = NXP_MANUFACTURER_ID; + furi_hal_random_fill_buf(&uid[1], 6); + // I'm not sure how this is generated, but the upper nybble always seems to be 8 + uid[6] &= 0x0F; + uid[6] |= 0x80; +} + +static void nfc_generate_mf_ul_common(MfUltralightData* mfu_data) { + mfu_data->iso14443_3a_data->uid_len = 7; + nfc_generate_mf_ul_uid(mfu_data->iso14443_3a_data->uid); + mfu_data->iso14443_3a_data->atqa[0] = 0x44; + mfu_data->iso14443_3a_data->atqa[1] = 0x00; + mfu_data->iso14443_3a_data->sak = 0x00; +} + +static void nfc_generate_calc_bcc(uint8_t* uid, uint8_t* bcc0, uint8_t* bcc1) { + *bcc0 = 0x88 ^ uid[0] ^ uid[1] ^ uid[2]; + *bcc1 = uid[3] ^ uid[4] ^ uid[5] ^ uid[6]; +} + +static void nfc_generate_mf_ul_copy_uid_with_bcc(MfUltralightData* mfu_data) { + memcpy(mfu_data->page[0].data, mfu_data->iso14443_3a_data->uid, 3); + memcpy(mfu_data->page[1].data, &mfu_data->iso14443_3a_data->uid[3], 4); + + nfc_generate_calc_bcc( + mfu_data->iso14443_3a_data->uid, &mfu_data->page[0].data[3], &mfu_data->page[2].data[0]); +} + +static void nfc_generate_mf_ul_orig(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + nfc_generate_mf_ul_common(mfu_data); + + mfu_data->type = MfUltralightTypeUnknown; + mfu_data->pages_total = 16; + mfu_data->pages_read = 16; + nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data); + // TODO: what's internal byte on page 2? + memset(&mfu_data->page[4], 0xff, sizeof(MfUltralightPage)); + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_mf_ul_with_config_common(MfUltralightData* mfu_data, uint8_t num_pages) { + nfc_generate_mf_ul_common(mfu_data); + + mfu_data->pages_total = num_pages; + mfu_data->pages_read = num_pages; + nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data); + uint16_t config_index = (num_pages - 4); + mfu_data->page[config_index].data[0] = 0x04; // STRG_MOD_EN + mfu_data->page[config_index].data[3] = 0xff; // AUTH0 + mfu_data->page[config_index + 1].data[1] = 0x05; // VCTID + memset(&mfu_data->page[config_index + 2], 0xff, sizeof(MfUltralightPage)); // Default PWD + if(num_pages > 20) { + mfu_data->page[config_index - 1].data[3] = MF_ULTRALIGHT_TEARING_FLAG_DEFAULT; + } +} + +static void nfc_generate_mf_ul_ev1_common(MfUltralightData* mfu_data, uint8_t num_pages) { + nfc_generate_mf_ul_with_config_common(mfu_data, num_pages); + memcpy(&mfu_data->version, version_bytes_mf0ulx1, sizeof(MfUltralightVersion)); + for(size_t i = 0; i < 3; ++i) { + mfu_data->tearing_flag[i].data[0] = MF_ULTRALIGHT_TEARING_FLAG_DEFAULT; + } + // TODO: what's internal byte on page 2? +} + +static void nfc_generate_mf_ul_11(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_mf_ul_ev1_common(mfu_data, 20); + mfu_data->type = MfUltralightTypeUL11; + mfu_data->version.prod_subtype = 0x01; + mfu_data->version.storage_size = 0x0B; + mfu_data->page[16].data[0] = 0x00; // Low capacitance version does not have STRG_MOD_EN + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_mf_ul_h11(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_mf_ul_ev1_common(mfu_data, 20); + mfu_data->type = MfUltralightTypeUL11; + mfu_data->version.prod_subtype = 0x02; + mfu_data->version.storage_size = 0x0B; + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_mf_ul_21(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_mf_ul_ev1_common(mfu_data, 41); + mfu_data->type = MfUltralightTypeUL21; + mfu_data->version.prod_subtype = 0x01; + mfu_data->version.storage_size = 0x0E; + mfu_data->page[37].data[0] = 0x00; // Low capacitance version does not have STRG_MOD_EN + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_mf_ul_h21(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_mf_ul_ev1_common(mfu_data, 41); + mfu_data->type = MfUltralightTypeUL21; + mfu_data->version.prod_subtype = 0x02; + mfu_data->version.storage_size = 0x0E; + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag203(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_mf_ul_common(mfu_data); + mfu_data->type = MfUltralightTypeNTAG203; + mfu_data->pages_total = 42; + mfu_data->pages_read = 42; + nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data); + mfu_data->page[2].data[1] = 0x48; // Internal byte + memcpy(&mfu_data->page[3], default_data_ntag203, sizeof(MfUltralightPage)); + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag21x_common(MfUltralightData* mfu_data, uint8_t num_pages) { + nfc_generate_mf_ul_with_config_common(mfu_data, num_pages); + memcpy(&mfu_data->version, version_bytes_ntag21x, sizeof(MfUltralightVersion)); + mfu_data->page[2].data[1] = 0x48; // Internal byte + // Capability container + mfu_data->page[3].data[0] = 0xE1; + mfu_data->page[3].data[1] = 0x10; +} + +static void nfc_generate_ntag213(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag21x_common(mfu_data, 45); + mfu_data->type = MfUltralightTypeNTAG213; + mfu_data->version.storage_size = 0x0F; + mfu_data->page[3].data[2] = 0x12; + // Default contents + memcpy(&mfu_data->page[4], default_data_ntag213, sizeof(default_data_ntag213)); + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag215(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag21x_common(mfu_data, 135); + mfu_data->type = MfUltralightTypeNTAG215; + mfu_data->version.storage_size = 0x11; + mfu_data->page[3].data[2] = 0x3E; + // Default contents + memcpy(&mfu_data->page[4], default_data_ntag215_216, sizeof(default_data_ntag215_216)); + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag216(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag21x_common(mfu_data, 231); + mfu_data->type = MfUltralightTypeNTAG216; + mfu_data->version.storage_size = 0x13; + mfu_data->page[3].data[2] = 0x6D; + // Default contents + memcpy(&mfu_data->page[4], default_data_ntag215_216, sizeof(default_data_ntag215_216)); + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag_i2c_common( + MfUltralightData* mfu_data, + MfUltralightType type, + uint16_t num_pages) { + nfc_generate_mf_ul_common(mfu_data); + + mfu_data->type = type; + memcpy(&mfu_data->version, version_bytes_ntag_i2c, sizeof(version_bytes_ntag_i2c)); + mfu_data->pages_total = num_pages; + mfu_data->pages_read = num_pages; + memcpy( + mfu_data->page[0].data, + mfu_data->iso14443_3a_data->uid, + mfu_data->iso14443_3a_data->uid_len); + mfu_data->page[1].data[3] = mfu_data->iso14443_3a_data->sak; + mfu_data->page[2].data[0] = mfu_data->iso14443_3a_data->atqa[0]; + mfu_data->page[2].data[1] = mfu_data->iso14443_3a_data->atqa[1]; + + uint16_t config_register_page = 0; + uint16_t session_register_page = 0; + + // Sync with mifare_ultralight.c + switch(type) { + case MfUltralightTypeNTAGI2C1K: + config_register_page = 227; + session_register_page = 229; + break; + case MfUltralightTypeNTAGI2C2K: + config_register_page = 481; + session_register_page = 483; + break; + case MfUltralightTypeNTAGI2CPlus1K: + case MfUltralightTypeNTAGI2CPlus2K: + config_register_page = 232; + session_register_page = 234; + break; + default: + furi_crash("Unknown MFUL"); + break; + } + + memcpy( + &mfu_data->page[config_register_page], + default_config_ntag_i2c, + sizeof(default_config_ntag_i2c)); + memcpy( + &mfu_data->page[session_register_page], + default_config_ntag_i2c, + sizeof(default_config_ntag_i2c)); +} + +static void nfc_generate_ntag_i2c_1k(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag_i2c_common(mfu_data, MfUltralightTypeNTAGI2C1K, 231); + mfu_data->version.prod_ver_minor = 0x01; + mfu_data->version.storage_size = 0x13; + memcpy(&mfu_data->page[3], default_data_ntag_i2c, sizeof(default_data_ntag_i2c)); + mfu_data->page[3].data[2] = 0x6D; // Size of tag in CC + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag_i2c_2k(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag_i2c_common(mfu_data, MfUltralightTypeNTAGI2C2K, 485); + mfu_data->version.prod_ver_minor = 0x01; + mfu_data->version.storage_size = 0x15; + memcpy(&mfu_data->page[3], default_data_ntag_i2c, sizeof(default_data_ntag_i2c)); + mfu_data->page[3].data[2] = 0xEA; // Size of tag in CC + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag_i2c_plus_common( + MfUltralightData* mfu_data, + MfUltralightType type, + uint16_t num_pages) { + nfc_generate_ntag_i2c_common(mfu_data, type, num_pages); + + uint16_t config_index = 227; + mfu_data->page[config_index].data[3] = 0xff; // AUTH0 + + memset(&mfu_data->page[config_index + 2], 0xFF, sizeof(MfUltralightPage)); // Default PWD +} + +static void nfc_generate_ntag_i2c_plus_1k(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag_i2c_plus_common(mfu_data, MfUltralightTypeNTAGI2CPlus1K, 236); + mfu_data->version.prod_ver_minor = 0x02; + mfu_data->version.storage_size = 0x13; + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag_i2c_plus_2k(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag_i2c_plus_common(mfu_data, MfUltralightTypeNTAGI2CPlus2K, 492); + mfu_data->version.prod_ver_minor = 0x02; + mfu_data->version.storage_size = 0x15; + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_mf_classic_uid(uint8_t* uid, uint8_t length) { + uid[0] = NXP_MANUFACTURER_ID; + furi_hal_random_fill_buf(&uid[1], length - 1); +} + +static void + nfc_generate_mf_classic_common(MfClassicData* data, uint8_t uid_len, MfClassicType type) { + data->iso14443_3a_data->uid_len = uid_len; + data->iso14443_3a_data->atqa[0] = 0x44; + data->iso14443_3a_data->atqa[1] = 0x00; + data->iso14443_3a_data->sak = 0x08; + data->type = type; +} + +static void nfc_generate_mf_classic_sector_trailer(MfClassicData* data, uint8_t block) { + // All keys are set to FFFF FFFF FFFFh at chip delivery and the bytes 6, 7 and 8 are set to FF0780h. + MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)data->block[block].data; + sec_tr->access_bits.data[0] = 0xFF; + sec_tr->access_bits.data[1] = 0x07; + sec_tr->access_bits.data[2] = 0x80; + sec_tr->access_bits.data[3] = 0x69; // Nice + + mf_classic_set_block_read(data, block, &data->block[block]); + mf_classic_set_key_found( + data, mf_classic_get_sector_by_block(block), MfClassicKeyTypeA, 0xFFFFFFFFFFFF); + mf_classic_set_key_found( + data, mf_classic_get_sector_by_block(block), MfClassicKeyTypeB, 0xFFFFFFFFFFFF); +} + +static void nfc_generate_mf_classic_block_0( + uint8_t* block, + uint8_t uid_len, + uint8_t sak, + uint8_t atqa0, + uint8_t atqa1) { + // Block length is always 16 bytes, and the UID can be either 4 or 7 bytes + furi_assert(uid_len == 4 || uid_len == 7); + furi_assert(block); + + if(uid_len == 4) { + // Calculate BCC + block[uid_len] = 0; + + for(int i = 0; i < uid_len; i++) { + block[uid_len] ^= block[i]; + } + } else { + uid_len -= 1; + } + + block[uid_len + 1] = sak; + block[uid_len + 2] = atqa0; + block[uid_len + 3] = atqa1; + + for(int i = uid_len + 4; i < 16; i++) { + block[i] = 0xFF; + } +} + +static void nfc_generate_mf_classic(NfcDevice* nfc_device, uint8_t uid_len, MfClassicType type) { + MfClassicData* mfc_data = mf_classic_alloc(); + + nfc_generate_mf_classic_uid(mfc_data->block[0].data, uid_len); + nfc_generate_mf_classic_common(mfc_data, uid_len, type); + + // Set the UID + mfc_data->iso14443_3a_data->uid[0] = NXP_MANUFACTURER_ID; + for(int i = 1; i < uid_len; i++) { + mfc_data->iso14443_3a_data->uid[i] = mfc_data->block[0].data[i]; + } + + mf_classic_set_block_read(mfc_data, 0, &mfc_data->block[0]); + + uint16_t block_num = mf_classic_get_total_block_num(type); + if(type == MfClassicType4k) { + // Set every block to 0xFF + for(uint16_t i = 1; i < block_num; i++) { + if(mf_classic_is_sector_trailer(i)) { + nfc_generate_mf_classic_sector_trailer(mfc_data, i); + } else { + memset(&mfc_data->block[i].data, 0xFF, 16); + } + mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); + } + // Set SAK to 18 + mfc_data->iso14443_3a_data->sak = 0x18; + } else if(type == MfClassicType1k) { + // Set every block to 0xFF + for(uint16_t i = 1; i < block_num; i++) { + if(mf_classic_is_sector_trailer(i)) { + nfc_generate_mf_classic_sector_trailer(mfc_data, i); + } else { + memset(&mfc_data->block[i].data, 0xFF, 16); + } + mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); + } + // Set SAK to 08 + mfc_data->iso14443_3a_data->sak = 0x08; + } else if(type == MfClassicTypeMini) { + // Set every block to 0xFF + for(uint16_t i = 1; i < block_num; i++) { + if(mf_classic_is_sector_trailer(i)) { + nfc_generate_mf_classic_sector_trailer(mfc_data, i); + } else { + memset(&mfc_data->block[i].data, 0xFF, 16); + } + mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); + } + // Set SAK to 09 + mfc_data->iso14443_3a_data->sak = 0x09; + } + + nfc_generate_mf_classic_block_0( + mfc_data->block[0].data, + uid_len, + mfc_data->iso14443_3a_data->sak, + mfc_data->iso14443_3a_data->atqa[0], + mfc_data->iso14443_3a_data->atqa[1]); + + mfc_data->type = type; + + nfc_device_set_data(nfc_device, NfcProtocolMfClassic, mfc_data); + mf_classic_free(mfc_data); +} + +static void nfc_generate_mf_classic_mini(NfcDevice* nfc_device) { + nfc_generate_mf_classic(nfc_device, 4, MfClassicTypeMini); +} + +static void nfc_generate_mf_classic_1k_4b_uid(NfcDevice* nfc_device) { + nfc_generate_mf_classic(nfc_device, 4, MfClassicType1k); +} + +static void nfc_generate_mf_classic_1k_7b_uid(NfcDevice* nfc_device) { + nfc_generate_mf_classic(nfc_device, 7, MfClassicType1k); +} + +static void nfc_generate_mf_classic_4k_4b_uid(NfcDevice* nfc_device) { + nfc_generate_mf_classic(nfc_device, 4, MfClassicType4k); +} + +static void nfc_generate_mf_classic_4k_7b_uid(NfcDevice* nfc_device) { + nfc_generate_mf_classic(nfc_device, 7, MfClassicType4k); +} + +static const NfcDataGenerator nfc_data_generator[NfcDataGeneratorTypeNum] = { + [NfcDataGeneratorTypeMfUltralight] = + { + .name = "Mifare Ultralight", + .handler = nfc_generate_mf_ul_orig, + }, + [NfcDataGeneratorTypeMfUltralightEV1_11] = + { + .name = "Mifare Ultralight EV1 11", + .handler = nfc_generate_mf_ul_11, + }, + [NfcDataGeneratorTypeMfUltralightEV1_H11] = + { + .name = "Mifare Ultralight EV1 H11", + .handler = nfc_generate_mf_ul_h11, + }, + [NfcDataGeneratorTypeMfUltralightEV1_21] = + { + .name = "Mifare Ultralight EV1 21", + .handler = nfc_generate_mf_ul_21, + }, + [NfcDataGeneratorTypeMfUltralightEV1_H21] = + { + .name = "Mifare Ultralight EV1 H21", + .handler = nfc_generate_mf_ul_h21, + }, + [NfcDataGeneratorTypeNTAG203] = + { + .name = "NTAG203", + .handler = nfc_generate_ntag203, + }, + [NfcDataGeneratorTypeNTAG213] = + { + .name = "NTAG213", + .handler = nfc_generate_ntag213, + }, + [NfcDataGeneratorTypeNTAG215] = + { + .name = "NTAG215", + .handler = nfc_generate_ntag215, + }, + [NfcDataGeneratorTypeNTAG216] = + { + .name = "NTAG216", + .handler = nfc_generate_ntag216, + }, + [NfcDataGeneratorTypeNTAGI2C1k] = + { + .name = "NTAG I2C 1k", + .handler = nfc_generate_ntag_i2c_1k, + }, + [NfcDataGeneratorTypeNTAGI2C2k] = + { + .name = "NTAG I2C 2k", + .handler = nfc_generate_ntag_i2c_2k, + }, + [NfcDataGeneratorTypeNTAGI2CPlus1k] = + { + .name = "NTAG I2C Plus 1k", + .handler = nfc_generate_ntag_i2c_plus_1k, + }, + [NfcDataGeneratorTypeNTAGI2CPlus2k] = + { + .name = "NTAG I2C Plus 2k", + .handler = nfc_generate_ntag_i2c_plus_2k, + }, + [NfcDataGeneratorTypeMfClassicMini] = + { + .name = "Mifare Mini", + .handler = nfc_generate_mf_classic_mini, + }, + [NfcDataGeneratorTypeMfClassic1k_4b] = + { + .name = "Mifare Classic 1k 4byte UID", + .handler = nfc_generate_mf_classic_1k_4b_uid, + }, + [NfcDataGeneratorTypeMfClassic1k_7b] = + { + .name = "Mifare Classic 1k 7byte UID", + .handler = nfc_generate_mf_classic_1k_7b_uid, + }, + [NfcDataGeneratorTypeMfClassic4k_4b] = + { + .name = "Mifare Classic 4k 4byte UID", + .handler = nfc_generate_mf_classic_4k_4b_uid, + }, + [NfcDataGeneratorTypeMfClassic4k_7b] = + { + .name = "Mifare Classic 4k 7byte UID", + .handler = nfc_generate_mf_classic_4k_7b_uid, + }, +}; + +const char* nfc_data_generator_get_name(NfcDataGeneratorType type) { + return nfc_data_generator[type].name; +} + +void nfc_data_generator_fill_data(NfcDataGeneratorType type, NfcDevice* nfc_device) { + nfc_data_generator[type].handler(nfc_device); +} diff --git a/lib/nfc/helpers/nfc_data_generator.h b/lib/nfc/helpers/nfc_data_generator.h new file mode 100644 index 000000000000..01f5dac16e5d --- /dev/null +++ b/lib/nfc/helpers/nfc_data_generator.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + NfcDataGeneratorTypeMfUltralight, + NfcDataGeneratorTypeMfUltralightEV1_11, + NfcDataGeneratorTypeMfUltralightEV1_H11, + NfcDataGeneratorTypeMfUltralightEV1_21, + NfcDataGeneratorTypeMfUltralightEV1_H21, + NfcDataGeneratorTypeNTAG203, + NfcDataGeneratorTypeNTAG213, + NfcDataGeneratorTypeNTAG215, + NfcDataGeneratorTypeNTAG216, + NfcDataGeneratorTypeNTAGI2C1k, + NfcDataGeneratorTypeNTAGI2C2k, + NfcDataGeneratorTypeNTAGI2CPlus1k, + NfcDataGeneratorTypeNTAGI2CPlus2k, + + NfcDataGeneratorTypeMfClassicMini, + NfcDataGeneratorTypeMfClassic1k_4b, + NfcDataGeneratorTypeMfClassic1k_7b, + NfcDataGeneratorTypeMfClassic4k_4b, + NfcDataGeneratorTypeMfClassic4k_7b, + + NfcDataGeneratorTypeNum, + +} NfcDataGeneratorType; + +const char* nfc_data_generator_get_name(NfcDataGeneratorType type); + +void nfc_data_generator_fill_data(NfcDataGeneratorType type, NfcDevice* nfc_device); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/nfc_generators.h b/lib/nfc/helpers/nfc_generators.h deleted file mode 100644 index 8cee67067cac..000000000000 --- a/lib/nfc/helpers/nfc_generators.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "../nfc_device.h" - -typedef void (*NfcGeneratorFunc)(NfcDeviceData* data); - -typedef struct { - const char* name; - NfcGeneratorFunc generator_func; -} NfcGenerator; - -extern const NfcGenerator* const nfc_generators[]; - -void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type); diff --git a/lib/nfc/protocols/nfc_util.c b/lib/nfc/helpers/nfc_util.c similarity index 100% rename from lib/nfc/protocols/nfc_util.c rename to lib/nfc/helpers/nfc_util.c diff --git a/lib/nfc/protocols/nfc_util.h b/lib/nfc/helpers/nfc_util.h similarity index 82% rename from lib/nfc/protocols/nfc_util.h rename to lib/nfc/helpers/nfc_util.h index 04fa7622b931..a9d5a3f8ab0c 100644 --- a/lib/nfc/protocols/nfc_util.h +++ b/lib/nfc/helpers/nfc_util.h @@ -2,6 +2,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest); uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len); @@ -11,3 +15,7 @@ uint8_t nfc_util_even_parity32(uint32_t data); uint8_t nfc_util_odd_parity8(uint8_t data); void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc.c b/lib/nfc/nfc.c new file mode 100644 index 000000000000..f4a58236d662 --- /dev/null +++ b/lib/nfc/nfc.c @@ -0,0 +1,603 @@ +#ifndef APP_UNIT_TESTS + +#include "nfc.h" + +#include +#include + +#define TAG "Nfc" + +#define NFC_MAX_BUFFER_SIZE (256) + +typedef enum { + NfcStateIdle, + NfcStateRunning, +} NfcState; + +typedef enum { + NfcPollerStateIdle, + NfcPollerStateStart, + NfcPollerStateReady, + NfcPollerStateReset, + NfcPollerStateStop, + + NfcPollerStateNum, +} NfcPollerState; + +typedef enum { + NfcCommStateIdle, + NfcCommStateWaitBlockTxTimer, + NfcCommStateReadyTx, + NfcCommStateWaitTxEnd, + NfcCommStateWaitRxStart, + NfcCommStateWaitRxEnd, + NfcCommStateFailed, +} NfcCommState; + +typedef enum { + NfcConfigurationStateIdle, + NfcConfigurationStateDone, +} NfcConfigurationState; + +struct Nfc { + NfcState state; + NfcPollerState poller_state; + NfcCommState comm_state; + NfcConfigurationState config_state; + NfcMode mode; + + uint32_t fdt_listen_fc; + uint32_t mask_rx_time_fc; + uint32_t fdt_poll_fc; + uint32_t fdt_poll_poll_us; + uint32_t guard_time_us; + NfcEventCallback callback; + void* context; + + uint8_t tx_buffer[NFC_MAX_BUFFER_SIZE]; + size_t tx_bits; + uint8_t rx_buffer[NFC_MAX_BUFFER_SIZE]; + size_t rx_bits; + + FuriThread* worker_thread; +}; + +typedef bool (*NfcWorkerPollerStateHandler)(Nfc* instance); + +static const FHalNfcTech nfc_tech_table[NfcModeNum][NfcTechNum] = { + [NfcModePoller] = + { + [NfcTechIso14443a] = FHalNfcTechIso14443a, + [NfcTechIso14443b] = FHalNfcTechIso14443b, + [NfcTechIso15693] = FHalNfcTechIso15693, + [NfcTechFelica] = FHalNfcTechFelica, + }, + [NfcModeListener] = + { + [NfcTechIso14443a] = FHalNfcTechIso14443a, + [NfcTechIso14443b] = FHalNfcTechInvalid, + [NfcTechIso15693] = FHalNfcTechIso15693, + [NfcTechFelica] = FHalNfcTechInvalid, + }, +}; + +static NfcError nfc_process_hal_error(FHalNfcError error) { + NfcError err = NfcErrorNone; + + if(error == FHalNfcErrorNone) { + err = NfcErrorNone; + } else if(error == FHalNfcErrorChipCommunication) { + err = NfcErrorInternal; + } + + return err; +} + +static int32_t nfc_worker_listener(void* context) { + furi_assert(context); + + Nfc* instance = context; + furi_assert(instance->callback); + furi_assert(instance->config_state == NfcConfigurationStateDone); + + instance->state = NfcStateRunning; + + f_hal_nfc_event_start(); + + NfcEventData event_data = {}; + event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE); + NfcEvent nfc_event = {.data = event_data}; + NfcCommand command = NfcCommandContinue; + + while(true) { + FHalNfcEvent event = f_hal_nfc_listener_wait_event(F_HAL_NFC_EVENT_WAIT_FOREVER); + if(event & FHalNfcEventAbortRequest) { + nfc_event.type = NfcEventTypeUserAbort; + instance->callback(nfc_event, instance->context); + break; + } + if(event & FHalNfcEventFieldOn) { + nfc_event.type = NfcEventTypeFieldOn; + instance->callback(nfc_event, instance->context); + } + if(event & FHalNfcEventFieldOff) { + nfc_event.type = NfcEventTypeFieldOff; + instance->callback(nfc_event, instance->context); + f_hal_nfc_listener_idle(); + } + // TODO maybe remove? + if(event & FHalNfcEventListenerActive) { + nfc_event.type = NfcEventTypeListenerActivated; + instance->callback(nfc_event, instance->context); + } + if(event & FHalNfcEventRxEnd) { + nfc_event.type = NfcEventTypeRxEnd; + f_hal_nfc_listener_rx( + instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits); + bit_buffer_copy_bits(event_data.buffer, instance->rx_buffer, instance->rx_bits); + command = instance->callback(nfc_event, instance->context); + if(command == NfcCommandStop) { + break; + } else if(command == NfcCommandReset) { + f_hal_nfc_listener_enable_rx(); + } else if(command == NfcCommandSleep) { + f_hal_nfc_listener_sleep(); + } + } + } + + f_hal_nfc_reset_mode(); + instance->config_state = NfcConfigurationStateIdle; + + bit_buffer_free(event_data.buffer); + f_hal_nfc_low_power_mode_start(); + return 0; +} + +bool nfc_worker_poller_idle_handler(Nfc* instance) { + instance->poller_state = NfcPollerStateStart; + + return false; +} + +bool nfc_worker_poller_start_handler(Nfc* instance) { + f_hal_nfc_poller_field_on(); + if(instance->guard_time_us) { + f_hal_nfc_timer_block_tx_start_us(instance->guard_time_us); + FHalNfcEvent event = f_hal_nfc_poller_wait_event(F_HAL_NFC_EVENT_WAIT_FOREVER); + furi_assert(event & FHalNfcEventTimerBlockTxExpired); + } + instance->poller_state = NfcPollerStateReady; + + return false; +} + +bool nfc_worker_poller_ready_handler(Nfc* instance) { + NfcCommand command = NfcCommandContinue; + + NfcEvent event = {.type = NfcEventTypePollerReady}; + command = instance->callback(event, instance->context); + if(command == NfcCommandReset) { + instance->poller_state = NfcPollerStateReset; + } else if(command == NfcCommandStop) { + instance->poller_state = NfcPollerStateStop; + } + + return false; +} + +bool nfc_worker_poller_reset_handler(Nfc* instance) { + f_hal_nfc_low_power_mode_start(); + furi_delay_ms(100); + f_hal_nfc_low_power_mode_stop(); + instance->poller_state = NfcPollerStateIdle; + + return false; +} + +bool nfc_worker_poller_stop_handler(Nfc* instance) { + f_hal_nfc_reset_mode(); + instance->config_state = NfcConfigurationStateIdle; + + f_hal_nfc_low_power_mode_start(); + instance->poller_state = NfcPollerStateIdle; + + return true; +} + +static const NfcWorkerPollerStateHandler nfc_worker_poller_state_handlers[NfcPollerStateNum] = { + [NfcPollerStateIdle] = nfc_worker_poller_idle_handler, + [NfcPollerStateStart] = nfc_worker_poller_start_handler, + [NfcPollerStateReady] = nfc_worker_poller_ready_handler, + [NfcPollerStateReset] = nfc_worker_poller_reset_handler, + [NfcPollerStateStop] = nfc_worker_poller_stop_handler, +}; + +static int32_t nfc_worker_poller(void* context) { + furi_assert(context); + + Nfc* instance = context; + furi_assert(instance->callback); + instance->state = NfcStateRunning; + instance->poller_state = NfcPollerStateIdle; + + f_hal_nfc_event_start(); + + bool exit = false; + while(!exit) { + exit = nfc_worker_poller_state_handlers[instance->poller_state](instance); + } + + return 0; +} + +Nfc* nfc_alloc() { + furi_check(f_hal_nfc_acquire() == FHalNfcErrorNone); + + Nfc* instance = malloc(sizeof(Nfc)); + instance->state = NfcStateIdle; + instance->comm_state = NfcCommStateIdle; + instance->config_state = NfcConfigurationStateIdle; + instance->poller_state = NfcPollerStateIdle; + + instance->worker_thread = furi_thread_alloc(); + furi_thread_set_name(instance->worker_thread, "NfcWorker"); + furi_thread_set_context(instance->worker_thread, instance); + furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHighest); + furi_thread_set_stack_size(instance->worker_thread, 8 * 1024); + + return instance; +} + +void nfc_free(Nfc* instance) { + furi_assert(instance); + furi_assert(instance->state == NfcStateIdle); + + furi_thread_free(instance->worker_thread); + free(instance); + + f_hal_nfc_release(); +} + +void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) { + furi_assert(instance); + furi_assert(mode < NfcModeNum); + furi_assert(tech < NfcTechNum); + furi_assert(instance->config_state == NfcConfigurationStateIdle); + + FHalNfcTech hal_tech = nfc_tech_table[mode][tech]; + if(hal_tech == FHalNfcTechInvalid) { + furi_crash("Unsupported mode for given tech"); + } + FHalNfcMode hal_mode = (mode == NfcModePoller) ? FHalNfcModePoller : FHalNfcModeListener; + f_hal_nfc_low_power_mode_stop(); + f_hal_nfc_set_mode(hal_mode, hal_tech); + + instance->mode = mode; + instance->config_state = NfcConfigurationStateDone; +} + +void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) { + furi_assert(instance); + instance->fdt_poll_fc = fdt_poll_fc; +} + +void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) { + furi_assert(instance); + instance->fdt_listen_fc = fdt_listen_fc; +} + +void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) { + furi_assert(instance); + instance->fdt_poll_poll_us = fdt_poll_poll_us; +} + +void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) { + furi_assert(instance); + instance->guard_time_us = guard_time_us; +} + +void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) { + furi_assert(instance); + instance->mask_rx_time_fc = mask_rx_time_fc; +} + +void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) { + furi_assert(instance); + furi_assert(instance->worker_thread); + furi_assert(callback); + furi_assert(instance->config_state == NfcConfigurationStateDone); + + instance->callback = callback; + instance->context = context; + if(instance->mode == NfcModePoller) { + furi_thread_set_callback(instance->worker_thread, nfc_worker_poller); + } else { + furi_thread_set_callback(instance->worker_thread, nfc_worker_listener); + } + furi_thread_start(instance->worker_thread); + instance->comm_state = NfcCommStateIdle; +} + +void nfc_stop(Nfc* instance) { + furi_assert(instance); + furi_assert(instance->state == NfcStateRunning); + + if(instance->mode == NfcModeListener) { + f_hal_nfc_abort(); + } + furi_thread_join(instance->worker_thread); + + instance->state = NfcStateIdle; +} + +NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + NfcError ret = NfcErrorNone; + FHalNfcError error = + f_hal_nfc_listener_tx(bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer)); + if(error != FHalNfcErrorNone) { + FURI_LOG_E(TAG, "Failed in listener TX"); + ret = nfc_process_hal_error(error); + } + + return ret; +} + +static NfcError nfc_poller_trx_state_machine(Nfc* instance, uint32_t fwt_fc) { + FHalNfcEvent event = 0; + NfcError error = NfcErrorNone; + + while(true) { + // TODO sanity timeout check + event = f_hal_nfc_poller_wait_event(F_HAL_NFC_EVENT_WAIT_FOREVER); + if(event & FHalNfcEventTimerBlockTxExpired) { + if(instance->comm_state == NfcCommStateWaitBlockTxTimer) { + instance->comm_state = NfcCommStateReadyTx; + } + } + if(event & FHalNfcEventTxEnd) { + if(instance->comm_state == NfcCommStateWaitTxEnd) { + if(fwt_fc) { + f_hal_nfc_timer_fwt_start(fwt_fc + F_HAL_NFC_TIMER_OFFSET_FC); + } + f_hal_nfc_timer_block_tx_start_us(instance->fdt_poll_poll_us); + instance->comm_state = NfcCommStateWaitRxStart; + } + } + if(event & FHalNfcEventRxStart) { + if(instance->comm_state == NfcCommStateWaitRxStart) { + f_hal_nfc_timer_block_tx_stop(); + f_hal_nfc_timer_fwt_stop(); + instance->comm_state = NfcCommStateWaitRxEnd; + } + } + if(event & FHalNfcEventRxEnd) { + f_hal_nfc_timer_block_tx_start(instance->fdt_poll_fc); + f_hal_nfc_timer_fwt_stop(); + instance->comm_state = NfcCommStateWaitBlockTxTimer; + break; + } + if(event & FHalNfcEventTimerFwtExpired) { + if(instance->comm_state == NfcCommStateWaitRxStart) { + error = NfcErrorTimeout; + FURI_LOG_W(TAG, "FWT Timeout"); + if(f_hal_nfc_timer_block_tx_is_running()) { + instance->comm_state = NfcCommStateWaitBlockTxTimer; + } else { + instance->comm_state = NfcCommStateReadyTx; + } + break; + } + } + } + + return error; +} + +NfcError nfc_iso14443a_poller_trx_custom_parity( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + furi_assert(instance->poller_state == NfcPollerStateReady); + + NfcError ret = NfcErrorNone; + FHalNfcError error = FHalNfcErrorNone; + do { + f_hal_nfc_trx_reset(); + bit_buffer_write_bytes_with_parity( + tx_buffer, instance->tx_buffer, sizeof(instance->tx_buffer), &instance->tx_bits); + error = + f_hal_nfc_iso14443a_poller_tx_custom_parity(instance->tx_buffer, instance->tx_bits); + if(error != FHalNfcErrorNone) { + FURI_LOG_E(TAG, "Failed in poller TX"); + ret = nfc_process_hal_error(error); + break; + } + instance->comm_state = NfcCommStateWaitTxEnd; + ret = nfc_poller_trx_state_machine(instance, fwt); + if(ret != NfcErrorNone) { + FURI_LOG_E(TAG, "Failed TRX state machine"); + break; + } + + error = f_hal_nfc_poller_rx( + instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits); + if(error != FHalNfcErrorNone) { + FURI_LOG_E(TAG, "Failed in poller RX"); + ret = nfc_process_hal_error(error); + break; + } + + bit_buffer_copy_bytes_with_parity(rx_buffer, instance->rx_buffer, instance->rx_bits); + } while(false); + + return ret; +} + +NfcError + nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + furi_assert(instance->poller_state == NfcPollerStateReady); + + NfcError ret = NfcErrorNone; + FHalNfcError error = FHalNfcErrorNone; + do { + f_hal_nfc_trx_reset(); + error = + f_hal_nfc_poller_tx(bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer)); + if(error != FHalNfcErrorNone) { + FURI_LOG_E(TAG, "Failed in poller TX"); + ret = nfc_process_hal_error(error); + break; + } + instance->comm_state = NfcCommStateWaitTxEnd; + ret = nfc_poller_trx_state_machine(instance, fwt); + if(ret != NfcErrorNone) { + FURI_LOG_E(TAG, "Failed TRX state machine"); + break; + } + + error = f_hal_nfc_poller_rx( + instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits); + if(error != FHalNfcErrorNone) { + FURI_LOG_E(TAG, "Failed in poller RX"); + ret = nfc_process_hal_error(error); + break; + } + + bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits); + } while(false); + + return ret; +} + +NfcError nfc_iso14443a_listener_set_col_res_data( + Nfc* instance, + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak) { + furi_assert(instance); + + FHalNfcError error = f_hal_nfc_iso14443a_listener_set_col_res_data(uid, uid_len, atqa, sak); + instance->comm_state = NfcCommStateIdle; + return nfc_process_hal_error(error); +} + +NfcError nfc_iso14443a_poller_trx_short_frame( + Nfc* instance, + NfcIso14443aShortFrame frame, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(rx_buffer); + + FHalNfcaShortFrame short_frame = (frame == NfcIso14443aShortFrameAllReqa) ? + FHalNfcaShortFrameAllReq : + FHalNfcaShortFrameSensReq; + + furi_assert(instance->poller_state == NfcPollerStateReady); + + NfcError ret = NfcErrorNone; + FHalNfcError error = FHalNfcErrorNone; + do { + f_hal_nfc_trx_reset(); + error = f_hal_nfc_iso14443a_poller_trx_short_frame(short_frame); + if(error != FHalNfcErrorNone) { + FURI_LOG_E(TAG, "Failed in poller TX"); + ret = nfc_process_hal_error(error); + break; + } + instance->comm_state = NfcCommStateWaitTxEnd; + ret = nfc_poller_trx_state_machine(instance, fwt); + if(ret != NfcErrorNone) { + FURI_LOG_E(TAG, "Failed TRX state machine"); + break; + } + + error = f_hal_nfc_poller_rx( + instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits); + if(error != FHalNfcErrorNone) { + FURI_LOG_E(TAG, "Failed in poller RX"); + ret = nfc_process_hal_error(error); + break; + } + + bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits); + } while(false); + + return ret; +} + +NfcError nfc_iso14443a_poller_trx_sdd_frame( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + furi_assert(instance->poller_state == NfcPollerStateReady); + + NfcError ret = NfcErrorNone; + FHalNfcError error = FHalNfcErrorNone; + do { + f_hal_nfc_trx_reset(); + error = f_hal_nfc_iso14443a_tx_sdd_frame( + bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer)); + if(error != FHalNfcErrorNone) { + FURI_LOG_E(TAG, "Failed in poller TX"); + ret = nfc_process_hal_error(error); + break; + } + instance->comm_state = NfcCommStateWaitTxEnd; + ret = nfc_poller_trx_state_machine(instance, fwt); + if(ret != NfcErrorNone) { + FURI_LOG_E(TAG, "Failed TRX state machine"); + break; + } + + error = f_hal_nfc_poller_rx( + instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits); + if(error != FHalNfcErrorNone) { + FURI_LOG_E(TAG, "Failed in poller RX"); + ret = nfc_process_hal_error(error); + break; + } + + bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits); + } while(false); + + return ret; +} + +NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + NfcError ret = NfcErrorNone; + FHalNfcError error = FHalNfcErrorNone; + + const uint8_t* tx_data = bit_buffer_get_data(tx_buffer); + const uint8_t* tx_parity = bit_buffer_get_parity(tx_buffer); + size_t tx_bits = bit_buffer_get_size(tx_buffer); + + error = f_hal_nfc_iso14443a_listener_tx_custom_parity(tx_data, tx_parity, tx_bits); + ret = nfc_process_hal_error(error); + + return ret; +} + +#endif // APP_UNIT_TESTS diff --git a/lib/nfc/nfc.h b/lib/nfc/nfc.h new file mode 100644 index 000000000000..6fdb88d7ccfa --- /dev/null +++ b/lib/nfc/nfc.h @@ -0,0 +1,131 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Nfc Nfc; + +typedef enum { + NfcEventTypeUserAbort, + NfcEventTypeFieldOn, + NfcEventTypeFieldOff, + NfcEventTypeTxStart, + NfcEventTypeTxEnd, + NfcEventTypeRxStart, + NfcEventTypeRxEnd, + + NfcEventTypeListenerActivated, + NfcEventTypePollerReady, +} NfcEventType; + +typedef struct { + BitBuffer* buffer; +} NfcEventData; + +typedef struct { + NfcEventType type; + NfcEventData data; +} NfcEvent; + +typedef enum { + NfcCommandContinue, + NfcCommandReset, + NfcCommandStop, + NfcCommandSleep, +} NfcCommand; + +typedef NfcCommand (*NfcEventCallback)(NfcEvent event, void* context); + +typedef enum { + NfcModePoller, + NfcModeListener, + + NfcModeNum, +} NfcMode; + +typedef enum { + NfcTechIso14443a, + NfcTechIso14443b, + NfcTechIso15693, + NfcTechFelica, + + NfcTechNum, +} NfcTech; + +typedef enum { + NfcErrorNone, + NfcErrorInternal, + NfcErrorTimeout, + NfcErrorWrongState, + NfcErrorCollision, + NfcErrorLinkLoss, + NfcErrorAbortRequest, +} NfcError; + +typedef enum { + NfcIso14443aShortFrameSensReq, + NfcIso14443aShortFrameAllReqa, +} NfcIso14443aShortFrame; + +Nfc* nfc_alloc(); + +void nfc_free(Nfc* instance); + +void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech); + +void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc); + +void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc); + +void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc); + +void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us); + +void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us); + +void nfc_start(Nfc* instance, NfcEventCallback callback, void* context); + +void nfc_stop(Nfc* instance); + +// Called from worker thread + +NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer); + +NfcError + nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt); + +// Technology specific API + +NfcError nfc_iso14443a_listener_set_col_res_data( + Nfc* instance, + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak); + +NfcError nfc_iso14443a_poller_trx_short_frame( + Nfc* instance, + NfcIso14443aShortFrame frame, + BitBuffer* rx_buffer, + uint32_t fwt); + +NfcError nfc_iso14443a_poller_trx_sdd_frame( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer); + +NfcError nfc_iso14443a_poller_trx_custom_parity( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_common.h b/lib/nfc/nfc_common.h new file mode 100644 index 000000000000..c6fe19c20340 --- /dev/null +++ b/lib/nfc/nfc_common.h @@ -0,0 +1,14 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFC_LSB_ATQA_FORMAT_VERSION (2) +#define NFC_MINIMUM_SUPPORTED_FORMAT_VERSION NFC_LSB_ATQA_FORMAT_VERSION +#define NFC_UNIFIED_FORMAT_VERSION (4) +#define NFC_CURRENT_FORMAT_VERSION NFC_UNIFIED_FORMAT_VERSION + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 5179130702d8..4e4250964936 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -1,1380 +1,350 @@ -#include "nfc_device.h" -#include "assets_icons.h" -#include "nfc_types.h" +#include "nfc_device_i.h" -#include -#include -#include +#include #include -#define TAG "NfcDevice" -#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/.cache") -#define NFC_DEVICE_KEYS_EXTENSION ".keys" +#include "nfc_common.h" +#include "protocols/nfc_device_defs.h" -static const char* nfc_file_header = "Flipper NFC device"; -static const uint32_t nfc_file_version = 3; +#define NFC_FILE_HEADER "Flipper NFC device" +#define NFC_DEV_TYPE_ERROR "Protocol type mismatch" -static const char* nfc_keys_file_header = "Flipper NFC keys"; -static const uint32_t nfc_keys_file_version = 1; +#define NFC_DEVICE_UID_KEY "UID" +#define NFC_DEVICE_TYPE_KEY "Device type" -// Protocols format versions -static const uint32_t nfc_mifare_classic_data_format_version = 2; -static const uint32_t nfc_mifare_ultralight_data_format_version = 1; +#define NFC_DEVICE_UID_MAX_LEN (10U) NfcDevice* nfc_device_alloc() { - NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); - nfc_dev->storage = furi_record_open(RECORD_STORAGE); - nfc_dev->dialogs = furi_record_open(RECORD_DIALOGS); - nfc_dev->load_path = furi_string_alloc(); - nfc_dev->dev_data.parsed_data = furi_string_alloc(); - nfc_dev->folder = furi_string_alloc(); - - // Rename cache folder name for backward compatibility - if(storage_common_stat(nfc_dev->storage, "/ext/nfc/cache", NULL) == FSE_OK) { - storage_common_rename(nfc_dev->storage, "/ext/nfc/cache", NFC_DEVICE_KEYS_FOLDER); - } - return nfc_dev; -} + NfcDevice* instance = malloc(sizeof(NfcDevice)); + instance->protocol = NfcProtocolInvalid; -void nfc_device_free(NfcDevice* nfc_dev) { - furi_assert(nfc_dev); - nfc_device_clear(nfc_dev); - furi_record_close(RECORD_STORAGE); - furi_record_close(RECORD_DIALOGS); - furi_string_free(nfc_dev->load_path); - furi_string_free(nfc_dev->dev_data.parsed_data); - furi_string_free(nfc_dev->folder); - free(nfc_dev); + return instance; } -static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_string) { - if(dev->format == NfcDeviceSaveFormatUid) { - furi_string_set(format_string, "UID"); - } else if(dev->format == NfcDeviceSaveFormatBankCard) { - furi_string_set(format_string, "Bank card"); - } else if(dev->format == NfcDeviceSaveFormatMifareUl) { - furi_string_set(format_string, nfc_mf_ul_type(dev->dev_data.mf_ul_data.type, true)); - } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { - furi_string_set(format_string, "Mifare Classic"); - } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { - furi_string_set(format_string, "Mifare DESFire"); - } else { - furi_string_set(format_string, "Unknown"); - } -} +void nfc_device_free(NfcDevice* instance) { + furi_assert(instance); -static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_string) { - if(furi_string_start_with_str(format_string, "UID")) { - dev->format = NfcDeviceSaveFormatUid; - dev->dev_data.protocol = NfcDeviceProtocolUnknown; - return true; - } - if(furi_string_start_with_str(format_string, "Bank card")) { - dev->format = NfcDeviceSaveFormatBankCard; - dev->dev_data.protocol = NfcDeviceProtocolEMV; - return true; - } - // Check Mifare Ultralight types - for(MfUltralightType type = MfUltralightTypeUnknown; type < MfUltralightTypeNum; type++) { - if(furi_string_equal(format_string, nfc_mf_ul_type(type, true))) { - dev->format = NfcDeviceSaveFormatMifareUl; - dev->dev_data.protocol = NfcDeviceProtocolMifareUl; - dev->dev_data.mf_ul_data.type = type; - return true; - } - } - if(furi_string_start_with_str(format_string, "Mifare Classic")) { - dev->format = NfcDeviceSaveFormatMifareClassic; - dev->dev_data.protocol = NfcDeviceProtocolMifareClassic; - return true; - } - if(furi_string_start_with_str(format_string, "Mifare DESFire")) { - dev->format = NfcDeviceSaveFormatMifareDesfire; - dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire; - return true; - } - return false; + nfc_device_clear(instance); + free(instance); } -static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - MfUltralightData* data = &dev->dev_data.mf_ul_data; - FuriString* temp_str; - temp_str = furi_string_alloc(); +void nfc_device_clear(NfcDevice* instance) { + furi_assert(instance); - // Save Mifare Ultralight specific data - do { - if(!flipper_format_write_comment_cstr(file, "Mifare Ultralight specific data")) break; - if(!flipper_format_write_uint32( - file, "Data format version", &nfc_mifare_ultralight_data_format_version, 1)) - break; - if(!flipper_format_write_hex(file, "Signature", data->signature, sizeof(data->signature))) - break; - if(!flipper_format_write_hex( - file, "Mifare version", (uint8_t*)&data->version, sizeof(data->version))) - break; - // Write conters and tearing flags data - bool counters_saved = true; - for(uint8_t i = 0; i < 3; i++) { - furi_string_printf(temp_str, "Counter %d", i); - if(!flipper_format_write_uint32( - file, furi_string_get_cstr(temp_str), &data->counter[i], 1)) { - counters_saved = false; - break; - } - furi_string_printf(temp_str, "Tearing %d", i); - if(!flipper_format_write_hex( - file, furi_string_get_cstr(temp_str), &data->tearing[i], 1)) { - counters_saved = false; - break; - } - } - if(!counters_saved) break; - // Write pages data - uint32_t pages_total = data->data_size / 4; - if(!flipper_format_write_uint32(file, "Pages total", &pages_total, 1)) break; - uint32_t pages_read = data->data_read / 4; - if(!flipper_format_write_uint32(file, "Pages read", &pages_read, 1)) break; - bool pages_saved = true; - for(uint16_t i = 0; i < data->data_size; i += 4) { - furi_string_printf(temp_str, "Page %d", i / 4); - if(!flipper_format_write_hex(file, furi_string_get_cstr(temp_str), &data->data[i], 4)) { - pages_saved = false; - break; - } + if(instance->protocol == NfcProtocolInvalid) { + furi_assert(instance->protocol_data == NULL); + } else if(instance->protocol < NfcProtocolNum) { + if(instance->protocol_data) { + nfc_devices[instance->protocol]->free(instance->protocol_data); + instance->protocol_data = NULL; } - if(!pages_saved) break; - - // Write authentication counter - uint32_t auth_counter = data->curr_authlim; - if(!flipper_format_write_uint32(file, "Failed authentication attempts", &auth_counter, 1)) - break; - - saved = true; - } while(false); - - furi_string_free(temp_str); - return saved; + instance->protocol = NfcProtocolInvalid; + } } -bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - MfUltralightData* data = &dev->dev_data.mf_ul_data; - FuriString* temp_str; - temp_str = furi_string_alloc(); - uint32_t data_format_version = 0; +void nfc_device_reset(NfcDevice* instance) { + furi_assert(instance); + furi_assert(instance->protocol < NfcProtocolNum); - do { - // Read Mifare Ultralight format version - if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { - if(!flipper_format_rewind(file)) break; - } - - // Read signature - if(!flipper_format_read_hex(file, "Signature", data->signature, sizeof(data->signature))) - break; - // Read Mifare version - if(!flipper_format_read_hex( - file, "Mifare version", (uint8_t*)&data->version, sizeof(data->version))) - break; - // Read counters and tearing flags - bool counters_parsed = true; - for(uint8_t i = 0; i < 3; i++) { - furi_string_printf(temp_str, "Counter %d", i); - if(!flipper_format_read_uint32( - file, furi_string_get_cstr(temp_str), &data->counter[i], 1)) { - counters_parsed = false; - break; - } - furi_string_printf(temp_str, "Tearing %d", i); - if(!flipper_format_read_hex( - file, furi_string_get_cstr(temp_str), &data->tearing[i], 1)) { - counters_parsed = false; - break; - } - } - if(!counters_parsed) break; - // Read pages - uint32_t pages_total = 0; - if(!flipper_format_read_uint32(file, "Pages total", &pages_total, 1)) break; - uint32_t pages_read = 0; - if(data_format_version < nfc_mifare_ultralight_data_format_version) { - pages_read = pages_total; - } else { - if(!flipper_format_read_uint32(file, "Pages read", &pages_read, 1)) break; - } - data->data_size = pages_total * 4; - data->data_read = pages_read * 4; - if(data->data_size > MF_UL_MAX_DUMP_SIZE || data->data_read > MF_UL_MAX_DUMP_SIZE) break; - bool pages_parsed = true; - for(uint16_t i = 0; i < pages_total; i++) { - furi_string_printf(temp_str, "Page %d", i); - if(!flipper_format_read_hex( - file, furi_string_get_cstr(temp_str), &data->data[i * 4], 4)) { - pages_parsed = false; - break; - } - } - if(!pages_parsed) break; + if(instance->protocol_data) { + nfc_devices[instance->protocol]->reset(instance->protocol_data); + } +} - // Read authentication counter - uint32_t auth_counter; - if(!flipper_format_read_uint32(file, "Failed authentication attempts", &auth_counter, 1)) - auth_counter = 0; - data->curr_authlim = auth_counter; +NfcProtocol nfc_device_get_protocol(const NfcDevice* instance) { + furi_assert(instance); + return instance->protocol; +} - data->auth_success = mf_ul_is_full_capture(data); +const NfcDeviceData* nfc_device_get_data(const NfcDevice* instance, NfcProtocol protocol) { + return nfc_device_get_data_ptr(instance, protocol); +} - parsed = true; - } while(false); +const char* nfc_device_get_protocol_name(NfcProtocol protocol) { + furi_assert(protocol < NfcProtocolNum); - furi_string_free(temp_str); - return parsed; + return nfc_devices[protocol]->protocol_name; } -static bool nfc_device_save_mifare_df_key_settings( - FlipperFormat* file, - MifareDesfireKeySettings* ks, - const char* prefix) { - bool saved = false; - FuriString* key; - key = furi_string_alloc(); +const char* nfc_device_get_name(const NfcDevice* instance, NfcDeviceNameType name_type) { + furi_assert(instance); + furi_assert(instance->protocol < NfcProtocolNum); - do { - furi_string_printf(key, "%s Change Key ID", prefix); - if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &ks->change_key_id, 1)) - break; - furi_string_printf(key, "%s Config Changeable", prefix); - if(!flipper_format_write_bool(file, furi_string_get_cstr(key), &ks->config_changeable, 1)) - break; - furi_string_printf(key, "%s Free Create Delete", prefix); - if(!flipper_format_write_bool(file, furi_string_get_cstr(key), &ks->free_create_delete, 1)) - break; - furi_string_printf(key, "%s Free Directory List", prefix); - if(!flipper_format_write_bool(file, furi_string_get_cstr(key), &ks->free_directory_list, 1)) - break; - furi_string_printf(key, "%s Key Changeable", prefix); - if(!flipper_format_write_bool( - file, furi_string_get_cstr(key), &ks->master_key_changeable, 1)) - break; - if(ks->flags) { - furi_string_printf(key, "%s Flags", prefix); - if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &ks->flags, 1)) break; - } - furi_string_printf(key, "%s Max Keys", prefix); - if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &ks->max_keys, 1)) break; - for(MifareDesfireKeyVersion* kv = ks->key_version_head; kv; kv = kv->next) { - furi_string_printf(key, "%s Key %d Version", prefix, kv->id); - if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &kv->version, 1)) break; - } - saved = true; - } while(false); - - furi_string_free(key); - return saved; + return nfc_devices[instance->protocol]->get_name(instance->protocol_data, name_type); } -bool nfc_device_load_mifare_df_key_settings( - FlipperFormat* file, - MifareDesfireKeySettings* ks, - const char* prefix) { - bool parsed = false; - FuriString* key; - key = furi_string_alloc(); +const uint8_t* nfc_device_get_uid(const NfcDevice* instance, size_t* uid_len) { + furi_assert(instance); + furi_assert(instance->protocol < NfcProtocolNum); - do { - furi_string_printf(key, "%s Change Key ID", prefix); - if(!flipper_format_read_hex(file, furi_string_get_cstr(key), &ks->change_key_id, 1)) break; - furi_string_printf(key, "%s Config Changeable", prefix); - if(!flipper_format_read_bool(file, furi_string_get_cstr(key), &ks->config_changeable, 1)) - break; - furi_string_printf(key, "%s Free Create Delete", prefix); - if(!flipper_format_read_bool(file, furi_string_get_cstr(key), &ks->free_create_delete, 1)) - break; - furi_string_printf(key, "%s Free Directory List", prefix); - if(!flipper_format_read_bool(file, furi_string_get_cstr(key), &ks->free_directory_list, 1)) - break; - furi_string_printf(key, "%s Key Changeable", prefix); - if(!flipper_format_read_bool( - file, furi_string_get_cstr(key), &ks->master_key_changeable, 1)) - break; - furi_string_printf(key, "%s Flags", prefix); - if(flipper_format_key_exist(file, furi_string_get_cstr(key))) { - if(!flipper_format_read_hex(file, furi_string_get_cstr(key), &ks->flags, 1)) break; - } - furi_string_printf(key, "%s Max Keys", prefix); - if(!flipper_format_read_hex(file, furi_string_get_cstr(key), &ks->max_keys, 1)) break; - ks->flags |= ks->max_keys >> 4; - ks->max_keys &= 0xF; - MifareDesfireKeyVersion** kv_head = &ks->key_version_head; - for(int key_id = 0; key_id < ks->max_keys; key_id++) { - furi_string_printf(key, "%s Key %d Version", prefix, key_id); - uint8_t version; - if(flipper_format_read_hex(file, furi_string_get_cstr(key), &version, 1)) { - MifareDesfireKeyVersion* kv = malloc(sizeof(MifareDesfireKeyVersion)); - memset(kv, 0, sizeof(MifareDesfireKeyVersion)); - kv->id = key_id; - kv->version = version; - *kv_head = kv; - kv_head = &kv->next; - } - } - parsed = true; - } while(false); - - furi_string_free(key); - return parsed; + return nfc_devices[instance->protocol]->get_uid(instance->protocol_data, uid_len); } -static bool nfc_device_save_mifare_df_app(FlipperFormat* file, MifareDesfireApplication* app) { - bool saved = false; - FuriString *prefix, *key; - prefix = - furi_string_alloc_printf("Application %02x%02x%02x", app->id[0], app->id[1], app->id[2]); - key = furi_string_alloc(); - uint8_t* tmp = NULL; - - do { - if(app->key_settings) { - if(!nfc_device_save_mifare_df_key_settings( - file, app->key_settings, furi_string_get_cstr(prefix))) - break; - } - if(!app->file_head) break; - uint32_t n_files = 0; - for(MifareDesfireFile* f = app->file_head; f; f = f->next) { - n_files++; - } - tmp = malloc(n_files); - int i = 0; - for(MifareDesfireFile* f = app->file_head; f; f = f->next) { - tmp[i++] = f->id; - } - furi_string_printf(key, "%s File IDs", furi_string_get_cstr(prefix)); - if(!flipper_format_write_hex(file, furi_string_get_cstr(key), tmp, n_files)) break; - bool saved_files = true; - for(MifareDesfireFile* f = app->file_head; f; f = f->next) { - saved_files = false; - furi_string_printf(key, "%s File %d Type", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &f->type, 1)) break; - furi_string_printf( - key, "%s File %d Communication Settings", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_hex(file, furi_string_get_cstr(key), &f->comm, 1)) break; - furi_string_printf( - key, "%s File %d Access Rights", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_hex( - file, furi_string_get_cstr(key), (uint8_t*)&f->access_rights, 2)) - break; - uint16_t size = 0; - if(f->type == MifareDesfireFileTypeStandard || - f->type == MifareDesfireFileTypeBackup) { - size = f->settings.data.size; - furi_string_printf(key, "%s File %d Size", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, furi_string_get_cstr(key), &f->settings.data.size, 1)) - break; - } else if(f->type == MifareDesfireFileTypeValue) { - furi_string_printf( - key, "%s File %d Hi Limit", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, furi_string_get_cstr(key), &f->settings.value.hi_limit, 1)) - break; - furi_string_printf( - key, "%s File %d Lo Limit", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, furi_string_get_cstr(key), &f->settings.value.lo_limit, 1)) - break; - furi_string_printf( - key, "%s File %d Limited Credit Value", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, furi_string_get_cstr(key), &f->settings.value.limited_credit_value, 1)) - break; - furi_string_printf( - key, "%s File %d Limited Credit Enabled", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_bool( - file, - furi_string_get_cstr(key), - &f->settings.value.limited_credit_enabled, - 1)) - break; - size = 4; - } else if( - f->type == MifareDesfireFileTypeLinearRecord || - f->type == MifareDesfireFileTypeCyclicRecord) { - furi_string_printf(key, "%s File %d Size", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, furi_string_get_cstr(key), &f->settings.record.size, 1)) - break; - furi_string_printf(key, "%s File %d Max", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, furi_string_get_cstr(key), &f->settings.record.max, 1)) - break; - furi_string_printf(key, "%s File %d Cur", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, furi_string_get_cstr(key), &f->settings.record.cur, 1)) - break; - size = f->settings.record.size * f->settings.record.cur; - } - if(f->contents) { - furi_string_printf(key, "%s File %d", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_write_hex(file, furi_string_get_cstr(key), f->contents, size)) - break; - } - saved_files = true; - } - if(!saved_files) { - break; - } - saved = true; - } while(false); +bool nfc_device_set_uid(NfcDevice* instance, const uint8_t* uid, size_t uid_len) { + furi_assert(instance); + furi_assert(instance->protocol < NfcProtocolNum); - free(tmp); - furi_string_free(prefix); - furi_string_free(key); - return saved; + return nfc_devices[instance->protocol]->set_uid(instance->protocol_data, uid, uid_len); } -bool nfc_device_load_mifare_df_app(FlipperFormat* file, MifareDesfireApplication* app) { - bool parsed = false; - FuriString *prefix, *key; - prefix = - furi_string_alloc_printf("Application %02x%02x%02x", app->id[0], app->id[1], app->id[2]); - key = furi_string_alloc(); - uint8_t* tmp = NULL; - MifareDesfireFile* f = NULL; +void nfc_device_set_data( + NfcDevice* instance, + NfcProtocol protocol, + const NfcDeviceData* protocol_data) { + furi_assert(instance); + furi_assert(protocol < NfcProtocolNum); - do { - app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); - memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); - if(!nfc_device_load_mifare_df_key_settings( - file, app->key_settings, furi_string_get_cstr(prefix))) { - free(app->key_settings); - app->key_settings = NULL; - break; - } - furi_string_printf(key, "%s File IDs", furi_string_get_cstr(prefix)); - uint32_t n_files; - if(!flipper_format_get_value_count(file, furi_string_get_cstr(key), &n_files)) break; - tmp = malloc(n_files); - if(!flipper_format_read_hex(file, furi_string_get_cstr(key), tmp, n_files)) break; - MifareDesfireFile** file_head = &app->file_head; - bool parsed_files = true; - for(uint32_t i = 0; i < n_files; i++) { - parsed_files = false; - f = malloc(sizeof(MifareDesfireFile)); - memset(f, 0, sizeof(MifareDesfireFile)); - f->id = tmp[i]; - furi_string_printf(key, "%s File %d Type", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_read_hex(file, furi_string_get_cstr(key), &f->type, 1)) break; - furi_string_printf( - key, "%s File %d Communication Settings", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_read_hex(file, furi_string_get_cstr(key), &f->comm, 1)) break; - furi_string_printf( - key, "%s File %d Access Rights", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_read_hex( - file, furi_string_get_cstr(key), (uint8_t*)&f->access_rights, 2)) - break; - if(f->type == MifareDesfireFileTypeStandard || - f->type == MifareDesfireFileTypeBackup) { - furi_string_printf(key, "%s File %d Size", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, furi_string_get_cstr(key), &f->settings.data.size, 1)) - break; - } else if(f->type == MifareDesfireFileTypeValue) { - furi_string_printf( - key, "%s File %d Hi Limit", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, furi_string_get_cstr(key), &f->settings.value.hi_limit, 1)) - break; - furi_string_printf( - key, "%s File %d Lo Limit", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, furi_string_get_cstr(key), &f->settings.value.lo_limit, 1)) - break; - furi_string_printf( - key, "%s File %d Limited Credit Value", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, furi_string_get_cstr(key), &f->settings.value.limited_credit_value, 1)) - break; - furi_string_printf( - key, "%s File %d Limited Credit Enabled", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_read_bool( - file, - furi_string_get_cstr(key), - &f->settings.value.limited_credit_enabled, - 1)) - break; - } else if( - f->type == MifareDesfireFileTypeLinearRecord || - f->type == MifareDesfireFileTypeCyclicRecord) { - furi_string_printf(key, "%s File %d Size", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, furi_string_get_cstr(key), &f->settings.record.size, 1)) - break; - furi_string_printf(key, "%s File %d Max", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, furi_string_get_cstr(key), &f->settings.record.max, 1)) - break; - furi_string_printf(key, "%s File %d Cur", furi_string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, furi_string_get_cstr(key), &f->settings.record.cur, 1)) - break; - } - furi_string_printf(key, "%s File %d", furi_string_get_cstr(prefix), f->id); - if(flipper_format_key_exist(file, furi_string_get_cstr(key))) { - uint32_t size; - if(!flipper_format_get_value_count(file, furi_string_get_cstr(key), &size)) break; - f->contents = malloc(size); - if(!flipper_format_read_hex(file, furi_string_get_cstr(key), f->contents, size)) - break; - } - *file_head = f; - file_head = &f->next; - f = NULL; - parsed_files = true; - } - if(!parsed_files) { - break; - } - parsed = true; - } while(false); + nfc_device_clear(instance); - if(f) { - free(f->contents); - free(f); - } - free(tmp); - furi_string_free(prefix); - furi_string_free(key); - return parsed; + instance->protocol = protocol; + instance->protocol_data = nfc_devices[protocol]->alloc(); + + nfc_devices[protocol]->copy(instance->protocol_data, protocol_data); } -static bool nfc_device_save_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - MifareDesfireData* data = &dev->dev_data.mf_df_data; - uint8_t* tmp = NULL; +void nfc_device_copy_data( + const NfcDevice* instance, + NfcProtocol protocol, + NfcDeviceData* protocol_data) { + furi_assert(instance); + furi_assert(protocol < NfcProtocolNum); + furi_assert(protocol_data); - do { - if(!flipper_format_write_comment_cstr(file, "Mifare DESFire specific data")) break; - if(!flipper_format_write_hex( - file, "PICC Version", (uint8_t*)&data->version, sizeof(data->version))) - break; - if(data->free_memory) { - if(!flipper_format_write_uint32(file, "PICC Free Memory", &data->free_memory->bytes, 1)) - break; - } - if(data->master_key_settings) { - if(!nfc_device_save_mifare_df_key_settings(file, data->master_key_settings, "PICC")) - break; - } - uint32_t n_apps = 0; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - n_apps++; - } - if(!flipper_format_write_uint32(file, "Application Count", &n_apps, 1)) break; - if(n_apps) { - tmp = malloc(n_apps * 3); - int i = 0; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - memcpy(tmp + i, app->id, 3); //-V769 - i += 3; - } - if(!flipper_format_write_hex(file, "Application IDs", tmp, n_apps * 3)) break; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - if(!nfc_device_save_mifare_df_app(file, app)) break; - } - } - saved = true; - } while(false); + if(instance->protocol != protocol) { + furi_crash(NFC_DEV_TYPE_ERROR); + } - free(tmp); - return saved; + nfc_devices[protocol]->copy(protocol_data, instance->protocol_data); } -bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - MifareDesfireData* data = &dev->dev_data.mf_df_data; - memset(data, 0, sizeof(MifareDesfireData)); - uint8_t* tmp = NULL; +bool nfc_device_is_equal(const NfcDevice* instance, const NfcDevice* other) { + furi_assert(instance); + furi_assert(other); - do { - if(!flipper_format_read_hex( - file, "PICC Version", (uint8_t*)&data->version, sizeof(data->version))) - break; - if(flipper_format_key_exist(file, "PICC Free Memory")) { - data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); - memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory)); - if(!flipper_format_read_uint32( - file, "PICC Free Memory", &data->free_memory->bytes, 1)) { - free(data->free_memory); - break; - } - } - if(flipper_format_key_exist(file, "PICC Change Key ID")) { - data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); - memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings)); - if(!nfc_device_load_mifare_df_key_settings(file, data->master_key_settings, "PICC")) { - free(data->master_key_settings); - data->master_key_settings = NULL; - break; - } - } - uint32_t n_apps; - if(!flipper_format_read_uint32(file, "Application Count", &n_apps, 1)) break; - if(n_apps) { - tmp = malloc(n_apps * 3); - if(!flipper_format_read_hex(file, "Application IDs", tmp, n_apps * 3)) break; - bool parsed_apps = true; - MifareDesfireApplication** app_head = &data->app_head; - for(uint32_t i = 0; i < n_apps; i++) { - MifareDesfireApplication* app = malloc(sizeof(MifareDesfireApplication)); - memset(app, 0, sizeof(MifareDesfireApplication)); - memcpy(app->id, &tmp[i * 3], 3); - if(!nfc_device_load_mifare_df_app(file, app)) { - free(app); - parsed_apps = false; - break; - } - *app_head = app; - app_head = &app->next; - } - if(!parsed_apps) { - // accept non-parsed apps, just log a warning: - FURI_LOG_W(TAG, "Non-parsed apps found!"); - } - } - parsed = true; - } while(false); + bool is_equal = false; + if(instance->protocol == other->protocol) { + is_equal = nfc_devices[instance->protocol]->is_equal( + instance->protocol_data, other->protocol_data); + } - free(tmp); - return parsed; + return is_equal; } -// Leave for backward compatibility -bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - EmvData* data = &dev->dev_data.emv_data; - memset(data, 0, sizeof(EmvData)); - uint32_t data_cnt = 0; - FuriString* temp_str; - temp_str = furi_string_alloc(); - - do { - // Load essential data - if(!flipper_format_get_value_count(file, "AID", &data_cnt)) break; - data->aid_len = data_cnt; - if(!flipper_format_read_hex(file, "AID", data->aid, data->aid_len)) break; - if(!flipper_format_read_string(file, "Name", temp_str)) break; - strlcpy(data->name, furi_string_get_cstr(temp_str), sizeof(data->name)); - if(!flipper_format_get_value_count(file, "Number", &data_cnt)) break; - data->number_len = data_cnt; - if(!flipper_format_read_hex(file, "Number", data->number, data->number_len)) break; - parsed = true; - // Load optional data - uint8_t exp_data[2] = {}; - if(flipper_format_read_hex(file, "Exp data", exp_data, 2)) { - data->exp_mon = exp_data[0]; - data->exp_year = exp_data[1]; - } - if(flipper_format_read_uint32(file, "Country code", &data_cnt, 1)) { - data->country_code = data_cnt; - } - if(flipper_format_read_uint32(file, "Currency code", &data_cnt, 1)) { - data->currency_code = data_cnt; - } - } while(false); +void nfc_device_set_loading_callback( + NfcDevice* instance, + NfcLoadingCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); - furi_string_free(temp_str); - return parsed; + instance->loading_callback = callback; + instance->loading_callback_context = context; } -static void nfc_device_write_mifare_classic_block( - FuriString* block_str, - MfClassicData* data, - uint8_t block_num) { - furi_string_reset(block_str); - bool is_sec_trailer = mf_classic_is_sector_trailer(block_num); - if(is_sec_trailer) { - uint8_t sector_num = mf_classic_get_sector_by_block(block_num); - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num); - // Write key A - for(size_t i = 0; i < sizeof(sec_tr->key_a); i++) { - if(mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) { - furi_string_cat_printf(block_str, "%02X ", sec_tr->key_a[i]); - } else { - furi_string_cat_printf(block_str, "?? "); - } - } - // Write Access bytes - for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i++) { - if(mf_classic_is_block_read(data, block_num)) { - furi_string_cat_printf(block_str, "%02X ", sec_tr->access_bits[i]); - } else { - furi_string_cat_printf(block_str, "?? "); - } - } - // Write key B - for(size_t i = 0; i < sizeof(sec_tr->key_b); i++) { - if(mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) { - furi_string_cat_printf(block_str, "%02X ", sec_tr->key_b[i]); - } else { - furi_string_cat_printf(block_str, "?? "); - } - } - } else { - // Write data block - for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { - if(mf_classic_is_block_read(data, block_num)) { - furi_string_cat_printf(block_str, "%02X ", data->block[block_num].value[i]); - } else { - furi_string_cat_printf(block_str, "?? "); - } - } - } - furi_string_trim(block_str); -} +bool nfc_device_save(NfcDevice* instance, const char* path) { + furi_assert(instance); + furi_assert(instance->protocol < NfcProtocolNum); + furi_assert(path); -static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { bool saved = false; - MfClassicData* data = &dev->dev_data.mf_classic_data; - FuriString* temp_str; - temp_str = furi_string_alloc(); - uint16_t blocks = 0; - - // Save Mifare Classic specific data - do { - if(!flipper_format_write_comment_cstr(file, "Mifare Classic specific data")) break; - - if(data->type == MfClassicTypeMini) { - if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "MINI")) break; - blocks = 20; - } else if(data->type == MfClassicType1k) { - if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; - blocks = 64; - } else if(data->type == MfClassicType4k) { - if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break; - blocks = 256; - } - if(!flipper_format_write_uint32( - file, "Data format version", &nfc_mifare_classic_data_format_version, 1)) - break; - if(!flipper_format_write_comment_cstr( - file, "Mifare Classic blocks, \'??\' means unknown data")) - break; - bool block_saved = true; - FuriString* block_str; - block_str = furi_string_alloc(); - for(size_t i = 0; i < blocks; i++) { - furi_string_printf(temp_str, "Block %d", i); - nfc_device_write_mifare_classic_block(block_str, data, i); - if(!flipper_format_write_string(file, furi_string_get_cstr(temp_str), block_str)) { - block_saved = false; - break; - } - } - furi_string_free(block_str); - if(!block_saved) break; - saved = true; - } while(false); - - furi_string_free(temp_str); - return saved; -} + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + FuriString* temp_str = furi_string_alloc(); -static void nfc_device_load_mifare_classic_block( - FuriString* block_str, - MfClassicData* data, - uint8_t block_num) { - furi_string_trim(block_str); - MfClassicBlock block_tmp = {}; - bool is_sector_trailer = mf_classic_is_sector_trailer(block_num); - uint8_t sector_num = mf_classic_get_sector_by_block(block_num); - uint16_t block_unknown_bytes_mask = 0; - - furi_string_trim(block_str); - for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { - char hi = furi_string_get_char(block_str, 3 * i); - char low = furi_string_get_char(block_str, 3 * i + 1); - uint8_t byte = 0; - if(hex_char_to_uint8(hi, low, &byte)) { - block_tmp.value[i] = byte; - } else { - FURI_BIT_SET(block_unknown_bytes_mask, i); - } + if(instance->loading_callback) { + instance->loading_callback(instance->loading_callback_context, true); } - if(block_unknown_bytes_mask == 0xffff) { - // All data is unknown, exit - return; - } + do { + // Open file + if(!flipper_format_buffered_file_open_always(ff, path)) break; - if(is_sector_trailer) { - MfClassicSectorTrailer* sec_tr_tmp = (MfClassicSectorTrailer*)&block_tmp; - // Load Key A - // Key A mask 0b0000000000111111 = 0x003f - if((block_unknown_bytes_mask & 0x003f) == 0) { - uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_a, sizeof(sec_tr_tmp->key_a)); - mf_classic_set_key_found(data, sector_num, MfClassicKeyA, key); - } - // Load Access Bits - // Access bits mask 0b0000001111000000 = 0x03c0 - if((block_unknown_bytes_mask & 0x03c0) == 0) { - mf_classic_set_block_read(data, block_num, &block_tmp); - } - // Load Key B - // Key B mask 0b1111110000000000 = 0xfc00 - if((block_unknown_bytes_mask & 0xfc00) == 0) { - uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_b, sizeof(sec_tr_tmp->key_b)); - mf_classic_set_key_found(data, sector_num, MfClassicKeyB, key); - } - } else { - if(block_unknown_bytes_mask == 0) { - mf_classic_set_block_read(data, block_num, &block_tmp); + // Write header + if(!flipper_format_write_header_cstr(ff, NFC_FILE_HEADER, NFC_CURRENT_FORMAT_VERSION)) + break; + + // Write allowed device types + furi_string_printf(temp_str, "%s can be ", NFC_DEVICE_TYPE_KEY); + for(NfcProtocol protocol = 0; protocol < NfcProtocolNum; ++protocol) { + furi_string_cat(temp_str, nfc_devices[protocol]->protocol_name); + if(protocol < NfcProtocolNum - 1) { + furi_string_cat(temp_str, ", "); + } } - } -} -static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - MfClassicData* data = &dev->dev_data.mf_classic_data; - FuriString* temp_str; - uint32_t data_format_version = 0; - temp_str = furi_string_alloc(); - uint16_t data_blocks = 0; - memset(data, 0, sizeof(MfClassicData)); + if(!flipper_format_write_comment(ff, temp_str)) break; - do { - // Read Mifare Classic type - if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break; - if(!furi_string_cmp(temp_str, "MINI")) { - data->type = MfClassicTypeMini; - data_blocks = 20; - } else if(!furi_string_cmp(temp_str, "1K")) { - data->type = MfClassicType1k; - data_blocks = 64; - } else if(!furi_string_cmp(temp_str, "4K")) { - data->type = MfClassicType4k; - data_blocks = 256; - } else { + // Write device type + if(!flipper_format_write_string_cstr( + ff, NFC_DEVICE_TYPE_KEY, nfc_devices[instance->protocol]->protocol_name)) break; - } - bool old_format = false; - // Read Mifare Classic format version - if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { - // Load unread sectors with zero keys access for backward compatibility - if(!flipper_format_rewind(file)) break; - old_format = true; - } else { - if(data_format_version < nfc_mifare_classic_data_format_version) { - old_format = true; - } - } + // Write UID + furi_string_printf(temp_str, "%s is common for all formats", NFC_DEVICE_UID_KEY); + if(!flipper_format_write_comment(ff, temp_str)) break; - // Read Mifare Classic blocks - bool block_read = true; - FuriString* block_str; - block_str = furi_string_alloc(); - for(size_t i = 0; i < data_blocks; i++) { - furi_string_printf(temp_str, "Block %d", i); - if(!flipper_format_read_string(file, furi_string_get_cstr(temp_str), block_str)) { - block_read = false; - break; - } - nfc_device_load_mifare_classic_block(block_str, data, i); - } - furi_string_free(block_str); - if(!block_read) break; - - // Set keys and blocks as unknown for backward compatibility - if(old_format) { - data->key_a_mask = 0ULL; - data->key_b_mask = 0ULL; - memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); - } + size_t uid_len; + const uint8_t* uid = nfc_device_get_uid(instance, &uid_len); + if(!flipper_format_write_hex(ff, NFC_DEVICE_UID_KEY, uid, uid_len)) break; + + // Write protocol-dependent data + if(!nfc_devices[instance->protocol]->save(instance->protocol_data, ff)) break; - parsed = true; + saved = true; } while(false); + if(instance->loading_callback) { + instance->loading_callback(instance->loading_callback_context, false); + } + furi_string_free(temp_str); - return parsed; -} + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); -static void nfc_device_get_key_cache_file_path(NfcDevice* dev, FuriString* file_path) { - uint8_t* uid = dev->dev_data.nfc_data.uid; - uint8_t uid_len = dev->dev_data.nfc_data.uid_len; - furi_string_set(file_path, NFC_DEVICE_KEYS_FOLDER "/"); - for(size_t i = 0; i < uid_len; i++) { - furi_string_cat_printf(file_path, "%02X", uid[i]); - } - furi_string_cat_printf(file_path, NFC_DEVICE_KEYS_EXTENSION); + return saved; } -static bool nfc_device_save_mifare_classic_keys(NfcDevice* dev) { - FlipperFormat* file = flipper_format_file_alloc(dev->storage); - MfClassicData* data = &dev->dev_data.mf_classic_data; - FuriString* temp_str; - temp_str = furi_string_alloc(); +static bool nfc_device_load_uid( + FlipperFormat* ff, + uint8_t* uid, + uint32_t* uid_len, + const uint32_t uid_maxlen) { + bool loaded = false; - nfc_device_get_key_cache_file_path(dev, temp_str); - bool save_success = false; do { - if(!storage_simply_mkdir(dev->storage, NFC_DEVICE_KEYS_FOLDER)) break; - if(!storage_simply_remove(dev->storage, furi_string_get_cstr(temp_str))) break; - if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; - if(!flipper_format_write_header_cstr(file, nfc_keys_file_header, nfc_keys_file_version)) - break; - if(data->type == MfClassicTypeMini) { - if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "MINI")) break; - } else if(data->type == MfClassicType1k) { - if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; - } else if(data->type == MfClassicType4k) { - if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break; - } - if(!flipper_format_write_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; - if(!flipper_format_write_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; - uint8_t sector_num = mf_classic_get_total_sectors_num(data->type); - bool key_save_success = true; - for(size_t i = 0; (i < sector_num) && (key_save_success); i++) { - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); - if(FURI_BIT(data->key_a_mask, i)) { - furi_string_printf(temp_str, "Key A sector %d", i); - key_save_success = flipper_format_write_hex( - file, furi_string_get_cstr(temp_str), sec_tr->key_a, 6); - } - if(!key_save_success) break; - if(FURI_BIT(data->key_b_mask, i)) { - furi_string_printf(temp_str, "Key B sector %d", i); - key_save_success = flipper_format_write_hex( - file, furi_string_get_cstr(temp_str), sec_tr->key_b, 6); - } - } - save_success = key_save_success; + uint32_t uid_len_current; + if(!flipper_format_get_value_count(ff, NFC_DEVICE_UID_KEY, &uid_len_current)) break; + if(uid_len_current > uid_maxlen) break; + if(!flipper_format_read_hex(ff, NFC_DEVICE_UID_KEY, uid, uid_len_current)) break; + + *uid_len = uid_len_current; + loaded = true; } while(false); - flipper_format_free(file); - furi_string_free(temp_str); - return save_success; + return loaded; } -bool nfc_device_load_key_cache(NfcDevice* dev) { - furi_assert(dev); - FuriString* temp_str; - temp_str = furi_string_alloc(); +static bool nfc_device_load_unified(NfcDevice* instance, FlipperFormat* ff, uint32_t version) { + bool loaded = false; - MfClassicData* data = &dev->dev_data.mf_classic_data; - nfc_device_get_key_cache_file_path(dev, temp_str); - FlipperFormat* file = flipper_format_file_alloc(dev->storage); + FuriString* temp_str = furi_string_alloc(); - bool load_success = false; do { - if(storage_common_stat(dev->storage, furi_string_get_cstr(temp_str), NULL) != FSE_OK) - break; - if(!flipper_format_file_open_existing(file, furi_string_get_cstr(temp_str))) break; - uint32_t version = 0; - if(!flipper_format_read_header(file, temp_str, &version)) break; - if(furi_string_cmp_str(temp_str, nfc_keys_file_header)) break; - if(version != nfc_keys_file_version) break; - if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break; - if(!furi_string_cmp(temp_str, "MINI")) { - data->type = MfClassicTypeMini; - } else if(!furi_string_cmp(temp_str, "1K")) { - data->type = MfClassicType1k; - } else if(!furi_string_cmp(temp_str, "4K")) { - data->type = MfClassicType4k; - } else { - break; - } - if(!flipper_format_read_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; - if(!flipper_format_read_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; - uint8_t sectors = mf_classic_get_total_sectors_num(data->type); - bool key_read_success = true; - for(size_t i = 0; (i < sectors) && (key_read_success); i++) { - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); - if(FURI_BIT(data->key_a_mask, i)) { - furi_string_printf(temp_str, "Key A sector %d", i); - key_read_success = flipper_format_read_hex( - file, furi_string_get_cstr(temp_str), sec_tr->key_a, 6); - } - if(!key_read_success) break; - if(FURI_BIT(data->key_b_mask, i)) { - furi_string_printf(temp_str, "Key B sector %d", i); - key_read_success = flipper_format_read_hex( - file, furi_string_get_cstr(temp_str), sec_tr->key_b, 6); + // Read Nfc device type + if(!flipper_format_read_string(ff, NFC_DEVICE_TYPE_KEY, temp_str)) break; + + // Detect protocol + NfcProtocol protocol; + for(protocol = 0; protocol < NfcProtocolNum; ++protocol) { + if(furi_string_equal(temp_str, nfc_devices[protocol]->protocol_name)) { + break; } } - load_success = key_read_success; - } while(false); - furi_string_free(temp_str); - flipper_format_free(file); + if(protocol == NfcProtocolNum) break; - return load_success; -} + nfc_device_clear(instance); -void nfc_device_set_name(NfcDevice* dev, const char* name) { - furi_assert(dev); + instance->protocol = protocol; + instance->protocol_data = nfc_devices[protocol]->alloc(); - strlcpy(dev->dev_name, name, NFC_DEV_NAME_MAX_LEN); -} + // Load UID + uint8_t uid[NFC_DEVICE_UID_MAX_LEN]; + uint32_t uid_len; -static void nfc_device_get_path_without_ext(FuriString* orig_path, FuriString* shadow_path) { - // TODO: this won't work if there is ".nfc" anywhere in the path other than - // at the end - size_t ext_start = furi_string_search(orig_path, NFC_APP_EXTENSION); - furi_string_set_n(shadow_path, orig_path, 0, ext_start); -} - -static void nfc_device_get_shadow_path(FuriString* orig_path, FuriString* shadow_path) { - nfc_device_get_path_without_ext(orig_path, shadow_path); - furi_string_cat_printf(shadow_path, "%s", NFC_APP_SHADOW_EXTENSION); -} - -static void nfc_device_get_folder_from_path(FuriString* path, FuriString* folder) { - size_t last_slash = furi_string_search_rchar(path, '/'); - if(last_slash == FURI_STRING_FAILURE) { - // No slashes in the path, treat the whole path as a folder - furi_string_set(folder, path); - } else { - furi_string_set_n(folder, path, 0, last_slash); - } -} - -bool nfc_device_save(NfcDevice* dev, const char* dev_name) { - furi_assert(dev); + if(!nfc_device_load_uid(ff, uid, &uid_len, NFC_DEVICE_UID_MAX_LEN)) break; + if(!nfc_device_set_uid(instance, uid, uid_len)) break; - bool saved = false; - FlipperFormat* file = flipper_format_file_alloc(dev->storage); - FuriHalNfcDevData* data = &dev->dev_data.nfc_data; - FuriString* temp_str; - temp_str = furi_string_alloc(); + // Load data + if(!nfc_devices[protocol]->load(instance->protocol_data, ff, version)) break; - do { - // Create directory if necessary - FuriString* folder = furi_string_alloc(); - // Get folder from filename (filename is in the form of "folder/filename.nfc", so the folder is "folder/") - furi_string_set(temp_str, dev_name); - // Get folder from filename - nfc_device_get_folder_from_path(temp_str, folder); - FURI_LOG_I("Nfc", "Saving to folder %s", furi_string_get_cstr(folder)); - if(!storage_simply_mkdir(dev->storage, furi_string_get_cstr(folder))) { - FURI_LOG_E("Nfc", "Failed to create folder %s", furi_string_get_cstr(folder)); - break; - } - furi_string_free(folder); - // First remove nfc device file if it was saved - // Open file - if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; - // Write header - if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; - // Write nfc device type - if(!flipper_format_write_comment_cstr( - file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic")) - break; - nfc_device_prepare_format_string(dev, temp_str); - if(!flipper_format_write_string(file, "Device type", temp_str)) break; - // Write UID, ATQA, SAK - if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) - break; - if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - // Save ATQA in MSB order for correct companion apps display - uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; - if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; - if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; - // Save more data if necessary - if(dev->format == NfcDeviceSaveFormatMifareUl) { - if(!nfc_device_save_mifare_ul_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { - if(!nfc_device_save_mifare_df_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { - // Save data - if(!nfc_device_save_mifare_classic_data(file, dev)) break; - // Save keys cache - if(!nfc_device_save_mifare_classic_keys(dev)) break; - } - saved = true; - } while(0); + loaded = true; + } while(false); - if(!saved) { //-V547 - dialog_message_show_storage_error(dev->dialogs, "Can not save\nkey file"); + if(!loaded) { + nfc_device_clear(instance); } + furi_string_free(temp_str); - flipper_format_free(file); - return saved; + return loaded; } -bool nfc_device_save_shadow(NfcDevice* dev, const char* path) { - dev->shadow_file_exist = true; - // Replace extension from .nfc to .shd if necessary - FuriString* orig_path = furi_string_alloc(); - furi_string_set_str(orig_path, path); - FuriString* shadow_path = furi_string_alloc(); - nfc_device_get_shadow_path(orig_path, shadow_path); +static bool nfc_device_load_legacy(NfcDevice* instance, FlipperFormat* ff, uint32_t version) { + bool loaded = false; - bool file_saved = nfc_device_save(dev, furi_string_get_cstr(shadow_path)); - furi_string_free(orig_path); - furi_string_free(shadow_path); + FuriString* temp_str = furi_string_alloc(); - return file_saved; -} + do { + // Read Nfc device type + if(!flipper_format_read_string(ff, NFC_DEVICE_TYPE_KEY, temp_str)) break; -static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dialog) { - bool parsed = false; - FlipperFormat* file = flipper_format_file_alloc(dev->storage); - FuriHalNfcDevData* data = &dev->dev_data.nfc_data; - uint32_t data_cnt = 0; - FuriString* temp_str; - temp_str = furi_string_alloc(); - bool deprecated_version = false; + nfc_device_clear(instance); - // Version 2 of file format had ATQA bytes swapped - uint32_t version_with_lsb_atqa = 2; + // Detect protocol + for(NfcProtocol protocol = 0; protocol < NfcProtocolNum; protocol++) { + instance->protocol = protocol; + instance->protocol_data = nfc_devices[protocol]->alloc(); - if(dev->loading_cb) { - dev->loading_cb(dev->loading_cb_ctx, true); - } + // Verify protocol + if(nfc_devices[protocol]->verify(instance->protocol_data, temp_str)) { + uint8_t uid[NFC_DEVICE_UID_MAX_LEN]; + uint32_t uid_len; - do { - // Check existence of shadow file - nfc_device_get_shadow_path(path, temp_str); - dev->shadow_file_exist = - storage_common_stat(dev->storage, furi_string_get_cstr(temp_str), NULL) == FSE_OK; - // Open shadow file if it exists. If not - open original - if(dev->shadow_file_exist) { - if(!flipper_format_file_open_existing(file, furi_string_get_cstr(temp_str))) break; - } else { - if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break; - } - // Read and verify file header - uint32_t version = 0; - if(!flipper_format_read_header(file, temp_str, &version)) break; - if(furi_string_cmp_str(temp_str, nfc_file_header)) break; - if(version != nfc_file_version) { - if(version < version_with_lsb_atqa) { - deprecated_version = true; + // Load data + loaded = nfc_device_load_uid(ff, uid, &uid_len, NFC_DEVICE_UID_MAX_LEN) && + nfc_device_set_uid(instance, uid, uid_len) && + nfc_devices[protocol]->load(instance->protocol_data, ff, version); break; } - } - // Read Nfc device type - if(!flipper_format_read_string(file, "Device type", temp_str)) break; - if(!nfc_device_parse_format_string(dev, temp_str)) break; - // Read and parse UID, ATQA and SAK - if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break; - if(!(data_cnt == 4 || data_cnt == 7)) break; - data->uid_len = data_cnt; - if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; - if(version == version_with_lsb_atqa) { - if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; - } else { - uint8_t atqa[2] = {}; - if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; - data->atqa[0] = atqa[1]; - data->atqa[1] = atqa[0]; - } - if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; - // Load CUID - uint8_t* cuid_start = data->uid; - if(data->uid_len == 7) { - cuid_start = &data->uid[3]; - } - data->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | - (cuid_start[3]); - // Parse other data - if(dev->format == NfcDeviceSaveFormatMifareUl) { - if(!nfc_device_load_mifare_ul_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { - if(!nfc_device_load_mifare_classic_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { - if(!nfc_device_load_mifare_df_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatBankCard) { - if(!nfc_device_load_bank_card_data(file, dev)) break; - } - parsed = true; - } while(false); - - if(dev->loading_cb) { - dev->loading_cb(dev->loading_cb_ctx, false); - } - if((!parsed) && (show_dialog)) { - if(deprecated_version) { - dialog_message_show_storage_error(dev->dialogs, "File format deprecated"); - } else { - dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile"); + nfc_device_clear(instance); } - } + + } while(false); furi_string_free(temp_str); - flipper_format_free(file); - return parsed; + return loaded; } -bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog) { - furi_assert(dev); - furi_assert(file_path); - - // Load device data - furi_string_set(dev->load_path, file_path); - bool dev_load = nfc_device_load_data(dev, dev->load_path, show_dialog); - if(dev_load) { - // Set device name - FuriString* filename; - filename = furi_string_alloc(); - path_extract_filename_no_ext(file_path, filename); - nfc_device_set_name(dev, furi_string_get_cstr(filename)); - furi_string_free(filename); - } +bool nfc_device_load(NfcDevice* instance, const char* path) { + furi_assert(instance); + furi_assert(path); - return dev_load; -} + bool loaded = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); -bool nfc_file_select(NfcDevice* dev) { - furi_assert(dev); - const char* folder = furi_string_get_cstr(dev->folder); - - // Input events and views are managed by file_browser - - const DialogsFileBrowserOptions browser_options = { - .extension = NFC_APP_EXTENSION, - .skip_assets = true, - .hide_dot_files = true, - .icon = &I_Nfc_10px, - .hide_ext = true, - .item_loader_callback = NULL, - .item_loader_context = NULL, - .base_path = folder, - }; - - bool res = - dialog_file_browser_show(dev->dialogs, dev->load_path, dev->load_path, &browser_options); - - if(res) { - FuriString* filename; - filename = furi_string_alloc(); - path_extract_filename(dev->load_path, filename, true); - strncpy(dev->dev_name, furi_string_get_cstr(filename), NFC_DEV_NAME_MAX_LEN); - res = nfc_device_load_data(dev, dev->load_path, true); - if(res) { - nfc_device_set_name(dev, dev->dev_name); - } - furi_string_free(filename); - } - - return res; -} + FuriString* temp_str; + temp_str = furi_string_alloc(); -void nfc_device_data_clear(NfcDeviceData* dev_data) { - if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { - mf_df_clear(&dev_data->mf_df_data); - } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { - memset(&dev_data->mf_classic_data, 0, sizeof(MfClassicData)); - } else if(dev_data->protocol == NfcDeviceProtocolMifareUl) { - mf_ul_reset(&dev_data->mf_ul_data); - } else if(dev_data->protocol == NfcDeviceProtocolEMV) { - memset(&dev_data->emv_data, 0, sizeof(EmvData)); + if(instance->loading_callback) { + instance->loading_callback(instance->loading_callback_context, true); } - memset(&dev_data->nfc_data, 0, sizeof(FuriHalNfcDevData)); - dev_data->protocol = NfcDeviceProtocolUnknown; - furi_string_reset(dev_data->parsed_data); -} - -void nfc_device_clear(NfcDevice* dev) { - furi_assert(dev); - - nfc_device_set_name(dev, ""); - nfc_device_data_clear(&dev->dev_data); - dev->format = NfcDeviceSaveFormatUid; - furi_string_reset(dev->load_path); -} - -bool nfc_device_delete(NfcDevice* dev, bool use_load_path) { - furi_assert(dev); - - bool deleted = false; - FuriString* file_path; - file_path = furi_string_alloc(); do { - // Delete original file - if(use_load_path && !furi_string_empty(dev->load_path)) { - furi_string_set(file_path, dev->load_path); - } else { - furi_string_printf( - file_path, - "%s/%s%s", - furi_string_get_cstr(dev->folder), - dev->dev_name, - NFC_APP_EXTENSION); - } - if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break; - // Delete shadow file if it exists - if(dev->shadow_file_exist) { - if(use_load_path && !furi_string_empty(dev->load_path)) { - nfc_device_get_shadow_path(dev->load_path, file_path); - } else { - furi_string_printf( - file_path, - "%s/%s%s", - furi_string_get_cstr(dev->folder), - dev->dev_name, - NFC_APP_SHADOW_EXTENSION); - } - if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break; - } - deleted = true; - } while(0); + if(!flipper_format_buffered_file_open_existing(ff, path)) break; - if(!deleted) { - dialog_message_show_storage_error(dev->dialogs, "Can not remove file"); - } - - furi_string_free(file_path); - return deleted; -} - -bool nfc_device_restore(NfcDevice* dev, bool use_load_path) { - furi_assert(dev); - furi_assert(dev->shadow_file_exist); + // Read and verify file header + uint32_t version = 0; + if(!flipper_format_read_header(ff, temp_str, &version)) break; - bool restored = false; - FuriString* path; + if(furi_string_cmp_str(temp_str, NFC_FILE_HEADER)) break; + if(version < NFC_MINIMUM_SUPPORTED_FORMAT_VERSION) break; - path = furi_string_alloc(); + // Select loading method + loaded = (version < NFC_UNIFIED_FORMAT_VERSION) ? + nfc_device_load_legacy(instance, ff, version) : + nfc_device_load_unified(instance, ff, version); - do { - if(use_load_path && !furi_string_empty(dev->load_path)) { - nfc_device_get_shadow_path(dev->load_path, path); - } else { - furi_string_printf( - path, - "%s/%s%s", - furi_string_get_cstr(dev->folder), - dev->dev_name, - NFC_APP_SHADOW_EXTENSION); - } - if(!storage_simply_remove(dev->storage, furi_string_get_cstr(path))) break; - dev->shadow_file_exist = false; - if(use_load_path && !furi_string_empty(dev->load_path)) { - furi_string_set(path, dev->load_path); - } else { - furi_string_printf( - path, - "%s/%s%s", - furi_string_get_cstr(dev->folder), - dev->dev_name, - NFC_APP_EXTENSION); - } - if(!nfc_device_load_data(dev, path, true)) break; - restored = true; - } while(0); + } while(false); - furi_string_free(path); - return restored; -} + if(instance->loading_callback) { + instance->loading_callback(instance->loading_callback_context, false); + } -void nfc_device_set_loading_callback(NfcDevice* dev, NfcLoadingCallback callback, void* context) { - furi_assert(dev); + furi_string_free(temp_str); + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); - dev->loading_cb = callback; - dev->loading_cb_ctx = context; + return loaded; } diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index df37ec3df53b..5a9bf1c03712 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -1,121 +1,60 @@ #pragma once +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include "protocols/nfc_device_base.h" +#include "protocols/nfc_protocol.h" #ifdef __cplusplus extern "C" { #endif -#define NFC_DEV_NAME_MAX_LEN 22 -#define NFC_READER_DATA_MAX_SIZE 64 -#define NFC_DICT_KEY_BATCH_SIZE 10 - -#define NFC_APP_EXTENSION ".nfc" -#define NFC_APP_SHADOW_EXTENSION ".shd" +typedef struct NfcDevice NfcDevice; typedef void (*NfcLoadingCallback)(void* context, bool state); -typedef enum { - NfcDeviceProtocolUnknown, - NfcDeviceProtocolEMV, - NfcDeviceProtocolMifareUl, - NfcDeviceProtocolMifareClassic, - NfcDeviceProtocolMifareDesfire, -} NfcProtocol; - -typedef enum { - NfcDeviceSaveFormatUid, - NfcDeviceSaveFormatBankCard, - NfcDeviceSaveFormatMifareUl, - NfcDeviceSaveFormatMifareClassic, - NfcDeviceSaveFormatMifareDesfire, -} NfcDeviceSaveFormat; - -typedef struct { - uint8_t data[NFC_READER_DATA_MAX_SIZE]; - uint16_t size; -} NfcReaderRequestData; - -typedef struct { - MfClassicDict* dict; - uint8_t current_sector; -} NfcMfClassicDictAttackData; - -typedef enum { - NfcReadModeAuto, - NfcReadModeMfClassic, - NfcReadModeMfUltralight, - NfcReadModeMfDesfire, - NfcReadModeNFCA, -} NfcReadMode; - -typedef struct { - FuriHalNfcDevData nfc_data; - NfcProtocol protocol; - NfcReadMode read_mode; - union { - NfcReaderRequestData reader_data; - NfcMfClassicDictAttackData mf_classic_dict_attack_data; - MfUltralightAuth mf_ul_auth; - }; - union { - EmvData emv_data; - MfUltralightData mf_ul_data; - MfClassicData mf_classic_data; - MifareDesfireData mf_df_data; - }; - FuriString* parsed_data; -} NfcDeviceData; - -typedef struct { - Storage* storage; - DialogsApp* dialogs; - NfcDeviceData dev_data; - char dev_name[NFC_DEV_NAME_MAX_LEN + 1]; - FuriString* load_path; - FuriString* folder; - NfcDeviceSaveFormat format; - bool shadow_file_exist; - - NfcLoadingCallback loading_cb; - void* loading_cb_ctx; -} NfcDevice; - NfcDevice* nfc_device_alloc(); -void nfc_device_free(NfcDevice* nfc_dev); +void nfc_device_free(NfcDevice* instance); + +void nfc_device_clear(NfcDevice* instance); + +void nfc_device_reset(NfcDevice* instance); + +NfcProtocol nfc_device_get_protocol(const NfcDevice* instance); -void nfc_device_set_name(NfcDevice* dev, const char* name); +const NfcDeviceData* nfc_device_get_data(const NfcDevice* instance, NfcProtocol protocol); -bool nfc_device_save(NfcDevice* dev, const char* dev_name); +const char* nfc_device_get_protocol_name(NfcProtocol protocol); -bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name); +const char* nfc_device_get_name(const NfcDevice* instance, NfcDeviceNameType name_type); -bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog); +const uint8_t* nfc_device_get_uid(const NfcDevice* instance, size_t* uid_len); -bool nfc_device_load_key_cache(NfcDevice* dev); +bool nfc_device_set_uid(NfcDevice* instance, const uint8_t* uid, size_t uid_len); -bool nfc_file_select(NfcDevice* dev); +void nfc_device_set_data( + NfcDevice* instance, + NfcProtocol protocol, + const NfcDeviceData* protocol_data); -void nfc_device_data_clear(NfcDeviceData* dev); +void nfc_device_copy_data( + const NfcDevice* instance, + NfcProtocol protocol, + NfcDeviceData* protocol_data); -void nfc_device_clear(NfcDevice* dev); +bool nfc_device_is_equal(const NfcDevice* instance, const NfcDevice* other); -bool nfc_device_delete(NfcDevice* dev, bool use_load_path); +void nfc_device_set_loading_callback( + NfcDevice* instance, + NfcLoadingCallback callback, + void* context); -bool nfc_device_restore(NfcDevice* dev, bool use_load_path); +bool nfc_device_save(NfcDevice* instance, const char* path); -void nfc_device_set_loading_callback(NfcDevice* dev, NfcLoadingCallback callback, void* context); +bool nfc_device_load(NfcDevice* instance, const char* path); #ifdef __cplusplus } diff --git a/lib/nfc/nfc_device_i.c b/lib/nfc/nfc_device_i.c new file mode 100644 index 000000000000..e98b04251e36 --- /dev/null +++ b/lib/nfc/nfc_device_i.c @@ -0,0 +1,37 @@ +#include "nfc_device_i.h" +#include "protocols/nfc_device_defs.h" + +#include + +static NfcDeviceData* + nfc_device_search_base_protocol_data(const NfcDevice* instance, NfcProtocol protocol) { + NfcProtocol protocol_tmp = instance->protocol; + NfcDeviceData* dev_data_tmp = instance->protocol_data; + + while(true) { + dev_data_tmp = nfc_devices[protocol_tmp]->get_base_data(dev_data_tmp); + protocol_tmp = nfc_protocol_get_parent(protocol_tmp); + if(protocol_tmp == protocol) { + break; + } + } + + return dev_data_tmp; +} + +NfcDeviceData* nfc_device_get_data_ptr(const NfcDevice* instance, NfcProtocol protocol) { + furi_assert(instance); + furi_assert(protocol < NfcProtocolNum); + + NfcDeviceData* dev_data = NULL; + + if(instance->protocol == protocol) { + dev_data = instance->protocol_data; + } else if(nfc_protocol_has_parent(instance->protocol, protocol)) { + dev_data = nfc_device_search_base_protocol_data(instance, protocol); + } else { + furi_crash("Incorrect protocol"); + } + + return dev_data; +} diff --git a/lib/nfc/nfc_device_i.h b/lib/nfc/nfc_device_i.h new file mode 100644 index 000000000000..437d77ad4c77 --- /dev/null +++ b/lib/nfc/nfc_device_i.h @@ -0,0 +1,21 @@ +#pragma once + +#include "nfc_device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct NfcDevice { + NfcProtocol protocol; + NfcDeviceData* protocol_data; + + NfcLoadingCallback loading_callback; + void* loading_callback_context; +}; + +NfcDeviceData* nfc_device_get_data_ptr(const NfcDevice* instance, NfcProtocol protocol); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_listener.c b/lib/nfc/nfc_listener.c new file mode 100644 index 000000000000..68dafdbde765 --- /dev/null +++ b/lib/nfc/nfc_listener.c @@ -0,0 +1,138 @@ +#include "nfc_listener.h" + +#include +#include + +#include + +typedef struct NfcListenerListElement { + NfcProtocol protocol; + NfcGenericInstance* listener; + const NfcListenerBase* listener_api; + struct NfcListenerListElement* child; +} NfcListenerListElement; + +typedef struct { + NfcListenerListElement* head; + NfcListenerListElement* tail; +} NfcListenerList; + +struct NfcListener { + NfcProtocol protocol; + Nfc* nfc; + NfcListenerList list; + NfcDevice* nfc_dev; +}; + +static void nfc_listener_list_alloc(NfcListener* instance) { + instance->list.head = malloc(sizeof(NfcListenerListElement)); + instance->list.head->protocol = instance->protocol; + + instance->list.head->listener_api = nfc_listeners_api[instance->protocol]; + instance->list.head->child = NULL; + instance->list.tail = instance->list.head; + + // Build linked list + do { + NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->list.head->protocol); + if(parent_protocol == NfcProtocolInvalid) break; + + NfcListenerListElement* parent = malloc(sizeof(NfcListenerListElement)); + parent->protocol = parent_protocol; + parent->listener_api = nfc_listeners_api[parent_protocol]; + parent->child = instance->list.head; + + instance->list.head = parent; + } while(true); + + // Allocate listener instances + NfcListenerListElement* iter = instance->list.head; + NfcDeviceData* data_tmp = nfc_device_get_data_ptr(instance->nfc_dev, iter->protocol); + iter->listener = iter->listener_api->alloc(instance->nfc, data_tmp); + + do { + if(iter->child == NULL) break; + data_tmp = nfc_device_get_data_ptr(instance->nfc_dev, iter->child->protocol); + iter->child->listener = iter->child->listener_api->alloc(iter->listener, data_tmp); + iter->listener_api->set_callback( + iter->listener, iter->child->listener_api->run, iter->child->listener); + + iter = iter->child; + } while(true); +} + +static void nfc_listener_list_free(NfcListener* instance) { + // Free listener instances + do { + instance->list.head->listener_api->free(instance->list.head->listener); + NfcListenerListElement* child = instance->list.head->child; + free(instance->list.head); + if(child == NULL) break; + instance->list.head = child; + } while(true); +} + +NfcListener* nfc_listener_alloc(Nfc* nfc, NfcProtocol protocol, const NfcDeviceData* data) { + furi_assert(nfc); + furi_assert(protocol < NfcProtocolNum); + furi_assert(data); + furi_assert(nfc_listeners_api[protocol]); + + NfcListener* instance = malloc(sizeof(NfcListener)); + instance->nfc = nfc; + instance->protocol = protocol; + instance->nfc_dev = nfc_device_alloc(); + nfc_device_set_data(instance->nfc_dev, protocol, data); + nfc_listener_list_alloc(instance); + + return instance; +} + +void nfc_listener_free(NfcListener* instance) { + furi_assert(instance); + + nfc_listener_list_free(instance); + nfc_device_free(instance->nfc_dev); + free(instance); +} + +NfcCommand nfc_listener_start_callback(NfcEvent event, void* context) { + furi_assert(context); + + NfcListener* instance = context; + furi_assert(instance->list.head); + + NfcCommand command = NfcCommandContinue; + NfcGenericEvent generic_event = { + .protocol = NfcProtocolInvalid, + .instance = instance->nfc, + .data = &event, + }; + + NfcListenerListElement* head_listener = instance->list.head; + command = head_listener->listener_api->run(generic_event, head_listener->listener); + + return command; +} + +void nfc_listener_start(NfcListener* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + + NfcListenerListElement* tail_element = instance->list.tail; + tail_element->listener_api->set_callback(tail_element->listener, callback, context); + nfc_start(instance->nfc, nfc_listener_start_callback, instance); +} + +void nfc_listener_stop(NfcListener* instance) { + furi_assert(instance); + + nfc_stop(instance->nfc); +} + +const NfcDeviceData* nfc_listener_get_data(NfcListener* instance, NfcProtocol protocol) { + furi_assert(instance); + furi_assert(instance->protocol == protocol); + + NfcListenerListElement* tail_element = instance->list.tail; + return tail_element->listener_api->get_data(tail_element->listener); +} diff --git a/lib/nfc/nfc_listener.h b/lib/nfc/nfc_listener.h new file mode 100644 index 000000000000..7addc82ec5a2 --- /dev/null +++ b/lib/nfc/nfc_listener.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct NfcListener NfcListener; + +NfcListener* nfc_listener_alloc(Nfc* nfc, NfcProtocol protocol, const NfcDeviceData* data); + +void nfc_listener_free(NfcListener* instance); + +void nfc_listener_start(NfcListener* instance, NfcGenericCallback callback, void* context); + +void nfc_listener_stop(NfcListener* instance); + +const NfcDeviceData* nfc_listener_get_data(NfcListener* instance, NfcProtocol protocol); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_poller.c b/lib/nfc/nfc_poller.c new file mode 100644 index 000000000000..ca572aaca77c --- /dev/null +++ b/lib/nfc/nfc_poller.c @@ -0,0 +1,209 @@ +#include "nfc_poller.h" + +#include + +#include + +typedef enum { + NfcPollerSessionStateIdle, + NfcPollerSessionStateActive, + NfcPollerSessionStateStopRequest, +} NfcPollerSessionState; + +typedef struct NfcPollerListElement { + NfcProtocol protocol; + NfcGenericInstance* poller; + const NfcPollerBase* poller_api; + struct NfcPollerListElement* child; +} NfcPollerListElement; + +typedef struct { + NfcPollerListElement* head; + NfcPollerListElement* tail; +} NfcPollerList; + +struct NfcPoller { + NfcProtocol protocol; + Nfc* nfc; + NfcPollerList list; + NfcPollerSessionState session_state; + bool protocol_detected; +}; + +static void nfc_poller_list_alloc(NfcPoller* instance) { + instance->list.head = malloc(sizeof(NfcPollerListElement)); + instance->list.head->protocol = instance->protocol; + instance->list.head->poller_api = nfc_pollers_api[instance->protocol]; + instance->list.head->child = NULL; + instance->list.tail = instance->list.head; + + do { + NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->list.head->protocol); + if(parent_protocol == NfcProtocolInvalid) break; + + NfcPollerListElement* parent = malloc(sizeof(NfcPollerListElement)); + parent->protocol = parent_protocol; + parent->poller_api = nfc_pollers_api[parent_protocol]; + parent->child = instance->list.head; + instance->list.head = parent; + } while(true); + + NfcPollerListElement* iter = instance->list.head; + iter->poller = iter->poller_api->alloc(instance->nfc); + + do { + if(iter->child == NULL) break; + iter->child->poller = iter->child->poller_api->alloc(iter->poller); + iter->poller_api->set_callback( + iter->poller, iter->child->poller_api->run, iter->child->poller); + + iter = iter->child; + } while(true); +} + +static void nfc_poller_list_free(NfcPoller* instance) { + do { + instance->list.head->poller_api->free(instance->list.head->poller); + NfcPollerListElement* child = instance->list.head->child; + free(instance->list.head); + if(child == NULL) break; + instance->list.head = child; + } while(true); +} + +NfcPoller* nfc_poller_alloc(Nfc* nfc, NfcProtocol protocol) { + furi_assert(nfc); + furi_assert(protocol < NfcProtocolNum); + + NfcPoller* instance = malloc(sizeof(NfcPoller)); + instance->session_state = NfcPollerSessionStateIdle; + instance->nfc = nfc; + instance->protocol = protocol; + nfc_poller_list_alloc(instance); + + return instance; +} + +void nfc_poller_free(NfcPoller* instance) { + furi_assert(instance); + + nfc_poller_list_free(instance); + free(instance); +} + +static NfcCommand nfc_poller_start_callback(NfcEvent event, void* context) { + furi_assert(context); + + NfcPoller* instance = context; + + NfcCommand command = NfcCommandContinue; + NfcGenericEvent poller_event = { + .protocol = NfcProtocolInvalid, + .instance = instance->nfc, + .data = &event, + }; + + if(event.type == NfcEventTypePollerReady) { + NfcPollerListElement* head_poller = instance->list.head; + command = head_poller->poller_api->run(poller_event, head_poller->poller); + } + + if(instance->session_state == NfcPollerSessionStateStopRequest) { + command = NfcCommandStop; + } + + return command; +} + +void nfc_poller_start(NfcPoller* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + furi_assert(instance->session_state == NfcPollerSessionStateIdle); + + NfcPollerListElement* tail_poller = instance->list.tail; + tail_poller->poller_api->set_callback(tail_poller->poller, callback, context); + + instance->session_state = NfcPollerSessionStateActive; + nfc_start(instance->nfc, nfc_poller_start_callback, instance); +} + +void nfc_poller_stop(NfcPoller* instance) { + furi_assert(instance); + furi_assert(instance->nfc); + + instance->session_state = NfcPollerSessionStateStopRequest; + nfc_stop(instance->nfc); + instance->session_state = NfcPollerSessionStateIdle; +} + +// TODO change name +static NfcCommand nfc_poller_detect_tail_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + + NfcPoller* instance = context; + NfcPollerListElement* tail_poller = instance->list.tail; + instance->protocol_detected = tail_poller->poller_api->detect(event, tail_poller->poller); + + return NfcCommandStop; +} + +static NfcCommand nfc_poller_detect_callback(NfcEvent event, void* context) { + furi_assert(context); + + NfcPoller* instance = context; + NfcPollerListElement* tail_poller = instance->list.tail; + NfcPollerListElement* head_poller = instance->list.head; + + NfcCommand command = NfcCommandContinue; + NfcGenericEvent poller_event = { + .protocol = NfcProtocolInvalid, + .instance = instance->nfc, + .data = &event, + }; + + // TODO hard to understand + if(event.type == NfcEventTypePollerReady) { + if(tail_poller == head_poller) { + instance->protocol_detected = + tail_poller->poller_api->detect(poller_event, tail_poller->poller); + command = NfcCommandStop; + } else { + command = head_poller->poller_api->run(poller_event, head_poller->poller); + } + } + + return command; +} + +bool nfc_poller_detect(NfcPoller* instance) { + furi_assert(instance); + furi_assert(instance->session_state == NfcPollerSessionStateIdle); + + instance->session_state = NfcPollerSessionStateActive; + NfcPollerListElement* tail_poller = instance->list.tail; + NfcPollerListElement* iter = instance->list.head; + + // TODO hard to understand + if(tail_poller != instance->list.head) { + while(iter->child != tail_poller) iter = iter->child; + iter->poller_api->set_callback(iter->poller, nfc_poller_detect_tail_callback, instance); + } + + nfc_start(instance->nfc, nfc_poller_detect_callback, instance); + nfc_stop(instance->nfc); + + // TODO hard to understand + if(tail_poller != instance->list.head) { + iter->poller_api->set_callback( + iter->poller, tail_poller->poller_api->run, tail_poller->poller); + } + + return instance->protocol_detected; +} + +const NfcDeviceData* nfc_poller_get_data(NfcPoller* instance) { + furi_assert(instance); + + NfcPollerListElement* tail_poller = instance->list.tail; + return tail_poller->poller_api->get_data(tail_poller->poller); +} diff --git a/lib/nfc/nfc_poller.h b/lib/nfc/nfc_poller.h new file mode 100644 index 000000000000..4c7603da7dae --- /dev/null +++ b/lib/nfc/nfc_poller.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct NfcPoller NfcPoller; + +NfcPoller* nfc_poller_alloc(Nfc* nfc, NfcProtocol protocol); + +void nfc_poller_free(NfcPoller* instance); + +void nfc_poller_start(NfcPoller* instance, NfcGenericCallback callback, void* context); + +void nfc_poller_stop(NfcPoller* instance); + +bool nfc_poller_detect(NfcPoller* instance); + +const NfcDeviceData* nfc_poller_get_data(NfcPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_scanner.c b/lib/nfc/nfc_scanner.c new file mode 100644 index 000000000000..f32110be9381 --- /dev/null +++ b/lib/nfc/nfc_scanner.c @@ -0,0 +1,268 @@ +#include "nfc_scanner.h" +#include "nfc_poller.h" + +#include + +#include + +#define TAG "NfcScanner" + +typedef enum { + NfcScannerStateIdle, + NfcScannerStateTryBasePollers, + NfcScannerStateFindChildrenProtocols, + NfcScannerStateDetectChildrenProtocols, + // TODO add state to retry base pollers after children pollers detection + NfcScannerStateComplete, + + NfcScannerStateNum, +} NfcScannerState; + +typedef enum { + NfcScannerSessionStateIdle, + NfcScannerSessionStateActive, + NfcScannerSessionStateStopRequest, +} NfcScannerSessionState; + +struct NfcScanner { + Nfc* nfc; + NfcScannerState state; + NfcScannerSessionState session_state; + + NfcScannerCallback callback; + void* context; + + NfcEvent nfc_event; + + NfcProtocol first_detected_protocol; + + size_t base_protocols_num; + size_t base_protocols_idx; + NfcProtocol base_protocols[NfcProtocolNum]; + + size_t detected_base_protocols_num; + NfcProtocol detected_base_protocols[NfcProtocolNum]; + + size_t children_protocols_num; + size_t children_protocols_idx; + NfcProtocol children_protocols[NfcProtocolNum]; + + size_t detected_protocols_num; + NfcProtocol detected_protocols[NfcProtocolNum]; + + NfcProtocol current_protocol; + + FuriThread* scan_worker; +}; + +static void nfc_scanner_reset(NfcScanner* instance) { + instance->base_protocols_idx = 0; + instance->base_protocols_num = 0; + + instance->children_protocols_idx = 0; + instance->children_protocols_num = 0; + + instance->detected_protocols_num = 0; + instance->detected_base_protocols_num = 0; + + instance->current_protocol = 0; +} + +typedef void (*NfcScannerStateHandler)(NfcScanner* instance); + +void nfc_scanner_state_handler_idle(NfcScanner* instance) { + for(size_t i = 0; i < NfcProtocolNum; i++) { + NfcProtocol parent_protocol = nfc_protocol_get_parent(i); + if(parent_protocol == NfcProtocolInvalid) { + instance->base_protocols[instance->base_protocols_num] = i; + instance->base_protocols_num++; + } + } + FURI_LOG_D(TAG, "Found %d base protocols", instance->base_protocols_num); + + instance->first_detected_protocol = NfcProtocolInvalid; + instance->state = NfcScannerStateTryBasePollers; +} + +void nfc_scanner_state_handler_try_base_pollers(NfcScanner* instance) { + do { + instance->current_protocol = instance->base_protocols[instance->base_protocols_idx]; + + if(instance->first_detected_protocol == instance->current_protocol) { + instance->state = NfcScannerStateFindChildrenProtocols; + break; + } + + NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol); + bool protocol_detected = nfc_poller_detect(poller); + nfc_poller_free(poller); + + if(protocol_detected) { + instance->detected_protocols[instance->detected_protocols_num] = + instance->current_protocol; + instance->detected_protocols_num++; + + instance->detected_base_protocols[instance->detected_base_protocols_num] = + instance->current_protocol; + instance->detected_base_protocols_num++; + + if(instance->first_detected_protocol == NfcProtocolInvalid) { + instance->first_detected_protocol = instance->current_protocol; + instance->current_protocol = NfcProtocolInvalid; + } + } + + instance->base_protocols_idx = + (instance->base_protocols_idx + 1) % instance->base_protocols_num; + } while(false); +} + +void nfc_scanner_state_handler_find_children_protocols(NfcScanner* instance) { + for(size_t i = 0; i < NfcProtocolNum; i++) { + for(size_t j = 0; j < instance->detected_base_protocols_num; j++) { + if(nfc_protocol_has_parent(i, instance->detected_base_protocols[j])) { + instance->children_protocols[instance->children_protocols_num] = i; + instance->children_protocols_num++; + } + } + } + + if(instance->children_protocols_num > 0) { + instance->state = NfcScannerStateDetectChildrenProtocols; + } else { + instance->state = NfcScannerStateComplete; + } + FURI_LOG_D(TAG, "Found %d children", instance->children_protocols_num); +} + +void nfc_scanner_state_handler_detect_children_protocols(NfcScanner* instance) { + furi_assert(instance->children_protocols_num); + + instance->current_protocol = instance->children_protocols[instance->children_protocols_idx]; + + NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol); + bool protocol_detected = nfc_poller_detect(poller); + nfc_poller_free(poller); + + if(protocol_detected) { + instance->detected_protocols[instance->detected_protocols_num] = + instance->current_protocol; + instance->detected_protocols_num++; + } + + instance->children_protocols_idx++; + if(instance->children_protocols_idx == instance->children_protocols_num) { + instance->state = NfcScannerStateComplete; + } +} + +static void nfc_scanner_filter_detected_protocols(NfcScanner* instance) { + size_t filtered_protocols_num = 0; + NfcProtocol filtered_protocols[NfcProtocolNum] = {}; + + for(size_t i = 0; i < instance->detected_protocols_num; i++) { + bool is_parent = false; + for(size_t j = i; j < instance->detected_protocols_num; j++) { + is_parent = nfc_protocol_has_parent( + instance->detected_protocols[j], instance->detected_protocols[i]); + if(is_parent) break; + } + if(!is_parent) { + filtered_protocols[filtered_protocols_num] = instance->detected_protocols[i]; + filtered_protocols_num++; + } + } + + instance->detected_protocols_num = filtered_protocols_num; + memcpy(instance->detected_protocols, filtered_protocols, filtered_protocols_num); +} + +void nfc_scanner_state_handler_complete(NfcScanner* instance) { + if(instance->detected_protocols_num > 1) { + nfc_scanner_filter_detected_protocols(instance); + } + FURI_LOG_I(TAG, "Detected %d protocols", instance->detected_protocols_num); + + NfcScannerEvent event = { + .type = NfcScannerEventTypeDetected, + .data = + { + .protocol_num = instance->detected_protocols_num, + .protocols = instance->detected_protocols, + }, + }; + + instance->callback(event, instance->context); + furi_delay_ms(100); +} + +static NfcScannerStateHandler nfc_scanner_state_handlers[NfcScannerStateNum] = { + [NfcScannerStateIdle] = nfc_scanner_state_handler_idle, + [NfcScannerStateTryBasePollers] = nfc_scanner_state_handler_try_base_pollers, + [NfcScannerStateFindChildrenProtocols] = nfc_scanner_state_handler_find_children_protocols, + [NfcScannerStateDetectChildrenProtocols] = nfc_scanner_state_handler_detect_children_protocols, + [NfcScannerStateComplete] = nfc_scanner_state_handler_complete, +}; + +static int32_t nfc_scanner_worker(void* context) { + furi_assert(context); + + NfcScanner* instance = context; + + while(instance->session_state == NfcScannerSessionStateActive) { + nfc_scanner_state_handlers[instance->state](instance); + } + + nfc_scanner_reset(instance); + + return 0; +} + +NfcScanner* nfc_scanner_alloc(Nfc* nfc) { + furi_assert(nfc); + + NfcScanner* instance = malloc(sizeof(NfcScanner)); + instance->nfc = nfc; + + return instance; +} + +void nfc_scanner_free(NfcScanner* instance) { + furi_assert(instance); + + free(instance); +} + +void nfc_scanner_start(NfcScanner* instance, NfcScannerCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + furi_assert(instance->session_state == NfcScannerSessionStateIdle); + furi_assert(instance->scan_worker == NULL); + + instance->callback = callback; + instance->context = context; + instance->session_state = NfcScannerSessionStateActive; + + instance->scan_worker = furi_thread_alloc(); + furi_thread_set_name(instance->scan_worker, "NfcScanWorker"); + furi_thread_set_context(instance->scan_worker, instance); + furi_thread_set_stack_size(instance->scan_worker, 4 * 1024); + furi_thread_set_callback(instance->scan_worker, nfc_scanner_worker); + + furi_thread_start(instance->scan_worker); +} + +void nfc_scanner_stop(NfcScanner* instance) { + furi_assert(instance); + furi_assert(instance->scan_worker); + + instance->session_state = NfcScannerSessionStateStopRequest; + furi_thread_join(instance->scan_worker); + instance->session_state = NfcScannerSessionStateIdle; + + furi_thread_free(instance->scan_worker); + instance->scan_worker = NULL; + instance->callback = NULL; + instance->context = NULL; + instance->state = NfcScannerStateIdle; +} diff --git a/lib/nfc/nfc_scanner.h b/lib/nfc/nfc_scanner.h new file mode 100644 index 000000000000..bcf1ddd51e56 --- /dev/null +++ b/lib/nfc/nfc_scanner.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct NfcScanner NfcScanner; + +typedef enum { + NfcScannerEventTypeDetected, +} NfcScannerEventType; + +typedef struct { + size_t protocol_num; + NfcProtocol* protocols; +} NfcScannerEventData; + +typedef struct { + NfcScannerEventType type; + NfcScannerEventData data; +} NfcScannerEvent; + +typedef void (*NfcScannerCallback)(NfcScannerEvent event, void* context); + +NfcScanner* nfc_scanner_alloc(Nfc* nfc); + +void nfc_scanner_free(NfcScanner* instance); + +void nfc_scanner_start(NfcScanner* instance, NfcScannerCallback callback, void* context); + +void nfc_scanner_stop(NfcScanner* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/crypto1.h b/lib/nfc/protocols/crypto1.h deleted file mode 100644 index 450d1534e320..000000000000 --- a/lib/nfc/protocols/crypto1.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include - -typedef struct { - uint32_t odd; - uint32_t even; -} Crypto1; - -void crypto1_reset(Crypto1* crypto1); - -void crypto1_init(Crypto1* crypto1, uint64_t key); - -uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted); - -uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted); - -uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted); - -uint32_t crypto1_filter(uint32_t in); - -uint32_t prng_successor(uint32_t x, uint32_t n); - -void crypto1_decrypt( - Crypto1* crypto, - uint8_t* encrypted_data, - uint16_t encrypted_data_bits, - uint8_t* decrypted_data); - -void crypto1_encrypt( - Crypto1* crypto, - uint8_t* keystream, - uint8_t* plain_data, - uint16_t plain_data_bits, - uint8_t* encrypted_data, - uint8_t* encrypted_parity); diff --git a/lib/nfc/protocols/felica/felica.c b/lib/nfc/protocols/felica/felica.c new file mode 100644 index 000000000000..4b7c91fb64e0 --- /dev/null +++ b/lib/nfc/protocols/felica/felica.c @@ -0,0 +1,147 @@ +#include "felica.h" + +#include + +#include + +#define FELICA_PROTOCOL_NAME "FeliCa" +#define FELICA_DEVICE_NAME "FeliCa" + +#define FELICA_DATA_FORMAT_VERSION "Data format version" +#define FELICA_MANUFACTURE_ID "Manufacture id" +#define FELICA_MANUFACTURE_PARAMETER "Manufacture parameter" + +static const uint32_t felica_data_format_version = 1; + +const NfcDeviceBase nfc_device_felica = { + .protocol_name = FELICA_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)felica_alloc, + .free = (NfcDeviceFree)felica_free, + .reset = (NfcDeviceReset)felica_reset, + .copy = (NfcDeviceCopy)felica_copy, + .verify = (NfcDeviceVerify)felica_verify, + .load = (NfcDeviceLoad)felica_load, + .save = (NfcDeviceSave)felica_save, + .is_equal = (NfcDeviceEqual)felica_is_equal, + .get_name = (NfcDeviceGetName)felica_get_device_name, + .get_uid = (NfcDeviceGetUid)felica_get_uid, + .set_uid = (NfcDeviceSetUid)felica_set_uid, + .get_base_data = (NfcDeviceGetBaseData)felica_get_base_data, +}; + +FelicaData* felica_alloc() { + FelicaData* data = malloc(sizeof(FelicaData)); + return data; +} + +void felica_free(FelicaData* data) { + furi_assert(data); + + free(data); +} + +void felica_reset(FelicaData* data) { + memset(data, 0, sizeof(FelicaData)); +} + +void felica_copy(FelicaData* data, const FelicaData* other) { + furi_assert(data); + furi_assert(other); + + *data = *other; +} + +bool felica_verify(FelicaData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + + return false; +} + +bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool parsed = false; + + do { + if(version < NFC_UNIFIED_FORMAT_VERSION) break; + + uint32_t data_format_version = 0; + if(!flipper_format_read_uint32(ff, FELICA_DATA_FORMAT_VERSION, &data_format_version, 1)) + break; + if(data_format_version != felica_data_format_version) break; + if(!flipper_format_read_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE)) + break; + if(!flipper_format_read_hex( + ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE)) + break; + + parsed = true; + } while(false); + + return parsed; +} + +bool felica_save(const FelicaData* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + if(!flipper_format_write_comment_cstr(ff, FELICA_PROTOCOL_NAME " specific data")) break; + if(!flipper_format_write_uint32( + ff, FELICA_DATA_FORMAT_VERSION, &felica_data_format_version, 1)) + break; + if(!flipper_format_write_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE)) + break; + if(!flipper_format_write_hex( + ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE)) + break; + + saved = true; + } while(false); + + return saved; +} + +bool felica_is_equal(const FelicaData* data, const FelicaData* other) { + furi_assert(data); + furi_assert(other); + + return memcmp(data, other, sizeof(FelicaData)) == 0; +} + +const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + + return FELICA_DEVICE_NAME; +} + +const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) { + furi_assert(data); + + // Consider Manufacturer ID as UID + if(uid_len) { + *uid_len = FELICA_IDM_SIZE; + } + + return data->idm.data; +} + +bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + // Consider Manufacturer ID as UID + const bool uid_valid = uid_len == FELICA_IDM_SIZE; + if(uid_valid) { + memcpy(data->idm.data, uid, uid_len); + } + + return uid_valid; +} + +FelicaData* felica_get_base_data(const FelicaData* data) { + UNUSED(data); + furi_crash("No base data"); +} diff --git a/lib/nfc/protocols/felica/felica.h b/lib/nfc/protocols/felica/felica.h new file mode 100644 index 000000000000..671a7220de25 --- /dev/null +++ b/lib/nfc/protocols/felica/felica.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FELICA_IDM_SIZE (8U) +#define FELICA_PMM_SIZE (8U) + +#define FELICA_GUARD_TIME_US (20000U) +#define FELICA_FDT_POLL_FC (10000U) +#define FELICA_POLL_POLL_MIN_US (1280U) + +#define FELICA_SYSTEM_CODE_CODE (0xFFFFU) +#define FELICA_TIME_SLOT_1 (0x00U) +#define FELICA_TIME_SLOT_2 (0x01U) +#define FELICA_TIME_SLOT_4 (0x03U) +#define FELICA_TIME_SLOT_8 (0x07U) +#define FELICA_TIME_SLOT_16 (0x0FU) + +typedef enum { + FelicaErrorNone, + FelicaErrorNotPresent, + FelicaErrorColResFailed, + FelicaErrorBufferOverflow, + FelicaErrorCommunication, + FelicaErrorFieldOff, + FelicaErrorWrongCrc, + FelicaErrorTimeout, +} FelicaError; + +typedef struct { + uint8_t data[FELICA_IDM_SIZE]; +} FelicaIDm; + +typedef struct { + uint8_t data[FELICA_PMM_SIZE]; +} FelicaPMm; + +typedef struct { + FelicaIDm idm; + FelicaPMm pmm; +} FelicaData; + +extern const NfcDeviceBase nfc_device_felica; + +FelicaData* felica_alloc(); + +void felica_free(FelicaData* data); + +void felica_reset(FelicaData* data); + +void felica_copy(FelicaData* data, const FelicaData* other); + +bool felica_verify(FelicaData* data, const FuriString* device_type); + +bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version); + +bool felica_save(const FelicaData* data, FlipperFormat* ff); + +bool felica_is_equal(const FelicaData* data, const FelicaData* other); + +const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type); + +const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len); + +bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len); + +FelicaData* felica_get_base_data(const FelicaData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/felica/felica_poller.c b/lib/nfc/protocols/felica/felica_poller.c new file mode 100644 index 000000000000..d4ef85dbbc0a --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller.c @@ -0,0 +1,117 @@ +#include "felica_poller_i.h" + +#include + +#include + +const FelicaData* felica_poller_get_data(FelicaPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static FelicaPoller* felica_poller_alloc(Nfc* nfc) { + furi_assert(nfc); + + FelicaPoller* instance = malloc(sizeof(FelicaPoller)); + instance->nfc = nfc; + instance->tx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE); + instance->rx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE); + + nfc_config(instance->nfc, NfcModePoller, NfcTechFelica); + nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US); + nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC); + nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US); + instance->data = felica_alloc(); + + instance->felica_event.data = &instance->felica_event_data; + instance->general_event.protocol = NfcProtocolFelica; + instance->general_event.data = &instance->felica_event; + instance->general_event.instance = instance; + + return instance; +} + +static void felica_poller_free(FelicaPoller* instance) { + furi_assert(instance); + + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(instance->data); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + felica_free(instance->data); + free(instance); +} + +static void + felica_poller_set_callback(FelicaPoller* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.data); + + FelicaPoller* instance = context; + NfcEvent* nfc_event = event.data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypePollerReady) { + if(instance->state != FelicaPollerStateActivated) { + FelicaError error = felica_poller_async_activate(instance, instance->data); + if(error == FelicaErrorNone) { + instance->felica_event.type = FelicaPollerEventTypeReady; + instance->felica_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + } else { + instance->felica_event.type = FelicaPollerEventTypeError; + instance->felica_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + // Add delay to switch context + furi_delay_ms(100); + } + } else { + instance->felica_event.type = FelicaPollerEventTypeReady; + instance->felica_event_data.error = FelicaErrorNone; + command = instance->callback(instance->general_event, instance->context); + } + } + + return command; +} + +static bool felica_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.instance); + furi_assert(event.protocol = NfcProtocolInvalid); + + bool protocol_detected = false; + FelicaPoller* instance = context; + NfcEvent* nfc_event = event.data; + furi_assert(instance->state == FelicaPollerStateIdle); + + if(nfc_event->type == NfcEventTypePollerReady) { + FelicaError error = felica_poller_async_activate(instance, instance->data); + protocol_detected = (error == FelicaErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_felica = { + .alloc = (NfcPollerAlloc)felica_poller_alloc, + .free = (NfcPollerFree)felica_poller_free, + .set_callback = (NfcPollerSetCallback)felica_poller_set_callback, + .run = (NfcPollerRun)felica_poller_run, + .detect = (NfcPollerDetect)felica_poller_detect, + .get_data = (NfcPollerGetData)felica_poller_get_data, +}; diff --git a/lib/nfc/protocols/felica/felica_poller.h b/lib/nfc/protocols/felica/felica_poller.h new file mode 100644 index 000000000000..7d0c9525e770 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller.h @@ -0,0 +1,30 @@ +#pragma once + +#include "felica.h" +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FelicaPoller FelicaPoller; + +typedef enum { + FelicaPollerEventTypeError, + FelicaPollerEventTypeReady, +} FelicaPollerEventType; + +typedef struct { + FelicaError error; +} FelicaPollerEventData; + +typedef struct { + FelicaPollerEventType type; + FelicaPollerEventData* data; +} FelicaPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/felica/felica_poller_defs.h b/lib/nfc/protocols/felica/felica_poller_defs.h new file mode 100644 index 000000000000..fc99dc75219b --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase nfc_poller_felica; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/felica/felica_poller_i.c b/lib/nfc/protocols/felica/felica_poller_i.c new file mode 100644 index 000000000000..7e9bb42bae27 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller_i.c @@ -0,0 +1,115 @@ +#include "felica_poller_i.h" + +#include + +#define TAG "FelicaPoller" + +static FelicaError felica_poller_process_error(NfcError error) { + switch(error) { + case NfcErrorNone: + return FelicaErrorNone; + case NfcErrorTimeout: + return FelicaErrorTimeout; + default: + return FelicaErrorNotPresent; + } +} + +static FelicaError felica_poller_frame_exchange( + FelicaPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + + const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer); + furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - FELICA_CRC_SIZE); + + felica_crc_append(instance->tx_buffer); + + FelicaError ret = FelicaErrorNone; + + do { + NfcError error = + nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = felica_poller_process_error(error); + break; + } + + bit_buffer_copy(rx_buffer, instance->rx_buffer); + if(!felica_crc_check(instance->rx_buffer)) { + ret = FelicaErrorWrongCrc; + break; + } + + felica_crc_trim(rx_buffer); + } while(false); + + return ret; +} + +FelicaError felica_poller_async_polling( + FelicaPoller* instance, + const FelicaPollerPollingCommand* cmd, + FelicaPollerPollingResponse* resp) { + furi_assert(instance); + furi_assert(cmd); + furi_assert(resp); + + bit_buffer_set_size_bytes(instance->tx_buffer, 2); + // Set frame len + // TODO Set length in felica_poller_frame_exchange() ? + bit_buffer_set_byte(instance->tx_buffer, 0, sizeof(FelicaPollerPollingCommand) + 2); + // Set command code + bit_buffer_set_byte(instance->tx_buffer, 1, FELICA_POLLER_CMD_POLLING_CODE); + // Set other data + bit_buffer_append_bytes( + instance->tx_buffer, (uint8_t*)cmd, sizeof(FelicaPollerPollingCommand)); + + FelicaError error = felica_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT); + + if(error == FelicaErrorNone) { + // TODO extract length in felica_poller_frame_exchange() ? + bit_buffer_write_bytes_mid(instance->rx_buffer, resp->idm.data, 2, sizeof(FelicaIDm)); + bit_buffer_write_bytes_mid( + instance->rx_buffer, resp->pmm.data, sizeof(FelicaIDm) + 2, sizeof(FelicaPMm)); + } + + return error; +} + +FelicaError felica_poller_async_activate(FelicaPoller* instance, FelicaData* data) { + furi_assert(instance); + + felica_reset(data); + + FelicaError ret; + + do { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send Polling command + const FelicaPollerPollingCommand polling_cmd = { + .system_code = FELICA_SYSTEM_CODE_CODE, + .request_code = 0, + .time_slot = FELICA_TIME_SLOT_1, + }; + FelicaPollerPollingResponse polling_resp = {}; + + ret = felica_poller_async_polling(instance, &polling_cmd, &polling_resp); + + if(ret != FelicaErrorNone) { + FURI_LOG_T(TAG, "Activation failed error: %d", ret); + break; + } + + data->idm = polling_resp.idm; + data->pmm = polling_resp.pmm; + instance->state = FelicaPollerStateActivated; + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/felica/felica_poller_i.h b/lib/nfc/protocols/felica/felica_poller_i.h new file mode 100644 index 000000000000..d458c9bb4628 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller_i.h @@ -0,0 +1,59 @@ +#pragma once + +#include "felica_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FELICA_POLLER_MAX_BUFFER_SIZE (256U) + +#define FELICA_POLLER_POLLING_FWT (200000U) + +#define FELICA_POLLER_CMD_POLLING_CODE (0x00U) + +typedef enum { + FelicaPollerStateIdle, + FelicaPollerStateActivated, +} FelicaPollerState; + +struct FelicaPoller { + Nfc* nfc; + FelicaPollerState state; + FelicaData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + NfcGenericEvent general_event; + FelicaPollerEvent felica_event; + FelicaPollerEventData felica_event_data; + NfcGenericCallback callback; + void* context; +}; + +typedef struct { + uint16_t system_code; + uint8_t request_code; + uint8_t time_slot; +} FelicaPollerPollingCommand; + +typedef struct { + FelicaIDm idm; + FelicaPMm pmm; + uint8_t request_data[2]; +} FelicaPollerPollingResponse; + +const FelicaData* felica_poller_get_data(FelicaPoller* instance); + +FelicaError felica_poller_async_polling( + FelicaPoller* instance, + const FelicaPollerPollingCommand* cmd, + FelicaPollerPollingResponse* resp); + +FelicaError felica_poller_async_activate(FelicaPoller* instance, FelicaData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a.c new file mode 100644 index 000000000000..612a5976b493 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a.c @@ -0,0 +1,155 @@ +#include "iso14443_3a.h" + +#include +#include + +#define ISO14443_3A_PROTOCOL_NAME_LEGACY "UID" +#define ISO14443_3A_PROTOCOL_NAME "ISO14443-3A" +#define ISO14443_3A_DEVICE_NAME "ISO14443-3A (Unknown)" + +#define ISO14443_3A_ATQA_KEY "ATQA" +#define ISO14443_3A_SAK_KEY "SAK" + +const NfcDeviceBase nfc_device_iso14443_3a = { + .protocol_name = ISO14443_3A_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)iso14443_3a_alloc, + .free = (NfcDeviceFree)iso14443_3a_free, + .reset = (NfcDeviceReset)iso14443_3a_reset, + .copy = (NfcDeviceCopy)iso14443_3a_copy, + .verify = (NfcDeviceVerify)iso14443_3a_verify, + .load = (NfcDeviceLoad)iso14443_3a_load, + .save = (NfcDeviceSave)iso14443_3a_save, + .is_equal = (NfcDeviceEqual)iso14443_3a_is_equal, + .get_name = (NfcDeviceGetName)iso14443_3a_get_device_name, + .get_uid = (NfcDeviceGetUid)iso14443_3a_get_uid, + .set_uid = (NfcDeviceSetUid)iso14443_3a_set_uid, + .get_base_data = (NfcDeviceGetBaseData)iso14443_3a_get_base_data, +}; + +Iso14443_3aData* iso14443_3a_alloc() { + Iso14443_3aData* data = malloc(sizeof(Iso14443_3aData)); + return data; +} + +void iso14443_3a_free(Iso14443_3aData* data) { + furi_assert(data); + + free(data); +} + +void iso14443_3a_reset(Iso14443_3aData* data) { + furi_assert(data); + + UNUSED(data); +} + +void iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other) { + furi_assert(data); + furi_assert(other); + + *data = *other; +} + +bool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal(device_type, ISO14443_3A_PROTOCOL_NAME_LEGACY); +} + +bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool parsed = false; + + do { + // Common to all format versions + if(!flipper_format_read_hex(ff, ISO14443_3A_ATQA_KEY, data->atqa, 2)) break; + if(!flipper_format_read_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break; + + if(version > NFC_LSB_ATQA_FORMAT_VERSION) { + // Swap ATQA bytes for newer versions + const uint8_t tmp = data->atqa[0]; + data->atqa[0] = data->atqa[1]; + data->atqa[1] = tmp; + } + + parsed = true; + } while(false); + + return parsed; +} + +bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + // Save ATQA in MSB order for correct companion apps display + const uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; + if(!flipper_format_write_comment_cstr(ff, ISO14443_3A_PROTOCOL_NAME " specific data")) + break; + + // Write ATQA and SAK + if(!flipper_format_write_hex(ff, ISO14443_3A_ATQA_KEY, atqa, 2)) break; + if(!flipper_format_write_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other) { + furi_assert(data); + furi_assert(other); + + return memcmp(data, other, sizeof(Iso14443_3aData)) == 0; +} + +const char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + return ISO14443_3A_DEVICE_NAME; +} + +const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len) { + furi_assert(data); + + if(uid_len) { + *uid_len = data->uid_len; + } + + return data->uid; +} + +bool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + const bool uid_valid = uid_len == ISO14443_3A_UID_4_BYTES || + uid_len == ISO14443_3A_UID_7_BYTES || + uid_len == ISO14443_3A_UID_10_BYTES; + + if(uid_valid) { + memcpy(data->uid, uid, uid_len); + data->uid_len = uid_len; + } + + return uid_valid; +} + +Iso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data) { + UNUSED(data); + furi_crash("No base data"); +} + +uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* iso14443_3a_data) { + furi_assert(iso14443_3a_data); + + uint32_t cuid = 0; + const uint8_t* cuid_start = iso14443_3a_data->uid; + if(iso14443_3a_data->uid_len == 7) { + cuid_start = &iso14443_3a_data->uid[3]; + } + cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | (cuid_start[3]); + + return cuid; +} diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a.h new file mode 100644 index 000000000000..1f4834f02d39 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO14443_3A_UID_4_BYTES (4U) +#define ISO14443_3A_UID_7_BYTES (7U) +#define ISO14443_3A_UID_10_BYTES (10U) +#define ISO14443_3A_MAX_UID_SIZE ISO14443_3A_UID_10_BYTES + +#define ISO14443_3A_GUARD_TIME_US (5000) +#define ISO14443_3A_FDT_POLL_FC (1620) +#define ISO14443_3A_FDT_LISTEN_FC (1172) +#define ISO14443_3A_POLLER_MASK_RX_FS ((ISO14443_3A_FDT_LISTEN_FC) / 2) +#define ISO14443_3A_POLL_POLL_MIN_US (1100) + +typedef enum { + Iso14443_3aErrorNone, + Iso14443_3aErrorNotPresent, + Iso14443_3aErrorColResFailed, + Iso14443_3aErrorBufferOverflow, + Iso14443_3aErrorCommunication, + Iso14443_3aErrorFieldOff, + Iso14443_3aErrorWrongCrc, + Iso14443_3aErrorTimeout, +} Iso14443_3aError; + +typedef struct { + uint8_t sens_resp[2]; +} Iso14443_3aSensResp; + +typedef struct { + uint8_t sel_cmd; + uint8_t sel_par; + uint8_t data[4]; // max data bit is 32 +} Iso14443_3aSddReq; + +typedef struct { + uint8_t nfcid[4]; + uint8_t bss; +} Iso14443_3aSddResp; + +typedef struct { + uint8_t sel_cmd; + uint8_t sel_par; + uint8_t nfcid[4]; + uint8_t bcc; +} Iso14443_3aSelReq; + +typedef struct { + uint8_t sak; +} Iso14443_3aSelResp; + +typedef struct { + uint8_t uid[ISO14443_3A_MAX_UID_SIZE]; + uint8_t uid_len; + uint8_t atqa[2]; + uint8_t sak; +} Iso14443_3aData; + +extern const NfcDeviceBase nfc_device_iso14443_3a; + +Iso14443_3aData* iso14443_3a_alloc(); + +void iso14443_3a_free(Iso14443_3aData* data); + +void iso14443_3a_reset(Iso14443_3aData* data); + +void iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other); + +bool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type); + +bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version); + +bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff); + +bool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other); + +const char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type); + +const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len); + +bool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data); + +uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* iso14443_3a_data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.c new file mode 100644 index 000000000000..1caa98f1bcf2 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.c @@ -0,0 +1,127 @@ +#include "iso14443_3a_listener_i.h" + +#include "nfc/protocols/nfc_listener_base.h" +#include "nfc/helpers/iso14443_crc.h" + +#include +#include + +#define TAG "Iso14443_3aListener" + +#define ISO14443_3A_LISTENER_MAX_BUFFER_SIZE (256) + +static bool iso14443_3a_listener_halt_received(BitBuffer* buf) { + bool halt_cmd_received = false; + + do { + if(bit_buffer_get_size_bytes(buf) != 4) break; + if(!iso14443_crc_check(Iso14443CrcTypeA, buf)) break; + if(bit_buffer_get_byte(buf, 0) != 0x50) break; + if(bit_buffer_get_byte(buf, 1) != 0x00) break; + halt_cmd_received = true; + } while(false); + + return halt_cmd_received; +} + +Iso14443_3aListener* iso14443_3a_listener_alloc(Nfc* nfc, Iso14443_3aData* data) { + furi_assert(nfc); + + Iso14443_3aListener* instance = malloc(sizeof(Iso14443_3aListener)); + instance->nfc = nfc; + instance->data = data; + instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_LISTENER_MAX_BUFFER_SIZE); + + instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data; + instance->generic_event.protocol = NfcProtocolIso14443_3a; + instance->generic_event.instance = instance; + instance->generic_event.data = &instance->iso14443_3a_event; + + nfc_set_fdt_listen_fc(instance->nfc, ISO14443_3A_FDT_LISTEN_FC); + nfc_config(instance->nfc, NfcModeListener, NfcTechIso14443a); + nfc_iso14443a_listener_set_col_res_data( + instance->nfc, + instance->data->uid, + instance->data->uid_len, + instance->data->atqa, + instance->data->sak); + + return instance; +} + +void iso14443_3a_listener_free(Iso14443_3aListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->tx_buffer); + + bit_buffer_free(instance->tx_buffer); + free(instance); +} + +void iso14443_3a_listener_set_callback( + Iso14443_3aListener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +const Iso14443_3aData* iso14443_3a_listener_get_data(Iso14443_3aListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +NfcCommand iso14443_3a_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.data); + + Iso14443_3aListener* instance = context; + NfcEvent* nfc_event = event.data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypeListenerActivated) { + instance->state = Iso14443_3aListenerStateActive; + } else if(nfc_event->type == NfcEventTypeFieldOff) { + instance->state = Iso14443_3aListenerStateIdle; + if(instance->callback) { + instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeFieldOff; + instance->callback(instance->generic_event, instance->context); + } + command = NfcCommandSleep; + } else if(nfc_event->type == NfcEventTypeRxEnd) { + if(iso14443_3a_listener_halt_received(nfc_event->data.buffer)) { + if(instance->callback) { + instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeHalted; + instance->callback(instance->generic_event, instance->context); + } + command = NfcCommandSleep; + } else { + if(iso14443_crc_check(Iso14443CrcTypeA, nfc_event->data.buffer)) { + instance->iso14443_3a_event.type = + Iso14443_3aListenerEventTypeReceivedStandardFrame; + iso14443_crc_trim(nfc_event->data.buffer); + } else { + instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeReceivedData; + } + instance->iso14443_3a_event_data.buffer = nfc_event->data.buffer; + if(instance->callback) { + command = instance->callback(instance->generic_event, instance->context); + } + } + } + + return command; +} + +const NfcListenerBase nfc_listener_iso14443_3a = { + .alloc = (NfcListenerAlloc)iso14443_3a_listener_alloc, + .free = (NfcListenerFree)iso14443_3a_listener_free, + .set_callback = (NfcListenerSetCallback)iso14443_3a_listener_set_callback, + .get_data = (NfcListenerGetData)iso14443_3a_listener_get_data, + .run = (NfcListenerRun)iso14443_3a_listener_run, +}; diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h new file mode 100644 index 000000000000..8a550ca0a892 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h @@ -0,0 +1,31 @@ +#pragma once + +#include "iso14443_3a.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso14443_3aListener Iso14443_3aListener; + +typedef enum { + Iso14443_3aListenerEventTypeFieldOff, + Iso14443_3aListenerEventTypeHalted, + + Iso14443_3aListenerEventTypeReceivedStandardFrame, + Iso14443_3aListenerEventTypeReceivedData, +} Iso14443_3aListenerEventType; + +typedef struct { + BitBuffer* buffer; +} Iso14443_3aListenerEventData; + +typedef struct { + Iso14443_3aListenerEventType type; + Iso14443_3aListenerEventData* data; +} Iso14443_3aListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_defs.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_defs.h new file mode 100644 index 000000000000..b92bf8837a92 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcListenerBase nfc_listener_iso14443_3a; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.c new file mode 100644 index 000000000000..46501503ceec --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.c @@ -0,0 +1,73 @@ +#include "iso14443_3a_listener_i.h" + +#include "nfc/helpers/iso14443_crc.h" + +#define TAG "Iso14443_3aListener" + +static Iso14443_3aError iso14443_3a_listener_process_nfc_error(NfcError error) { + Iso14443_3aError ret = Iso14443_3aErrorNone; + + if(error == NfcErrorNone) { + ret = Iso14443_3aErrorNone; + } else if(error == NfcErrorTimeout) { + ret = Iso14443_3aErrorTimeout; + } else { + ret = Iso14443_3aErrorFieldOff; + } + + return ret; +} + +Iso14443_3aError + iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + Iso14443_3aError ret = Iso14443_3aErrorNone; + NfcError error = nfc_listener_tx(instance->nfc, tx_buffer); + if(error != NfcErrorNone) { + FURI_LOG_W(TAG, "Tx error: %d", error); + ret = iso14443_3a_listener_process_nfc_error(error); + } + + return ret; +} + +Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity( + Iso14443_3aListener* instance, + const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + Iso14443_3aError ret = Iso14443_3aErrorNone; + NfcError error = nfc_iso14443a_listener_tx_custom_parity(instance->nfc, tx_buffer); + if(error != NfcErrorNone) { + FURI_LOG_W(TAG, "Tx error: %d", error); + ret = iso14443_3a_listener_process_nfc_error(error); + } + + return ret; +}; + +Iso14443_3aError iso14443_3a_listener_send_standard_frame( + Iso14443_3aListener* instance, + const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(instance->tx_buffer); + + Iso14443_3aError ret = Iso14443_3aErrorNone; + do { + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer); + + NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer); + if(error != NfcErrorNone) { + FURI_LOG_W(TAG, "Tx error: %d", error); + ret = iso14443_3a_listener_process_nfc_error(error); + break; + } + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h new file mode 100644 index 000000000000..0113a1cb8924 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h @@ -0,0 +1,42 @@ +#pragma once + +#include "iso14443_3a_listener.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Iso14443_3aListenerStateIdle, + Iso14443_3aListenerStateActive, +} Iso14443_3aListenerState; + +struct Iso14443_3aListener { + Nfc* nfc; + Iso14443_3aData* data; + Iso14443_3aListenerState state; + + BitBuffer* tx_buffer; + + NfcGenericEvent generic_event; + Iso14443_3aListenerEvent iso14443_3a_event; + Iso14443_3aListenerEventData iso14443_3a_event_data; + NfcGenericCallback callback; + void* context; +}; + +Iso14443_3aError + iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer); + +Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity( + Iso14443_3aListener* instance, + const BitBuffer* tx_buffer); + +Iso14443_3aError iso14443_3a_listener_send_standard_frame( + Iso14443_3aListener* instance, + const BitBuffer* tx_buffer); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.c new file mode 100644 index 000000000000..65f0ae629d12 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.c @@ -0,0 +1,124 @@ +#include "iso14443_3a_poller_i.h" + +#include + +#include + +#define TAG "ISO14443_3A" + +const Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static Iso14443_3aPoller* iso14443_3a_poller_alloc(Nfc* nfc) { + furi_assert(nfc); + + Iso14443_3aPoller* instance = malloc(sizeof(Iso14443_3aPoller)); + instance->nfc = nfc; + instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE); + instance->rx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE); + + nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443a); + nfc_set_guard_time_us(instance->nfc, ISO14443_3A_GUARD_TIME_US); + nfc_set_fdt_poll_fc(instance->nfc, ISO14443_3A_FDT_POLL_FC); + nfc_set_fdt_poll_poll_us(instance->nfc, ISO14443_3A_POLL_POLL_MIN_US); + instance->data = iso14443_3a_alloc(); + + instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data; + instance->general_event.protocol = NfcProtocolIso14443_3a; + instance->general_event.data = &instance->iso14443_3a_event; + instance->general_event.instance = instance; + + return instance; +} + +static void iso14443_3a_poller_free_new(Iso14443_3aPoller* iso14443_3a_poller) { + furi_assert(iso14443_3a_poller); + + Iso14443_3aPoller* instance = iso14443_3a_poller; + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(instance->data); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + iso14443_3a_free(instance->data); + free(instance); +} + +static void iso14443_3a_poller_set_callback( + Iso14443_3aPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand iso14443_3a_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.data); + + Iso14443_3aPoller* instance = context; + NfcEvent* nfc_event = event.data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypePollerReady) { + if(instance->state != Iso14443_3aPollerStateActivated) { + Iso14443_3aData data = {}; + Iso14443_3aError error = iso14443_3a_poller_async_activate(instance, &data); + if(error == Iso14443_3aErrorNone) { + instance->state = Iso14443_3aPollerStateActivated; + instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady; + instance->iso14443_3a_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + } else { + instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeError; + instance->iso14443_3a_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + // Add delay to switch context + furi_delay_ms(100); + } + } else { + instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady; + instance->iso14443_3a_event_data.error = Iso14443_3aErrorNone; + command = instance->callback(instance->general_event, instance->context); + } + } + + return command; +} + +static bool iso14443_3a_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.instance); + furi_assert(event.protocol = NfcProtocolInvalid); + + bool protocol_detected = false; + Iso14443_3aPoller* instance = context; + NfcEvent* nfc_event = event.data; + furi_assert(instance->state == Iso14443_3aPollerStateIdle); + + if(nfc_event->type == NfcEventTypePollerReady) { + Iso14443_3aError error = iso14443_3a_poller_async_activate(instance, NULL); + protocol_detected = (error == Iso14443_3aErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_iso14443_3a = { + .alloc = (NfcPollerAlloc)iso14443_3a_poller_alloc, + .free = (NfcPollerFree)iso14443_3a_poller_free_new, + .set_callback = (NfcPollerSetCallback)iso14443_3a_poller_set_callback, + .run = (NfcPollerRun)iso14443_3a_poller_run, + .detect = (NfcPollerDetect)iso14443_3a_poller_detect, + .get_data = (NfcPollerGetData)iso14443_3a_poller_get_data, +}; diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h new file mode 100644 index 000000000000..a9ffb220fd96 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h @@ -0,0 +1,30 @@ +#pragma once + +#include "iso14443_3a.h" +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso14443_3aPoller Iso14443_3aPoller; + +typedef enum { + Iso14443_3aPollerEventTypeError, + Iso14443_3aPollerEventTypeReady, +} Iso14443_3aPollerEventType; + +typedef struct { + Iso14443_3aError error; +} Iso14443_3aPollerEventData; + +typedef struct { + Iso14443_3aPollerEventType type; + Iso14443_3aPollerEventData* data; +} Iso14443_3aPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_defs.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_defs.h new file mode 100644 index 000000000000..5ca9f538727a --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase nfc_poller_iso14443_3a; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c new file mode 100644 index 000000000000..16bead746bd3 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c @@ -0,0 +1,326 @@ +#include "iso14443_3a_poller_i.h" + +#include + +#include "nfc/helpers/iso14443_crc.h" + +#define TAG "ISO14443_3A" + +static Iso14443_3aError iso14443_3a_poller_process_error(NfcError error) { + Iso14443_3aError ret = Iso14443_3aErrorNone; + if(error == NfcErrorNone) { + ret = Iso14443_3aErrorNone; + } else if(error == NfcErrorTimeout) { + ret = Iso14443_3aErrorTimeout; + } else { + ret = Iso14443_3aErrorNotPresent; + } + return ret; +} + +static Iso14443_3aError iso14443_3a_poller_prepare_trx(Iso14443_3aPoller* instance) { + Iso14443_3aError ret = Iso14443_3aErrorNone; + + if(instance->state == Iso14443_3aPollerStateIdle) { + ret = iso14443_3a_poller_async_activate(instance, NULL); + } + + return ret; +} + +static Iso14443_3aError iso14443_3a_poller_standard_frame_exchange( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + uint16_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer); + furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - 2); + + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer); + Iso14443_3aError ret = Iso14443_3aErrorNone; + + do { + NfcError error = + nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = iso14443_3a_poller_process_error(error); + break; + } + + bit_buffer_copy(rx_buffer, instance->rx_buffer); + if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_buffer)) { + ret = Iso14443_3aErrorWrongCrc; + break; + } + + iso14443_crc_trim(rx_buffer); + } while(false); + + return ret; +} + +Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance) { + furi_assert(instance); + furi_assert(instance->nfc); + + NfcError error = NfcErrorNone; + Iso14443_3aError ret = Iso14443_3aErrorNone; + do { + error = nfc_iso14443a_poller_trx_short_frame( + instance->nfc, + NfcIso14443aShortFrameSensReq, + instance->rx_buffer, + ISO14443_3A_FDT_LISTEN_FC); + if(error != NfcErrorNone) { + ret = iso14443_3a_poller_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) { + ret = Iso14443_3aErrorCommunication; + break; + } + } while(false); + + return ret; +} + +Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance) { + furi_assert(instance); + furi_assert(instance->nfc); + furi_assert(instance->tx_buffer); + + uint8_t halt_cmd[2] = {0x50, 0x00}; + bit_buffer_copy_bytes(instance->tx_buffer, halt_cmd, sizeof(halt_cmd)); + + iso14443_3a_poller_standard_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC); + + instance->state = Iso14443_3aPollerStateIdle; + return Iso14443_3aErrorNone; +} + +Iso14443_3aError iso14443_3a_poller_async_activate( + Iso14443_3aPoller* instance, + Iso14443_3aData* iso14443_3a_data) { + furi_assert(instance); + furi_assert(instance->nfc); + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + + // Reset Iso14443_3a poller state + memset(&instance->col_res, 0, sizeof(instance->col_res)); + memset(instance->data, 0, sizeof(Iso14443_3aData)); + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Halt if necessary + if(instance->state != Iso14443_3aPollerStateIdle) { + iso14443_3a_poller_halt(instance); + instance->state = Iso14443_3aPollerStateIdle; + } + + NfcError error = NfcErrorNone; + Iso14443_3aError ret = Iso14443_3aErrorNone; + + bool activated = false; + do { + error = nfc_iso14443a_poller_trx_short_frame( + instance->nfc, + NfcIso14443aShortFrameSensReq, + instance->rx_buffer, + ISO14443_3A_FDT_LISTEN_FC); + if(error != NfcErrorNone) { + ret = Iso14443_3aErrorNotPresent; + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) { + FURI_LOG_W(TAG, "Wrong sens response size"); + ret = Iso14443_3aErrorCommunication; + break; + } + bit_buffer_write_bytes( + instance->rx_buffer, + &instance->col_res.sens_resp, + sizeof(instance->col_res.sens_resp)); + memcpy( + instance->data->atqa, + &instance->col_res.sens_resp, + sizeof(instance->col_res.sens_resp)); + + instance->state = Iso14443_3aPollerStateColResInProgress; + instance->col_res.cascade_level = 0; + instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade; + + while(instance->state == Iso14443_3aPollerStateColResInProgress) { + if(instance->col_res.state == Iso14443_3aPollerColResStateStateNewCascade) { + bit_buffer_set_size_bytes(instance->tx_buffer, 2); + bit_buffer_set_byte( + instance->tx_buffer, + 0, + ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level)); + bit_buffer_set_byte(instance->tx_buffer, 1, ISO14443_3A_POLLER_SEL_PAR(2, 0)); + error = nfc_iso14443a_poller_trx_sdd_frame( + instance->nfc, + instance->tx_buffer, + instance->rx_buffer, + ISO14443_3A_FDT_LISTEN_FC); + if(error != NfcErrorNone) { + FURI_LOG_E(TAG, "Sdd request failed: %d", error); + instance->state = Iso14443_3aPollerStateColResFailed; + ret = Iso14443_3aErrorColResFailed; + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != 5) { + FURI_LOG_E(TAG, "Sdd response wrong length"); + instance->state = Iso14443_3aPollerStateColResFailed; + ret = Iso14443_3aErrorColResFailed; + break; + } + // TODO BCC check here + bit_buffer_write_bytes( + instance->rx_buffer, &instance->col_res.sdd_resp, sizeof(Iso14443_3aSddResp)); + instance->col_res.state = Iso14443_3aPollerColResStateStateSelectCascade; + } else if(instance->col_res.state == Iso14443_3aPollerColResStateStateSelectCascade) { + instance->col_res.sel_req.sel_cmd = + ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level); + instance->col_res.sel_req.sel_par = ISO14443_3A_POLLER_SEL_PAR(7, 0); + memcpy( + instance->col_res.sel_req.nfcid, + instance->col_res.sdd_resp.nfcid, + sizeof(instance->col_res.sdd_resp.nfcid)); + instance->col_res.sel_req.bcc = instance->col_res.sdd_resp.bss; + bit_buffer_copy_bytes( + instance->tx_buffer, + (uint8_t*)&instance->col_res.sel_req, + sizeof(instance->col_res.sel_req)); + ret = iso14443_3a_poller_send_standard_frame( + instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC); + if(ret != Iso14443_3aErrorNone) { + FURI_LOG_E(TAG, "Sel request failed: %d", ret); + instance->state = Iso14443_3aPollerStateColResFailed; + ret = Iso14443_3aErrorColResFailed; + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != + sizeof(instance->col_res.sel_resp)) { + FURI_LOG_E(TAG, "Sel response wrong length"); + instance->state = Iso14443_3aPollerStateColResFailed; + ret = Iso14443_3aErrorColResFailed; + break; + } + bit_buffer_write_bytes( + instance->rx_buffer, + &instance->col_res.sel_resp, + sizeof(instance->col_res.sel_resp)); + FURI_LOG_T(TAG, "Sel resp: %02X", instance->col_res.sel_resp.sak); + if(instance->col_res.sel_req.nfcid[0] == ISO14443_3A_POLLER_SDD_CL) { + // Copy part of UID + memcpy( + &instance->data->uid[instance->data->uid_len], + &instance->col_res.sel_req.nfcid[1], + 3); + instance->data->uid_len += 3; + instance->col_res.cascade_level++; + instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade; + } else { + FURI_LOG_T(TAG, "Col resolution complete"); + instance->data->sak = instance->col_res.sel_resp.sak; + memcpy( + &instance->data->uid[instance->data->uid_len], + &instance->col_res.sel_req.nfcid[0], + 4); + instance->data->uid_len += 4; + instance->col_res.state = Iso14443_3aPollerColResStateStateSuccess; + instance->state = Iso14443_3aPollerStateActivated; + } + } + } + + activated = (instance->state == Iso14443_3aPollerStateActivated); + } while(false); + + if(activated && iso14443_3a_data) { + *iso14443_3a_data = *instance->data; + } + + return ret; +} + +Iso14443_3aError iso14443_3a_poller_txrx_custom_parity( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + Iso14443_3aError ret = Iso14443_3aErrorNone; + NfcError error = NfcErrorNone; + + do { + ret = iso14443_3a_poller_prepare_trx(instance); + if(ret != Iso14443_3aErrorNone) break; + + error = nfc_iso14443a_poller_trx_custom_parity(instance->nfc, tx_buffer, rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = iso14443_3a_poller_process_error(error); + break; + } + } while(false); + + return ret; +} + +Iso14443_3aError iso14443_3a_poller_txrx( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + Iso14443_3aError ret = Iso14443_3aErrorNone; + NfcError error = NfcErrorNone; + + do { + ret = iso14443_3a_poller_prepare_trx(instance); + if(ret != Iso14443_3aErrorNone) break; + + error = nfc_poller_trx(instance->nfc, tx_buffer, rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = iso14443_3a_poller_process_error(error); + break; + } + } while(false); + + return ret; +} + +Iso14443_3aError iso14443_3a_poller_send_standard_frame( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + Iso14443_3aError ret = Iso14443_3aErrorNone; + + do { + ret = iso14443_3a_poller_prepare_trx(instance); + if(ret != Iso14443_3aErrorNone) break; + + ret = iso14443_3a_poller_standard_frame_exchange(instance, tx_buffer, rx_buffer, fwt); + if(ret != Iso14443_3aErrorNone) break; + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h new file mode 100644 index 000000000000..08483eb0009c --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h @@ -0,0 +1,93 @@ +#pragma once + +#include "iso14443_3a_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO14443_3A_POLLER_MAX_BUFFER_SIZE (512U) + +#define ISO14443_3A_POLLER_SEL_CMD(cascade_lvl) (0x93 + 2 * (cascade_lvl)) +#define ISO14443_3A_POLLER_SEL_PAR(bytes, bits) (((bytes) << 4 & 0xf0U) | ((bits)&0x0fU)) +#define ISO14443_3A_POLLER_SDD_CL (0x88U) + +typedef enum { + Iso14443_3aPollerColResStateStateIdle, + Iso14443_3aPollerColResStateStateNewCascade, + Iso14443_3aPollerColResStateStateSelectCascade, + Iso14443_3aPollerColResStateStateSuccess, + Iso14443_3aPollerColResStateStateFail, +} Iso14443_3aPollerColResState; + +typedef struct { + Iso14443_3aPollerColResState state; + Iso14443_3aSensResp sens_resp; + Iso14443_3aSddReq sdd_req; + Iso14443_3aSddResp sdd_resp; + Iso14443_3aSelReq sel_req; + Iso14443_3aSelResp sel_resp; + uint8_t cascade_level; +} Iso14443_3aPollerColRes; + +typedef enum { + Iso14443_3aPollerStateIdle, + Iso14443_3aPollerStateColResInProgress, + Iso14443_3aPollerStateColResFailed, + Iso14443_3aPollerStateActivated, +} Iso14443_3aPollerState; + +typedef enum { + Iso14443_3aPollerConfigStateIdle, + Iso14443_3aPollerConfigStateDone, +} Iso14443_3aPollerConfigState; + +struct Iso14443_3aPoller { + Nfc* nfc; + Iso14443_3aPollerState state; + Iso14443_3aPollerConfigState config_state; + Iso14443_3aPollerColRes col_res; + Iso14443_3aData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + NfcGenericEvent general_event; + Iso14443_3aPollerEvent iso14443_3a_event; + Iso14443_3aPollerEventData iso14443_3a_event_data; + NfcGenericCallback callback; + void* context; +}; + +const Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance); + +Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance); + +Iso14443_3aError iso14443_3a_poller_async_activate( + Iso14443_3aPoller* instance, + Iso14443_3aData* iso14443_3a_data); + +Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance); + +Iso14443_3aError iso14443_3a_poller_txrx_custom_parity( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +Iso14443_3aError iso14443_3a_poller_txrx( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +Iso14443_3aError iso14443_3a_poller_send_standard_frame( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync_api.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync_api.c new file mode 100644 index 000000000000..ed1e30dc2a7d --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync_api.c @@ -0,0 +1,58 @@ +#include "iso14443_3a_poller_sync_api.h" + +#include "iso14443_3a_poller_i.h" +#include + +#include + +#define ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0) + +typedef struct { + Iso14443_3aPoller* instance; + FuriThreadId thread_id; + Iso14443_3aError error; + Iso14443_3aData data; +} Iso14443_3aPollerContext; + +NfcCommand iso14443_3a_poller_read_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + Iso14443_3aPollerContext* poller_context = context; + Iso14443_3aPoller* iso14443_3a_poller = event.instance; + Iso14443_3aPollerEvent* iso14443_3a_event = event.data; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + iso14443_3a_copy(&poller_context->data, iso14443_3a_poller->data); + } + poller_context->error = iso14443_3a_event->data->error; + + furi_thread_flags_set(poller_context->thread_id, ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE); + + return NfcCommandStop; +} + +Iso14443_3aError iso14443_3a_poller_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data) { + furi_assert(nfc); + furi_assert(iso14443_3a_data); + + Iso14443_3aPollerContext poller_context = {}; + poller_context.thread_id = furi_thread_get_current_id(); + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolIso14443_3a); + nfc_poller_start(poller, iso14443_3a_poller_read_callback, &poller_context); + furi_thread_flags_wait( + ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + if(poller_context.error == Iso14443_3aErrorNone) { + *iso14443_3a_data = poller_context.data; + } + + return poller_context.error; +} \ No newline at end of file diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync_api.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync_api.h new file mode 100644 index 000000000000..ed17ff43244d --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync_api.h @@ -0,0 +1,14 @@ +#pragma once + +#include "iso14443_3a.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +Iso14443_3aError iso14443_3a_poller_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b.c b/lib/nfc/protocols/iso14443_3b/iso14443_3b.c new file mode 100644 index 000000000000..ca12a6acd423 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b.c @@ -0,0 +1,145 @@ +#include "iso14443_3b.h" + +#include + +#include +#include + +#define ISO14443_3B_PROTOCOL_NAME "ISO14443-3B" +#define ISO14443_3B_DEVICE_NAME "ISO14443-3B (Unknown)" + +#define ISO14443_3B_APP_DATA_KEY "Application data" +#define ISO14443_3B_PROTOCOL_INFO_KEY "Protocol info" + +const NfcDeviceBase nfc_device_iso14443_3b = { + .protocol_name = ISO14443_3B_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)iso14443_3b_alloc, + .free = (NfcDeviceFree)iso14443_3b_free, + .reset = (NfcDeviceReset)iso14443_3b_reset, + .copy = (NfcDeviceCopy)iso14443_3b_copy, + .verify = (NfcDeviceVerify)iso14443_3b_verify, + .load = (NfcDeviceLoad)iso14443_3b_load, + .save = (NfcDeviceSave)iso14443_3b_save, + .is_equal = (NfcDeviceEqual)iso14443_3b_is_equal, + .get_name = (NfcDeviceGetName)iso14443_3b_get_device_name, + .get_uid = (NfcDeviceGetUid)iso14443_3b_get_uid, + .set_uid = (NfcDeviceSetUid)iso14443_3b_set_uid, + .get_base_data = (NfcDeviceGetBaseData)iso14443_3b_get_base_data, +}; + +Iso14443_3bData* iso14443_3b_alloc() { + Iso14443_3bData* data = malloc(sizeof(Iso14443_3bData)); + return data; +} + +void iso14443_3b_free(Iso14443_3bData* data) { + furi_assert(data); + + free(data); +} + +void iso14443_3b_reset(Iso14443_3bData* data) { + memset(data, 0, sizeof(Iso14443_3bData)); +} + +void iso14443_3b_copy(Iso14443_3bData* data, const Iso14443_3bData* other) { + furi_assert(data); + furi_assert(other); + + *data = *other; +} + +bool iso14443_3b_verify(Iso14443_3bData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + // TODO: How to distinguish from old ISO14443-3/4a? + return false; +} + +bool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool parsed = false; + + do { + if(version < NFC_UNIFIED_FORMAT_VERSION) break; + + if(!flipper_format_read_hex( + ff, ISO14443_3B_APP_DATA_KEY, data->app_data, ISO14443_3B_APP_DATA_SIZE)) + break; + if(!flipper_format_read_hex( + ff, + ISO14443_3B_PROTOCOL_INFO_KEY, + data->protocol_info, + ISO14443_3B_PROTOCOL_INFO_SIZE)) + break; + + parsed = true; + } while(false); + + return parsed; +} + +bool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + if(!flipper_format_write_comment_cstr(ff, ISO14443_3B_PROTOCOL_NAME " specific data")) + break; + if(!flipper_format_write_hex( + ff, ISO14443_3B_APP_DATA_KEY, data->app_data, ISO14443_3B_APP_DATA_SIZE)) + break; + if(!flipper_format_write_hex( + ff, + ISO14443_3B_PROTOCOL_INFO_KEY, + data->protocol_info, + ISO14443_3B_PROTOCOL_INFO_SIZE)) + break; + saved = true; + } while(false); + + return saved; +} + +bool iso14443_3b_is_equal(const Iso14443_3bData* data, const Iso14443_3bData* other) { + furi_assert(data); + furi_assert(other); + + return memcmp(data, other, sizeof(Iso14443_3bData)) == 0; +} + +const char* iso14443_3b_get_device_name(const Iso14443_3bData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + + return ISO14443_3B_DEVICE_NAME; +} + +const uint8_t* iso14443_3b_get_uid(const Iso14443_3bData* data, size_t* uid_len) { + furi_assert(data); + + if(uid_len) { + *uid_len = ISO14443_3B_UID_SIZE; + } + + return data->uid; +} + +bool iso14443_3b_set_uid(Iso14443_3bData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + const bool uid_valid = uid_len == ISO14443_3B_UID_SIZE; + + if(uid_valid) { + memcpy(data->uid, uid, uid_len); + } + + return uid_valid; +} + +Iso14443_3bData* iso14443_3b_get_base_data(const Iso14443_3bData* data) { + UNUSED(data); + furi_crash("No base data"); +} diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b.h new file mode 100644 index 000000000000..5bea83dcdf47 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO14443_3B_UID_SIZE (4U) +#define ISO14443_3B_APP_DATA_SIZE (4U) +#define ISO14443_3B_PROTOCOL_INFO_SIZE (3U) + +#define ISO14443_3B_GUARD_TIME_US (5000U) +#define ISO14443_3B_FDT_POLL_FC (9000U) +#define ISO14443_3B_FDT_ATTRIB_FC (42000U) +#define ISO14443_3B_POLL_POLL_MIN_US (1280U) + +#define ISO14443_3B_REQB_ALL (1U << 3) + +#define ISO14443_3B_ATTRIB_FRAME_SIZE_32 (0x02) +#define ISO14443_3B_ATTRIB_FRAME_SIZE_40 (0x03) +#define ISO14443_3B_ATTRIB_FRAME_SIZE_48 (0x04) +#define ISO14443_3B_ATTRIB_FRAME_SIZE_64 (0x05) +#define ISO14443_3B_ATTRIB_FRAME_SIZE_96 (0x06) +#define ISO14443_3B_ATTRIB_FRAME_SIZE_128 (0x07) +#define ISO14443_3B_ATTRIB_FRAME_SIZE_256 (0x08) + +typedef enum { + Iso14443_3bErrorNone, + Iso14443_3bErrorNotPresent, + Iso14443_3bErrorColResFailed, + Iso14443_3bErrorBufferOverflow, + Iso14443_3bErrorCommunication, + Iso14443_3bErrorFieldOff, + Iso14443_3bErrorWrongCrc, + Iso14443_3bErrorTimeout, +} Iso14443_3bError; + +typedef struct { + uint8_t flag; + uint8_t uid[ISO14443_3B_UID_SIZE]; + uint8_t app_data[ISO14443_3B_APP_DATA_SIZE]; + uint8_t protocol_info[ISO14443_3B_PROTOCOL_INFO_SIZE]; +} Iso14443_3bAtqB; + +typedef struct { + uint8_t uid[ISO14443_3B_UID_SIZE]; + uint8_t app_data[ISO14443_3B_APP_DATA_SIZE]; + uint8_t protocol_info[ISO14443_3B_PROTOCOL_INFO_SIZE]; +} Iso14443_3bData; + +extern const NfcDeviceBase nfc_device_iso14443_3b; + +Iso14443_3bData* iso14443_3b_alloc(); + +void iso14443_3b_free(Iso14443_3bData* data); + +void iso14443_3b_reset(Iso14443_3bData* data); + +void iso14443_3b_copy(Iso14443_3bData* data, const Iso14443_3bData* other); + +bool iso14443_3b_verify(Iso14443_3bData* data, const FuriString* device_type); + +bool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version); + +bool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff); + +bool iso14443_3b_is_equal(const Iso14443_3bData* data, const Iso14443_3bData* other); + +const char* iso14443_3b_get_device_name(const Iso14443_3bData* data, NfcDeviceNameType name_type); + +const uint8_t* iso14443_3b_get_uid(const Iso14443_3bData* data, size_t* uid_len); + +bool iso14443_3b_set_uid(Iso14443_3bData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_3bData* iso14443_3b_get_base_data(const Iso14443_3bData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.c b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.c new file mode 100644 index 000000000000..2ec9ec6a6fbc --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.c @@ -0,0 +1,121 @@ +#include "iso14443_3b_poller_i.h" + +#include + +#include + +#define TAG "ISO14443_3bPoller" + +const Iso14443_3bData* iso14443_3b_poller_get_data(Iso14443_3bPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static Iso14443_3bPoller* iso14443_3b_poller_alloc(Nfc* nfc) { + furi_assert(nfc); + + Iso14443_3bPoller* instance = malloc(sizeof(Iso14443_3bPoller)); + instance->nfc = nfc; + instance->tx_buffer = bit_buffer_alloc(ISO14443_3B_POLLER_MAX_BUFFER_SIZE); + instance->rx_buffer = bit_buffer_alloc(ISO14443_3B_POLLER_MAX_BUFFER_SIZE); + + nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443b); + nfc_set_guard_time_us(instance->nfc, ISO14443_3B_GUARD_TIME_US); + nfc_set_fdt_poll_fc(instance->nfc, ISO14443_3B_FDT_POLL_FC); + nfc_set_fdt_poll_poll_us(instance->nfc, ISO14443_3B_POLL_POLL_MIN_US); + instance->data = iso14443_3b_alloc(); + + instance->iso14443_3b_event.data = &instance->iso14443_3b_event_data; + instance->general_event.protocol = NfcProtocolIso14443_3b; + instance->general_event.data = &instance->iso14443_3b_event; + instance->general_event.instance = instance; + + return instance; +} + +static void iso14443_3b_poller_free(Iso14443_3bPoller* instance) { + furi_assert(instance); + + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(instance->data); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + iso14443_3b_free(instance->data); + free(instance); +} + +static void iso14443_3b_poller_set_callback( + Iso14443_3bPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand iso14443_3b_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.data); + + Iso14443_3bPoller* instance = context; + NfcEvent* nfc_event = event.data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypePollerReady) { + if(instance->state != Iso14443_3bPollerStateActivated) { + Iso14443_3bError error = iso14443_3b_poller_async_activate(instance, instance->data); + if(error == Iso14443_3bErrorNone) { + instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeReady; + instance->iso14443_3b_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + } else { + instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeError; + instance->iso14443_3b_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + // Add delay to switch context + furi_delay_ms(100); + } + } else { + instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeReady; + instance->iso14443_3b_event_data.error = Iso14443_3bErrorNone; + command = instance->callback(instance->general_event, instance->context); + } + } + + return command; +} + +static bool iso14443_3b_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.instance); + furi_assert(event.protocol = NfcProtocolInvalid); + + bool protocol_detected = false; + Iso14443_3bPoller* instance = context; + NfcEvent* nfc_event = event.data; + furi_assert(instance->state == Iso14443_3bPollerStateIdle); + + if(nfc_event->type == NfcEventTypePollerReady) { + Iso14443_3bError error = iso14443_3b_poller_async_activate(instance, instance->data); + protocol_detected = (error == Iso14443_3bErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_iso14443_3b = { + .alloc = (NfcPollerAlloc)iso14443_3b_poller_alloc, + .free = (NfcPollerFree)iso14443_3b_poller_free, + .set_callback = (NfcPollerSetCallback)iso14443_3b_poller_set_callback, + .run = (NfcPollerRun)iso14443_3b_poller_run, + .detect = (NfcPollerDetect)iso14443_3b_poller_detect, + .get_data = (NfcPollerGetData)iso14443_3b_poller_get_data, +}; diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h new file mode 100644 index 000000000000..d25d9dbe9fef --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h @@ -0,0 +1,30 @@ +#pragma once + +#include "iso14443_3b.h" +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso14443_3bPoller Iso14443_3bPoller; + +typedef enum { + Iso14443_3bPollerEventTypeError, + Iso14443_3bPollerEventTypeReady, +} Iso14443_3bPollerEventType; + +typedef struct { + Iso14443_3bError error; +} Iso14443_3bPollerEventData; + +typedef struct { + Iso14443_3bPollerEventType type; + Iso14443_3bPollerEventData* data; +} Iso14443_3bPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_defs.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_defs.h new file mode 100644 index 000000000000..ba8e6cb18c06 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase nfc_poller_iso14443_3b; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c new file mode 100644 index 000000000000..e3ca07deab7d --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c @@ -0,0 +1,184 @@ +#include "iso14443_3b_poller_i.h" + +#include + +#define TAG "Iso14443_3bPoller" + +static Iso14443_3bError iso14443_3b_poller_process_error(NfcError error) { + switch(error) { + case NfcErrorNone: + return Iso14443_3bErrorNone; + case NfcErrorTimeout: + return Iso14443_3bErrorTimeout; + default: + return Iso14443_3bErrorNotPresent; + } +} + +static Iso14443_3bError iso14443_3b_poller_prepare_trx(Iso14443_3bPoller* instance) { + furi_assert(instance); + + if(instance->state == Iso14443_3bPollerStateIdle) { + return iso14443_3b_poller_async_activate(instance, NULL); + } + + return Iso14443_3bErrorNone; +} + +static Iso14443_3bError iso14443_3b_poller_frame_exchange( + Iso14443_3bPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + + const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer); + furi_assert( + tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO14443_CRC_SIZE); + + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso14443_crc_append(Iso14443CrcTypeB, instance->tx_buffer); + + Iso14443_3bError ret = Iso14443_3bErrorNone; + + do { + NfcError error = + nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = iso14443_3b_poller_process_error(error); + break; + } + + bit_buffer_copy(rx_buffer, instance->rx_buffer); + if(!iso14443_crc_check(Iso14443CrcTypeB, instance->rx_buffer)) { + ret = Iso14443_3bErrorWrongCrc; + break; + } + + iso14443_crc_trim(rx_buffer); + } while(false); + + return ret; +} + +Iso14443_3bError + iso14443_3b_poller_async_activate(Iso14443_3bPoller* instance, Iso14443_3bData* data) { + furi_assert(instance); + furi_assert(instance->nfc); + + iso14443_3b_reset(data); + + Iso14443_3bError ret; + + do { + instance->state = Iso14443_3bPollerStateColResInProgress; + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send REQB + bit_buffer_append_byte(instance->tx_buffer, 0x05); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + bit_buffer_append_byte(instance->tx_buffer, 0x08); + + ret = iso14443_3b_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_POLL_FC); + if(ret != Iso14443_3bErrorNone) { + instance->state = Iso14443_3bPollerStateColResFailed; + break; + } + + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(Iso14443_3bAtqB)) { + FURI_LOG_D(TAG, "Unexpected REQB response"); + instance->state = Iso14443_3bPollerStateColResFailed; + ret = Iso14443_3bErrorCommunication; + break; + } + + instance->state = Iso14443_3bPollerStateActivationInProgress; + + const Iso14443_3bAtqB* atqb = + (const Iso14443_3bAtqB*)bit_buffer_get_data(instance->rx_buffer); + + memcpy(data->uid, atqb->uid, ISO14443_3B_UID_SIZE); + memcpy(data->app_data, atqb->app_data, ISO14443_3B_APP_DATA_SIZE); + memcpy(data->protocol_info, atqb->protocol_info, ISO14443_3B_PROTOCOL_INFO_SIZE); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send ATTRIB + bit_buffer_append_byte(instance->tx_buffer, 0x1d); + bit_buffer_append_bytes(instance->tx_buffer, data->uid, ISO14443_3B_UID_SIZE); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + bit_buffer_append_byte(instance->tx_buffer, ISO14443_3B_ATTRIB_FRAME_SIZE_256); + bit_buffer_append_byte(instance->tx_buffer, 0x01); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + + ret = iso14443_3b_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_ATTRIB_FC); + if(ret != Iso14443_3bErrorNone) { + instance->state = Iso14443_3bPollerStateActivationFailed; + break; + } + + if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1 || + bit_buffer_get_byte(instance->rx_buffer, 0) != 0) { + FURI_LOG_D(TAG, "Unexpected ATTRIB response"); + instance->state = Iso14443_3bPollerStateActivationFailed; + ret = Iso14443_3bErrorCommunication; + break; + } + + instance->state = Iso14443_3bPollerStateActivated; + } while(false); + + return ret; +} + +Iso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance) { + furi_assert(instance); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_append_byte(instance->tx_buffer, 0x50); + bit_buffer_append_bytes(instance->tx_buffer, instance->data->uid, ISO14443_3B_UID_SIZE); + + Iso14443_3bError ret; + + do { + ret = iso14443_3b_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_POLL_FC); + if(ret != Iso14443_3bErrorNone) { + break; + } + + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(uint8_t) || + bit_buffer_get_byte(instance->rx_buffer, 0) != 0) { + ret = Iso14443_3bErrorCommunication; + break; + } + + instance->state = Iso14443_3bPollerStateIdle; + } while(false); + + return ret; +} + +Iso14443_3bError iso14443_3b_poller_send_frame( + Iso14443_3bPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + Iso14443_3bError ret; + + do { + ret = iso14443_3b_poller_prepare_trx(instance); + if(ret != Iso14443_3bErrorNone) break; + + ret = iso14443_3b_poller_frame_exchange(instance, tx_buffer, rx_buffer, fwt); + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h new file mode 100644 index 000000000000..ce42eb40ed35 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h @@ -0,0 +1,49 @@ +#pragma once + +#include "iso14443_3b_poller.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO14443_3B_POLLER_MAX_BUFFER_SIZE (256U) + +typedef enum { + Iso14443_3bPollerStateIdle, + Iso14443_3bPollerStateColResInProgress, + Iso14443_3bPollerStateColResFailed, + Iso14443_3bPollerStateActivationInProgress, + Iso14443_3bPollerStateActivationFailed, + Iso14443_3bPollerStateActivated, +} Iso14443_3bPollerState; + +struct Iso14443_3bPoller { + Nfc* nfc; + Iso14443_3bPollerState state; + Iso14443_3bData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + NfcGenericEvent general_event; + Iso14443_3bPollerEvent iso14443_3b_event; + Iso14443_3bPollerEventData iso14443_3b_event_data; + NfcGenericCallback callback; + void* context; +}; + +const Iso14443_3bData* iso14443_3b_poller_get_data(Iso14443_3bPoller* instance); + +Iso14443_3bError + iso14443_3b_poller_async_activate(Iso14443_3bPoller* instance, Iso14443_3bData* data); + +Iso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance); + +Iso14443_3bError iso14443_3b_poller_send_frame( + Iso14443_3bPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a.c new file mode 100644 index 000000000000..ef562b695101 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a.c @@ -0,0 +1,144 @@ +#include "iso14443_4a_i.h" + +#include + +#define ISO14443_4A_PROTOCOL_NAME "ISO14443-4A" +#define ISO14443_4A_DEVICE_NAME "ISO14443-4A (Unknown)" +#define ISO14443_4A_ATS_BIT (1U << 5) + +#define ISO14443_4A_ATS_KEY "ATS" + +const NfcDeviceBase nfc_device_iso14443_4a = { + .protocol_name = ISO14443_4A_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)iso14443_4a_alloc, + .free = (NfcDeviceFree)iso14443_4a_free, + .reset = (NfcDeviceReset)iso14443_4a_reset, + .copy = (NfcDeviceCopy)iso14443_4a_copy, + .verify = (NfcDeviceVerify)iso14443_4a_verify, + .load = (NfcDeviceLoad)iso14443_4a_load, + .save = (NfcDeviceSave)iso14443_4a_save, + .is_equal = (NfcDeviceEqual)iso14443_4a_is_equal, + .get_name = (NfcDeviceGetName)iso14443_4a_get_device_name, + .get_uid = (NfcDeviceGetUid)iso14443_4a_get_uid, + .set_uid = (NfcDeviceSetUid)iso14443_4a_set_uid, + .get_base_data = (NfcDeviceGetBaseData)iso14443_4a_get_base_data, +}; + +Iso14443_4aData* iso14443_4a_alloc() { + Iso14443_4aData* data = malloc(sizeof(Iso14443_4aData)); + + data->iso14443_3a_data = iso14443_3a_alloc(); + data->ats_data = simple_array_alloc(&simple_array_config_uint8_t); + + return data; +} + +void iso14443_4a_free(Iso14443_4aData* data) { + furi_assert(data); + + simple_array_free(data->ats_data); + iso14443_3a_free(data->iso14443_3a_data); + + free(data); +} + +void iso14443_4a_reset(Iso14443_4aData* data) { + furi_assert(data); + + iso14443_3a_reset(data->iso14443_3a_data); + simple_array_reset(data->ats_data); +} + +void iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other) { + furi_assert(data); + furi_assert(other); + + iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data); + simple_array_copy(data->ats_data, other->ats_data); +} + +bool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + + // Empty, unified file format only + return false; +} + +bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool parsed = false; + + do { + if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break; + if(flipper_format_key_exist(ff, ISO14443_4A_ATS_KEY)) { + uint32_t ats_size = 0; + if(!flipper_format_get_value_count(ff, ISO14443_4A_ATS_KEY, &ats_size)) break; + + if(ats_size < sizeof(Iso14443_4aAtsData)) break; + simple_array_init(data->ats_data, ats_size); + + if(!flipper_format_read_hex( + ff, ISO14443_4A_ATS_KEY, simple_array_get_data(data->ats_data), ats_size)) + break; + } else { + iso14443_4a_ats_fill_default(data->ats_data); + } + parsed = true; + } while(false); + + return parsed; +} + +bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break; + if(!flipper_format_write_comment_cstr(ff, ISO14443_4A_PROTOCOL_NAME " specific data")) + break; + if(!flipper_format_write_hex( + ff, + ISO14443_4A_ATS_KEY, + simple_array_cget_data(data->ats_data), + simple_array_get_count(data->ats_data))) + break; + saved = true; + } while(false); + + return saved; +} + +bool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other) { + return iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data); +} + +const char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + return ISO14443_4A_DEVICE_NAME; +} + +const uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len) { + return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len); +} + +bool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len); +} + +Iso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data) { + furi_assert(data); + + return data->iso14443_3a_data; +} + +bool iso14443_4a_is_ats_supported(const Iso14443_4aData* data) { + const Iso14443_3aData* iso14443_3a_data = data->iso14443_3a_data; + return iso14443_3a_data->sak & ISO14443_4A_ATS_BIT; +} diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a.h new file mode 100644 index 000000000000..3c2d62760deb --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Iso14443_4aErrorNone, + Iso14443_4aErrorNotPresent, + Iso14443_4aErrorProtocol, + Iso14443_4aErrorTimeout, +} Iso14443_4aError; + +typedef struct { + Iso14443_3aData* iso14443_3a_data; + SimpleArray* ats_data; +} Iso14443_4aData; + +extern const NfcDeviceBase nfc_device_iso14443_4a; + +// Virtual methods + +Iso14443_4aData* iso14443_4a_alloc(); + +void iso14443_4a_free(Iso14443_4aData* data); + +void iso14443_4a_reset(Iso14443_4aData* data); + +void iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other); + +bool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type); + +bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version); + +bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff); + +bool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other); + +const char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type); + +const uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len); + +bool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data); + +// Getters & Tests + +bool iso14443_4a_is_ats_supported(const Iso14443_4aData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.c new file mode 100644 index 000000000000..ae70a81bd584 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.c @@ -0,0 +1,66 @@ +#include "iso14443_4a_i.h" + +// T0 bits +#define ISO14443_4A_ATS_T0_TA1_FLAG (1U << 4) +#define ISO14443_4A_ATS_T0_TB1_FLAG (1U << 5) +#define ISO14443_4A_ATS_T0_TC1_FLAG (1U << 6) + +// TA_1 bits +#define ISO14443_4A_ATS_TA1_SAME_D (1U << 7) + +// TB_1 bits +#define ISO14443_4A_ATS_TB1_SFGI (0U << 0) +#define ISO14443_4A_ATS_TB1_FWI (14U << 4) + +// TC_1 bits +#define ISO14443_4A_ATS_TC1_NAD_FLAG (1U << 0) +#define ISO14443_4A_ATS_TC1_CID_FLAG (1U << 1) + +bool iso14443_4a_ats_parse(SimpleArray* data, const BitBuffer* buf) { + bool can_parse = false; + + do { + const size_t buf_size = bit_buffer_get_size_bytes(buf); + if(buf_size < sizeof(Iso14443_4aAtsData)) break; + + const size_t ats_size = bit_buffer_get_byte(buf, 0); + if(ats_size != buf_size) break; + + simple_array_init(data, ats_size); + bit_buffer_write_bytes(buf, simple_array_get_data(data), ats_size); + + can_parse = true; + } while(false); + + return can_parse; +} + +void iso14443_4a_ats_fill_default(SimpleArray* data) { + simple_array_init(data, sizeof(Iso14443_4aAtsData)); + + Iso14443_4aAtsData* ats_data = simple_array_get_data(data); + + ats_data->tl = sizeof(Iso14443_4aAtsData); + ats_data->t0 = ISO14443_4A_ATS_T0_TA1_FLAG | ISO14443_4A_ATS_T0_TB1_FLAG | + ISO14443_4A_ATS_T0_TC1_FLAG | ISO14443_4A_FSCI_256; + ats_data->ta_1 = ISO14443_4A_ATS_TA1_SAME_D; + ats_data->tb_1 = ISO14443_4A_ATS_TB1_FWI | ISO14443_4A_ATS_TB1_SFGI; + ats_data->tc_1 = ISO14443_4A_ATS_TC1_CID_FLAG; +} + +Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error) { + switch(error) { + case Iso14443_3aErrorNone: + return Iso14443_4aErrorNone; + case Iso14443_3aErrorNotPresent: + return Iso14443_4aErrorNotPresent; + case Iso14443_3aErrorColResFailed: + case Iso14443_3aErrorCommunication: + case Iso14443_3aErrorWrongCrc: + return Iso14443_4aErrorProtocol; + case Iso14443_3aErrorTimeout: + return Iso14443_4aErrorTimeout; + default: + return Iso14443_4aErrorProtocol; + } +} diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h new file mode 100644 index 000000000000..a1e95774d53a --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h @@ -0,0 +1,45 @@ +#pragma once + +#include "iso14443_4a.h" + +#define ISO14443_4A_CMD_READ_ATS (0xE0) + +#define ISO14443_4A_FSDI_16 (0x00) +#define ISO14443_4A_FSDI_24 (0x01) +#define ISO14443_4A_FSDI_32 (0x02) +#define ISO14443_4A_FSDI_40 (0x03) +#define ISO14443_4A_FSDI_48 (0x04) +#define ISO14443_4A_FSDI_64 (0x05) +#define ISO14443_4A_FSDI_96 (0x06) +#define ISO14443_4A_FSDI_128 (0x07) +#define ISO14443_4A_FSDI_256 (0x08) + +#define ISO14443_4A_FSCI_16 ISO14443_4A_FSDI_16 +#define ISO14443_4A_FSCI_24 ISO14443_4A_FSDI_24 +#define ISO14443_4A_FSCI_32 ISO14443_4A_FSDI_32 +#define ISO14443_4A_FSCI_40 ISO14443_4A_FSDI_40 +#define ISO14443_4A_FSCI_48 ISO14443_4A_FSDI_48 +#define ISO14443_4A_FSCI_64 ISO14443_4A_FSDI_64 +#define ISO14443_4A_FSCI_96 ISO14443_4A_FSDI_96 +#define ISO14443_4A_FSCI_128 ISO14443_4A_FSDI_128 +#define ISO14443_4A_FSCI_256 ISO14443_4A_FSDI_256 + +#define ISO14443_4A_BLOCK_PCB (1U << 1) +#define ISO14443_4A_BLOCK_PCB_I (0U) +#define ISO14443_4A_BLOCK_PCB_R (5U << 5) +#define ISO14443_4A_BLOCK_PCB_S (3U << 6) + +typedef struct { + uint8_t tl; + uint8_t t0; + uint8_t ta_1; + uint8_t tb_1; + uint8_t tc_1; + uint8_t t1_tk[]; +} Iso14443_4aAtsData; + +bool iso14443_4a_ats_parse(SimpleArray* data, const BitBuffer* buf); + +void iso14443_4a_ats_fill_default(SimpleArray* data); + +Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error); diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c new file mode 100644 index 000000000000..f8544271403b --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c @@ -0,0 +1,99 @@ +#include "iso14443_4a_listener_i.h" + +#include +#include + +#define TAG "Iso14443_4aListener" + +#define ISO14443_4A_LISTENER_BUF_SIZE (256U) + +static Iso14443_4aListener* + iso14443_4a_listener_alloc(Iso14443_3aListener* iso14443_3a_listener, Iso14443_4aData* data) { + furi_assert(iso14443_3a_listener); + + Iso14443_4aListener* instance = malloc(sizeof(Iso14443_4aListener)); + instance->iso14443_3a_listener = iso14443_3a_listener; + instance->data = data; + + instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_LISTENER_BUF_SIZE); + + instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data; + instance->generic_event.protocol = NfcProtocolIso14443_4a; + instance->generic_event.instance = instance; + instance->generic_event.data = &instance->iso14443_4a_event; + + return instance; +} + +static void iso14443_4a_listener_free(Iso14443_4aListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->tx_buffer); + + bit_buffer_free(instance->tx_buffer); + free(instance); +} + +static void iso14443_4a_listener_set_callback( + Iso14443_4aListener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +static const Iso14443_4aData* iso14443_4a_listener_get_data(Iso14443_4aListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(event.data); + + Iso14443_4aListener* instance = context; + Iso14443_3aListenerEvent* iso14443_3a_event = event.data; + BitBuffer* rx_buffer = iso14443_3a_event->data->buffer; + NfcCommand command = NfcCommandContinue; + + if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) { + if(instance->state == Iso14443_4aListenerStateIdle) { + if(bit_buffer_get_size_bytes(rx_buffer) == 2 && + bit_buffer_get_byte(rx_buffer, 0) == ISO14443_4A_CMD_READ_ATS) { + if(iso14443_4a_listener_send_ats(instance, instance->data->ats_data) != + Iso14443_4aErrorNone) { + command = NfcCommandContinue; + } else { + instance->state = Iso14443_4aListenerStateActive; + } + } + } else { + instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData; + instance->iso14443_4a_event.data->buffer = rx_buffer; + + if(instance->callback) { + command = instance->callback(instance->generic_event, instance->context); + } + } + } else if( + iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted || + iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff) { + instance->state = Iso14443_4aListenerStateIdle; + command = NfcCommandContinue; + } + + return command; +} + +const NfcListenerBase nfc_listener_iso14443_4a = { + .alloc = (NfcListenerAlloc)iso14443_4a_listener_alloc, + .free = (NfcListenerFree)iso14443_4a_listener_free, + .set_callback = (NfcListenerSetCallback)iso14443_4a_listener_set_callback, + .get_data = (NfcListenerGetData)iso14443_4a_listener_get_data, + .run = (NfcListenerRun)iso14443_4a_listener_run, +}; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h new file mode 100644 index 000000000000..ba649847b274 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "iso14443_4a.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso14443_4aListener Iso14443_4aListener; + +typedef enum { + Iso14443_4aListenerEventTypeHalted, + Iso14443_4aListenerEventTypeReceivedData, +} Iso14443_4aListenerEventType; + +typedef struct { + BitBuffer* buffer; +} Iso14443_4aListenerEventData; + +typedef struct { + Iso14443_4aListenerEventType type; + Iso14443_4aListenerEventData* data; +} Iso14443_4aListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_defs.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_defs.h new file mode 100644 index 000000000000..06f20846a7d6 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcListenerBase nfc_listener_iso14443_4a; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c new file mode 100644 index 000000000000..7aaa18548cca --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c @@ -0,0 +1,13 @@ +#include "iso14443_4a_listener_i.h" + +#include + +Iso14443_4aError + iso14443_4a_listener_send_ats(Iso14443_4aListener* instance, const SimpleArray* data) { + bit_buffer_copy_bytes( + instance->tx_buffer, simple_array_cget_data(data), simple_array_get_count(data)); + + const Iso14443_3aError error = iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + return iso14443_4a_process_error(error); +} diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h new file mode 100644 index 000000000000..c98eecc5c0e5 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "iso14443_4a_listener.h" +#include "iso14443_4a_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Iso14443_4aListenerStateIdle, + Iso14443_4aListenerStateActive, +} Iso14443_4aListenerState; + +struct Iso14443_4aListener { + Iso14443_3aListener* iso14443_3a_listener; + Iso14443_4aData* data; + Iso14443_4aListenerState state; + + BitBuffer* tx_buffer; + + NfcGenericEvent generic_event; + Iso14443_4aListenerEvent iso14443_4a_event; + Iso14443_4aListenerEventData iso14443_4a_event_data; + NfcGenericCallback callback; + void* context; +}; + +Iso14443_4aError + iso14443_4a_listener_send_ats(Iso14443_4aListener* instance, const SimpleArray* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.c new file mode 100644 index 000000000000..90e5c9cc0dc8 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.c @@ -0,0 +1,150 @@ +#include "iso14443_4a_poller_i.h" + +#include + +#include + +#define TAG "Iso14443_4aPoller" + +#define ISO14443_4A_POLLER_BUF_SIZE (256U) + +typedef NfcCommand (*Iso14443_4aPollerStateHandler)(Iso14443_4aPoller* instance); + +const Iso14443_4aData* iso14443_4a_poller_get_data(Iso14443_4aPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static Iso14443_4aPoller* iso14443_4a_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) { + Iso14443_4aPoller* instance = malloc(sizeof(Iso14443_4aPoller)); + instance->iso14443_3a_poller = iso14443_3a_poller; + instance->data = iso14443_4a_alloc(); + instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE); + + instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data; + + instance->general_event.protocol = NfcProtocolIso14443_4a; + instance->general_event.data = &instance->iso14443_4a_event; + instance->general_event.instance = instance; + + return instance; +} + +static void iso14443_4a_poller_free(Iso14443_4aPoller* instance) { + furi_assert(instance); + + iso14443_4a_free(instance->data); + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + free(instance); +} + +static NfcCommand iso14443_4a_poller_handler_idle(Iso14443_4aPoller* instance) { + iso14443_3a_copy( + instance->data->iso14443_3a_data, + iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); + + instance->poller_state = Iso14443_4aPollerStateReadAts; + instance->protocol_state.block_number = 0; + return NfcCommandContinue; +} + +static NfcCommand iso14443_4a_poller_handler_read_ats(Iso14443_4aPoller* instance) { + Iso14443_4aError error = iso14443_4a_poller_async_read_ats(instance, instance->data->ats_data); + if(error == Iso14443_4aErrorNone) { + FURI_LOG_D(TAG, "Read ATS success"); + instance->poller_state = Iso14443_4aPollerStateReady; + } else { + FURI_LOG_D(TAG, "Failed to read ATS"); + instance->poller_state = Iso14443_4aPollerStateError; + } + + return NfcCommandContinue; +} + +static NfcCommand iso14443_4a_poller_handler_error(Iso14443_4aPoller* instance) { + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->iso14443_4a_event_data.error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->poller_state = Iso14443_4aPollerStateIdle; + return command; +} + +static NfcCommand iso14443_4a_poller_handler_ready(Iso14443_4aPoller* instance) { + instance->iso14443_4a_event.type = Iso14443_4aPollerEventTypeReady; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const Iso14443_4aPollerStateHandler + iso14443_4a_poller_state_handler[Iso14443_4aPollerStateNum] = { + [Iso14443_4aPollerStateIdle] = iso14443_4a_poller_handler_idle, + [Iso14443_4aPollerStateReadAts] = iso14443_4a_poller_handler_read_ats, + [Iso14443_4aPollerStateError] = iso14443_4a_poller_handler_error, + [Iso14443_4aPollerStateReady] = iso14443_4a_poller_handler_ready, +}; + +static void iso14443_4a_poller_set_callback( + Iso14443_4aPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand iso14443_4a_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + Iso14443_4aPoller* instance = context; + furi_assert(instance); + furi_assert(instance->callback); + + Iso14443_3aPollerEvent* iso14443_3a_event = event.data; + furi_assert(iso14443_3a_event); + + NfcCommand command = NfcCommandContinue; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + command = iso14443_4a_poller_state_handler[instance->poller_state](instance); + } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + instance->iso14443_4a_event.type = Iso14443_4aPollerEventTypeError; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool iso14443_4a_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + const Iso14443_4aPoller* instance = context; + furi_assert(instance); + + const Iso14443_3aPollerEvent* iso14443_3a_event = event.data; + furi_assert(iso14443_3a_event); + iso14443_3a_copy( + instance->data->iso14443_3a_data, + iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); + + bool protocol_detected = false; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + protocol_detected = iso14443_4a_is_ats_supported(instance->data); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_iso14443_4a = { + .alloc = (NfcPollerAlloc)iso14443_4a_poller_alloc, + .free = (NfcPollerFree)iso14443_4a_poller_free, + .set_callback = (NfcPollerSetCallback)iso14443_4a_poller_set_callback, + .run = (NfcPollerRun)iso14443_4a_poller_run, + .detect = (NfcPollerDetect)iso14443_4a_poller_detect, + .get_data = (NfcPollerGetData)iso14443_4a_poller_get_data, +}; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h new file mode 100644 index 000000000000..b224299e0a73 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "iso14443_4a.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso14443_4aPoller Iso14443_4aPoller; + +typedef enum { + Iso14443_4aPollerEventTypeError, + Iso14443_4aPollerEventTypeReady, +} Iso14443_4aPollerEventType; + +typedef struct { + Iso14443_4aError error; +} Iso14443_4aPollerEventData; + +typedef struct { + Iso14443_4aPollerEventType type; + Iso14443_4aPollerEventData* data; +} Iso14443_4aPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_defs.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_defs.h new file mode 100644 index 000000000000..aa62166742bf --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase nfc_poller_iso14443_4a; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c new file mode 100644 index 000000000000..53d90e9684e0 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c @@ -0,0 +1,85 @@ +#include "iso14443_4a_poller_i.h" + +#include + +#include "iso14443_4a_i.h" + +#define TAG "Iso14443_4aPoller" + +Iso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance) { + furi_assert(instance); + + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->poller_state = Iso14443_4aPollerStateIdle; + + return Iso14443_4aErrorNone; +} + +Iso14443_4aError + iso14443_4a_poller_async_read_ats(Iso14443_4aPoller* instance, SimpleArray* data) { + furi_assert(instance); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO14443_4A_CMD_READ_ATS); + bit_buffer_append_byte(instance->tx_buffer, ISO14443_4A_FSDI_256 << 4); + + Iso14443_4aError error = Iso14443_4aErrorNone; + + do { + const Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + ISO14443_4A_POLLER_ATS_FWT_FC); + + if(iso14443_3a_error != Iso14443_3aErrorNone) { + FURI_LOG_E(TAG, "ATS request failed"); + error = iso14443_4a_process_error(iso14443_3a_error); + break; + + } else if(!iso14443_4a_ats_parse(data, instance->rx_buffer)) { + FURI_LOG_E(TAG, "Failed to parse ATS response"); + error = Iso14443_4aErrorProtocol; + break; + } + + } while(false); + + return error; +} + +Iso14443_4aError iso14443_4a_poller_send_block( + Iso14443_4aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + + const uint8_t pcb = ISO14443_4A_BLOCK_PCB_I | ISO14443_4A_BLOCK_PCB | + instance->protocol_state.block_number; + instance->protocol_state.block_number ^= 1; + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, pcb); + bit_buffer_append(instance->tx_buffer, tx_buffer); + + Iso14443_4aError error = Iso14443_4aErrorNone; + + do { + Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, instance->tx_buffer, instance->rx_buffer, fwt); + + if(iso14443_3a_error != Iso14443_3aErrorNone) { + error = iso14443_4a_process_error(iso14443_3a_error); + break; + + } else if(!bit_buffer_starts_with_byte(instance->rx_buffer, pcb)) { + error = Iso14443_4aErrorProtocol; + break; + } + + bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(pcb)); + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h new file mode 100644 index 000000000000..0673d74dcdf5 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h @@ -0,0 +1,64 @@ +#pragma once + +#include + +#include "iso14443_4a_poller.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO14443_4A_POLLER_ATS_FWT_FC (12000) + +typedef enum { + Iso14443_4aPollerStateIdle, + Iso14443_4aPollerStateReadAts, + Iso14443_4aPollerStateError, + Iso14443_4aPollerStateReady, + + Iso14443_4aPollerStateNum, +} Iso14443_4aPollerState; + +typedef enum { + Iso14443_4aPollerSessionStateIdle, + Iso14443_4aPollerSessionStateActive, + Iso14443_4aPollerSessionStateStopRequest, +} Iso14443_4aPollerSessionState; + +typedef struct { + uint32_t block_number; +} Iso14443_4aPollerProtocolState; + +struct Iso14443_4aPoller { + Iso14443_3aPoller* iso14443_3a_poller; + Iso14443_4aPollerState poller_state; + Iso14443_4aPollerSessionState session_state; + Iso14443_4aPollerProtocolState protocol_state; + Iso14443_4aError error; + Iso14443_4aData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + Iso14443_4aPollerEventData iso14443_4a_event_data; + Iso14443_4aPollerEvent iso14443_4a_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error); + +const Iso14443_4aData* iso14443_4a_poller_get_data(Iso14443_4aPoller* instance); + +Iso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance); + +Iso14443_4aError iso14443_4a_poller_async_read_ats(Iso14443_4aPoller* instance, SimpleArray* data); + +Iso14443_4aError iso14443_4a_poller_send_block( + Iso14443_4aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3.c b/lib/nfc/protocols/iso15693_3/iso15693_3.c new file mode 100644 index 000000000000..adc9c9558c49 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3.c @@ -0,0 +1,356 @@ +#include "iso15693_3.h" +#include "iso15693_3_device_defs.h" + +#include + +#define ISO15693_3_PROTOCOL_NAME "ISO15693-3" +#define ISO15693_3_PROTOCOL_NAME_LEGACY "ISO15693" +#define ISO15693_3_DEVICE_NAME "ISO15693-3 (Unknown)" + +#define ISO15693_3_LOCK_DSFID_LEGACY (1U << 0) +#define ISO15693_3_LOCK_AFI_LEGACY (1U << 1) + +#define ISO15693_3_DSFID_KEY "DSFID" +#define ISO15693_3_AFI_KEY "AFI" +#define ISO15693_3_IC_REF_KEY "IC Reference" +#define ISO15693_3_BLOCK_COUNT_KEY "Block Count" +#define ISO15693_3_BLOCK_SIZE_KEY "Block Size" +#define ISO15693_3_DATA_CONTENT_KEY "Data Content" +#define ISO15693_3_LOCK_DSFID_KEY "Lock DSFID" +#define ISO15693_3_LOCK_AFI_KEY "Lock AFI" +#define ISO15693_3_SECURITY_STATUS_KEY "Security Status" + +const NfcDeviceBase nfc_device_iso15693_3 = { + .protocol_name = ISO15693_3_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)iso15693_3_alloc, + .free = (NfcDeviceFree)iso15693_3_free, + .reset = (NfcDeviceReset)iso15693_3_reset, + .copy = (NfcDeviceCopy)iso15693_3_copy, + .verify = (NfcDeviceVerify)iso15693_3_verify, + .load = (NfcDeviceLoad)iso15693_3_load, + .save = (NfcDeviceSave)iso15693_3_save, + .is_equal = (NfcDeviceEqual)iso15693_3_is_equal, + .get_name = (NfcDeviceGetName)iso15693_3_get_device_name, + .get_uid = (NfcDeviceGetUid)iso15693_3_get_uid, + .set_uid = (NfcDeviceSetUid)iso15693_3_set_uid, + .get_base_data = (NfcDeviceGetBaseData)iso15693_3_get_base_data, +}; + +Iso15693_3Data* iso15693_3_alloc() { + Iso15693_3Data* data = malloc(sizeof(Iso15693_3Data)); + + data->block_data = simple_array_alloc(&simple_array_config_uint8_t); + data->block_security = simple_array_alloc(&simple_array_config_uint8_t); + + return data; +} + +void iso15693_3_free(Iso15693_3Data* data) { + furi_assert(data); + + simple_array_free(data->block_data); + simple_array_free(data->block_security); + free(data); +} + +void iso15693_3_reset(Iso15693_3Data* data) { + furi_assert(data); + + memset(data->uid, 0, ISO15693_3_UID_SIZE); + memset(&data->system_info, 0, sizeof(Iso15693_3SystemInfo)); + memset(&data->settings, 0, sizeof(Iso15693_3Settings)); + + simple_array_reset(data->block_data); + simple_array_reset(data->block_security); +} + +void iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other) { + furi_assert(data); + furi_assert(other); + + memcpy(data->uid, other->uid, ISO15693_3_UID_SIZE); + + data->system_info = other->system_info; + data->settings = other->settings; + + simple_array_copy(data->block_data, other->block_data); + simple_array_copy(data->block_security, other->block_security); +} + +bool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal(device_type, ISO15693_3_PROTOCOL_NAME_LEGACY); +} + +static inline bool iso15693_3_load_security_legacy(Iso15693_3Data* data, FlipperFormat* ff) { + bool loaded = false; + uint8_t* legacy_data = NULL; + + do { + uint32_t value_count; + if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count)) + break; + if(simple_array_get_count(data->block_security) + 1 != value_count) break; + + legacy_data = malloc(value_count); + if(!flipper_format_read_hex(ff, ISO15693_3_SECURITY_STATUS_KEY, legacy_data, value_count)) + break; + + // First legacy data byte is lock bits + data->settings.lock_bits.dsfid = legacy_data[0] & ISO15693_3_LOCK_DSFID_LEGACY; + data->settings.lock_bits.afi = legacy_data[0] & ISO15693_3_LOCK_AFI_LEGACY; + + // The rest are block security + memcpy( + &legacy_data[1], + simple_array_get_data(data->block_security), + simple_array_get_count(data->block_security)); + + loaded = true; + } while(false); + + if(legacy_data) free(legacy_data); + + return loaded; +} + +static inline bool iso15693_3_load_security(Iso15693_3Data* data, FlipperFormat* ff) { + bool loaded = false; + + do { + uint32_t value_count; + if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count)) + break; + if(simple_array_get_count(data->block_security) != value_count) break; + if(!flipper_format_read_hex( + ff, + ISO15693_3_SECURITY_STATUS_KEY, + simple_array_get_data(data->block_security), + simple_array_get_count(data->block_security))) + break; + + loaded = true; + } while(false); + + return loaded; +} + +bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + UNUSED(version); + + bool loaded = false; + + do { + if(flipper_format_key_exist(ff, ISO15693_3_DSFID_KEY)) { + if(!flipper_format_read_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1)) + break; + data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_DSFID; + } + + if(flipper_format_key_exist(ff, ISO15693_3_AFI_KEY)) { + if(!flipper_format_read_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break; + data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_AFI; + } + + if(flipper_format_key_exist(ff, ISO15693_3_IC_REF_KEY)) { + if(!flipper_format_read_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1)) + break; + data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_IC_REF; + } + + const bool has_lock_bits = flipper_format_key_exist(ff, ISO15693_3_LOCK_DSFID_KEY) && + flipper_format_key_exist(ff, ISO15693_3_LOCK_AFI_KEY); + if(has_lock_bits) { + Iso15693_3LockBits* lock_bits = &data->settings.lock_bits; + if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_DSFID_KEY, &lock_bits->dsfid, 1)) + break; + if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_AFI_KEY, &lock_bits->afi, 1)) break; + } + + if(flipper_format_key_exist(ff, ISO15693_3_BLOCK_COUNT_KEY) && + flipper_format_key_exist(ff, ISO15693_3_BLOCK_SIZE_KEY)) { + uint32_t block_count; + if(!flipper_format_read_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1)) break; + + data->system_info.block_count = block_count; + data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_MEMORY; + + if(!flipper_format_read_hex( + ff, ISO15693_3_BLOCK_SIZE_KEY, &(data->system_info.block_size), 1)) + break; + + simple_array_init( + data->block_data, data->system_info.block_size * data->system_info.block_count); + + if(!flipper_format_read_hex( + ff, + ISO15693_3_DATA_CONTENT_KEY, + simple_array_get_data(data->block_data), + simple_array_get_count(data->block_data))) + break; + + if(flipper_format_key_exist(ff, ISO15693_3_SECURITY_STATUS_KEY)) { + simple_array_init(data->block_security, data->system_info.block_count); + + const bool security_loaded = has_lock_bits ? + iso15693_3_load_security(data, ff) : + iso15693_3_load_security_legacy(data, ff); + if(!security_loaded) break; + } + } + + loaded = true; + } while(false); + + return loaded; +} + +bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + if(!flipper_format_write_comment_cstr(ff, ISO15693_3_PROTOCOL_NAME " specific data")) + break; + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + if(!flipper_format_write_comment_cstr(ff, "Data Storage Format Identifier")) break; + if(!flipper_format_write_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1)) + break; + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { + if(!flipper_format_write_comment_cstr(ff, "Application Family Identifier")) break; + if(!flipper_format_write_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break; + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { + if(!flipper_format_write_comment_cstr(ff, "IC Reference - Vendor specific meaning")) + break; + if(!flipper_format_write_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1)) + break; + } + + if(!flipper_format_write_comment_cstr(ff, "Lock Bits")) break; + if(!flipper_format_write_bool( + ff, ISO15693_3_LOCK_DSFID_KEY, &data->settings.lock_bits.dsfid, 1)) + break; + if(!flipper_format_write_bool( + ff, ISO15693_3_LOCK_AFI_KEY, &data->settings.lock_bits.afi, 1)) + break; + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + const uint32_t block_count = data->system_info.block_count; + if(!flipper_format_write_comment_cstr( + ff, "Number of memory blocks, valid range = 1..256")) + break; + if(!flipper_format_write_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1)) + break; + + if(!flipper_format_write_comment_cstr( + ff, "Size of a single memory block, valid range = 01...20 (hex)")) + break; + if(!flipper_format_write_hex( + ff, ISO15693_3_BLOCK_SIZE_KEY, &data->system_info.block_size, 1)) + break; + + if(!flipper_format_write_hex( + ff, + ISO15693_3_DATA_CONTENT_KEY, + simple_array_cget_data(data->block_data), + simple_array_get_count(data->block_data))) + break; + + if(!flipper_format_write_comment_cstr( + ff, "Block Security Status: 01 = locked, 00 = not locked")) + break; + if(!flipper_format_write_hex( + ff, + ISO15693_3_SECURITY_STATUS_KEY, + simple_array_cget_data(data->block_security), + simple_array_get_count(data->block_security))) + break; + } + saved = true; + } while(false); + + return saved; +} + +bool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other) { + furi_assert(data); + furi_assert(other); + + return memcmp(data->uid, other->uid, ISO15693_3_UID_SIZE) == 0 && + memcmp(&data->settings, &other->settings, sizeof(Iso15693_3Settings)) == 0 && + memcmp(&data->system_info, &other->system_info, sizeof(Iso15693_3SystemInfo)) == 0 && + simple_array_is_equal(data->block_data, other->block_data) && + simple_array_is_equal(data->block_security, other->block_security); +} + +const char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + + return ISO15693_3_DEVICE_NAME; +} + +const uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len) { + furi_assert(data); + + if(uid_len) *uid_len = ISO15693_3_UID_SIZE; + return data->uid; +} + +bool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + furi_assert(uid); + + bool uid_valid = uid_len == ISO15693_3_UID_SIZE; + + if(uid_valid) { + memcpy(data->uid, uid, uid_len); + } + + return uid_valid; +} + +Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data) { + UNUSED(data); + furi_crash("No base data"); +} + +bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index) { + furi_assert(data); + furi_assert(block_index < data->system_info.block_count); + + return *(const uint8_t*)simple_array_cget(data->block_security, block_index); +} + +uint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data) { + furi_assert(data); + + return data->uid[1]; +} + +uint16_t iso15693_3_get_block_count(const Iso15693_3Data* data) { + furi_assert(data); + + return data->system_info.block_count; +} + +uint8_t iso15693_3_get_block_size(const Iso15693_3Data* data) { + furi_assert(data); + + return data->system_info.block_size; +} + +const uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index) { + furi_assert(data); + furi_assert(data->system_info.block_count > block_index); + + return (const uint8_t*)simple_array_cget( + data->block_data, block_index * data->system_info.block_size); +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3.h b/lib/nfc/protocols/iso15693_3/iso15693_3.h new file mode 100644 index 000000000000..5d4158ab9e3c --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3.h @@ -0,0 +1,163 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO15693_3_UID_SIZE (8U) + +#define ISO15693_3_GUARD_TIME_US (5000U) +#define ISO15693_3_FDT_POLL_FC (4202U) +#define ISO15693_3_FDT_LISTEN_FC (4320U) +#define ISO15693_3_POLL_POLL_MIN_US (1500U) + +#define ISO15693_3_REQ_FLAG_SUBCARRIER_1 (0U << 0) +#define ISO15693_3_REQ_FLAG_SUBCARRIER_2 (1U << 0) +#define ISO15693_3_REQ_FLAG_DATA_RATE_LO (0U << 1) +#define ISO15693_3_REQ_FLAG_DATA_RATE_HI (1U << 1) +#define ISO15693_3_REQ_FLAG_INVENTORY_T4 (0U << 2) +#define ISO15693_3_REQ_FLAG_INVENTORY_T5 (1U << 2) +#define ISO15693_3_REQ_FLAG_EXTENSION (1U << 3) + +#define ISO15693_3_REQ_FLAG_T4_SELECTED (1U << 4) +#define ISO15693_3_REQ_FLAG_T4_ADDRESSED (1U << 5) +#define ISO15693_3_REQ_FLAG_T4_OPTION (1U << 6) + +#define ISO15693_3_REQ_FLAG_T5_AFI_PRESENT (1U << 4) +#define ISO15693_3_REQ_FLAG_T5_N_SLOTS_16 (0U << 5) +#define ISO15693_3_REQ_FLAG_T5_N_SLOTS_1 (1U << 5) +#define ISO15693_3_REQ_FLAG_T5_OPTION (1U << 6) + +#define ISO15693_3_RESP_FLAG_NONE (0U) +#define ISO15693_3_RESP_FLAG_ERROR (1U << 0) +#define ISO15693_3_RESP_FLAG_EXTENSION (1U << 3) + +#define ISO15693_3_RESP_ERROR_NOT_SUPPORTED (0x01U) +#define ISO15693_3_RESP_ERROR_FORMAT (0x02U) +#define ISO15693_3_RESP_ERROR_OPTION (0x03U) +#define ISO15693_3_RESP_ERROR_UNKNOWN (0x0FU) +#define ISO15693_3_RESP_ERROR_BLOCK_UNAVAILABLE (0x10U) +#define ISO15693_3_RESP_ERROR_BLOCK_ALREADY_LOCKED (0x11U) +#define ISO15693_3_RESP_ERROR_BLOCK_LOCKED (0x12U) +#define ISO15693_3_RESP_ERROR_BLOCK_WRITE (0x13U) +#define ISO15693_3_RESP_ERROR_BLOCK_LOCK (0x14U) +#define ISO15693_3_RESP_ERROR_CUSTOM_START (0xA0U) +#define ISO15693_3_RESP_ERROR_CUSTOM_END (0xDFU) + +#define ISO15693_3_CMD_MANDATORY_START (0x01U) +#define ISO15693_3_CMD_INVENTORY (0x01U) +#define ISO15693_3_CMD_STAY_QUIET (0x02U) +#define ISO15693_3_CMD_MANDATORY_RFU (0x03U) +#define ISO15693_3_CMD_OPTIONAL_START (0x20U) +#define ISO15693_3_CMD_READ_BLOCK (0x20U) +#define ISO15693_3_CMD_WRITE_BLOCK (0x21U) +#define ISO15693_3_CMD_LOCK_BLOCK (0x22U) +#define ISO15693_3_CMD_READ_MULTI_BLOCKS (0x23U) +#define ISO15693_3_CMD_WRITE_MULTI_BLOCKS (0x24U) +#define ISO15693_3_CMD_SELECT (0x25U) +#define ISO15693_3_CMD_RESET_TO_READY (0x26U) +#define ISO15693_3_CMD_WRITE_AFI (0x27U) +#define ISO15693_3_CMD_LOCK_AFI (0x28U) +#define ISO15693_3_CMD_WRITE_DSFID (0x29U) +#define ISO15693_3_CMD_LOCK_DSFID (0x2AU) +#define ISO15693_3_CMD_GET_SYS_INFO (0x2BU) +#define ISO15693_3_CMD_GET_BLOCKS_SECURITY (0x2CU) +#define ISO15693_3_CMD_OPTIONAL_RFU (0x2DU) +#define ISO15693_3_CMD_CUSTOM_START (0xA0U) + +#define ISO15693_3_MANDATORY_COUNT (ISO15693_3_CMD_MANDATORY_RFU - ISO15693_3_CMD_MANDATORY_START) +#define ISO15693_3_OPTIONAL_COUNT (ISO15693_3_CMD_OPTIONAL_RFU - ISO15693_3_CMD_OPTIONAL_START) + +#define ISO15693_3_SYSINFO_FLAG_DSFID (1U << 0) +#define ISO15693_3_SYSINFO_FLAG_AFI (1U << 1) +#define ISO15693_3_SYSINFO_FLAG_MEMORY (1U << 2) +#define ISO15693_3_SYSINFO_FLAG_IC_REF (1U << 3) + +typedef enum { + Iso15693_3ErrorNone, + Iso15693_3ErrorNotPresent, + Iso15693_3ErrorBufferEmpty, + Iso15693_3ErrorBufferOverflow, + Iso15693_3ErrorFieldOff, + Iso15693_3ErrorWrongCrc, + Iso15693_3ErrorTimeout, + Iso15693_3ErrorFormat, + Iso15693_3ErrorIgnore, + Iso15693_3ErrorNotSupported, + Iso15693_3ErrorUidMismatch, + Iso15693_3ErrorFullyHandled, + Iso15693_3ErrorUnexpectedResponse, + Iso15693_3ErrorInternal, + Iso15693_3ErrorCustom, + Iso15693_3ErrorUnknown, +} Iso15693_3Error; + +typedef struct { + uint8_t flags; + uint8_t dsfid; + uint8_t afi; + uint8_t ic_ref; + uint16_t block_count; + uint8_t block_size; +} Iso15693_3SystemInfo; + +typedef struct { + bool dsfid; + bool afi; +} Iso15693_3LockBits; + +typedef struct { + Iso15693_3LockBits lock_bits; +} Iso15693_3Settings; + +typedef struct { + uint8_t uid[ISO15693_3_UID_SIZE]; + Iso15693_3SystemInfo system_info; + Iso15693_3Settings settings; + SimpleArray* block_data; + SimpleArray* block_security; +} Iso15693_3Data; + +Iso15693_3Data* iso15693_3_alloc(); + +void iso15693_3_free(Iso15693_3Data* data); + +void iso15693_3_reset(Iso15693_3Data* data); + +void iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other); + +bool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type); + +bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version); + +bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff); + +bool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other); + +const char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceNameType name_type); + +const uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len); + +bool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len); + +Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data); + +// Getters and tests + +bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index); + +uint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data); + +uint16_t iso15693_3_get_block_count(const Iso15693_3Data* data); + +uint8_t iso15693_3_get_block_size(const Iso15693_3Data* data); + +const uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_device_defs.h b/lib/nfc/protocols/iso15693_3/iso15693_3_device_defs.h new file mode 100644 index 000000000000..dffa137b8d20 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_device_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcDeviceBase nfc_device_iso15693_3; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_i.c new file mode 100644 index 000000000000..3d8d95c3a1d0 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_i.c @@ -0,0 +1,263 @@ +#include "iso15693_3_i.h" + +bool iso15693_3_error_response_parse(Iso15693_3Error* error, const BitBuffer* buf) { + furi_assert(error); + + if(bit_buffer_get_size_bytes(buf) == 0) { + // YEET! + *error = Iso15693_3ErrorBufferEmpty; + return true; + } + + typedef struct { + uint8_t flags; + uint8_t error; + } ErrorResponseLayout; + + const ErrorResponseLayout* resp = (const ErrorResponseLayout*)bit_buffer_get_data(buf); + + if((resp->flags & ISO15693_3_RESP_FLAG_ERROR) == 0) { + // No error flag is set, the data does not contain an error frame + return false; + } else if(bit_buffer_get_size_bytes(buf) < sizeof(ErrorResponseLayout)) { + // Error bit is set, but not enough data to determine the error + *error = Iso15693_3ErrorUnexpectedResponse; + return true; + } else if( + resp->error >= ISO15693_3_RESP_ERROR_CUSTOM_START && + resp->error <= ISO15693_3_RESP_ERROR_CUSTOM_END) { + // Custom vendor-specific error, must be checked in the respective protocol implementation + *error = Iso15693_3ErrorCustom; + return true; + } + + switch(resp->error) { + case ISO15693_3_RESP_ERROR_NOT_SUPPORTED: + case ISO15693_3_RESP_ERROR_OPTION: + *error = Iso15693_3ErrorNotSupported; + break; + case ISO15693_3_RESP_ERROR_FORMAT: + *error = Iso15693_3ErrorFormat; + break; + case ISO15693_3_RESP_ERROR_BLOCK_UNAVAILABLE: + case ISO15693_3_RESP_ERROR_BLOCK_ALREADY_LOCKED: + case ISO15693_3_RESP_ERROR_BLOCK_LOCKED: + case ISO15693_3_RESP_ERROR_BLOCK_WRITE: + case ISO15693_3_RESP_ERROR_BLOCK_LOCK: + *error = Iso15693_3ErrorInternal; + break; + case ISO15693_3_RESP_ERROR_UNKNOWN: + default: + *error = Iso15693_3ErrorUnknown; + } + + return true; +} + +Iso15693_3Error iso15693_3_inventory_response_parse(uint8_t* data, const BitBuffer* buf) { + furi_assert(data); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + do { + if(iso15693_3_error_response_parse(&ret, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t dsfid; + uint8_t uid[ISO15693_3_UID_SIZE]; + } InventoryResponseLayout; + + if(bit_buffer_get_size_bytes(buf) != sizeof(InventoryResponseLayout)) { + ret = Iso15693_3ErrorUnexpectedResponse; + break; + } + + const InventoryResponseLayout* resp = + (const InventoryResponseLayout*)bit_buffer_get_data(buf); + // Reverse UID for backward compatibility + for(uint32_t i = 0; i < ISO15693_3_UID_SIZE; ++i) { + data[i] = resp->uid[ISO15693_3_UID_SIZE - i - 1]; + } + + } while(false); + + return ret; +} + +Iso15693_3Error + iso15693_3_system_info_response_parse(Iso15693_3SystemInfo* data, const BitBuffer* buf) { + furi_assert(data); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + do { + if(iso15693_3_error_response_parse(&ret, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t info_flags; + uint8_t uid[ISO15693_3_UID_SIZE]; + uint8_t extra[]; + } SystemInfoResponseLayout; + + if(bit_buffer_get_size_bytes(buf) < sizeof(SystemInfoResponseLayout)) { + ret = Iso15693_3ErrorUnexpectedResponse; + break; + } + + const SystemInfoResponseLayout* resp = + (const SystemInfoResponseLayout*)bit_buffer_get_data(buf); + + const uint8_t* extra = resp->extra; + const size_t extra_size = (resp->info_flags & ISO15693_3_SYSINFO_FLAG_DSFID ? 1 : 0) + + (resp->info_flags & ISO15693_3_SYSINFO_FLAG_AFI ? 1 : 0) + + (resp->info_flags & ISO15693_3_SYSINFO_FLAG_MEMORY ? 2 : 0) + + (resp->info_flags & ISO15693_3_SYSINFO_FLAG_IC_REF ? 1 : 0); + + if(extra_size != bit_buffer_get_size_bytes(buf) - sizeof(SystemInfoResponseLayout)) { + ret = Iso15693_3ErrorUnexpectedResponse; + break; + } + + data->flags = resp->info_flags; + + if(data->flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + data->dsfid = *extra++; + } + + if(data->flags & ISO15693_3_SYSINFO_FLAG_AFI) { + data->afi = *extra++; + } + + if(data->flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + // Add 1 to get actual values + data->block_count = *extra++ + 1; + data->block_size = (*extra++ & 0x1F) + 1; + } + + if(data->flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { + data->ic_ref = *extra; + } + + } while(false); + + return ret; +} + +Iso15693_3Error + iso15693_3_read_block_response_parse(uint8_t* data, uint8_t block_size, const BitBuffer* buf) { + furi_assert(data); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + do { + if(iso15693_3_error_response_parse(&ret, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t block_data[]; + } ReadBlockResponseLayout; + + const size_t buf_size = bit_buffer_get_size_bytes(buf); + const size_t received_block_size = buf_size - sizeof(ReadBlockResponseLayout); + + if(buf_size <= sizeof(ReadBlockResponseLayout) || received_block_size != block_size) { + ret = Iso15693_3ErrorUnexpectedResponse; + break; + } + + const ReadBlockResponseLayout* resp = + (const ReadBlockResponseLayout*)bit_buffer_get_data(buf); + memcpy(data, resp->block_data, received_block_size); + + } while(false); + + return ret; +} + +Iso15693_3Error iso15693_3_get_block_security_response_parse( + uint8_t* data, + uint16_t block_count, + const BitBuffer* buf) { + furi_assert(data); + furi_assert(block_count); + Iso15693_3Error ret = Iso15693_3ErrorNone; + + do { + if(iso15693_3_error_response_parse(&ret, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t block_security[]; + } GetBlockSecurityResponseLayout; + + const size_t buf_size = bit_buffer_get_size_bytes(buf); + const size_t received_block_count = buf_size - sizeof(GetBlockSecurityResponseLayout); + + if(buf_size <= sizeof(GetBlockSecurityResponseLayout) || + received_block_count != block_count) { + ret = Iso15693_3ErrorUnexpectedResponse; + break; + } + + const GetBlockSecurityResponseLayout* resp = + (const GetBlockSecurityResponseLayout*)bit_buffer_get_data(buf); + + memcpy(data, resp->block_security, received_block_count); + + } while(false); + + return ret; +} + +void iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf) { + for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) { + // Reverse the UID + bit_buffer_append_byte(buf, data->uid[ISO15693_3_UID_SIZE - i - 1]); + } +} + +void iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf) { + furi_assert(block_num < data->system_info.block_count); + + const uint32_t block_offset = block_num * data->system_info.block_size; + const uint8_t* block_data = simple_array_cget(data->block_data, block_offset); + + bit_buffer_append_bytes(buf, block_data, data->system_info.block_size); +} + +void iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_index, bool locked) { + furi_assert(data); + furi_assert(block_index < data->system_info.block_count); + + *(uint8_t*)simple_array_get(data->block_security, block_index) = locked ? 1 : 0; +} + +void iso15693_3_set_block_data( + Iso15693_3Data* data, + uint8_t block_num, + const uint8_t* block_data, + size_t block_data_size) { + furi_assert(block_num < data->system_info.block_count); + furi_assert(block_data_size == data->system_info.block_size); + + const uint32_t block_offset = block_num * data->system_info.block_size; + uint8_t* block = simple_array_get(data->block_data, block_offset); + + memcpy(block, block_data, block_data_size); +} + +void iso15693_3_append_block_security( + const Iso15693_3Data* data, + uint8_t block_num, + BitBuffer* buf) { + bit_buffer_append_byte(buf, *(uint8_t*)simple_array_cget(data->block_security, block_num)); +} + +bool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid) { + for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) { + if(data->uid[i] != uid[ISO15693_3_UID_SIZE - i - 1]) return false; + } + return true; +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_i.h b/lib/nfc/protocols/iso15693_3/iso15693_3_i.h new file mode 100644 index 000000000000..253bda7f5985 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_i.h @@ -0,0 +1,58 @@ +#pragma once + +#include "iso15693_3.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Check if the buffer contains an error frame and if it does, determine + * the error type. + * NOTE: No changes are done to the result if no error is present. + * + * @param [out] data Pointer to the resulting error value. + * @param [in] buf Data buffer to be checked + * + * @return True if data contains an error frame or is empty, false otherwise + */ +bool iso15693_3_error_response_parse(Iso15693_3Error* error, const BitBuffer* buf); + +Iso15693_3Error iso15693_3_inventory_response_parse(uint8_t* data, const BitBuffer* buf); + +Iso15693_3Error + iso15693_3_system_info_response_parse(Iso15693_3SystemInfo* data, const BitBuffer* buf); + +Iso15693_3Error + iso15693_3_read_block_response_parse(uint8_t* data, uint8_t block_size, const BitBuffer* buf); + +Iso15693_3Error iso15693_3_get_block_security_response_parse( + uint8_t* data, + uint16_t block_count, + const BitBuffer* buf); + +void iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf); + +void iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf); + +void iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_index, bool locked); + +void iso15693_3_set_block_data( + Iso15693_3Data* data, + uint8_t block_num, + const uint8_t* block_data, + size_t block_data_size); + +void iso15693_3_append_block_security( + const Iso15693_3Data* data, + uint8_t block_num, + BitBuffer* buf); + +// NOTE: the uid parameter has reversed byte order with respect to data +bool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c new file mode 100644 index 000000000000..2ead83ce560d --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c @@ -0,0 +1,110 @@ +#include "iso15693_3_listener_i.h" + +#include + +#include +#include +#include + +#define TAG "Iso15693_3Listener" + +#define ISO15693_3_LISTENER_BUFFER_SIZE (64U) + +Iso15693_3Listener* iso15693_3_listener_alloc(Nfc* nfc, Iso15693_3Data* data) { + furi_assert(nfc); + + Iso15693_3Listener* instance = malloc(sizeof(Iso15693_3Listener)); + instance->nfc = nfc; + instance->data = data; + + instance->tx_buffer = bit_buffer_alloc(ISO15693_3_LISTENER_BUFFER_SIZE); + + instance->iso15693_3_event.data = &instance->iso15693_3_event_data; + instance->generic_event.protocol = NfcProtocolIso15693_3; + instance->generic_event.instance = instance; + instance->generic_event.data = &instance->iso15693_3_event; + + nfc_set_fdt_listen_fc(instance->nfc, ISO15693_3_FDT_LISTEN_FC); + nfc_config(instance->nfc, NfcModeListener, NfcTechIso15693); + + return instance; +} + +void iso15693_3_listener_free(Iso15693_3Listener* instance) { + furi_assert(instance); + + bit_buffer_free(instance->tx_buffer); + + free(instance); +} + +void iso15693_3_listener_set_callback( + Iso15693_3Listener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +const Iso15693_3Data* iso15693_3_listener_get_data(Iso15693_3Listener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +NfcCommand iso15693_3_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.data); + + Iso15693_3Listener* instance = context; + NfcEvent* nfc_event = event.data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypeRxEnd) { + BitBuffer* rx_buffer = nfc_event->data.buffer; + + if(iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) { + iso13239_crc_trim(rx_buffer); + + const Iso15693_3Error error = iso15693_3_listener_process_request(instance, rx_buffer); + + if(error == Iso15693_3ErrorNotSupported) { + if(instance->callback) { + instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeCustomCommand; + instance->iso15693_3_event.data->buffer = rx_buffer; + command = instance->callback(instance->generic_event, instance->context); + } + + } else if(error == Iso15693_3ErrorUidMismatch) { + iso15693_3_listener_process_uid_mismatch(instance, rx_buffer); + } + + } else if(bit_buffer_get_size(rx_buffer) == 0) { + // Special case: Single EOF + const Iso15693_3Error error = iso15693_3_listener_process_single_eof(instance); + if(error == Iso15693_3ErrorUnexpectedResponse) { + if(instance->callback) { + instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeSingleEof; + command = instance->callback(instance->generic_event, instance->context); + } + } + } else { + FURI_LOG_D( + TAG, "Wrong CRC, buffer size: %zu", bit_buffer_get_size(nfc_event->data.buffer)); + } + } + + return command; +} + +const NfcListenerBase nfc_listener_iso15693_3 = { + .alloc = (NfcListenerAlloc)iso15693_3_listener_alloc, + .free = (NfcListenerFree)iso15693_3_listener_free, + .set_callback = (NfcListenerSetCallback)iso15693_3_listener_set_callback, + .get_data = (NfcListenerGetData)iso15693_3_listener_get_data, + .run = (NfcListenerRun)iso15693_3_listener_run, +}; diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener.h b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.h new file mode 100644 index 000000000000..c69d18db4e7f --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "iso15693_3.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso15693_3Listener Iso15693_3Listener; + +typedef enum { + Iso15693_3ListenerEventTypeFieldOff, + Iso15693_3ListenerEventTypeCustomCommand, + Iso15693_3ListenerEventTypeSingleEof, +} Iso15693_3ListenerEventType; + +typedef struct { + BitBuffer* buffer; +} Iso15693_3ListenerEventData; + +typedef struct { + Iso15693_3ListenerEventType type; + Iso15693_3ListenerEventData* data; +} Iso15693_3ListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_defs.h b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_defs.h new file mode 100644 index 000000000000..0543b6f9299a --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcListenerBase nfc_listener_iso15693_3; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c new file mode 100644 index 000000000000..7bb3359dad40 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c @@ -0,0 +1,884 @@ +#include "iso15693_3_listener_i.h" + +#include + +#define TAG "Iso15693_3Listener" + +typedef Iso15693_3Error (*Iso15693_3RequestHandler)( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags); + +typedef struct { + Iso15693_3RequestHandler mandatory[ISO15693_3_MANDATORY_COUNT]; + Iso15693_3RequestHandler optional[ISO15693_3_OPTIONAL_COUNT]; +} Iso15693_3ListenerHandlerTable; + +static Iso15693_3Error + iso15693_3_listener_extension_handler(Iso15693_3Listener* instance, uint32_t command, ...) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + if(instance->extension_table == NULL) break; + + Iso15693_3ExtensionHandler handler = NULL; + + if(command < ISO15693_3_CMD_MANDATORY_RFU) { + const Iso15693_3ExtensionHandler* mandatory = instance->extension_table->mandatory; + handler = mandatory[command - ISO15693_3_CMD_MANDATORY_START]; + } else if(command >= ISO15693_3_CMD_OPTIONAL_START && command < ISO15693_3_CMD_OPTIONAL_RFU) { + const Iso15693_3ExtensionHandler* optional = instance->extension_table->optional; + handler = optional[command - ISO15693_3_CMD_OPTIONAL_START]; + } + + if(handler == NULL) break; + + va_list args; + va_start(args, command); + + error = handler(instance->extension_context, args); + + va_end(args); + + } while(false); + return error; +} + +static Iso15693_3Error iso15693_3_listener_inventory_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const bool afi_flag = flags & ISO15693_3_REQ_FLAG_T5_AFI_PRESENT; + const size_t data_size_min = sizeof(uint8_t) * (afi_flag ? 2 : 1); + + if(data_size < data_size_min) { + error = Iso15693_3ErrorFormat; + break; + } + + if(afi_flag) { + const uint8_t afi = *data++; + // When AFI flag is set, ignore non-matching requests + if(afi != instance->data->system_info.afi) break; + } + + const uint8_t mask_len = *data++; + const size_t data_size_required = data_size_min + mask_len; + + if(data_size != data_size_required) { + error = Iso15693_3ErrorFormat; + break; + } + + if(mask_len != 0) { + // TODO: Take mask_len and mask_value into account (if present) + } + + error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_INVENTORY); + if(error != Iso15693_3ErrorNone) break; + + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid); // DSFID + iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_stay_quiet_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + instance->state = Iso15693_3ListenerStateQuiet; + return Iso15693_3ErrorIgnore; +} + +static Iso15693_3Error iso15693_3_listener_read_block_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t block_num; + } Iso15693_3ReadBlockRequestLayout; + + const Iso15693_3ReadBlockRequestLayout* request = + (const Iso15693_3ReadBlockRequestLayout*)data; + + if(data_size != sizeof(Iso15693_3ReadBlockRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index = request->block_num; + const uint32_t block_count_max = instance->data->system_info.block_count; + + if(block_index >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler( + instance, ISO15693_3_CMD_READ_BLOCK, block_index); + if(error != Iso15693_3ErrorNone) break; + + if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) { + iso15693_3_append_block_security( + instance->data, block_index, instance->tx_buffer); // Block security (optional) + } + + iso15693_3_append_block(instance->data, block_index, instance->tx_buffer); // Block data + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_write_block_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t block_num; + uint8_t block_data[]; + } Iso15693_3WriteBlockRequestLayout; + + const Iso15693_3WriteBlockRequestLayout* request = + (const Iso15693_3WriteBlockRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteBlockRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index = request->block_num; + const uint32_t block_count_max = instance->data->system_info.block_count; + const uint32_t block_size_max = instance->data->system_info.block_size; + const size_t block_size_received = data_size - sizeof(Iso15693_3WriteBlockRequestLayout); + + if(block_index >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } else if(block_size_received != block_size_max) { + error = Iso15693_3ErrorInternal; + break; + } else if(iso15693_3_is_block_locked(instance->data, block_index)) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler( + instance, ISO15693_3_CMD_WRITE_BLOCK, block_index, request->block_data); + if(error != Iso15693_3ErrorNone) break; + + iso15693_3_set_block_data( + instance->data, block_index, request->block_data, block_size_received); + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_lock_block_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t block_num; + } Iso15693_3LockBlockRequestLayout; + + const Iso15693_3LockBlockRequestLayout* request = + (const Iso15693_3LockBlockRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size != sizeof(Iso15693_3LockBlockRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index = request->block_num; + const uint32_t block_count_max = instance->data->system_info.block_count; + + if(block_index >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } else if(iso15693_3_is_block_locked(instance->data, block_index)) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler( + instance, ISO15693_3_CMD_LOCK_BLOCK, block_index); + if(error != Iso15693_3ErrorNone) break; + + iso15693_3_set_block_locked(instance->data, block_index, true); + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_read_multi_blocks_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t first_block_num; + uint8_t block_count; + } Iso15693_3ReadMultiBlocksRequestLayout; + + const Iso15693_3ReadMultiBlocksRequestLayout* request = + (const Iso15693_3ReadMultiBlocksRequestLayout*)data; + + if(data_size != sizeof(Iso15693_3ReadMultiBlocksRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index_start = request->first_block_num; + const uint32_t block_index_end = block_index_start + request->block_count; + + const uint32_t block_count = request->block_count + 1; + const uint32_t block_count_max = instance->data->system_info.block_count; + const uint32_t block_count_available = block_count_max - block_index_start; + + if(block_count > block_count_available) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler( + instance, + ISO15693_3_CMD_READ_MULTI_BLOCKS, + (uint32_t)block_index_start, + (uint32_t)block_index_end); + if(error != Iso15693_3ErrorNone) break; + + for(uint32_t i = block_index_start; i <= block_index_end; ++i) { + if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) { + iso15693_3_append_block_security( + instance->data, i, instance->tx_buffer); // Block security (optional) + } + iso15693_3_append_block(instance->data, i, instance->tx_buffer); // Block data + } + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_write_multi_blocks_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t first_block_num; + uint8_t block_count; + uint8_t block_data[]; + } Iso15693_3WriteMultiBlocksRequestLayout; + + const Iso15693_3WriteMultiBlocksRequestLayout* request = + (const Iso15693_3WriteMultiBlocksRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteMultiBlocksRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index_start = request->first_block_num; + const uint32_t block_index_end = block_index_start + request->block_count; + + const uint32_t block_count = request->block_count + 1; + const uint32_t block_count_max = instance->data->system_info.block_count; + const uint32_t block_count_available = block_count_max - block_index_start; + + const size_t block_data_size = data_size - sizeof(Iso15693_3WriteMultiBlocksRequestLayout); + const size_t block_size = block_data_size / block_count; + const size_t block_size_max = instance->data->system_info.block_size; + + if(block_count > block_count_available) { + error = Iso15693_3ErrorInternal; + break; + } else if(block_size != block_size_max) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler( + instance, ISO15693_3_CMD_WRITE_MULTI_BLOCKS, block_index_start, block_index_end); + if(error != Iso15693_3ErrorNone) break; + + for(uint32_t i = block_index_start; i <= block_index_end; ++i) { + if(iso15693_3_is_block_locked(instance->data, i)) { + error = Iso15693_3ErrorInternal; + break; + } + } + + if(error != Iso15693_3ErrorNone) break; + + for(uint32_t i = block_index_start; i < block_count + request->first_block_num; ++i) { + const uint8_t* block_data = &request->block_data[block_size * i]; + iso15693_3_set_block_data(instance->data, i, block_data, block_size); + } + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_select_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + if(!(flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED)) { + error = Iso15693_3ErrorFormat; + break; + } + + instance->state = Iso15693_3ListenerStateSelected; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_reset_to_ready_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + instance->state = Iso15693_3ListenerStateReady; + return Iso15693_3ErrorNone; +} + +static Iso15693_3Error iso15693_3_listener_write_afi_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t afi; + } Iso15693_3WriteAfiRequestLayout; + + const Iso15693_3WriteAfiRequestLayout* request = + (const Iso15693_3WriteAfiRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteAfiRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } else if(instance->data->settings.lock_bits.afi) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_WRITE_AFI); + if(error != Iso15693_3ErrorNone) break; + + instance->data->system_info.afi = request->afi; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_lock_afi_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + Iso15693_3LockBits* lock_bits = &instance->data->settings.lock_bits; + + if(lock_bits->afi) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_LOCK_AFI); + if(error != Iso15693_3ErrorNone) break; + + lock_bits->afi = true; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_write_dsfid_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t dsfid; + } Iso15693_3WriteDsfidRequestLayout; + + const Iso15693_3WriteDsfidRequestLayout* request = + (const Iso15693_3WriteDsfidRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteDsfidRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } else if(instance->data->settings.lock_bits.dsfid) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_WRITE_DSFID); + if(error != Iso15693_3ErrorNone) break; + + instance->data->system_info.dsfid = request->dsfid; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_lock_dsfid_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + Iso15693_3LockBits* lock_bits = &instance->data->settings.lock_bits; + + if(lock_bits->dsfid) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_LOCK_DSFID); + if(error != Iso15693_3ErrorNone) break; + + lock_bits->dsfid = true; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_get_system_info_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const uint8_t system_flags = instance->data->system_info.flags; + bit_buffer_append_byte(instance->tx_buffer, system_flags); // System info flags + + iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID + + if(system_flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid); + } + if(system_flags & ISO15693_3_SYSINFO_FLAG_AFI) { + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.afi); + } + if(system_flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + const uint8_t memory_info[2] = { + instance->data->system_info.block_count - 1, + instance->data->system_info.block_size - 1, + }; + bit_buffer_append_bytes(instance->tx_buffer, memory_info, COUNT_OF(memory_info)); + } + if(system_flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.ic_ref); + } + + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_get_multi_blocks_security_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(flags); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t first_block_num; + uint8_t block_count; + } Iso15693_3GetMultiBlocksSecurityRequestLayout; + + const Iso15693_3GetMultiBlocksSecurityRequestLayout* request = + (const Iso15693_3GetMultiBlocksSecurityRequestLayout*)data; + + if(data_size < sizeof(Iso15693_3GetMultiBlocksSecurityRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index_start = request->first_block_num; + const uint32_t block_index_end = block_index_start + request->block_count; + + const uint32_t block_count_max = instance->data->system_info.block_count; + + if(block_index_end >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } + + for(uint32_t i = block_index_start; i <= block_index_end; ++i) { + bit_buffer_append_byte( + instance->tx_buffer, iso15693_3_is_block_locked(instance->data, i) ? 1 : 0); + } + } while(false); + + return error; +} + +const Iso15693_3ListenerHandlerTable iso15693_3_handler_table = { + .mandatory = + { + iso15693_3_listener_inventory_handler, + iso15693_3_listener_stay_quiet_handler, + }, + .optional = + { + iso15693_3_listener_read_block_handler, + iso15693_3_listener_write_block_handler, + iso15693_3_listener_lock_block_handler, + iso15693_3_listener_read_multi_blocks_handler, + iso15693_3_listener_write_multi_blocks_handler, + iso15693_3_listener_select_handler, + iso15693_3_listener_reset_to_ready_handler, + iso15693_3_listener_write_afi_handler, + iso15693_3_listener_lock_afi_handler, + iso15693_3_listener_write_dsfid_handler, + iso15693_3_listener_lock_dsfid_handler, + iso15693_3_listener_get_system_info_handler, + iso15693_3_listener_get_multi_blocks_security_handler, + }, +}; + +static Iso15693_3Error iso15693_3_listener_handle_standard_request( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t command, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + Iso15693_3RequestHandler handler = NULL; + + if(command < ISO15693_3_CMD_MANDATORY_RFU) { + handler = iso15693_3_handler_table.mandatory[command - ISO15693_3_CMD_MANDATORY_START]; + } else if(command >= ISO15693_3_CMD_OPTIONAL_START && command < ISO15693_3_CMD_OPTIONAL_RFU) { + handler = iso15693_3_handler_table.optional[command - ISO15693_3_CMD_OPTIONAL_START]; + } + + if(handler == NULL) { + error = Iso15693_3ErrorNotSupported; + break; + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_NONE); + + error = handler(instance, data, data_size, flags); + + // The request was fully handled in the protocol extension, no further action necessary + if(error == Iso15693_3ErrorFullyHandled) { + error = Iso15693_3ErrorNone; + } + + // Several commands may not require an answer + if(error == Iso15693_3ErrorFormat || error == Iso15693_3ErrorIgnore) break; + + // TODO: Move it to a separate function + if(error != Iso15693_3ErrorNone) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_ERROR); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_ERROR_UNKNOWN); + } + + Iso15693_3ListenerSessionState* session_state = &instance->session_state; + + if(!session_state->wait_for_eof) { + error = iso15693_3_listener_send_frame(instance, instance->tx_buffer); + } + + } while(false); + + return error; +} + +static inline Iso15693_3Error iso15693_3_listener_handle_custom_request( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size) { + Iso15693_3Error error; + + do { + typedef struct { + uint8_t manufacturer; + uint8_t extra[]; + } Iso15693_3CustomRequestLayout; + + if(data_size < sizeof(Iso15693_3CustomRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const Iso15693_3CustomRequestLayout* request = (const Iso15693_3CustomRequestLayout*)data; + + if(request->manufacturer != iso15693_3_get_manufacturer_id(instance->data)) { + error = Iso15693_3ErrorIgnore; + break; + } + + // This error code will trigger the CustomCommand listener event + error = Iso15693_3ErrorNotSupported; + } while(false); + + return error; +} + +Iso15693_3Error iso15693_3_listener_set_extension_handler_table( + Iso15693_3Listener* instance, + const Iso15693_3ExtensionHandlerTable* table, + void* context) { + furi_assert(instance); + furi_assert(context); + + instance->extension_table = table; + instance->extension_context = context; + return Iso15693_3ErrorNone; +} + +Iso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance) { + furi_assert(instance); + instance->state = Iso15693_3ListenerStateReady; + return Iso15693_3ErrorNone; +} + +static Iso15693_3Error iso15693_3_listener_process_nfc_error(NfcError error) { + Iso15693_3Error ret = Iso15693_3ErrorNone; + + if(error == NfcErrorNone) { + ret = Iso15693_3ErrorNone; + } else if(error == NfcErrorTimeout) { + ret = Iso15693_3ErrorTimeout; + } else { + ret = Iso15693_3ErrorFieldOff; + } + + return ret; +} + +Iso15693_3Error + iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer); + + NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer); + return iso15693_3_listener_process_nfc_error(error); +} + +Iso15693_3Error + iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t flags; + uint8_t command; + uint8_t data[]; + } Iso15693_3RequestLayout; + + const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer); + const size_t buf_size_min = sizeof(Iso15693_3RequestLayout); + + if(buf_size < buf_size_min) { + error = Iso15693_3ErrorFormat; + break; + } + + const Iso15693_3RequestLayout* request = + (const Iso15693_3RequestLayout*)bit_buffer_get_data(rx_buffer); + + Iso15693_3ListenerSessionState* session_state = &instance->session_state; + + if((request->flags & ISO15693_3_REQ_FLAG_INVENTORY_T5) == 0) { + session_state->selected = request->flags & ISO15693_3_REQ_FLAG_T4_SELECTED; + session_state->addressed = request->flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED; + + if(session_state->selected && session_state->addressed) { + // A request mode can be either addressed or selected, but not both + error = Iso15693_3ErrorUnknown; + break; + } else if(instance->state == Iso15693_3ListenerStateQuiet) { + // If the card is quiet, ignore non-addressed commands + if(session_state->addressed) { + error = Iso15693_3ErrorIgnore; + break; + } + } else if(instance->state != Iso15693_3ListenerStateSelected) { + // If the card is not selected, ignore selected commands + if(session_state->selected) { + error = Iso15693_3ErrorIgnore; + break; + } + } + } else { + // If the card is quiet, ignore inventory commands + if(instance->state == Iso15693_3ListenerStateQuiet) { + error = Iso15693_3ErrorIgnore; + break; + } + + session_state->selected = false; + session_state->addressed = false; + } + + if(request->command >= ISO15693_3_CMD_CUSTOM_START) { + // Custom commands are properly handled in the protocol-specific top-level poller + error = iso15693_3_listener_handle_custom_request( + instance, request->data, buf_size - buf_size_min); + break; + } + + const uint8_t* data; + size_t data_size; + + if(session_state->addressed) { + // In addressed mode, UID must be included in each command + const size_t buf_size_min_addr = buf_size_min + ISO15693_3_UID_SIZE; + + if(buf_size < buf_size_min_addr) { + error = Iso15693_3ErrorFormat; + break; + } else if(!iso15693_3_is_equal_uid(instance->data, request->data)) { + error = Iso15693_3ErrorUidMismatch; + break; + } + + data = &request->data[ISO15693_3_UID_SIZE]; + data_size = buf_size - buf_size_min_addr; + + } else { + data = request->data; + data_size = buf_size - buf_size_min; + } + + error = iso15693_3_listener_handle_standard_request( + instance, data, data_size, request->command, request->flags); + + } while(false); + + return error; +} + +Iso15693_3Error iso15693_3_listener_process_single_eof(Iso15693_3Listener* instance) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + if(!instance->session_state.wait_for_eof) { + error = Iso15693_3ErrorUnexpectedResponse; + break; + } + + instance->session_state.wait_for_eof = false; + + error = iso15693_3_listener_send_frame(instance, instance->tx_buffer); + } while(false); + + return error; +} + +Iso15693_3Error iso15693_3_listener_process_uid_mismatch( + Iso15693_3Listener* instance, + const BitBuffer* rx_buffer) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + // No checks, assuming they have been made beforehand + typedef struct { + uint8_t flags; + uint8_t command; + } Iso15693_3RequestLayout; + + const Iso15693_3RequestLayout* request = + (const Iso15693_3RequestLayout*)bit_buffer_get_data(rx_buffer); + + if(request->command == ISO15693_3_CMD_SELECT) { + if(instance->state == Iso15693_3ListenerStateSelected) { + error = iso15693_3_listener_ready(instance); + } + } + + return error; +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.h b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.h new file mode 100644 index 000000000000..a9e0822bff9a --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include "iso15693_3_listener.h" + +#include "iso15693_3_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Iso15693_3ListenerStateReady, + Iso15693_3ListenerStateSelected, + Iso15693_3ListenerStateQuiet, +} Iso15693_3ListenerState; + +typedef struct { + bool selected; + bool addressed; + bool wait_for_eof; +} Iso15693_3ListenerSessionState; + +typedef Iso15693_3Error (*Iso15693_3ExtensionHandler)(void* context, va_list args); + +typedef struct { + Iso15693_3ExtensionHandler mandatory[ISO15693_3_MANDATORY_COUNT]; + Iso15693_3ExtensionHandler optional[ISO15693_3_OPTIONAL_COUNT]; +} Iso15693_3ExtensionHandlerTable; + +struct Iso15693_3Listener { + Nfc* nfc; + Iso15693_3Data* data; + Iso15693_3ListenerState state; + Iso15693_3ListenerSessionState session_state; + BitBuffer* tx_buffer; + + NfcGenericEvent generic_event; + Iso15693_3ListenerEvent iso15693_3_event; + Iso15693_3ListenerEventData iso15693_3_event_data; + NfcGenericCallback callback; + void* context; + + const Iso15693_3ExtensionHandlerTable* extension_table; + void* extension_context; +}; + +Iso15693_3Error iso15693_3_listener_set_extension_handler_table( + Iso15693_3Listener* instance, + const Iso15693_3ExtensionHandlerTable* table, + void* context); + +Iso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance); + +Iso15693_3Error + iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer); + +Iso15693_3Error + iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer); + +Iso15693_3Error iso15693_3_listener_process_single_eof(Iso15693_3Listener* instance); + +Iso15693_3Error iso15693_3_listener_process_uid_mismatch( + Iso15693_3Listener* instance, + const BitBuffer* rx_buffer); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller.c b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.c new file mode 100644 index 000000000000..3447e2f0fb89 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.c @@ -0,0 +1,122 @@ +#include "iso15693_3_poller_i.h" + +#include + +#include + +#define TAG "ISO15693_3Poller" + +const Iso15693_3Data* iso15693_3_poller_get_data(Iso15693_3Poller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static Iso15693_3Poller* iso15693_3_poller_alloc(Nfc* nfc) { + furi_assert(nfc); + + Iso15693_3Poller* instance = malloc(sizeof(Iso15693_3Poller)); + instance->nfc = nfc; + instance->tx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE); + instance->rx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE); + + nfc_config(instance->nfc, NfcModePoller, NfcTechIso15693); + nfc_set_guard_time_us(instance->nfc, ISO15693_3_GUARD_TIME_US); + nfc_set_fdt_poll_fc(instance->nfc, ISO15693_3_FDT_POLL_FC); + nfc_set_fdt_poll_poll_us(instance->nfc, ISO15693_3_POLL_POLL_MIN_US); + instance->data = iso15693_3_alloc(); + + instance->iso15693_3_event.data = &instance->iso15693_3_event_data; + instance->general_event.protocol = NfcProtocolIso15693_3; + instance->general_event.data = &instance->iso15693_3_event; + instance->general_event.instance = instance; + + return instance; +} + +static void iso15693_3_poller_free(Iso15693_3Poller* instance) { + furi_assert(instance); + + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(instance->data); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + iso15693_3_free(instance->data); + free(instance); +} + +static void iso15693_3_poller_set_callback( + Iso15693_3Poller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand iso15693_3_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.data); + + Iso15693_3Poller* instance = context; + NfcEvent* nfc_event = event.data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypePollerReady) { + if(instance->state != Iso15693_3PollerStateActivated) { + Iso15693_3Error error = iso15693_3_poller_async_activate(instance, instance->data); + if(error == Iso15693_3ErrorNone) { + instance->iso15693_3_event.type = Iso15693_3PollerEventTypeReady; + instance->iso15693_3_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + } else { + instance->iso15693_3_event.type = Iso15693_3PollerEventTypeError; + instance->iso15693_3_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + // Add delay to switch context + furi_delay_ms(100); + } + } else { + instance->iso15693_3_event.type = Iso15693_3PollerEventTypeReady; + instance->iso15693_3_event_data.error = Iso15693_3ErrorNone; + command = instance->callback(instance->general_event, instance->context); + } + } + + return command; +} + +static bool iso15693_3_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.instance); + furi_assert(event.protocol = NfcProtocolInvalid); + + bool protocol_detected = false; + Iso15693_3Poller* instance = context; + NfcEvent* nfc_event = event.data; + furi_assert(instance->state == Iso15693_3PollerStateIdle); + + if(nfc_event->type == NfcEventTypePollerReady) { + uint8_t uid[ISO15693_3_UID_SIZE]; + Iso15693_3Error error = iso15693_3_poller_async_inventory(instance, uid); + protocol_detected = (error == Iso15693_3ErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_iso15693_3 = { + .alloc = (NfcPollerAlloc)iso15693_3_poller_alloc, + .free = (NfcPollerFree)iso15693_3_poller_free, + .set_callback = (NfcPollerSetCallback)iso15693_3_poller_set_callback, + .run = (NfcPollerRun)iso15693_3_poller_run, + .detect = (NfcPollerDetect)iso15693_3_poller_detect, + .get_data = (NfcPollerGetData)iso15693_3_poller_get_data, +}; diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller.h b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.h new file mode 100644 index 000000000000..9d73242f1ad5 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.h @@ -0,0 +1,29 @@ +#pragma once + +#include "iso15693_3.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso15693_3Poller Iso15693_3Poller; + +typedef enum { + Iso15693_3PollerEventTypeError, + Iso15693_3PollerEventTypeReady, +} Iso15693_3PollerEventType; + +typedef struct { + Iso15693_3Error error; +} Iso15693_3PollerEventData; + +typedef struct { + Iso15693_3PollerEventType type; + Iso15693_3PollerEventData* data; +} Iso15693_3PollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_defs.h b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_defs.h new file mode 100644 index 000000000000..0e38908dab1b --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase nfc_poller_iso15693_3; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c new file mode 100644 index 000000000000..8b8d8cee04b9 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c @@ -0,0 +1,303 @@ +#include "iso15693_3_poller_i.h" + +#include + +#define TAG "Iso15693_3Poller" + +#define BITS_IN_BYTE (8) + +#define ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY (32U) + +static Iso15693_3Error iso15693_3_poller_process_nfc_error(NfcError error) { + switch(error) { + case NfcErrorNone: + return Iso15693_3ErrorNone; + case NfcErrorTimeout: + return Iso15693_3ErrorTimeout; + default: + return Iso15693_3ErrorNotPresent; + } +} + +static Iso15693_3Error iso15693_3_poller_filter_error(Iso15693_3Error error) { + switch(error) { + /* If a particular optional command is not supported, the card might + * respond with a "Not supported" error or not respond at all. + * Therefore, treat these errors as non-critical ones. */ + case Iso15693_3ErrorNotSupported: + case Iso15693_3ErrorTimeout: + return Iso15693_3ErrorNone; + default: + return error; + } +} + +static Iso15693_3Error iso15693_3_poller_prepare_trx(Iso15693_3Poller* instance) { + furi_assert(instance); + + if(instance->state == Iso15693_3PollerStateIdle) { + return iso15693_3_poller_async_activate(instance, NULL); + } + + return Iso15693_3ErrorNone; +} + +static Iso15693_3Error iso15693_3_poller_frame_exchange( + Iso15693_3Poller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + do { + if(bit_buffer_get_size_bytes(tx_buffer) > + bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO13239_CRC_SIZE) { + ret = Iso15693_3ErrorBufferOverflow; + break; + } + + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer); + + NfcError error = + nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = iso15693_3_poller_process_nfc_error(error); + break; + } + + if(!iso13239_crc_check(Iso13239CrcTypeDefault, instance->rx_buffer)) { + ret = Iso15693_3ErrorWrongCrc; + break; + } + + iso13239_crc_trim(instance->rx_buffer); + bit_buffer_copy(rx_buffer, instance->rx_buffer); + } while(false); + + return ret; +} + +Iso15693_3Error + iso15693_3_poller_async_activate(Iso15693_3Poller* instance, Iso15693_3Data* data) { + furi_assert(instance); + furi_assert(instance->nfc); + + iso15693_3_reset(data); + + Iso15693_3Error ret; + + do { + instance->state = Iso15693_3PollerStateColResInProgress; + + // Inventory: Mandatory command + ret = iso15693_3_poller_async_inventory(instance, data->uid); + if(ret != Iso15693_3ErrorNone) { + instance->state = Iso15693_3PollerStateColResFailed; + break; + } + + instance->state = Iso15693_3PollerStateActivated; + + // Get system info: Optional command + Iso15693_3SystemInfo* system_info = &data->system_info; + ret = iso15693_3_poller_async_get_system_info(instance, system_info); + if(ret != Iso15693_3ErrorNone) { + ret = iso15693_3_poller_filter_error(ret); + break; + } + + // Read blocks: Optional command + simple_array_init(data->block_data, system_info->block_count * system_info->block_size); + ret = iso15693_3_poller_async_read_blocks( + instance, + simple_array_get_data(data->block_data), + system_info->block_count, + system_info->block_size); + if(ret != Iso15693_3ErrorNone) { + ret = iso15693_3_poller_filter_error(ret); + break; + } + + // Get block security status: Optional command + simple_array_init(data->block_security, system_info->block_count); + + ret = iso15693_3_poller_async_get_blocks_security( + instance, simple_array_get_data(data->block_security), system_info->block_count); + if(ret != Iso15693_3ErrorNone) { + ret = iso15693_3_poller_filter_error(ret); + break; + } + + } while(false); + + return ret; +} + +Iso15693_3Error iso15693_3_poller_async_inventory(Iso15693_3Poller* instance, uint8_t* uid) { + furi_assert(instance); + furi_assert(instance->nfc); + furi_assert(uid); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send INVENTORY + bit_buffer_append_byte( + instance->tx_buffer, + ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI | + ISO15693_3_REQ_FLAG_INVENTORY_T5 | ISO15693_3_REQ_FLAG_T5_N_SLOTS_1); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_INVENTORY); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + + Iso15693_3Error ret; + + do { + ret = iso15693_3_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); + if(ret != Iso15693_3ErrorNone) break; + + ret = iso15693_3_inventory_response_parse(uid, instance->rx_buffer); + } while(false); + + return ret; +} + +Iso15693_3Error iso15693_3_poller_async_get_system_info( + Iso15693_3Poller* instance, + Iso15693_3SystemInfo* data) { + furi_assert(instance); + furi_assert(data); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send GET SYSTEM INFO + bit_buffer_append_byte( + instance->tx_buffer, ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI); + + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_GET_SYS_INFO); + + Iso15693_3Error ret; + + do { + ret = iso15693_3_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); + if(ret != Iso15693_3ErrorNone) break; + + ret = iso15693_3_system_info_response_parse(data, instance->rx_buffer); + } while(false); + + return ret; +} + +Iso15693_3Error iso15693_3_poller_async_read_block( + Iso15693_3Poller* instance, + uint8_t* data, + uint8_t block_number, + uint8_t block_size) { + furi_assert(instance); + furi_assert(data); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_append_byte( + instance->tx_buffer, ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_READ_BLOCK); + bit_buffer_append_byte(instance->tx_buffer, block_number); + + Iso15693_3Error ret; + + do { + ret = iso15693_3_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); + if(ret != Iso15693_3ErrorNone) break; + + ret = iso15693_3_read_block_response_parse(data, block_size, instance->rx_buffer); + } while(false); + + return ret; +} + +Iso15693_3Error iso15693_3_poller_async_read_blocks( + Iso15693_3Poller* instance, + uint8_t* data, + uint16_t block_count, + uint8_t block_size) { + furi_assert(instance); + furi_assert(data); + furi_assert(block_count); + furi_assert(block_size); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + for(uint32_t i = 0; i < block_count; ++i) { + ret = iso15693_3_poller_async_read_block(instance, &data[block_size * i], i, block_size); + if(ret != Iso15693_3ErrorNone) break; + } + + return ret; +} + +Iso15693_3Error iso15693_3_poller_async_get_blocks_security( + Iso15693_3Poller* instance, + uint8_t* data, + uint16_t block_count) { + furi_assert(instance); + furi_assert(data); + + // Limit the number of blocks to 32 in a single query + const uint32_t num_queries = block_count / ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY + + (block_count % ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY ? 1 : 0); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + for(uint32_t i = 0; i < num_queries; ++i) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_append_byte( + instance->tx_buffer, + ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI); + + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_GET_BLOCKS_SECURITY); + + const uint8_t start_block_num = i * ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY; + bit_buffer_append_byte(instance->tx_buffer, start_block_num); + + const uint8_t block_count_per_query = + MIN(block_count - start_block_num, (uint16_t)ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY); + // Block count byte must be 1 less than the desired count + bit_buffer_append_byte(instance->tx_buffer, block_count_per_query - 1); + + ret = iso15693_3_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); + if(ret != Iso15693_3ErrorNone) break; + + ret = iso15693_3_get_block_security_response_parse( + &data[start_block_num], block_count_per_query, instance->rx_buffer); + if(ret != Iso15693_3ErrorNone) break; + } + + return ret; +} + +Iso15693_3Error iso15693_3_poller_send_frame( + Iso15693_3Poller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + Iso15693_3Error ret; + + do { + ret = iso15693_3_poller_prepare_trx(instance); + if(ret != Iso15693_3ErrorNone) break; + + ret = iso15693_3_poller_frame_exchange(instance, tx_buffer, rx_buffer, fwt); + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.h b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.h new file mode 100644 index 000000000000..154ee684c97c --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.h @@ -0,0 +1,70 @@ +#pragma once + +#include "iso15693_3_poller.h" + +#include "iso15693_3_i.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO15693_3_POLLER_MAX_BUFFER_SIZE (64U) + +typedef enum { + Iso15693_3PollerStateIdle, + Iso15693_3PollerStateColResInProgress, + Iso15693_3PollerStateColResFailed, + Iso15693_3PollerStateActivated, +} Iso15693_3PollerState; + +struct Iso15693_3Poller { + Nfc* nfc; + Iso15693_3PollerState state; + Iso15693_3Data* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + NfcGenericEvent general_event; + Iso15693_3PollerEvent iso15693_3_event; + Iso15693_3PollerEventData iso15693_3_event_data; + NfcGenericCallback callback; + void* context; +}; + +const Iso15693_3Data* iso15693_3_poller_get_data(Iso15693_3Poller* instance); + +Iso15693_3Error iso15693_3_poller_async_activate(Iso15693_3Poller* instance, Iso15693_3Data* data); + +Iso15693_3Error iso15693_3_poller_async_inventory(Iso15693_3Poller* instance, uint8_t* uid); + +Iso15693_3Error + iso15693_3_poller_async_get_system_info(Iso15693_3Poller* instance, Iso15693_3SystemInfo* data); + +Iso15693_3Error iso15693_3_poller_async_read_block( + Iso15693_3Poller* instance, + uint8_t* data, + uint8_t block_number, + uint8_t block_size); + +Iso15693_3Error iso15693_3_poller_async_read_blocks( + Iso15693_3Poller* instance, + uint8_t* data, + uint16_t block_count, + uint8_t block_size); + +Iso15693_3Error iso15693_3_poller_async_get_blocks_security( + Iso15693_3Poller* instance, + uint8_t* data, + uint16_t block_count); + +Iso15693_3Error iso15693_3_poller_send_frame( + Iso15693_3Poller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/crypto1.c b/lib/nfc/protocols/mf_classic/crypto1.c new file mode 100644 index 000000000000..70f897254489 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/crypto1.c @@ -0,0 +1,172 @@ +#include "crypto1.h" + +#include +#include + +// Algorithm from https://github.com/RfidResearchGroup/proxmark3.git + +#define SWAPENDIAN(x) \ + ((x) = ((x) >> 8 & 0xff00ff) | ((x)&0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16) +#define LF_POLY_ODD (0x29CE5C) +#define LF_POLY_EVEN (0x870804) + +#define BEBIT(x, n) FURI_BIT(x, (n) ^ 24) + +Crypto1* crypto1_alloc() { + Crypto1* instance = malloc(sizeof(Crypto1)); + + return instance; +} + +void crypto1_free(Crypto1* instance) { + furi_assert(instance); + + free(instance); +} + +void crypto1_reset(Crypto1* crypto1) { + furi_assert(crypto1); + crypto1->even = 0; + crypto1->odd = 0; +} + +void crypto1_init(Crypto1* crypto1, uint64_t key) { + furi_assert(crypto1); + crypto1->even = 0; + crypto1->odd = 0; + for(int8_t i = 47; i > 0; i -= 2) { + crypto1->odd = crypto1->odd << 1 | FURI_BIT(key, (i - 1) ^ 7); + crypto1->even = crypto1->even << 1 | FURI_BIT(key, i ^ 7); + } +} + +static uint32_t crypto1_filter(uint32_t in) { + uint32_t out = 0; + out = 0xf22c0 >> (in & 0xf) & 16; + out |= 0x6c9c0 >> (in >> 4 & 0xf) & 8; + out |= 0x3c8b0 >> (in >> 8 & 0xf) & 4; + out |= 0x1e458 >> (in >> 12 & 0xf) & 2; + out |= 0x0d938 >> (in >> 16 & 0xf) & 1; + return FURI_BIT(0xEC57E80A, out); +} + +uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) { + furi_assert(crypto1); + uint8_t out = crypto1_filter(crypto1->odd); + uint32_t feed = out & (!!is_encrypted); + feed ^= !!in; + feed ^= LF_POLY_ODD & crypto1->odd; + feed ^= LF_POLY_EVEN & crypto1->even; + crypto1->even = crypto1->even << 1 | (nfc_util_even_parity32(feed)); + + FURI_SWAP(crypto1->odd, crypto1->even); + return out; +} + +uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) { + furi_assert(crypto1); + uint8_t out = 0; + for(uint8_t i = 0; i < 8; i++) { + out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i; + } + return out; +} + +uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) { + furi_assert(crypto1); + uint32_t out = 0; + for(uint8_t i = 0; i < 32; i++) { + out |= crypto1_bit(crypto1, BEBIT(in, i), is_encrypted) << (24 ^ i); + } + return out; +} + +uint32_t prng_successor(uint32_t x, uint32_t n) { + SWAPENDIAN(x); + while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; + + return SWAPENDIAN(x); +} + +void crypto1_decrypt(Crypto1* crypto, const BitBuffer* buff, BitBuffer* out) { + furi_assert(crypto); + furi_assert(buff); + furi_assert(out); + + size_t bits = bit_buffer_get_size(buff); + bit_buffer_set_size(out, bits); + const uint8_t* encrypted_data = bit_buffer_get_data(buff); + if(bits < 8) { + uint8_t decrypted_byte = 0; + uint8_t encrypted_byte = encrypted_data[0]; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 0)) << 0; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 1)) << 1; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 2)) << 2; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 3)) << 3; + bit_buffer_set_byte(out, 0, decrypted_byte); + } else { + for(size_t i = 0; i < bits / 8; i++) { + uint8_t decrypted_byte = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; + bit_buffer_set_byte(out, i, decrypted_byte); + } + } +} + +void crypto1_encrypt(Crypto1* crypto, uint8_t* keystream, const BitBuffer* buff, BitBuffer* out) { + furi_assert(crypto); + furi_assert(buff); + furi_assert(out); + + size_t bits = bit_buffer_get_size(buff); + bit_buffer_set_size(out, bits); + const uint8_t* plain_data = bit_buffer_get_data(buff); + if(bits < 8) { + uint8_t encrypted_byte = 0; + for(size_t i = 0; i < bits; i++) { + encrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; + } + bit_buffer_set_byte(out, 0, encrypted_byte); + } else { + for(size_t i = 0; i < bits / 8; i++) { + uint8_t encrypted_byte = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ + plain_data[i]; + bool parity_bit = + ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01); + bit_buffer_set_byte_with_parity(out, i, encrypted_byte, parity_bit); + } + } +} + +void crypto1_encrypt_reader_nonce( + Crypto1* crypto, + uint64_t key, + uint32_t cuid, + uint8_t* nt, + uint8_t* nr, + BitBuffer* out) { + furi_assert(crypto); + furi_assert(nt); + furi_assert(nr); + furi_assert(out); + + bit_buffer_set_size_bytes(out, 8); + uint32_t nt_num = nfc_util_bytes2num(nt, sizeof(uint32_t)); + + crypto1_init(crypto, key); + crypto1_word(crypto, nt_num ^ cuid, 0); + + for(size_t i = 0; i < 4; i++) { + uint8_t byte = crypto1_byte(crypto, nr[i], 0) ^ nr[i]; + bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nr[i])) & 0x01); + bit_buffer_set_byte_with_parity(out, i, byte, parity_bit); + nr[i] = byte; + } + + nt_num = prng_successor(nt_num, 32); + for(size_t i = 4; i < 8; i++) { + nt_num = prng_successor(nt_num, 8); + uint8_t byte = crypto1_byte(crypto, 0, 0) ^ (uint8_t)(nt_num); + bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt_num)) & 0x01); + bit_buffer_set_byte_with_parity(out, i, byte, parity_bit); + } +} diff --git a/lib/nfc/protocols/mf_classic/crypto1.h b/lib/nfc/protocols/mf_classic/crypto1.h new file mode 100644 index 000000000000..f2bdb272b0e6 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/crypto1.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t odd; + uint32_t even; +} Crypto1; + +Crypto1* crypto1_alloc(); + +void crypto1_free(Crypto1* instance); + +void crypto1_reset(Crypto1* crypto1); + +void crypto1_init(Crypto1* crypto1, uint64_t key); + +uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted); + +uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted); + +uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted); + +void crypto1_decrypt(Crypto1* crypto, const BitBuffer* buff, BitBuffer* out); + +void crypto1_encrypt(Crypto1* crypto, uint8_t* keystream, const BitBuffer* buff, BitBuffer* out); + +void crypto1_encrypt_reader_nonce( + Crypto1* crypto, + uint64_t key, + uint32_t cuid, + uint8_t* nt, + uint8_t* nr, + BitBuffer* out); + +uint32_t prng_successor(uint32_t x, uint32_t n); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic.c b/lib/nfc/protocols/mf_classic/mf_classic.c new file mode 100644 index 000000000000..5724d8aea8bd --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic.c @@ -0,0 +1,777 @@ +#include "mf_classic.h" + +#include +#include + +#include + +#define MF_CLASSIC_PROTOCOL_NAME "Mifare Classic" + +typedef struct { + uint8_t sectors_total; + uint16_t blocks_total; + const char* full_name; + const char* type_name; +} MfClassicFeatures; + +static const uint32_t mf_classic_data_format_version = 2; + +static const MfClassicFeatures mf_classic_features[MfClassicTypeNum] = { + [MfClassicTypeMini] = + { + .sectors_total = 5, + .blocks_total = 20, + .full_name = "Mifare Classic Mini 0.3K", + .type_name = "MINI", + }, + [MfClassicType1k] = + { + .sectors_total = 16, + .blocks_total = 64, + .full_name = "Mifare Classic 1K", + .type_name = "1K", + }, + [MfClassicType4k] = + { + .sectors_total = 40, + .blocks_total = 256, + .full_name = "Mifare Classic 4K", + .type_name = "4K", + }, +}; + +const NfcDeviceBase nfc_device_mf_classic = { + .protocol_name = MF_CLASSIC_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)mf_classic_alloc, + .free = (NfcDeviceFree)mf_classic_free, + .reset = (NfcDeviceReset)mf_classic_reset, + .copy = (NfcDeviceCopy)mf_classic_copy, + .verify = (NfcDeviceVerify)mf_classic_verify, + .load = (NfcDeviceLoad)mf_classic_load, + .save = (NfcDeviceSave)mf_classic_save, + .is_equal = (NfcDeviceEqual)mf_classic_is_equal, + .get_name = (NfcDeviceGetName)mf_classic_get_device_name, + .get_uid = (NfcDeviceGetUid)mf_classic_get_uid, + .set_uid = (NfcDeviceSetUid)mf_classic_set_uid, + .get_base_data = (NfcDeviceGetBaseData)mf_classic_get_base_data, +}; + +MfClassicData* mf_classic_alloc() { + MfClassicData* data = malloc(sizeof(MfClassicData)); + data->iso14443_3a_data = iso14443_3a_alloc(); + return data; +} + +void mf_classic_free(MfClassicData* data) { + furi_assert(data); + + iso14443_3a_free(data->iso14443_3a_data); + free(data); +} + +void mf_classic_reset(MfClassicData* data) { + furi_assert(data); + + iso14443_3a_reset(data->iso14443_3a_data); +} + +void mf_classic_copy(MfClassicData* data, const MfClassicData* other) { + furi_assert(data); + furi_assert(other); + + iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data); + for(size_t i = 0; i < COUNT_OF(data->block); i++) { + data->block[i] = other->block[i]; + } + for(size_t i = 0; i < COUNT_OF(data->block_read_mask); i++) { + data->block_read_mask[i] = other->block_read_mask[i]; + } + data->type = other->type; + data->key_a_mask = other->key_a_mask; + data->key_b_mask = other->key_b_mask; +} + +bool mf_classic_verify(MfClassicData* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal_str(device_type, "Mifare Classic"); +} + +static void mf_classic_parse_block(FuriString* block_str, MfClassicData* data, uint8_t block_num) { + furi_string_trim(block_str); + MfClassicBlock block_tmp = {}; + bool is_sector_trailer = mf_classic_is_sector_trailer(block_num); + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + uint16_t block_unknown_bytes_mask = 0; + + furi_string_trim(block_str); + for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { + char hi = furi_string_get_char(block_str, 3 * i); + char low = furi_string_get_char(block_str, 3 * i + 1); + uint8_t byte = 0; + if(hex_char_to_uint8(hi, low, &byte)) { + block_tmp.data[i] = byte; + } else { + FURI_BIT_SET(block_unknown_bytes_mask, i); + } + } + + if(block_unknown_bytes_mask != 0xffff) { + if(is_sector_trailer) { + MfClassicSectorTrailer* sec_tr_tmp = (MfClassicSectorTrailer*)&block_tmp; + // Load Key A + // Key A mask 0b0000000000111111 = 0x003f + if((block_unknown_bytes_mask & 0x003f) == 0) { + uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_a.data, sizeof(MfClassicKey)); + mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeA, key); + } + // Load Access Bits + // Access bits mask 0b0000001111000000 = 0x03c0 + if((block_unknown_bytes_mask & 0x03c0) == 0) { + mf_classic_set_block_read(data, block_num, &block_tmp); + } + // Load Key B + // Key B mask 0b1111110000000000 = 0xfc00 + if((block_unknown_bytes_mask & 0xfc00) == 0) { + uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_b.data, sizeof(MfClassicKey)); + mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeB, key); + } + } else { + if(block_unknown_bytes_mask == 0) { + mf_classic_set_block_read(data, block_num, &block_tmp); + } + } + } +} + +bool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + FuriString* temp_str = furi_string_alloc(); + bool parsed = false; + + do { + // Read ISO14443_3A data + if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break; + + // Read Mifare Classic type + if(!flipper_format_read_string(ff, "Mifare Classic type", temp_str)) break; + bool type_parsed = false; + for(size_t i = 0; i < MfClassicTypeNum; i++) { + if(furi_string_equal_str(temp_str, mf_classic_features[i].type_name)) { + data->type = i; + type_parsed = true; + } + } + if(!type_parsed) break; + + // Read format version + uint32_t data_format_version = 0; + bool old_format = false; + // Read Mifare Classic format version + if(!flipper_format_read_uint32(ff, "Data format version", &data_format_version, 1)) { + // Load unread sectors with zero keys access for backward compatibility + if(!flipper_format_rewind(ff)) break; + old_format = true; + } else { + if(data_format_version < mf_classic_data_format_version) { + old_format = true; + } + } + + // Read Mifare Classic blocks + bool block_read = true; + FuriString* block_str = furi_string_alloc(); + uint16_t blocks_total = mf_classic_get_total_block_num(data->type); + for(size_t i = 0; i < blocks_total; i++) { + furi_string_printf(temp_str, "Block %d", i); + if(!flipper_format_read_string(ff, furi_string_get_cstr(temp_str), block_str)) { + block_read = false; + break; + } + mf_classic_parse_block(block_str, data, i); + } + furi_string_free(block_str); + if(!block_read) break; + + // Set keys and blocks as unknown for backward compatibility + if(old_format) { + data->key_a_mask = 0ULL; + data->key_b_mask = 0ULL; + memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); + } + + parsed = true; + } while(false); + + furi_string_free(temp_str); + + return parsed; +} + +static void + mf_classic_set_block_str(FuriString* block_str, const MfClassicData* data, uint8_t block_num) { + furi_string_reset(block_str); + bool is_sec_trailer = mf_classic_is_sector_trailer(block_num); + if(is_sec_trailer) { + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num); + // Write key A + for(size_t i = 0; i < sizeof(sec_tr->key_a); i++) { + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeA)) { + furi_string_cat_printf(block_str, "%02X ", sec_tr->key_a.data[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + // Write Access bytes + for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i++) { + if(mf_classic_is_block_read(data, block_num)) { + furi_string_cat_printf(block_str, "%02X ", sec_tr->access_bits.data[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + // Write key B + for(size_t i = 0; i < sizeof(sec_tr->key_b); i++) { + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeB)) { + furi_string_cat_printf(block_str, "%02X ", sec_tr->key_b.data[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + } else { + // Write data block + for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { + if(mf_classic_is_block_read(data, block_num)) { + furi_string_cat_printf(block_str, "%02X ", data->block[block_num].data[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + } + furi_string_trim(block_str); +} + +bool mf_classic_save(const MfClassicData* data, FlipperFormat* ff) { + furi_assert(data); + + FuriString* temp_str = furi_string_alloc(); + bool saved = false; + + do { + if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break; + + if(!flipper_format_write_comment_cstr(ff, "Mifare Classic specific data")) break; + if(!flipper_format_write_string_cstr( + ff, "Mifare Classic type", mf_classic_features[data->type].type_name)) + break; + if(!flipper_format_write_uint32( + ff, "Data format version", &mf_classic_data_format_version, 1)) + break; + if(!flipper_format_write_comment_cstr( + ff, "Mifare Classic blocks, \'??\' means unknown data")) + break; + + uint16_t blocks_total = mf_classic_get_total_block_num(data->type); + FuriString* block_str = furi_string_alloc(); + bool block_saved = true; + for(size_t i = 0; i < blocks_total; i++) { + furi_string_printf(temp_str, "Block %d", i); + mf_classic_set_block_str(block_str, data, i); + if(!flipper_format_write_string(ff, furi_string_get_cstr(temp_str), block_str)) { + block_saved = false; + break; + } + } + furi_string_free(block_str); + if(!block_saved) break; + + saved = true; + } while(false); + + furi_string_free(temp_str); + + return saved; +} + +bool mf_classic_is_equal(const MfClassicData* data, const MfClassicData* other) { + bool is_equal = false; + bool data_array_is_equal = true; + + do { + if(!iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data)) break; + if(data->type != other->type) break; + if(data->key_a_mask != other->key_a_mask) break; + if(data->key_b_mask != other->key_b_mask) break; + + for(size_t i = 0; i < COUNT_OF(data->block_read_mask); i++) { + if(data->block_read_mask[i] != other->block_read_mask[i]) { + data_array_is_equal = false; + break; + } + } + if(!data_array_is_equal) break; + + for(size_t i = 0; i < COUNT_OF(data->block); i++) { + if(memcmp(&data->block[i], &other->block[i], sizeof(data->block[i]))) { + data_array_is_equal = false; + break; + } + } + if(!data_array_is_equal) break; + + is_equal = true; + } while(false); + + return is_equal; +} + +const char* mf_classic_get_device_name(const MfClassicData* data, NfcDeviceNameType name_type) { + furi_assert(data); + furi_assert(data->type < MfClassicTypeNum); + + if(name_type == NfcDeviceNameTypeFull) { + return mf_classic_features[data->type].full_name; + } else { + return mf_classic_features[data->type].type_name; + } +} + +const uint8_t* mf_classic_get_uid(const MfClassicData* data, size_t* uid_len) { + furi_assert(data); + + return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len); +} + +bool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len); +} + +Iso14443_3aData* mf_classic_get_base_data(const MfClassicData* data) { + furi_assert(data); + + return data->iso14443_3a_data; +} + +uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { + return mf_classic_features[type].sectors_total; +} + +uint16_t mf_classic_get_total_block_num(MfClassicType type) { + return mf_classic_features[type].blocks_total; +} + +bool mf_classic_detect_protocol(Iso14443_3aData* data, MfClassicType* type) { + furi_assert(data); + + uint8_t atqa0 = data->atqa[0]; + uint8_t atqa1 = data->atqa[1]; + uint8_t sak = data->sak; + bool mf_classic_detected = false; + MfClassicType tmp_type = MfClassicTypeMini; + + if((atqa0 == 0x44) || (atqa0 == 0x04)) { + if((sak == 0x08) || (sak == 0x88)) { + tmp_type = MfClassicType1k; + mf_classic_detected = true; + } else if(sak == 0x09) { + tmp_type = MfClassicTypeMini; + mf_classic_detected = true; + } + } else if((atqa0 == 0x01) && (atqa1 == 0x0f) && (sak == 0x01)) { + // Skylender support + tmp_type = MfClassicType1k; + mf_classic_detected = true; + } else if(((atqa0 == 0x42) || (atqa0 == 0x02)) && (sak == 0x18)) { + tmp_type = MfClassicType4k; + mf_classic_detected = true; + } + + if(type) { + *type = tmp_type; + } + + return mf_classic_detected; +} + +uint8_t mf_classic_get_sector_trailer_num_by_sector(uint8_t sector) { + uint8_t block_num = 0; + + if(sector < 32) { + block_num = sector * 4 + 3; + } else if(sector < 40) { + block_num = 32 * 4 + (sector - 32) * 16 + 15; + } else { + furi_crash("Wrong sector num"); + } + + return block_num; +} + +uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) { + uint8_t sec_tr_block_num = 0; + + if(block < 128) { + sec_tr_block_num = block | 0x03; + } else { + sec_tr_block_num = block | 0x0f; + } + + return sec_tr_block_num; +} + +MfClassicSectorTrailer* + mf_classic_get_sector_trailer_by_sector(const MfClassicData* data, uint8_t sector_num) { + furi_assert(data); + + uint8_t sec_tr_block = mf_classic_get_sector_trailer_num_by_sector(sector_num); + MfClassicSectorTrailer* sec_trailer = (MfClassicSectorTrailer*)&data->block[sec_tr_block]; + + return sec_trailer; +} + +bool mf_classic_is_sector_trailer(uint8_t block) { + return block == mf_classic_get_sector_trailer_num_by_block(block); +} + +uint8_t mf_classic_get_sector_by_block(uint8_t block) { + uint8_t sector = 0; + + if(block < 128) { + sector = (block | 0x03) / 4; + } else { + sector = 32 + ((block | 0x0f) - 32 * 4) / 16; + } + + return sector; +} + +bool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint8_t* addr) { + furi_assert(block); + furi_assert(value); + + uint32_t v = *(uint32_t*)&block->data[0]; + uint32_t v_inv = *(uint32_t*)&block->data[4]; + uint32_t v1 = *(uint32_t*)&block->data[8]; + + bool val_checks = + ((v == v1) && (v == ~v_inv) && (block->data[12] == (~block->data[13] & 0xFF)) && + (block->data[14] == (~block->data[15] & 0xFF)) && (block->data[12] == block->data[14])); + if(value) { + *value = (int32_t)v; + } + if(addr) { + *addr = block->data[12]; + } + return val_checks; +} + +void mf_classic_value_to_block(int32_t value, uint8_t addr, MfClassicBlock* block) { + furi_assert(block); + + uint32_t v_inv = ~((uint32_t)value); + + memcpy(&block->data[0], &value, 4); //-V1086 + memcpy(&block->data[4], &v_inv, 4); //-V1086 + memcpy(&block->data[8], &value, 4); //-V1086 + + block->data[12] = addr; + block->data[13] = ~addr & 0xFF; + block->data[14] = addr; + block->data[15] = ~addr & 0xFF; +} + +bool mf_classic_is_key_found( + const MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type) { + furi_assert(data); + + bool key_found = false; + if(key_type == MfClassicKeyTypeA) { + key_found = (FURI_BIT(data->key_a_mask, sector_num) == 1); + } else if(key_type == MfClassicKeyTypeB) { + key_found = (FURI_BIT(data->key_b_mask, sector_num) == 1); + } + + return key_found; +} + +void mf_classic_set_key_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type, + uint64_t key) { + furi_assert(data); + + uint8_t key_arr[6] = {}; + MfClassicSectorTrailer* sec_trailer = + mf_classic_get_sector_trailer_by_sector(data, sector_num); + nfc_util_num2bytes(key, 6, key_arr); + if(key_type == MfClassicKeyTypeA) { + memcpy(sec_trailer->key_a.data, key_arr, sizeof(MfClassicKey)); + FURI_BIT_SET(data->key_a_mask, sector_num); + } else if(key_type == MfClassicKeyTypeB) { + memcpy(sec_trailer->key_b.data, key_arr, sizeof(MfClassicKey)); + FURI_BIT_SET(data->key_b_mask, sector_num); + } +} + +void mf_classic_set_key_not_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type) { + furi_assert(data); + + if(key_type == MfClassicKeyTypeA) { + FURI_BIT_CLEAR(data->key_a_mask, sector_num); + } else if(key_type == MfClassicKeyTypeB) { + FURI_BIT_CLEAR(data->key_b_mask, sector_num); + } +} + +bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num) { + furi_assert(data); + + return (FURI_BIT(data->block_read_mask[block_num / 32], block_num % 32) == 1); +} + +void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data) { + furi_assert(data); + + if(mf_classic_is_sector_trailer(block_num)) { + memcpy(&data->block[block_num].data[6], &block_data->data[6], 4); + } else { + memcpy(data->block[block_num].data, block_data->data, MF_CLASSIC_BLOCK_SIZE); + } + FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32); +} + +uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) { + furi_assert(sector < 40); + + uint8_t block = 0; + if(sector < 32) { + block = sector * 4; + } else { + block = 32 * 4 + (sector - 32) * 16; + } + + return block; +} + +uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) { + furi_assert(sector < 40); + return sector < 32 ? 4 : 16; +} + +void mf_classic_get_read_sectors_and_keys( + const MfClassicData* data, + uint8_t* sectors_read, + uint8_t* keys_found) { + furi_assert(data); + furi_assert(sectors_read); + furi_assert(keys_found); + + *sectors_read = 0; + *keys_found = 0; + uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + for(size_t i = 0; i < sectors_total; i++) { + if(mf_classic_is_key_found(data, i, MfClassicKeyTypeA)) { + *keys_found += 1; + } + if(mf_classic_is_key_found(data, i, MfClassicKeyTypeB)) { + *keys_found += 1; + } + uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); + uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i); + bool blocks_read = true; + for(size_t j = first_block; j < first_block + total_blocks_in_sec; j++) { + blocks_read = mf_classic_is_block_read(data, j); + if(!blocks_read) break; + } + if(blocks_read) { + *sectors_read += 1; + } + } +} + +bool mf_classic_is_card_read(const MfClassicData* data) { + furi_assert(data); + + uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + uint8_t sectors_read = 0; + uint8_t keys_found = 0; + mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); + bool card_read = (sectors_read == sectors_total) && (keys_found == sectors_total * 2); + + return card_read; +} + +bool mf_classic_is_sector_read(const MfClassicData* data, uint8_t sector_num) { + furi_assert(data); + + bool sector_read = false; + do { + if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeA)) break; + if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeB)) break; + uint8_t start_block = mf_classic_get_first_block_num_of_sector(sector_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num); + uint8_t block_read = true; + for(size_t i = start_block; i < start_block + total_blocks; i++) { + block_read = mf_classic_is_block_read(data, i); + if(!block_read) break; + } + sector_read = block_read; + } while(false); + + return sector_read; +} + +static bool mf_classic_is_allowed_access_sector_trailer( + MfClassicData* data, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicAction action) { + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num); + uint8_t* access_bits_arr = sec_tr->access_bits.data; + uint8_t AC = ((access_bits_arr[1] >> 5) & 0x04) | ((access_bits_arr[2] >> 2) & 0x02) | + ((access_bits_arr[2] >> 7) & 0x01); + + switch(action) { + case MfClassicActionKeyARead: { + return false; + } + case MfClassicActionKeyAWrite: + case MfClassicActionKeyBWrite: { + return ( + (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x01)) || + (key_type == MfClassicKeyTypeB && (AC == 0x04 || AC == 0x03))); + } + case MfClassicActionKeyBRead: { + return (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x02 || AC == 0x01)); + } + case MfClassicActionACRead: { + return ( + (key_type == MfClassicKeyTypeA) || + (key_type == MfClassicKeyTypeB && !(AC == 0x00 || AC == 0x02 || AC == 0x01))); + } + case MfClassicActionACWrite: { + return ( + (key_type == MfClassicKeyTypeA && (AC == 0x01)) || + (key_type == MfClassicKeyTypeB && (AC == 0x03 || AC == 0x05))); + } + default: + return false; + } + return true; +} + +bool mf_classic_is_allowed_access_data_block( + MfClassicSectorTrailer* sec_tr, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicAction action) { + furi_assert(sec_tr); + + uint8_t* access_bits_arr = sec_tr->access_bits.data; + + if(block_num == 0 && action == MfClassicActionDataWrite) { + return false; + } + + uint8_t sector_block = 0; + if(block_num <= 128) { + sector_block = block_num & 0x03; + } else { + sector_block = (block_num & 0x0f) / 5; + } + + uint8_t AC; + switch(sector_block) { + case 0x00: { + AC = ((access_bits_arr[1] >> 2) & 0x04) | ((access_bits_arr[2] << 1) & 0x02) | + ((access_bits_arr[2] >> 4) & 0x01); + break; + } + case 0x01: { + AC = ((access_bits_arr[1] >> 3) & 0x04) | ((access_bits_arr[2] >> 0) & 0x02) | + ((access_bits_arr[2] >> 5) & 0x01); + break; + } + case 0x02: { + AC = ((access_bits_arr[1] >> 4) & 0x04) | ((access_bits_arr[2] >> 1) & 0x02) | + ((access_bits_arr[2] >> 6) & 0x01); + break; + } + default: + return false; + } + + switch(action) { + case MfClassicActionDataRead: { + return ( + (key_type == MfClassicKeyTypeA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) || + (key_type == MfClassicKeyTypeB && !(AC == 0x07))); + } + case MfClassicActionDataWrite: { + return ( + (key_type == MfClassicKeyTypeA && (AC == 0x00)) || + (key_type == MfClassicKeyTypeB && + (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03))); + } + case MfClassicActionDataInc: { + return ( + (key_type == MfClassicKeyTypeA && (AC == 0x00)) || + (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x06))); + } + case MfClassicActionDataDec: { + return ( + (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) || + (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x06 || AC == 0x01))); + } + default: + return false; + } + + return false; +} + +bool mf_classic_is_allowed_access( + MfClassicData* data, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicAction action) { + furi_assert(data); + + bool access_allowed = false; + if(mf_classic_is_sector_trailer(block_num)) { + access_allowed = + mf_classic_is_allowed_access_sector_trailer(data, block_num, key_type, action); + } else { + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num); + access_allowed = + mf_classic_is_allowed_access_data_block(sec_tr, block_num, key_type, action); + } + + return access_allowed; +} + +bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num) { + furi_assert(data); + + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sector_num); + + // Check if key A can write, if it can, it's transport configuration, not data block + return !mf_classic_is_allowed_access_data_block( + sec_tr, block_num, MfClassicKeyTypeA, MfClassicActionDataWrite) && + (mf_classic_is_allowed_access_data_block( + sec_tr, block_num, MfClassicKeyTypeB, MfClassicActionDataInc) || + mf_classic_is_allowed_access_data_block( + sec_tr, block_num, MfClassicKeyTypeB, MfClassicActionDataDec)); +} diff --git a/lib/nfc/protocols/mf_classic/mf_classic.h b/lib/nfc/protocols/mf_classic/mf_classic.h new file mode 100644 index 000000000000..1965a0884542 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic.h @@ -0,0 +1,234 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_CLASSIC_CMD_AUTH_KEY_A (0x60U) +#define MF_CLASSIC_CMD_AUTH_KEY_B (0x61U) +#define MF_CLASSIC_CMD_READ_BLOCK (0x30U) +#define MF_CLASSIC_CMD_WRITE_BLOCK (0xA0U) +#define MF_CLASSIC_CMD_VALUE_DEC (0xC0U) +#define MF_CLASSIC_CMD_VALUE_INC (0xC1U) +#define MF_CLASSIC_CMD_VALUE_RESTORE (0xC2U) +#define MF_CLASSIC_CMD_VALUE_TRANSFER (0xB0U) + +#define MF_CLASSIC_CMD_HALT_MSB (0x50) +#define MF_CLASSIC_CMD_HALT_LSB (0x00) +#define MF_CLASSIC_CMD_ACK (0x0A) +#define MF_CLASSIC_CMD_NACK (0x00) + +#define MF_CLASSIC_TOTAL_SECTORS_MAX (40) +#define MF_CLASSIC_TOTAL_BLOCKS_MAX (256) +#define MF_CLASSIC_READ_MASK_SIZE (MF_CLASSIC_TOTAL_BLOCKS_MAX / 32) +#define MF_CLASSIC_BLOCK_SIZE (16) +#define MF_CLASSIC_KEY_SIZE (6) +#define MF_CLASSIC_ACCESS_BYTES_SIZE (4) + +#define MF_CLASSIC_NT_SIZE (4) +#define MF_CLASSIC_NR_SIZE (4) +#define MF_CLASSIC_AR_SIZE (4) +#define MF_CLASSIC_AT_SIZE (4) + +typedef enum { + MfClassicErrorNone, + MfClassicErrorNotPresent, + MfClassicErrorProtocol, + MfClassicErrorAuth, + MfClassicErrorTimeout, +} MfClassicError; + +typedef enum { + MfClassicTypeMini, + MfClassicType1k, + MfClassicType4k, + + MfClassicTypeNum, +} MfClassicType; + +typedef enum { + MfClassicActionDataRead, + MfClassicActionDataWrite, + MfClassicActionDataInc, + MfClassicActionDataDec, + + MfClassicActionKeyARead, + MfClassicActionKeyAWrite, + MfClassicActionKeyBRead, + MfClassicActionKeyBWrite, + MfClassicActionACRead, + MfClassicActionACWrite, +} MfClassicAction; + +typedef enum { + MfClassicValueCommandIncrement, + MfClassicValueCommandDecrement, + MfClassicValueCommandRestore, + + MfClassicValueCommandInvalid, +} MfClassicValueCommand; + +typedef struct { + uint8_t data[MF_CLASSIC_BLOCK_SIZE]; +} MfClassicBlock; + +typedef enum { + MfClassicKeyTypeA, + MfClassicKeyTypeB, +} MfClassicKeyType; + +typedef struct { + uint8_t data[MF_CLASSIC_KEY_SIZE]; +} MfClassicKey; + +typedef struct { + uint8_t data[MF_CLASSIC_ACCESS_BYTES_SIZE]; +} MfClassicAccessBits; + +typedef struct { + uint8_t data[MF_CLASSIC_NT_SIZE]; +} MfClassicNt; + +typedef struct { + uint8_t data[MF_CLASSIC_AT_SIZE]; +} MfClassicAt; + +typedef struct { + uint8_t data[MF_CLASSIC_NR_SIZE]; +} MfClassicNr; + +typedef struct { + uint8_t data[MF_CLASSIC_AR_SIZE]; +} MfClassicAr; + +typedef struct { + uint8_t block_num; + MfClassicKey key; + MfClassicKeyType key_type; + MfClassicNt nt; + MfClassicNr nr; + MfClassicAr ar; + MfClassicAt at; +} MfClassicAuthContext; + +typedef struct { + MfClassicKey key_a; + MfClassicAccessBits access_bits; + MfClassicKey key_b; +} MfClassicSectorTrailer; + +typedef struct { + uint64_t key_a_mask; + MfClassicKey key_a[MF_CLASSIC_TOTAL_SECTORS_MAX]; + uint64_t key_b_mask; + MfClassicKey key_b[MF_CLASSIC_TOTAL_SECTORS_MAX]; +} MfClassicDeviceKeys; + +typedef struct { + Iso14443_3aData* iso14443_3a_data; + MfClassicType type; + uint32_t block_read_mask[MF_CLASSIC_READ_MASK_SIZE]; + uint64_t key_a_mask; + uint64_t key_b_mask; + MfClassicBlock block[MF_CLASSIC_TOTAL_BLOCKS_MAX]; +} MfClassicData; + +extern const NfcDeviceBase nfc_device_mf_classic; + +MfClassicData* mf_classic_alloc(); + +void mf_classic_free(MfClassicData* data); + +void mf_classic_reset(MfClassicData* data); + +void mf_classic_copy(MfClassicData* data, const MfClassicData* other); + +bool mf_classic_verify(MfClassicData* data, const FuriString* device_type); + +bool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version); + +bool mf_classic_save(const MfClassicData* data, FlipperFormat* ff); + +bool mf_classic_is_equal(const MfClassicData* data, const MfClassicData* other); + +const char* mf_classic_get_device_name(const MfClassicData* data, NfcDeviceNameType name_type); + +const uint8_t* mf_classic_get_uid(const MfClassicData* data, size_t* uid_len); + +bool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_3aData* mf_classic_get_base_data(const MfClassicData* data); + +bool mf_classic_detect_protocol(Iso14443_3aData* data, MfClassicType* type); + +uint8_t mf_classic_get_total_sectors_num(MfClassicType type); + +uint16_t mf_classic_get_total_block_num(MfClassicType type); + +uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector); + +uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector); + +uint8_t mf_classic_get_sector_trailer_num_by_sector(uint8_t sector); + +uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block); + +MfClassicSectorTrailer* + mf_classic_get_sector_trailer_by_sector(const MfClassicData* data, uint8_t sector_num); + +bool mf_classic_is_sector_trailer(uint8_t block); + +uint8_t mf_classic_get_sector_by_block(uint8_t block); + +bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num); + +bool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint8_t* addr); + +void mf_classic_value_to_block(int32_t value, uint8_t addr, MfClassicBlock* block); + +bool mf_classic_is_key_found( + const MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type); + +void mf_classic_set_key_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type, + uint64_t key); + +void mf_classic_set_key_not_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type); + +bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num); + +void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data); + +bool mf_classic_is_sector_read(const MfClassicData* data, uint8_t sector_num); + +void mf_classic_get_read_sectors_and_keys( + const MfClassicData* data, + uint8_t* sectors_read, + uint8_t* keys_found); + +bool mf_classic_is_card_read(const MfClassicData* data); + +bool mf_classic_is_allowed_access_data_block( + MfClassicSectorTrailer* sec_tr, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicAction action); + +bool mf_classic_is_allowed_access( + MfClassicData* data, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicAction action); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener.c b/lib/nfc/protocols/mf_classic/mf_classic_listener.c new file mode 100644 index 000000000000..feda949ce4f9 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener.c @@ -0,0 +1,645 @@ +#include "mf_classic_listener_i.h" + +#include + +#include +#include + +#include +#include + +#define TAG "MfClassicListener" + +#define MF_CLASSIC_MAX_BUFF_SIZE (64) + +typedef MfClassicListenerCommand ( + *MfClassicListenerCommandHandler)(MfClassicListener* instance, BitBuffer* buf); + +typedef struct { + uint8_t cmd_start_byte; + size_t cmd_len_bits; + size_t command_num; + MfClassicListenerCommandHandler* handler; +} MfClassicListenerCmd; + +static void mf_classic_listener_prepare_emulation(MfClassicListener* instance) { + instance->total_block_num = mf_classic_get_total_block_num(instance->data->type); +} + +static void mf_classic_listener_reset_state(MfClassicListener* instance) { + crypto1_reset(instance->crypto); + memset(&instance->auth_context, 0, sizeof(MfClassicAuthContext)); + instance->comm_state = MfClassicListenerCommStatePlain; + instance->state = MfClassicListenerStateIdle; + instance->auth_state = MfClassicListenerAuthStateIdle; + instance->cmd_in_progress = false; + instance->current_cmd_handler_idx = 0; + instance->transfer_value = 0; + instance->value_cmd = MfClassicValueCommandInvalid; +} + +static MfClassicListenerCommand + mf_classic_listener_halt_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + + if(bit_buffer_get_byte(buff, 1) == MF_CLASSIC_CMD_HALT_LSB) { + mf_classic_listener_reset_state(instance); + command = MfClassicListenerCommandSleep; + } + + return command; +} + +static MfClassicListenerCommand mf_classic_listnener_auth_first_part_handler( + MfClassicListener* instance, + MfClassicKeyType key_type, + uint8_t block_num) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + do { + if(block_num >= instance->total_block_num) { + mf_classic_listener_reset_state(instance); + break; + } + + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(instance->data, sector_num); + MfClassicKey* key = (key_type == MfClassicKeyTypeA) ? &sec_tr->key_a : &sec_tr->key_b; + uint64_t key_num = nfc_util_bytes2num(key->data, sizeof(MfClassicKey)); + uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data); + + instance->auth_context.key_type = key_type; + instance->auth_context.block_num = block_num; + + furi_hal_random_fill_buf(instance->auth_context.nt.data, sizeof(MfClassicNt)); + uint32_t nt_num = nfc_util_bytes2num(instance->auth_context.nt.data, sizeof(MfClassicNt)); + + crypto1_init(instance->crypto, key_num); + if(instance->comm_state == MfClassicListenerCommStatePlain) { + crypto1_word(instance->crypto, nt_num ^ cuid, 0); + bit_buffer_copy_bytes( + instance->tx_encrypted_buffer, + instance->auth_context.nt.data, + sizeof(MfClassicNt)); + iso14443_3a_listener_tx(instance->iso14443_3a_listener, instance->tx_encrypted_buffer); + command = MfClassicListenerCommandProcessed; + } else { + uint8_t key_stream[4] = {}; + nfc_util_num2bytes(nt_num ^ cuid, sizeof(uint32_t), key_stream); + bit_buffer_copy_bytes( + instance->tx_plain_buffer, instance->auth_context.nt.data, sizeof(MfClassicNt)); + crypto1_encrypt( + instance->crypto, + key_stream, + instance->tx_plain_buffer, + instance->tx_encrypted_buffer); + + iso14443_3a_listener_tx(instance->iso14443_3a_listener, instance->tx_encrypted_buffer); + + command = MfClassicListenerCommandProcessed; + } + + instance->cmd_in_progress = true; + instance->current_cmd_handler_idx++; + instance->auth_state = MfClassicListenerAuthStateStarted; + } while(false); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_auth_key_a_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = mf_classic_listnener_auth_first_part_handler( + instance, MfClassicKeyTypeA, bit_buffer_get_byte(buff, 1)); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_auth_key_b_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = mf_classic_listnener_auth_first_part_handler( + instance, MfClassicKeyTypeB, bit_buffer_get_byte(buff, 1)); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_auth_second_part_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandSilent; + + do { + instance->cmd_in_progress = false; + + if(bit_buffer_get_size_bytes(buff) != (sizeof(MfClassicNr) + sizeof(MfClassicAr))) { + mf_classic_listener_reset_state(instance); + break; + } + bit_buffer_write_bytes_mid(buff, instance->auth_context.nr.data, 0, sizeof(MfClassicNr)); + bit_buffer_write_bytes_mid( + buff, instance->auth_context.ar.data, sizeof(MfClassicNr), sizeof(MfClassicAr)); + + if(instance->callback) { + instance->mfc_event.type = MfClassicListenerEventTypeAuthContextPartCollected, + instance->mfc_event_data.auth_context = instance->auth_context; + instance->callback(instance->generic_event, instance->context); + } + + uint32_t nr_num = nfc_util_bytes2num(instance->auth_context.nr.data, sizeof(MfClassicNr)); + uint32_t ar_num = nfc_util_bytes2num(instance->auth_context.ar.data, sizeof(MfClassicAr)); + + crypto1_word(instance->crypto, nr_num, 1); + uint32_t nt_num = nfc_util_bytes2num(instance->auth_context.nt.data, sizeof(MfClassicNt)); + uint32_t secret_poller = ar_num ^ crypto1_word(instance->crypto, 0, 0); + if(secret_poller != prng_successor(nt_num, 64)) { + FURI_LOG_D( + TAG, "Wrong reader key: %08lX != %08lX", secret_poller, prng_successor(nt_num, 64)); + mf_classic_listener_reset_state(instance); + break; + } + + uint32_t at_num = prng_successor(nt_num, 96); + nfc_util_num2bytes(at_num, sizeof(uint32_t), instance->auth_context.at.data); + bit_buffer_copy_bytes( + instance->tx_plain_buffer, instance->auth_context.at.data, sizeof(MfClassicAr)); + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + iso14443_3a_listener_tx_with_custom_parity( + instance->iso14443_3a_listener, instance->tx_encrypted_buffer); + + instance->auth_state = MfClassicListenerAuthStateIdle; + instance->state = MfClassicListenerStateAuthComplete; + instance->comm_state = MfClassicListenerCommStateEncrypted; + command = MfClassicListenerCommandProcessed; + + if(instance->callback) { + instance->mfc_event.type = MfClassicListenerEventTypeAuthContextFullCollected, + instance->mfc_event_data.auth_context = instance->auth_context; + instance->callback(instance->generic_event, instance->context); + } + } while(false); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_read_block_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + MfClassicAuthContext* auth_ctx = &instance->auth_context; + + do { + if(instance->state != MfClassicListenerStateAuthComplete) break; + + uint8_t block_num = bit_buffer_get_byte(buff, 1); + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num); + if(sector_num != auth_sector_num) break; + + MfClassicBlock access_block = instance->data->block[block_num]; + + if(mf_classic_is_sector_trailer(block_num)) { + MfClassicSectorTrailer* access_sec_tr = (MfClassicSectorTrailer*)&access_block; + if(!mf_classic_is_allowed_access( + instance->data, block_num, auth_ctx->key_type, MfClassicActionKeyARead)) { + memset(access_sec_tr->key_a.data, 0, sizeof(MfClassicKey)); + } + if(!mf_classic_is_allowed_access( + instance->data, block_num, auth_ctx->key_type, MfClassicActionKeyBRead)) { + memset(access_sec_tr->key_b.data, 0, sizeof(MfClassicKey)); + } + if(!mf_classic_is_allowed_access( + instance->data, block_num, auth_ctx->key_type, MfClassicActionACRead)) { + memset(access_sec_tr->access_bits.data, 0, sizeof(MfClassicAccessBits)); + } + } else if(!mf_classic_is_allowed_access( + instance->data, block_num, auth_ctx->key_type, MfClassicActionDataRead)) { + break; + } + + bit_buffer_copy_bytes( + instance->tx_plain_buffer, access_block.data, sizeof(MfClassicBlock)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + iso14443_3a_listener_tx_with_custom_parity( + instance->iso14443_3a_listener, instance->tx_encrypted_buffer); + command = MfClassicListenerCommandProcessed; + } while(false); + + return command; +} + +static MfClassicListenerCommand mf_classic_listener_write_block_first_part_handler( + MfClassicListener* instance, + BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + MfClassicAuthContext* auth_ctx = &instance->auth_context; + + do { + if(instance->state != MfClassicListenerStateAuthComplete) break; + + uint8_t block_num = bit_buffer_get_byte(buff, 1); + if(block_num >= instance->total_block_num) break; + + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num); + if(sector_num != auth_sector_num) break; + + instance->cmd_in_progress = true; + instance->current_cmd_handler_idx++; + command = MfClassicListenerCommandAck; + } while(false); + + return command; +} + +static MfClassicListenerCommand mf_classic_listener_write_block_second_part_handler( + MfClassicListener* instance, + BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + MfClassicAuthContext* auth_ctx = &instance->auth_context; + + do { + instance->cmd_in_progress = false; + + size_t buff_size = bit_buffer_get_size_bytes(buff); + if(buff_size != sizeof(MfClassicBlock) + sizeof(uint16_t)) break; + + uint8_t block_num = auth_ctx->block_num; + MfClassicKeyType key_type = auth_ctx->key_type; + MfClassicBlock block = instance->data->block[block_num]; + + if(mf_classic_is_sector_trailer(block_num)) { + if(mf_classic_is_allowed_access( + instance->data, block_num, key_type, MfClassicActionKeyAWrite)) { + bit_buffer_write_bytes_mid(buff, block.data, 0, sizeof(MfClassicKey)); + } + if(mf_classic_is_allowed_access( + instance->data, block_num, key_type, MfClassicActionKeyBWrite)) { + bit_buffer_write_bytes_mid(buff, block.data, 10, sizeof(MfClassicKey)); + } + if(mf_classic_is_allowed_access( + instance->data, block_num, key_type, MfClassicActionACWrite)) { + bit_buffer_write_bytes_mid(buff, block.data, 6, sizeof(MfClassicAccessBits)); + } + } else { + if(mf_classic_is_allowed_access( + instance->data, block_num, key_type, MfClassicActionDataWrite)) { + bit_buffer_write_bytes_mid(buff, block.data, 0, sizeof(MfClassicBlock)); + } else { + break; + } + } + + instance->data->block[block_num] = block; + command = MfClassicListenerCommandAck; + } while(false); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_value_cmd_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + MfClassicAuthContext* auth_ctx = &instance->auth_context; + + do { + if(instance->state != MfClassicListenerStateAuthComplete) break; + + uint8_t block_num = bit_buffer_get_byte(buff, 1); + if(block_num >= instance->total_block_num) break; + + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num); + if(sector_num != auth_sector_num) break; + + uint8_t cmd = bit_buffer_get_byte(buff, 0); + MfClassicAction action = MfClassicActionDataDec; + if(cmd == MF_CLASSIC_CMD_VALUE_DEC) { + instance->value_cmd = MfClassicValueCommandDecrement; + action = MfClassicActionDataDec; + } else if(cmd == MF_CLASSIC_CMD_VALUE_INC) { + instance->value_cmd = MfClassicValueCommandIncrement; + action = MfClassicActionDataInc; + } else if(cmd == MF_CLASSIC_CMD_VALUE_RESTORE) { + instance->value_cmd = MfClassicValueCommandRestore; + } else { + break; + } + + if(!mf_classic_is_allowed_access(instance->data, block_num, auth_ctx->key_type, action)) { + break; + } + + if(!mf_classic_block_to_value( + &instance->data->block[block_num], &instance->transfer_value, NULL)) { + break; + } + + instance->cmd_in_progress = true; + instance->current_cmd_handler_idx++; + command = MfClassicListenerCommandAck; + } while(false); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_value_dec_handler(MfClassicListener* instance, BitBuffer* buff) { + return mf_classic_listener_value_cmd_handler(instance, buff); +} + +static MfClassicListenerCommand + mf_classic_listener_value_inc_handler(MfClassicListener* instance, BitBuffer* buff) { + return mf_classic_listener_value_cmd_handler(instance, buff); +} + +static MfClassicListenerCommand + mf_classic_listener_value_restore_handler(MfClassicListener* instance, BitBuffer* buff) { + return mf_classic_listener_value_cmd_handler(instance, buff); +} + +static MfClassicListenerCommand + mf_classic_listener_value_data_receive_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + + do { + if(bit_buffer_get_size_bytes(buff) != 6) break; + uint8_t data_arr[4] = {}; + bit_buffer_write_bytes_mid(buff, data_arr, 0, sizeof(data_arr)); + int32_t data = *(int32_t*)data_arr; + if(data < 0) { + data = -data; + } + + if(instance->value_cmd == MfClassicValueCommandDecrement) { + data = -data; + } else if(instance->value_cmd == MfClassicValueCommandRestore) { + data = 0; + } + + instance->transfer_value += data; + + instance->cmd_in_progress = true; + instance->current_cmd_handler_idx++; + command = MfClassicListenerCommandSilent; + } while(false); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_value_transfer_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + MfClassicAuthContext* auth_ctx = &instance->auth_context; + + do { + instance->cmd_in_progress = false; + + if(bit_buffer_get_size_bytes(buff) != 4) break; + if(bit_buffer_get_byte(buff, 0) != MF_CLASSIC_CMD_VALUE_TRANSFER) break; + + uint8_t block_num = bit_buffer_get_byte(buff, 1); + if(!mf_classic_is_allowed_access( + instance->data, block_num, auth_ctx->key_type, MfClassicActionDataDec)) { + break; + } + + mf_classic_value_to_block( + instance->transfer_value, block_num, &instance->data->block[block_num]); + instance->transfer_value = 0; + + command = MfClassicListenerCommandAck; + } while(false); + + return command; +} + +static MfClassicListenerCommandHandler mf_classic_listener_halt_handlers[] = { + mf_classic_listener_halt_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_auth_key_a_handlers[] = { + mf_classic_listener_auth_key_a_handler, + mf_classic_listener_auth_second_part_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_auth_key_b_handlers[] = { + mf_classic_listener_auth_key_b_handler, + mf_classic_listener_auth_second_part_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_read_block_handlers[] = { + mf_classic_listener_read_block_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_write_block_handlers[] = { + mf_classic_listener_write_block_first_part_handler, + mf_classic_listener_write_block_second_part_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_value_dec_handlers[] = { + mf_classic_listener_value_dec_handler, + mf_classic_listener_value_data_receive_handler, + mf_classic_listener_value_transfer_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_value_inc_handlers[] = { + mf_classic_listener_value_inc_handler, + mf_classic_listener_value_data_receive_handler, + mf_classic_listener_value_transfer_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_value_restore_handlers[] = { + mf_classic_listener_value_restore_handler, + mf_classic_listener_value_data_receive_handler, + mf_classic_listener_value_transfer_handler, +}; + +static const MfClassicListenerCmd mf_classic_listener_cmd_handlers[] = { + { + .cmd_start_byte = MF_CLASSIC_CMD_HALT_MSB, + .cmd_len_bits = 4 * 8, + .command_num = COUNT_OF(mf_classic_listener_halt_handlers), + .handler = mf_classic_listener_halt_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_AUTH_KEY_A, + .cmd_len_bits = 2 * 8, + .command_num = COUNT_OF(mf_classic_listener_auth_key_a_handlers), + .handler = mf_classic_listener_auth_key_a_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_AUTH_KEY_B, + .cmd_len_bits = 2 * 8, + .command_num = COUNT_OF(mf_classic_listener_auth_key_b_handlers), + .handler = mf_classic_listener_auth_key_b_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_READ_BLOCK, + .cmd_len_bits = 4 * 8, + .command_num = COUNT_OF(mf_classic_listener_read_block_handlers), + .handler = mf_classic_listener_read_block_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_WRITE_BLOCK, + .cmd_len_bits = 4 * 8, + .command_num = COUNT_OF(mf_classic_listener_write_block_handlers), + .handler = mf_classic_listener_write_block_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_VALUE_DEC, + .cmd_len_bits = 4 * 8, + .command_num = COUNT_OF(mf_classic_listener_value_dec_handlers), + .handler = mf_classic_listener_value_dec_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_VALUE_INC, + .cmd_len_bits = 4 * 8, + .command_num = COUNT_OF(mf_classic_listener_value_inc_handlers), + .handler = mf_classic_listener_value_inc_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_VALUE_RESTORE, + .cmd_len_bits = 4 * 8, + .command_num = COUNT_OF(mf_classic_listener_value_restore_handlers), + .handler = mf_classic_listener_value_restore_handlers, + }, +}; + +static void mf_classic_listener_send_short_frame(MfClassicListener* instance, uint8_t data) { + BitBuffer* tx_buffer = instance->tx_plain_buffer; + + bit_buffer_set_size(instance->tx_plain_buffer, 4); + bit_buffer_set_byte(instance->tx_plain_buffer, 0, data); + if(instance->comm_state == MfClassicListenerCommStateEncrypted) { + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + tx_buffer = instance->tx_encrypted_buffer; + } + + iso14443_3a_listener_tx_with_custom_parity(instance->iso14443_3a_listener, tx_buffer); +} + +NfcCommand mf_classic_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + NfcCommand command = NfcCommandContinue; + MfClassicListener* instance = context; + Iso14443_3aListenerEvent* iso3_event = event.data; + BitBuffer* rx_buffer_plain; + + if(iso3_event->type == Iso14443_3aListenerEventTypeFieldOff) { + mf_classic_listener_reset_state(instance); + command = NfcCommandSleep; + } else if( + (iso3_event->type == Iso14443_3aListenerEventTypeReceivedData) || + (iso3_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame)) { + if(instance->comm_state == MfClassicListenerCommStateEncrypted) { + crypto1_decrypt(instance->crypto, iso3_event->data->buffer, instance->rx_plain_buffer); + rx_buffer_plain = instance->rx_plain_buffer; + } else { + rx_buffer_plain = iso3_event->data->buffer; + } + + MfClassicListenerCommand mfc_command = MfClassicListenerCommandNack; + if(instance->cmd_in_progress) { + mfc_command = + mf_classic_listener_cmd_handlers[instance->current_cmd_idx] + .handler[instance->current_cmd_handler_idx](instance, rx_buffer_plain); + } else { + for(size_t i = 0; i < COUNT_OF(mf_classic_listener_cmd_handlers); i++) { + if(bit_buffer_get_size(rx_buffer_plain) != + mf_classic_listener_cmd_handlers[i].cmd_len_bits) { + continue; + } + if(bit_buffer_get_byte(rx_buffer_plain, 0) != + mf_classic_listener_cmd_handlers[i].cmd_start_byte) { + continue; + } + instance->current_cmd_idx = i; + instance->current_cmd_handler_idx = 0; + mfc_command = + mf_classic_listener_cmd_handlers[i].handler[0](instance, rx_buffer_plain); + break; + } + } + + if(mfc_command == MfClassicListenerCommandAck) { + mf_classic_listener_send_short_frame(instance, MF_CLASSIC_CMD_ACK); + } else if(mfc_command == MfClassicListenerCommandNack) { + mf_classic_listener_send_short_frame(instance, MF_CLASSIC_CMD_NACK); + } else if(mfc_command == MfClassicListenerCommandSilent) { + command = NfcCommandReset; + } else if(mfc_command == MfClassicListenerCommandSleep) { + command = NfcCommandSleep; + } + } else if(iso3_event->type == Iso14443_3aListenerEventTypeHalted) { + mf_classic_listener_reset_state(instance); + } + + return command; +} + +MfClassicListener* + mf_classic_listener_alloc(Iso14443_3aListener* iso14443_3a_listener, MfClassicData* data) { + MfClassicListener* instance = malloc(sizeof(MfClassicListener)); + instance->iso14443_3a_listener = iso14443_3a_listener; + instance->data = data; + mf_classic_listener_prepare_emulation(instance); + + instance->crypto = crypto1_alloc(); + instance->tx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + instance->tx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + instance->rx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + + instance->mfc_event.data = &instance->mfc_event_data; + instance->generic_event.protocol = NfcProtocolMfClassic; + instance->generic_event.data = &instance->mfc_event; + instance->generic_event.instance = instance; + + return instance; +} + +void mf_classic_listener_free(MfClassicListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->crypto); + furi_assert(instance->rx_plain_buffer); + furi_assert(instance->tx_encrypted_buffer); + furi_assert(instance->tx_plain_buffer); + + crypto1_free(instance->crypto); + bit_buffer_free(instance->rx_plain_buffer); + bit_buffer_free(instance->tx_encrypted_buffer); + bit_buffer_free(instance->tx_plain_buffer); + + free(instance); +} + +void mf_classic_listener_set_callback( + MfClassicListener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +const MfClassicData* mf_classic_listener_get_data(const MfClassicListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +const NfcListenerBase mf_classic_listener = { + .alloc = (NfcListenerAlloc)mf_classic_listener_alloc, + .free = (NfcListenerFree)mf_classic_listener_free, + .set_callback = (NfcListenerSetCallback)mf_classic_listener_set_callback, + .get_data = (NfcListenerGetData)mf_classic_listener_get_data, + .run = (NfcListenerRun)mf_classic_listener_run, +}; diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener.h b/lib/nfc/protocols/mf_classic/mf_classic_listener.h new file mode 100644 index 000000000000..5169de62d81c --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener.h @@ -0,0 +1,27 @@ +#pragma once + +#include "mf_classic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfClassicListener MfClassicListener; + +typedef enum { + MfClassicListenerEventTypeAuthContextPartCollected, + MfClassicListenerEventTypeAuthContextFullCollected, +} MfClassicListenerEventType; + +typedef union { + MfClassicAuthContext auth_context; +} MfClassicListenerEventData; + +typedef struct { + MfClassicListenerEventType type; + MfClassicListenerEventData* data; +} MfClassicListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener_defs.h b/lib/nfc/protocols/mf_classic/mf_classic_listener_defs.h new file mode 100644 index 000000000000..6c7a1dd6a74e --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcListenerBase mf_classic_listener; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h b/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h new file mode 100644 index 000000000000..9ad834dd871b --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h @@ -0,0 +1,69 @@ +#pragma once + +#include "mf_classic_listener.h" +#include +#include +#include "crypto1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MfClassicListenerCommandProcessed, + MfClassicListenerCommandAck, + MfClassicListenerCommandNack, + MfClassicListenerCommandSilent, + MfClassicListenerCommandSleep, +} MfClassicListenerCommand; + +typedef enum { + MfClassicListenerStateIdle, + MfClassicListenerStateAuthComplete, +} MfClassicListenerState; + +typedef enum { + MfClassicListenerAuthStateIdle, + MfClassicListenerAuthStateStarted, + MfClassicListenerAuthStatePollerSecretReceived, +} MfClassicListenerAuthState; + +typedef enum { + MfClassicListenerCommStatePlain, + MfClassicListenerCommStateEncrypted, +} MfClassicListenerCommState; + +struct MfClassicListener { + Iso14443_3aListener* iso14443_3a_listener; + MfClassicListenerState state; + MfClassicListenerAuthState auth_state; + MfClassicListenerCommState comm_state; + + MfClassicData* data; + BitBuffer* tx_plain_buffer; + BitBuffer* tx_encrypted_buffer; + BitBuffer* rx_plain_buffer; + + Crypto1* crypto; + MfClassicAuthContext auth_context; + + // Value operation data + int32_t transfer_value; + MfClassicValueCommand value_cmd; + + NfcGenericEvent generic_event; + MfClassicListenerEvent mfc_event; + MfClassicListenerEventData mfc_event_data; + NfcGenericCallback callback; + void* context; + + bool cmd_in_progress; + size_t current_cmd_idx; + size_t current_cmd_handler_idx; + + size_t total_block_num; +}; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.c b/lib/nfc/protocols/mf_classic/mf_classic_poller.c new file mode 100644 index 000000000000..5ff7b41f4237 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.c @@ -0,0 +1,784 @@ +#include "mf_classic_poller_i.h" + +#include + +#include + +#define TAG "MfClassicPoller" + +#define MF_CLASSIC_MAX_BUFF_SIZE (64) + +typedef NfcCommand (*MfClassicPollerReadHandler)(MfClassicPoller* instance); + +MfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) { + furi_assert(iso14443_3a_poller); + + MfClassicPoller* instance = malloc(sizeof(MfClassicPoller)); + instance->iso14443_3a_poller = iso14443_3a_poller; + instance->data = mf_classic_alloc(); + instance->crypto = crypto1_alloc(); + instance->tx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + instance->tx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + instance->rx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + instance->rx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + + instance->mfc_event.data = &instance->mfc_event_data; + + instance->general_event.protocol = NfcProtocolMfClassic; + instance->general_event.data = &instance->mfc_event; + instance->general_event.instance = instance; + + return instance; +} + +void mf_classic_poller_free(MfClassicPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->crypto); + furi_assert(instance->tx_plain_buffer); + furi_assert(instance->rx_plain_buffer); + furi_assert(instance->tx_encrypted_buffer); + furi_assert(instance->rx_encrypted_buffer); + + mf_classic_free(instance->data); + crypto1_free(instance->crypto); + bit_buffer_free(instance->tx_plain_buffer); + bit_buffer_free(instance->rx_plain_buffer); + bit_buffer_free(instance->tx_encrypted_buffer); + bit_buffer_free(instance->rx_encrypted_buffer); + + free(instance); +} + +static NfcCommand mf_classic_poller_handle_data_update(MfClassicPoller* instance) { + MfClassicPollerEventDataUpdate* data_update = &instance->mfc_event_data.data_update; + + mf_classic_get_read_sectors_and_keys( + instance->data, &data_update->sectors_read, &data_update->keys_found); + data_update->current_sector = instance->mode_ctx.dict_attack_ctx.current_sector; + instance->mfc_event.type = MfClassicPollerEventTypeDataUpdate; + return instance->callback(instance->general_event, instance->context); +} + +NfcCommand mf_classic_poller_handler_start(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + iso14443_3a_copy( + instance->data->iso14443_3a_data, + iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); + mf_classic_detect_protocol(instance->data->iso14443_3a_data, &instance->data->type); + instance->sectors_total = mf_classic_get_total_sectors_num(instance->data->type); + + memset(&instance->mode_ctx, 0, sizeof(MfClassicPollerModeContext)); + + instance->mfc_event.type = MfClassicPollerEventTypeRequestMode; + command = instance->callback(instance->general_event, instance->context); + + if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttack) { + mf_classic_copy(instance->data, instance->mfc_event_data.poller_mode.data); + instance->state = MfClassicPollerStateRequestKey; + } else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeRead) { + instance->state = MfClassicPollerStateRequestReadSector; + } else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeWrite) { + instance->state = MfClassicPollerStateRequestSectorTrailer; + } else { + furi_crash("Invalid mode selected"); + } + + return command; +} + +NfcCommand mf_classic_poller_handler_request_sector_trailer(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + + if(write_ctx->current_sector == instance->sectors_total) { + instance->state = MfClassicPollerStateSuccess; + } else { + instance->mfc_event.type = MfClassicPollerEventTypeRequestSectorTrailer; + instance->mfc_event_data.sec_tr_data.sector_num = write_ctx->current_sector; + command = instance->callback(instance->general_event, instance->context); + if(instance->mfc_event_data.sec_tr_data.sector_trailer_provided) { + instance->state = MfClassicPollerStateCheckWriteConditions; + memcpy( + &write_ctx->sec_tr, + &instance->mfc_event_data.sec_tr_data.sector_trailer, + sizeof(MfClassicSectorTrailer)); + write_ctx->current_block = + MAX(1, mf_classic_get_first_block_num_of_sector(write_ctx->current_sector)); + + } else { + write_ctx->current_sector++; + } + } + + return command; +} + +NfcCommand mf_classic_handler_check_write_conditions(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + MfClassicSectorTrailer* sec_tr = &write_ctx->sec_tr; + + do { + // Check last block in sector to write + uint8_t sec_tr_block_num = + mf_classic_get_sector_trailer_num_by_sector(write_ctx->current_sector); + if(write_ctx->current_block == sec_tr_block_num) { + write_ctx->current_sector++; + instance->state = MfClassicPollerStateRequestSectorTrailer; + break; + } + + // Check write and read access + if(mf_classic_is_allowed_access_data_block( + sec_tr, write_ctx->current_block, MfClassicKeyTypeA, MfClassicActionDataWrite)) { + write_ctx->key_type_write = MfClassicKeyTypeA; + } else if(mf_classic_is_allowed_access_data_block( + sec_tr, + write_ctx->current_block, + MfClassicKeyTypeB, + MfClassicActionDataWrite)) { + write_ctx->key_type_write = MfClassicKeyTypeB; + } else { + FURI_LOG_D(TAG, "Not allowed to write block %d", write_ctx->current_block); + write_ctx->current_block++; + break; + } + + if(mf_classic_is_allowed_access_data_block( + sec_tr, + write_ctx->current_block, + write_ctx->key_type_write, + MfClassicActionDataRead)) { + write_ctx->key_type_read = write_ctx->key_type_write; + } else { + write_ctx->key_type_read = write_ctx->key_type_write == MfClassicKeyTypeA ? + MfClassicKeyTypeB : + MfClassicKeyTypeA; + if(!mf_classic_is_allowed_access_data_block( + sec_tr, + write_ctx->current_block, + write_ctx->key_type_read, + MfClassicActionDataRead)) { + FURI_LOG_D(TAG, "Not allowed to read block %d", write_ctx->current_block); + write_ctx->current_block++; + break; + } + } + + write_ctx->need_halt_before_write = + (write_ctx->key_type_read != write_ctx->key_type_write); + instance->state = MfClassicPollerStateReadBlock; + } while(false); + + return command; +} + +NfcCommand mf_classic_poller_handler_read_block(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + + MfClassicKey* auth_key = write_ctx->key_type_read == MfClassicKeyTypeA ? + &write_ctx->sec_tr.key_a : + &write_ctx->sec_tr.key_b; + MfClassicError error = MfClassicErrorNone; + + do { + // Authenticate to sector + error = mf_classic_async_auth( + instance, write_ctx->current_block, auth_key, write_ctx->key_type_read, NULL); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to auth to block %d", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; + } + + // Read block from tag + error = + mf_classic_async_read_block(instance, write_ctx->current_block, &write_ctx->tag_block); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %d", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; + } + + if(write_ctx->need_halt_before_write) { + mf_classic_async_halt(instance); + } + instance->state = MfClassicPollerStateWriteBlock; + } while(false); + + return command; +} + +NfcCommand mf_classic_poller_handler_write_block(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + MfClassicKey* auth_key = write_ctx->key_type_write == MfClassicKeyTypeA ? + &write_ctx->sec_tr.key_a : + &write_ctx->sec_tr.key_b; + MfClassicError error = MfClassicErrorNone; + + do { + // Request block to write + instance->mfc_event.type = MfClassicPollerEventTypeRequestWriteBlock; + instance->mfc_event_data.write_block_data.block_num = write_ctx->current_block; + command = instance->callback(instance->general_event, instance->context); + if(!instance->mfc_event_data.write_block_data.write_block_provided) break; + + // Compare tag and saved block + if(memcmp( + write_ctx->tag_block.data, + instance->mfc_event_data.write_block_data.write_block.data, + sizeof(MfClassicBlock)) == 0) { + FURI_LOG_D(TAG, "Block %d is equal. Skip writing", write_ctx->current_block); + break; + } + + // Reauth if necessary + if(write_ctx->need_halt_before_write) { + error = mf_classic_async_auth( + instance, write_ctx->current_block, auth_key, write_ctx->key_type_write, NULL); + if(error != MfClassicErrorNone) { + FURI_LOG_D( + TAG, "Failed to auth to block %d for writing", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; + } + } + + // Write block + error = mf_classic_async_write_block( + instance, + write_ctx->current_block, + &instance->mfc_event_data.write_block_data.write_block); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to write block %d", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; + } + + } while(false); + + mf_classic_async_halt(instance); + write_ctx->current_block++; + instance->state = MfClassicPollerStateCheckWriteConditions; + + return command; +} + +NfcCommand mf_classic_poller_handler_request_read_sector(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx; + MfClassicPollerEventDataReadSectorRequest* sec_read = + &instance->mfc_event_data.read_sector_request_data; + instance->mfc_event.type = MfClassicPollerEventTypeRequestReadSector; + command = instance->callback(instance->general_event, instance->context); + + if(!sec_read->key_provided) { + instance->state = MfClassicPollerStateSuccess; + } else { + sec_read_ctx->current_sector = sec_read->sector_num; + sec_read_ctx->key = sec_read->key; + sec_read_ctx->key_type = sec_read->key_type; + sec_read_ctx->current_block = + mf_classic_get_first_block_num_of_sector(sec_read->sector_num); + sec_read_ctx->auth_passed = false; + instance->state = MfClassicPollerStateReadSectorBlocks; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_request_read_sector_blocks(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx; + + do { + MfClassicError error = MfClassicErrorNone; + + if(!sec_read_ctx->auth_passed) { + uint64_t key = nfc_util_bytes2num(sec_read_ctx->key.data, sizeof(MfClassicKey)); + FURI_LOG_D( + TAG, + "Auth to block %d with key %c: %06llx", + sec_read_ctx->current_block, + sec_read_ctx->key_type == MfClassicKeyTypeA ? 'A' : 'B', + key); + error = mf_classic_async_auth( + instance, + sec_read_ctx->current_block, + &sec_read_ctx->key, + sec_read_ctx->key_type, + NULL); + if(error != MfClassicErrorNone) break; + + sec_read_ctx->auth_passed = true; + if(!mf_classic_is_key_found( + instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type)) { + mf_classic_set_key_found( + instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type, key); + } + } + if(mf_classic_is_block_read(instance->data, sec_read_ctx->current_block)) break; + + FURI_LOG_D(TAG, "Reading block %d", sec_read_ctx->current_block); + MfClassicBlock read_block = {}; + error = mf_classic_async_read_block(instance, sec_read_ctx->current_block, &read_block); + if(error == MfClassicErrorNone) { + mf_classic_set_block_read(instance->data, sec_read_ctx->current_block, &read_block); + } else { + mf_classic_async_halt(instance); + sec_read_ctx->auth_passed = false; + } + } while(false); + + uint8_t sec_tr_num = mf_classic_get_sector_trailer_num_by_sector(sec_read_ctx->current_sector); + sec_read_ctx->current_block++; + if(sec_read_ctx->current_block > sec_tr_num) { + mf_classic_async_halt(instance); + instance->state = MfClassicPollerStateRequestReadSector; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_request_key(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + instance->mfc_event.type = MfClassicPollerEventTypeRequestKey; + command = instance->callback(instance->general_event, instance->context); + if(instance->mfc_event_data.key_request_data.key_provided) { + dict_attack_ctx->current_key = instance->mfc_event_data.key_request_data.key; + instance->state = MfClassicPollerStateAuthKeyA; + } else { + instance->state = MfClassicPollerStateNextSector; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_auth_a(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) { + instance->state = MfClassicPollerStateAuthKeyB; + } else { + uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); + FURI_LOG_D(TAG, "Auth to block %d with key A: %06llx", block, key); + + MfClassicError error = mf_classic_async_auth( + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL); + if(error == MfClassicErrorNone) { + FURI_LOG_I(TAG, "Key A found"); + mf_classic_set_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeA; + dict_attack_ctx->current_block = block; + dict_attack_ctx->auth_passed = true; + instance->state = MfClassicPollerStateReadSector; + } else { + mf_classic_async_halt(instance); + instance->state = MfClassicPollerStateAuthKeyB; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_auth_b(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB)) { + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) { + instance->state = MfClassicPollerStateNextSector; + } else { + instance->state = MfClassicPollerStateRequestKey; + } + } else { + uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); + FURI_LOG_D(TAG, "Auth to block %d with key B: %06llx", block, key); + + MfClassicError error = mf_classic_async_auth( + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL); + if(error == MfClassicErrorNone) { + FURI_LOG_I(TAG, "Key B found"); + mf_classic_set_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeB; + dict_attack_ctx->current_block = block; + + dict_attack_ctx->auth_passed = true; + instance->state = MfClassicPollerStateReadSector; + } else { + mf_classic_async_halt(instance); + instance->state = MfClassicPollerStateRequestKey; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_next_sector(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + dict_attack_ctx->current_sector++; + if(dict_attack_ctx->current_sector == instance->sectors_total) { + instance->state = MfClassicPollerStateSuccess; + } else { + instance->mfc_event.type = MfClassicPollerEventTypeNextSector; + instance->mfc_event_data.next_sector_data.current_sector = dict_attack_ctx->current_sector; + command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateRequestKey; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_read_sector(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + MfClassicError error = MfClassicErrorNone; + uint8_t block_num = dict_attack_ctx->current_block; + MfClassicBlock block = {}; + + do { + if(mf_classic_is_block_read(instance->data, block_num)) break; + + if(!dict_attack_ctx->auth_passed) { + error = mf_classic_async_auth( + instance, + block_num, + &dict_attack_ctx->current_key, + dict_attack_ctx->current_key_type, + NULL); + if(error != MfClassicErrorNone) { + instance->state = MfClassicPollerStateNextSector; + FURI_LOG_W(TAG, "Failed to re-auth. Go to next sector"); + break; + } + } + + FURI_LOG_D(TAG, "Reading block %d", block_num); + error = mf_classic_async_read_block(instance, block_num, &block); + + if(error != MfClassicErrorNone) { + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + FURI_LOG_D(TAG, "Failed to read block %d", block_num); + } else { + mf_classic_set_block_read(instance->data, block_num, &block); + } + } while(false); + + uint8_t sec_tr_block_num = + mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->current_sector); + dict_attack_ctx->current_block++; + if(dict_attack_ctx->current_block > sec_tr_block_num) { + mf_classic_poller_handle_data_update(instance); + + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + + if(dict_attack_ctx->current_sector == instance->sectors_total) { + instance->state = MfClassicPollerStateNextSector; + } else { + dict_attack_ctx->reuse_key_sector = dict_attack_ctx->current_sector; + instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart; + instance->mfc_event_data.key_attack_data.current_sector = + dict_attack_ctx->reuse_key_sector; + command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateKeyReuseStart; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_key_reuse_start(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) { + dict_attack_ctx->current_key_type = MfClassicKeyTypeB; + instance->state = MfClassicPollerStateKeyReuseAuthKeyB; + } else { + dict_attack_ctx->reuse_key_sector++; + if(dict_attack_ctx->reuse_key_sector > instance->sectors_total) { + instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStop; + command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateRequestKey; + } else { + instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart; + instance->mfc_event_data.key_attack_data.current_sector = + dict_attack_ctx->reuse_key_sector; + command = instance->callback(instance->general_event, instance->context); + + dict_attack_ctx->current_key_type = MfClassicKeyTypeA; + instance->state = MfClassicPollerStateKeyReuseAuthKeyA; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_key_reuse_auth_key_a(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA)) { + instance->state = MfClassicPollerStateKeyReuseStart; + } else { + uint8_t block = + mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); + FURI_LOG_D(TAG, "Key attack auth to block %d with key A: %06llx", block, key); + + MfClassicError error = mf_classic_async_auth( + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL); + if(error == MfClassicErrorNone) { + FURI_LOG_I(TAG, "Key A found"); + mf_classic_set_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeA; + dict_attack_ctx->current_block = block; + dict_attack_ctx->auth_passed = true; + instance->state = MfClassicPollerStateKeyReuseReadSector; + } else { + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + instance->state = MfClassicPollerStateKeyReuseStart; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_key_reuse_auth_key_b(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB)) { + instance->state = MfClassicPollerStateKeyReuseStart; + } else { + uint8_t block = + mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); + FURI_LOG_D(TAG, "Key attack auth to block %d with key B: %06llx", block, key); + + MfClassicError error = mf_classic_async_auth( + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL); + if(error == MfClassicErrorNone) { + FURI_LOG_I(TAG, "Key B found"); + mf_classic_set_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeB; + dict_attack_ctx->current_block = block; + + dict_attack_ctx->auth_passed = true; + instance->state = MfClassicPollerStateKeyReuseReadSector; + } else { + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + instance->state = MfClassicPollerStateKeyReuseStart; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_key_reuse_read_sector(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + MfClassicError error = MfClassicErrorNone; + uint8_t block_num = dict_attack_ctx->current_block; + MfClassicBlock block = {}; + + do { + if(mf_classic_is_block_read(instance->data, block_num)) break; + + if(!dict_attack_ctx->auth_passed) { + error = mf_classic_async_auth( + instance, + block_num, + &dict_attack_ctx->current_key, + dict_attack_ctx->current_key_type, + NULL); + if(error != MfClassicErrorNone) { + instance->state = MfClassicPollerStateKeyReuseStart; + break; + } + } + + FURI_LOG_D(TAG, "Reading block %d", block_num); + error = mf_classic_async_read_block(instance, block_num, &block); + + if(error != MfClassicErrorNone) { + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + FURI_LOG_D(TAG, "Failed to read block %d", block_num); + } else { + mf_classic_set_block_read(instance->data, block_num, &block); + } + } while(false); + + uint8_t sec_tr_block_num = + mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->reuse_key_sector); + dict_attack_ctx->current_block++; + if(dict_attack_ctx->current_block > sec_tr_block_num) { + mf_classic_async_halt(instance); + dict_attack_ctx->auth_passed = false; + + mf_classic_poller_handle_data_update(instance); + instance->state = MfClassicPollerStateKeyReuseStart; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_success(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->mfc_event.type = MfClassicPollerEventTypeSuccess; + command = instance->callback(instance->general_event, instance->context); + + return command; +} + +NfcCommand mf_classic_poller_handler_fail(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->mfc_event.type = MfClassicPollerEventTypeFail; + command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateStart; + + return command; +} + +static const MfClassicPollerReadHandler + mf_classic_poller_dict_attack_handler[MfClassicPollerStateNum] = { + [MfClassicPollerStateStart] = mf_classic_poller_handler_start, + [MfClassicPollerStateRequestSectorTrailer] = + mf_classic_poller_handler_request_sector_trailer, + [MfClassicPollerStateCheckWriteConditions] = mf_classic_handler_check_write_conditions, + [MfClassicPollerStateReadBlock] = mf_classic_poller_handler_read_block, + [MfClassicPollerStateWriteBlock] = mf_classic_poller_handler_write_block, + [MfClassicPollerStateNextSector] = mf_classic_poller_handler_next_sector, + [MfClassicPollerStateRequestKey] = mf_classic_poller_handler_request_key, + [MfClassicPollerStateRequestReadSector] = mf_classic_poller_handler_request_read_sector, + [MfClassicPollerStateReadSectorBlocks] = + mf_classic_poller_handler_request_read_sector_blocks, + [MfClassicPollerStateAuthKeyA] = mf_classic_poller_handler_auth_a, + [MfClassicPollerStateAuthKeyB] = mf_classic_poller_handler_auth_b, + [MfClassicPollerStateReadSector] = mf_classic_poller_handler_read_sector, + [MfClassicPollerStateKeyReuseStart] = mf_classic_poller_handler_key_reuse_start, + [MfClassicPollerStateKeyReuseAuthKeyA] = mf_classic_poller_handler_key_reuse_auth_key_a, + [MfClassicPollerStateKeyReuseAuthKeyB] = mf_classic_poller_handler_key_reuse_auth_key_b, + [MfClassicPollerStateKeyReuseReadSector] = mf_classic_poller_handler_key_reuse_read_sector, + [MfClassicPollerStateSuccess] = mf_classic_poller_handler_success, + [MfClassicPollerStateFail] = mf_classic_poller_handler_fail, +}; + +NfcCommand mf_classic_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(context); + + MfClassicPoller* instance = context; + Iso14443_3aPollerEvent* iso14443_3a_event = event.data; + NfcCommand command = NfcCommandContinue; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + if(instance->card_state == MfClassicCardStateLost) { + instance->card_state = MfClassicCardStateDetected; + instance->mfc_event.type = MfClassicPollerEventTypeCardDetected; + instance->callback(instance->general_event, instance->context); + } + command = mf_classic_poller_dict_attack_handler[instance->state](instance); + } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + if(instance->card_state == MfClassicCardStateDetected) { + instance->card_state = MfClassicCardStateLost; + instance->mfc_event.type = MfClassicPollerEventTypeCardLost; + command = instance->callback(instance->general_event, instance->context); + } + } + + return command; +} + +bool mf_classic_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(context); + + Iso14443_3aPoller* iso3_poller = event.instance; + Iso14443_3aPollerEvent* iso14443_3a_event = event.data; + bool detected = false; + const uint8_t auth_cmd[] = {MF_CLASSIC_CMD_AUTH_KEY_A, 0}; + BitBuffer* tx_buffer = bit_buffer_alloc(COUNT_OF(auth_cmd)); + bit_buffer_copy_bytes(tx_buffer, auth_cmd, COUNT_OF(auth_cmd)); + BitBuffer* rx_buffer = bit_buffer_alloc(sizeof(MfClassicNt)); + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + Iso14443_3aError error = iso14443_3a_poller_send_standard_frame( + iso3_poller, tx_buffer, rx_buffer, MF_CLASSIC_FWT_FC); + if(error == Iso14443_3aErrorWrongCrc) { + if(bit_buffer_get_size_bytes(rx_buffer) == sizeof(MfClassicNt)) { + detected = true; + } + } + } + + return detected; +} + +void mf_classic_poller_set_callback( + MfClassicPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +const MfClassicData* mf_classic_poller_get_data(const MfClassicPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +const NfcPollerBase mf_classic_poller = { + .alloc = (NfcPollerAlloc)mf_classic_poller_alloc, + .free = (NfcPollerFree)mf_classic_poller_free, + .set_callback = (NfcPollerSetCallback)mf_classic_poller_set_callback, + .run = (NfcPollerRun)mf_classic_poller_run, + .detect = (NfcPollerDetect)mf_classic_poller_detect, + .get_data = (NfcPollerGetData)mf_classic_poller_get_data, +}; diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.h b/lib/nfc/protocols/mf_classic/mf_classic_poller.h new file mode 100644 index 000000000000..da1f3c3dce54 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.h @@ -0,0 +1,109 @@ +#pragma once + +#include "mf_classic.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfClassicPoller MfClassicPoller; + +typedef enum { + // Start event + MfClassicPollerEventTypeRequestMode, + + // Read with key cache events + MfClassicPollerEventTypeRequestReadSector, + + // Write events + MfClassicPollerEventTypeRequestSectorTrailer, + MfClassicPollerEventTypeRequestWriteBlock, + + // Dictionary attack events + MfClassicPollerEventTypeRequestKey, + MfClassicPollerEventTypeNextSector, + MfClassicPollerEventTypeDataUpdate, + MfClassicPollerEventTypeFoundKeyA, + MfClassicPollerEventTypeFoundKeyB, + MfClassicPollerEventTypeCardNotDetected, + MfClassicPollerEventTypeKeyAttackStart, + MfClassicPollerEventTypeKeyAttackStop, + MfClassicPollerEventTypeKeyAttackNextSector, + + // Common events + MfClassicPollerEventTypeCardDetected, + MfClassicPollerEventTypeCardLost, + MfClassicPollerEventTypeSuccess, + MfClassicPollerEventTypeFail, +} MfClassicPollerEventType; + +typedef enum { + MfClassicPollerModeRead, + MfClassicPollerModeWrite, + MfClassicPollerModeDictAttack, +} MfClassicPollerMode; + +typedef struct { + MfClassicPollerMode mode; + const MfClassicData* data; +} MfClassicPollerEventDataRequestMode; + +typedef struct { + uint8_t current_sector; +} MfClassicPollerEventDataDictAttackNextSector; + +typedef struct { + uint8_t sectors_read; + uint8_t keys_found; + uint8_t current_sector; +} MfClassicPollerEventDataUpdate; + +typedef struct { + MfClassicKey key; + bool key_provided; +} MfClassicPollerEventDataKeyRequest; + +typedef struct { + uint8_t sector_num; + MfClassicKey key; + MfClassicKeyType key_type; + bool key_provided; +} MfClassicPollerEventDataReadSectorRequest; + +typedef struct { + uint8_t sector_num; + MfClassicBlock sector_trailer; + bool sector_trailer_provided; +} MfClassicPollerEventDataSectorTrailerRequest; + +typedef struct { + uint8_t block_num; + MfClassicBlock write_block; + bool write_block_provided; +} MfClassicPollerEventDataWriteBlockRequest; + +typedef struct { + uint8_t current_sector; +} MfClassicPollerEventKeyAttackData; + +typedef union { + MfClassicError error; + MfClassicPollerEventDataRequestMode poller_mode; + MfClassicPollerEventDataDictAttackNextSector next_sector_data; + MfClassicPollerEventDataKeyRequest key_request_data; + MfClassicPollerEventDataUpdate data_update; + MfClassicPollerEventDataReadSectorRequest read_sector_request_data; + MfClassicPollerEventKeyAttackData key_attack_data; + MfClassicPollerEventDataSectorTrailerRequest sec_tr_data; + MfClassicPollerEventDataWriteBlockRequest write_block_data; +} MfClassicPollerEventData; + +typedef struct { + MfClassicPollerEventType type; + MfClassicPollerEventData* data; +} MfClassicPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_defs.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_defs.h new file mode 100644 index 000000000000..d0c0b18fcfc2 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase mf_classic_poller; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.c b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.c new file mode 100644 index 000000000000..7f84ee5c2792 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.c @@ -0,0 +1,368 @@ +#include "mf_classic_poller_i.h" + +#include +#include + +#include + +#define TAG "MfCLassicPoller" + +MfClassicError mf_classic_process_error(Iso14443_3aError error) { + MfClassicError ret = MfClassicErrorNone; + + switch(error) { + case Iso14443_3aErrorNone: + ret = MfClassicErrorNone; + break; + case Iso14443_3aErrorNotPresent: + ret = MfClassicErrorNotPresent; + break; + case Iso14443_3aErrorColResFailed: + case Iso14443_3aErrorCommunication: + case Iso14443_3aErrorWrongCrc: + ret = MfClassicErrorProtocol; + break; + case Iso14443_3aErrorTimeout: + ret = MfClassicErrorTimeout; + break; + default: + ret = MfClassicErrorProtocol; + break; + } + + return ret; +} + +MfClassicError mf_classic_async_auth( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicAuthContext* data) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + iso14443_3a_copy( + instance->data->iso14443_3a_data, + iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); + uint8_t auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B : + MF_CLASSIC_CMD_AUTH_KEY_A; + uint8_t auth_cmd[2] = {auth_type, block_num}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd)); + + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_plain_buffer, + instance->rx_plain_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorWrongCrc) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_plain_buffer) != sizeof(MfClassicNt)) { + ret = MfClassicErrorProtocol; + break; + } + + MfClassicNt nt = {}; + bit_buffer_write_bytes(instance->rx_plain_buffer, nt.data, sizeof(nt.data)); + if(data) { + data->nt = nt; + } + uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data); + uint64_t key_num = nfc_util_bytes2num(key->data, sizeof(MfClassicKey)); + MfClassicNr nr = {}; + furi_hal_random_fill_buf(nr.data, sizeof(MfClassicNr)); + + crypto1_encrypt_reader_nonce( + instance->crypto, key_num, cuid, nt.data, nr.data, instance->tx_encrypted_buffer); + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_encrypted_buffer) != 4) { + ret = MfClassicErrorAuth; + } + + crypto1_word(instance->crypto, 0, 0); + instance->auth_state = MfClassicAuthStatePassed; + + if(data) { + data->nr = nr; + const uint8_t* nr_ar = bit_buffer_get_data(instance->tx_encrypted_buffer); + memcpy(data->ar.data, &nr_ar[4], sizeof(MfClassicAr)); + bit_buffer_write_bytes( + instance->rx_encrypted_buffer, data->at.data, sizeof(MfClassicAt)); + } + } while(false); + + if(ret != MfClassicErrorNone) { + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + } + + return ret; +} + +MfClassicError mf_classic_async_halt(MfClassicPoller* instance) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t halt_cmd[2] = {MF_CLASSIC_CMD_HALT_MSB, MF_CLASSIC_CMD_HALT_LSB}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, halt_cmd, sizeof(halt_cmd)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorTimeout) { + ret = mf_classic_process_error(error); + break; + } + instance->auth_state = MfClassicAuthStateIdle; + instance->iso14443_3a_poller->state = Iso14443_3aPollerStateIdle; + } while(false); + + return ret; +} + +MfClassicError mf_classic_async_read_block( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicBlock* data) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t read_block_cmd[2] = {MF_CLASSIC_CMD_READ_BLOCK, block_num}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, read_block_cmd, sizeof(read_block_cmd)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_encrypted_buffer) != + (sizeof(MfClassicBlock) + 2)) { + ret = MfClassicErrorProtocol; + break; + } + + crypto1_decrypt( + instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer); + + if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_plain_buffer)) { + FURI_LOG_D(TAG, "CRC error"); + ret = MfClassicErrorProtocol; + break; + } + + iso14443_crc_trim(instance->rx_plain_buffer); + bit_buffer_write_bytes(instance->rx_plain_buffer, data->data, sizeof(MfClassicBlock)); + } while(false); + + return ret; +} + +MfClassicError mf_classic_async_write_block( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicBlock* data) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t write_block_cmd[2] = {MF_CLASSIC_CMD_WRITE_BLOCK, block_num}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, write_block_cmd, sizeof(write_block_cmd)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) { + ret = MfClassicErrorProtocol; + break; + } + + crypto1_decrypt( + instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer); + + if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) { + FURI_LOG_D(TAG, "Not ACK received"); + ret = MfClassicErrorProtocol; + break; + } + + bit_buffer_copy_bytes(instance->tx_plain_buffer, data->data, sizeof(MfClassicBlock)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) { + ret = MfClassicErrorProtocol; + break; + } + + crypto1_decrypt( + instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer); + + if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) { + FURI_LOG_D(TAG, "Not ACK received"); + ret = MfClassicErrorProtocol; + break; + } + } while(false); + + return ret; +} + +MfClassicError mf_classic_async_value_cmd( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicValueCommand cmd, + int32_t data) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t cmd_value = 0; + if(cmd == MfClassicValueCommandDecrement) { + cmd_value = MF_CLASSIC_CMD_VALUE_DEC; + } else if(cmd == MfClassicValueCommandIncrement) { + cmd_value = MF_CLASSIC_CMD_VALUE_INC; + } else { + cmd_value = MF_CLASSIC_CMD_VALUE_RESTORE; + } + uint8_t value_cmd[2] = {cmd_value, block_num}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, value_cmd, sizeof(value_cmd)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) { + ret = MfClassicErrorProtocol; + break; + } + + crypto1_decrypt( + instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer); + + if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) { + FURI_LOG_D(TAG, "Not ACK received"); + ret = MfClassicErrorProtocol; + break; + } + + uint8_t data_arr[4] = {}; + memcpy(data_arr, &data, sizeof(int32_t)); + bit_buffer_copy_bytes(instance->tx_plain_buffer, data_arr, sizeof(data_arr)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + + // Command processed if tag doesn't respond + if(error != Iso14443_3aErrorTimeout) { + ret = MfClassicErrorProtocol; + break; + } + ret = MfClassicErrorNone; + } while(false); + + return ret; +} + +MfClassicError mf_classic_async_value_transfer(MfClassicPoller* instance, uint8_t block_num) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t transfer_cmd[2] = {MF_CLASSIC_CMD_VALUE_TRANSFER, block_num}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, transfer_cmd, sizeof(transfer_cmd)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) { + ret = MfClassicErrorProtocol; + break; + } + + crypto1_decrypt( + instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer); + + if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) { + FURI_LOG_D(TAG, "Not ACK received"); + ret = MfClassicErrorProtocol; + break; + } + + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h new file mode 100644 index 000000000000..48d80392e857 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h @@ -0,0 +1,188 @@ +#pragma once + +#include "mf_classic_poller.h" +#include +#include +#include "crypto1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_CLASSIC_FWT_FC (60000) + +typedef enum { + MfClassicAuthStateIdle, + MfClassicAuthStatePassed, +} MfClassicAuthState; + +typedef enum { + MfClassicCardStateDetected, + MfClassicCardStateLost, +} MfClassicCardState; + +typedef enum { + MfClassicPollerStateStart, + + // Write states + MfClassicPollerStateRequestSectorTrailer, + MfClassicPollerStateCheckWriteConditions, + MfClassicPollerStateReadBlock, + MfClassicPollerStateWriteBlock, + + // Read states + MfClassicPollerStateRequestReadSector, + MfClassicPollerStateReadSectorBlocks, + + // Dict attack states + MfClassicPollerStateNextSector, + MfClassicPollerStateRequestKey, + MfClassicPollerStateReadSector, + MfClassicPollerStateAuthKeyA, + MfClassicPollerStateAuthKeyB, + MfClassicPollerStateKeyReuseStart, + MfClassicPollerStateKeyReuseAuthKeyA, + MfClassicPollerStateKeyReuseAuthKeyB, + MfClassicPollerStateKeyReuseReadSector, + MfClassicPollerStateSuccess, + MfClassicPollerStateFail, + + MfClassicPollerStateNum, +} MfClassicPollerState; + +typedef struct { + uint8_t current_sector; + MfClassicSectorTrailer sec_tr; + uint8_t current_block; + MfClassicKeyType key_type_read; + MfClassicKeyType key_type_write; + bool need_halt_before_write; + MfClassicBlock tag_block; +} MfClassicPollerWriteContext; + +typedef struct { + uint8_t current_sector; + MfClassicKey current_key; + MfClassicKeyType current_key_type; + bool auth_passed; + uint8_t current_block; + uint8_t reuse_key_sector; +} MfClassicPollerDictAttackContext; + +typedef struct { + uint8_t current_sector; + uint16_t current_block; + MfClassicKeyType key_type; + MfClassicKey key; + bool auth_passed; +} MfClassicPollerReadContext; + +typedef union { + MfClassicPollerWriteContext write_ctx; + MfClassicPollerDictAttackContext dict_attack_ctx; + MfClassicPollerReadContext read_ctx; + +} MfClassicPollerModeContext; + +struct MfClassicPoller { + Iso14443_3aPoller* iso14443_3a_poller; + + MfClassicPollerState state; + MfClassicAuthState auth_state; + MfClassicCardState card_state; + + uint8_t sectors_total; + MfClassicPollerModeContext mode_ctx; + + Crypto1* crypto; + BitBuffer* tx_plain_buffer; + BitBuffer* tx_encrypted_buffer; + BitBuffer* rx_plain_buffer; + BitBuffer* rx_encrypted_buffer; + MfClassicData* data; + + NfcGenericEvent general_event; + MfClassicPollerEvent mfc_event; + MfClassicPollerEventData mfc_event_data; + NfcGenericCallback callback; + void* context; +}; + +typedef struct { + uint8_t block_num; + MfClassicKey key; + MfClassicKeyType key_type; + MfClassicBlock block; +} MfClassicReadBlockContext; + +typedef struct { + uint8_t block_num; + MfClassicKey key; + MfClassicKeyType key_type; + MfClassicBlock block; +} MfClassicWriteBlockContext; + +typedef struct { + uint8_t block_num; + MfClassicKey key; + MfClassicKeyType key_type; + int32_t value; +} MfClassicReadValueContext; + +typedef struct { + uint8_t block_num; + MfClassicKey key; + MfClassicKeyType key_type; + MfClassicValueCommand value_cmd; + int32_t data; + int32_t new_value; +} MfClassicChangeValueContext; + +typedef struct { + MfClassicDeviceKeys keys; + uint8_t current_sector; +} MfClassicReadContext; + +typedef union { + MfClassicAuthContext auth_context; + MfClassicReadBlockContext read_block_context; + MfClassicWriteBlockContext write_block_context; + MfClassicReadValueContext read_value_context; + MfClassicChangeValueContext change_value_context; + MfClassicReadContext read_context; +} MfClassicPollerContextData; + +MfClassicError mf_classic_process_error(Iso14443_3aError error); + +MfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller); + +void mf_classic_poller_free(MfClassicPoller* instance); + +MfClassicError mf_classic_async_auth( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicAuthContext* data); + +MfClassicError mf_classic_async_halt(MfClassicPoller* instance); + +MfClassicError + mf_classic_async_read_block(MfClassicPoller* instance, uint8_t block_num, MfClassicBlock* data); + +MfClassicError mf_classic_async_write_block( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicBlock* data); + +MfClassicError mf_classic_async_value_cmd( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicValueCommand cmd, + int32_t data); + +MfClassicError mf_classic_async_value_transfer(MfClassicPoller* instance, uint8_t block_num); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.c b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.c new file mode 100644 index 000000000000..21db8cd6ae9b --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.c @@ -0,0 +1,462 @@ +#include "mf_classic_poller_i.h" + +#include + +#include + +#define TAG "MfClassicPoller" + +#define MF_CLASSIC_POLLER_COMPLETE_EVENT (1UL << 0) + +typedef enum { + MfClassicPollerCmdTypeAuth, + MfClassicPollerCmdTypeReadBlock, + MfClassicPollerCmdTypeWriteBlock, + MfClassicPollerCmdTypeReadValue, + MfClassicPollerCmdTypeChangeValue, + + MfClassicPollerCmdTypeNum, +} MfClassicPollerCmdType; + +typedef struct { + MfClassicPollerCmdType cmd_type; + FuriThreadId thread_id; + MfClassicError error; + MfClassicPollerContextData data; +} MfClassicPollerContext; + +typedef MfClassicError ( + *MfClassicPollerCmdHandler)(MfClassicPoller* poller, MfClassicPollerContextData* data); + +static MfClassicError + mf_classic_poller_auth_handler(MfClassicPoller* poller, MfClassicPollerContextData* data) { + return mf_classic_async_auth( + poller, + data->auth_context.block_num, + &data->auth_context.key, + data->auth_context.key_type, + &data->auth_context); +} + +static MfClassicError mf_classic_poller_read_block_handler( + MfClassicPoller* poller, + MfClassicPollerContextData* data) { + MfClassicError error = MfClassicErrorNone; + + do { + error = mf_classic_async_auth( + poller, + data->read_block_context.block_num, + &data->read_block_context.key, + data->read_block_context.key_type, + NULL); + if(error != MfClassicErrorNone) break; + + error = mf_classic_async_read_block( + poller, data->read_block_context.block_num, &data->read_block_context.block); + if(error != MfClassicErrorNone) break; + + error = mf_classic_async_halt(poller); + if(error != MfClassicErrorNone) break; + + } while(false); + + return error; +} + +static MfClassicError mf_classic_poller_write_block_handler( + MfClassicPoller* poller, + MfClassicPollerContextData* data) { + MfClassicError error = MfClassicErrorNone; + + do { + error = mf_classic_async_auth( + poller, + data->read_block_context.block_num, + &data->read_block_context.key, + data->read_block_context.key_type, + NULL); + if(error != MfClassicErrorNone) break; + + error = mf_classic_async_write_block( + poller, data->write_block_context.block_num, &data->write_block_context.block); + if(error != MfClassicErrorNone) break; + + error = mf_classic_async_halt(poller); + if(error != MfClassicErrorNone) break; + + } while(false); + + return error; +} + +static MfClassicError mf_classic_poller_read_value_handler( + MfClassicPoller* poller, + MfClassicPollerContextData* data) { + MfClassicError error = MfClassicErrorNone; + + do { + error = mf_classic_async_auth( + poller, + data->read_value_context.block_num, + &data->read_value_context.key, + data->read_value_context.key_type, + NULL); + if(error != MfClassicErrorNone) break; + + MfClassicBlock block = {}; + error = mf_classic_async_read_block(poller, data->read_value_context.block_num, &block); + if(error != MfClassicErrorNone) break; + + if(!mf_classic_block_to_value(&block, &data->read_value_context.value, NULL)) { + error = MfClassicErrorProtocol; + break; + } + + error = mf_classic_async_halt(poller); + if(error != MfClassicErrorNone) break; + + } while(false); + + return error; +} + +static MfClassicError mf_classic_poller_change_value_handler( + MfClassicPoller* poller, + MfClassicPollerContextData* data) { + MfClassicError error = MfClassicErrorNone; + + do { + error = mf_classic_async_auth( + poller, + data->change_value_context.block_num, + &data->change_value_context.key, + data->change_value_context.key_type, + NULL); + if(error != MfClassicErrorNone) break; + + error = mf_classic_async_value_cmd( + poller, + data->change_value_context.block_num, + data->change_value_context.value_cmd, + data->change_value_context.data); + if(error != MfClassicErrorNone) break; + + error = mf_classic_async_value_transfer(poller, data->change_value_context.block_num); + if(error != MfClassicErrorNone) break; + + MfClassicBlock block = {}; + error = mf_classic_async_read_block(poller, data->change_value_context.block_num, &block); + if(error != MfClassicErrorNone) break; + + error = mf_classic_async_halt(poller); + if(error != MfClassicErrorNone) break; + + if(!mf_classic_block_to_value(&block, &data->change_value_context.new_value, NULL)) { + error = MfClassicErrorProtocol; + break; + } + + } while(false); + + return error; +} + +static const MfClassicPollerCmdHandler mf_classic_poller_cmd_handlers[MfClassicPollerCmdTypeNum] = { + [MfClassicPollerCmdTypeAuth] = mf_classic_poller_auth_handler, + [MfClassicPollerCmdTypeReadBlock] = mf_classic_poller_read_block_handler, + [MfClassicPollerCmdTypeWriteBlock] = mf_classic_poller_write_block_handler, + [MfClassicPollerCmdTypeReadValue] = mf_classic_poller_read_value_handler, + [MfClassicPollerCmdTypeChangeValue] = mf_classic_poller_change_value_handler, +}; + +static NfcCommand mf_ultralgiht_poller_cmd_callback(NfcGenericEvent event, void* context) { + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(event.data); + furi_assert(context); + + MfClassicPollerContext* poller_context = context; + Iso14443_3aPollerEvent* iso14443_3a_event = event.data; + Iso14443_3aPoller* iso14443_3a_poller = event.instance; + MfClassicPoller* mfc_poller = mf_classic_poller_alloc(iso14443_3a_poller); + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + poller_context->error = mf_classic_poller_cmd_handlers[poller_context->cmd_type]( + mfc_poller, &poller_context->data); + } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + poller_context->error = mf_classic_process_error(iso14443_3a_event->data->error); + } + + furi_thread_flags_set(poller_context->thread_id, MF_CLASSIC_POLLER_COMPLETE_EVENT); + + mf_classic_poller_free(mfc_poller); + + return NfcCommandStop; +} + +static MfClassicError mf_classic_poller_cmd_execute(Nfc* nfc, MfClassicPollerContext* poller_ctx) { + furi_assert(poller_ctx->cmd_type < MfClassicPollerCmdTypeNum); + + poller_ctx->thread_id = furi_thread_get_current_id(); + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolIso14443_3a); + nfc_poller_start(poller, mf_ultralgiht_poller_cmd_callback, poller_ctx); + furi_thread_flags_wait(MF_CLASSIC_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(MF_CLASSIC_POLLER_COMPLETE_EVENT); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + return poller_ctx->error; +} + +MfClassicError mf_classic_poller_auth( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicAuthContext* data) { + furi_assert(nfc); + furi_assert(key); + furi_assert(data); + + MfClassicPollerContext poller_context = { + .cmd_type = MfClassicPollerCmdTypeAuth, + .data.auth_context.block_num = block_num, + .data.auth_context.key = *key, + .data.auth_context.key_type = key_type, + }; + + MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context); + + if(error == MfClassicErrorNone) { + *data = poller_context.data.auth_context; + } + + return error; +} + +MfClassicError mf_classic_poller_read_block( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicBlock* data) { + furi_assert(nfc); + furi_assert(key); + furi_assert(data); + + MfClassicPollerContext poller_context = { + .cmd_type = MfClassicPollerCmdTypeReadBlock, + .data.read_block_context.block_num = block_num, + .data.read_block_context.key = *key, + .data.read_block_context.key_type = key_type, + }; + + MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context); + + if(error == MfClassicErrorNone) { + *data = poller_context.data.read_block_context.block; + } + + return error; +} + +MfClassicError mf_classic_poller_write_block( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicBlock* data) { + furi_assert(nfc); + furi_assert(key); + furi_assert(data); + + MfClassicPollerContext poller_context = { + .cmd_type = MfClassicPollerCmdTypeWriteBlock, + .data.write_block_context.block_num = block_num, + .data.write_block_context.key = *key, + .data.write_block_context.key_type = key_type, + .data.write_block_context.block = *data, + }; + + MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context); + + return error; +} + +MfClassicError mf_classic_poller_read_value( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + int32_t* value) { + furi_assert(nfc); + furi_assert(key); + furi_assert(value); + + MfClassicPollerContext poller_context = { + .cmd_type = MfClassicPollerCmdTypeReadValue, + .data.write_block_context.block_num = block_num, + .data.write_block_context.key = *key, + .data.write_block_context.key_type = key_type, + }; + + MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context); + + if(error == MfClassicErrorNone) { + *value = poller_context.data.read_value_context.value; + } + + return error; +} + +MfClassicError mf_classic_poller_change_value( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + int32_t data, + int32_t* new_value) { + furi_assert(nfc); + furi_assert(key); + furi_assert(new_value); + + MfClassicValueCommand command = MfClassicValueCommandRestore; + int32_t command_data = 0; + if(data > 0) { + command = MfClassicValueCommandIncrement; + command_data = data; + } else if(data < 0) { + command = MfClassicValueCommandDecrement; + command_data = -data; + } + + MfClassicPollerContext poller_context = { + .cmd_type = MfClassicPollerCmdTypeChangeValue, + .data.change_value_context.block_num = block_num, + .data.change_value_context.key = *key, + .data.change_value_context.key_type = key_type, + .data.change_value_context.value_cmd = command, + .data.change_value_context.data = command_data, + }; + + MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context); + + if(error == MfClassicErrorNone) { + *new_value = poller_context.data.change_value_context.new_value; + } + + return error; +} + +static bool mf_classic_poller_read_get_next_key( + MfClassicReadContext* read_ctx, + uint8_t* sector_num, + MfClassicKey* key, + MfClassicKeyType* key_type) { + bool next_key_found = false; + + for(uint8_t i = read_ctx->current_sector; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) { + if(FURI_BIT(read_ctx->keys.key_a_mask, i)) { + FURI_BIT_CLEAR(read_ctx->keys.key_a_mask, i); + *key = read_ctx->keys.key_a[i]; + *key_type = MfClassicKeyTypeA; + *sector_num = i; + + next_key_found = true; + break; + } + if(FURI_BIT(read_ctx->keys.key_b_mask, i)) { + FURI_BIT_CLEAR(read_ctx->keys.key_b_mask, i); + *key = read_ctx->keys.key_b[i]; + *key_type = MfClassicKeyTypeB; + *sector_num = i; + + next_key_found = true; + read_ctx->current_sector = i; + break; + } + } + + return next_key_found; +} + +NfcCommand mf_classic_poller_read_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcCommand command = NfcCommandContinue; + MfClassicPollerContext* poller_context = context; + MfClassicPollerEvent* mfc_event = event.data; + + if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + poller_context->error = MfClassicErrorNotPresent; + command = NfcCommandStop; + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + mfc_event->data->poller_mode.mode = MfClassicPollerModeRead; + } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) { + MfClassicPollerEventDataReadSectorRequest* req_data = + &mfc_event->data->read_sector_request_data; + MfClassicKey key = {}; + MfClassicKeyType key_type = MfClassicKeyTypeA; + uint8_t sector_num = 0; + if(mf_classic_poller_read_get_next_key( + &poller_context->data.read_context, §or_num, &key, &key_type)) { + req_data->sector_num = sector_num; + req_data->key = key; + req_data->key_type = key_type; + req_data->key_provided = true; + } else { + req_data->key_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + command = NfcCommandStop; + } + + if(command == NfcCommandStop) { + furi_thread_flags_set(poller_context->thread_id, MF_CLASSIC_POLLER_COMPLETE_EVENT); + } + + return command; +} + +MfClassicError + mf_classic_poller_read(Nfc* nfc, const MfClassicDeviceKeys* keys, MfClassicData* data) { + furi_assert(nfc); + furi_assert(keys); + furi_assert(data); + + MfClassicError error = MfClassicErrorNone; + MfClassicPollerContext poller_context = {}; + poller_context.thread_id = furi_thread_get_current_id(); + poller_context.data.read_context.keys = *keys; + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfClassic); + nfc_poller_start(poller, mf_classic_poller_read_callback, &poller_context); + furi_thread_flags_wait(MF_CLASSIC_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(MF_CLASSIC_POLLER_COMPLETE_EVENT); + + nfc_poller_stop(poller); + + if(poller_context.error != MfClassicErrorNone) { + error = poller_context.error; + } else { + const MfClassicData* mfc_data = nfc_poller_get_data(poller); + uint8_t sectors_read = 0; + uint8_t keys_found = 0; + + mf_classic_get_read_sectors_and_keys(mfc_data, §ors_read, &keys_found); + if((sectors_read > 0) || (keys_found > 0)) { + mf_classic_copy(data, mfc_data); + } else { + error = MfClassicErrorNotPresent; + } + } + + nfc_poller_free(poller); + + return error; +} diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.h new file mode 100644 index 000000000000..58c3b96f852a --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync_api.h @@ -0,0 +1,51 @@ +#pragma once + +#include "mf_classic.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +MfClassicError mf_classic_poller_auth( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicAuthContext* data); + +MfClassicError mf_classic_poller_read_block( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicBlock* data); + +MfClassicError mf_classic_poller_write_block( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicBlock* data); + +MfClassicError mf_classic_poller_read_value( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + int32_t* value); + +MfClassicError mf_classic_poller_change_value( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + int32_t data, + int32_t* new_value); + +MfClassicError + mf_classic_poller_read(Nfc* nfc, const MfClassicDeviceKeys* keys, MfClassicData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire.c b/lib/nfc/protocols/mf_desfire/mf_desfire.c new file mode 100644 index 000000000000..4483f95db145 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire.c @@ -0,0 +1,282 @@ +#include "mf_desfire_i.h" + +#include + +#define MF_DESFIRE_PROTOCOL_NAME "Mifare DESFire" + +const NfcDeviceBase nfc_device_mf_desfire = { + .protocol_name = MF_DESFIRE_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)mf_desfire_alloc, + .free = (NfcDeviceFree)mf_desfire_free, + .reset = (NfcDeviceReset)mf_desfire_reset, + .copy = (NfcDeviceCopy)mf_desfire_copy, + .verify = (NfcDeviceVerify)mf_desfire_verify, + .load = (NfcDeviceLoad)mf_desfire_load, + .save = (NfcDeviceSave)mf_desfire_save, + .is_equal = (NfcDeviceEqual)mf_desfire_is_equal, + .get_name = (NfcDeviceGetName)mf_desfire_get_device_name, + .get_uid = (NfcDeviceGetUid)mf_desfire_get_uid, + .set_uid = (NfcDeviceSetUid)mf_desfire_set_uid, + .get_base_data = (NfcDeviceGetBaseData)mf_desfire_get_base_data, +}; + +MfDesfireData* mf_desfire_alloc() { + MfDesfireData* data = malloc(sizeof(MfDesfireData)); + data->iso14443_4a_data = iso14443_4a_alloc(); + data->master_key_versions = simple_array_alloc(&mf_desfire_key_version_array_config); + data->application_ids = simple_array_alloc(&mf_desfire_app_id_array_config); + data->applications = simple_array_alloc(&mf_desfire_application_array_config); + + return data; +} + +void mf_desfire_free(MfDesfireData* data) { + furi_assert(data); + + mf_desfire_reset(data); + simple_array_free(data->applications); + simple_array_free(data->application_ids); + simple_array_free(data->master_key_versions); + iso14443_4a_free(data->iso14443_4a_data); + free(data); +} + +void mf_desfire_reset(MfDesfireData* data) { + furi_assert(data); + + iso14443_4a_reset(data->iso14443_4a_data); + + memset(&data->version, 0, sizeof(MfDesfireVersion)); + memset(&data->free_memory, 0, sizeof(MfDesfireFreeMemory)); + + simple_array_reset(data->master_key_versions); + simple_array_reset(data->application_ids); + simple_array_reset(data->applications); +} + +void mf_desfire_copy(MfDesfireData* data, const MfDesfireData* other) { + furi_assert(data); + furi_assert(other); + + mf_desfire_reset(data); + + iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data); + + data->version = other->version; + data->free_memory = other->free_memory; + data->master_key_settings = other->master_key_settings; + + simple_array_copy(data->master_key_versions, other->master_key_versions); + simple_array_copy(data->application_ids, other->application_ids); + simple_array_copy(data->applications, other->applications); +} + +bool mf_desfire_verify(MfDesfireData* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal_str(device_type, MF_DESFIRE_PROTOCOL_NAME); +} + +bool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + FuriString* prefix = furi_string_alloc(); + + bool success = false; + + do { + if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break; + + if(!mf_desfire_version_load(&data->version, ff)) break; + if(!mf_desfire_free_memory_load(&data->free_memory, ff)) break; + if(!mf_desfire_key_settings_load( + &data->master_key_settings, MF_DESFIRE_FFF_PICC_PREFIX, ff)) + break; + + const uint32_t master_key_version_count = data->master_key_settings.max_keys; + simple_array_init(data->master_key_versions, master_key_version_count); + + uint32_t i; + for(i = 0; i < master_key_version_count; ++i) { + if(!mf_desfire_key_version_load( + simple_array_get(data->master_key_versions, i), + MF_DESFIRE_FFF_PICC_PREFIX, + i, + ff)) + break; + } + + if(i != master_key_version_count) break; + + uint32_t application_count; + if(!mf_desfire_application_count_load(&application_count, ff)) break; + + if(application_count > 0) { + simple_array_init(data->application_ids, application_count); + if(!mf_desfire_application_ids_load( + simple_array_get_data(data->application_ids), application_count, ff)) + break; + + simple_array_init(data->applications, application_count); + for(i = 0; i < application_count; ++i) { + const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i); + furi_string_printf( + prefix, + "%s %02x%02x%02x", + MF_DESFIRE_FFF_APP_PREFIX, + app_id->data[0], + app_id->data[1], + app_id->data[2]); + + if(!mf_desfire_application_load( + simple_array_get(data->applications, i), furi_string_get_cstr(prefix), ff)) + break; + } + + if(i != application_count) break; + } + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff) { + furi_assert(data); + + FuriString* prefix = furi_string_alloc(); + + bool success = false; + + do { + if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break; + + if(!flipper_format_write_comment_cstr(ff, MF_DESFIRE_PROTOCOL_NAME " specific data")) + break; + if(!mf_desfire_version_save(&data->version, ff)) break; + if(!mf_desfire_free_memory_save(&data->free_memory, ff)) break; + if(!mf_desfire_key_settings_save( + &data->master_key_settings, MF_DESFIRE_FFF_PICC_PREFIX, ff)) + break; + + const uint32_t master_key_version_count = + simple_array_get_count(data->master_key_versions); + + uint32_t i; + for(i = 0; i < master_key_version_count; ++i) { + if(!mf_desfire_key_version_save( + simple_array_cget(data->master_key_versions, i), + MF_DESFIRE_FFF_PICC_PREFIX, + i, + ff)) + break; + } + + if(i != master_key_version_count) break; + + const uint32_t application_count = simple_array_get_count(data->application_ids); + if(!mf_desfire_application_count_save(&application_count, ff)) break; + + if(application_count > 0) { + if(!mf_desfire_application_ids_save( + simple_array_cget_data(data->application_ids), application_count, ff)) + break; + + for(i = 0; i < application_count; ++i) { + const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i); + furi_string_printf( + prefix, + "%s %02x%02x%02x", + MF_DESFIRE_FFF_APP_PREFIX, + app_id->data[0], + app_id->data[1], + app_id->data[2]); + + const MfDesfireApplication* app = simple_array_cget(data->applications, i); + if(!mf_desfire_application_save(app, furi_string_get_cstr(prefix), ff)) break; + } + + if(i != application_count) break; + } + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool mf_desfire_is_equal(const MfDesfireData* data, const MfDesfireData* other) { + furi_assert(data); + furi_assert(other); + + // TODO: Complete equality method + return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data); +} + +const char* mf_desfire_get_device_name(const MfDesfireData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + return MF_DESFIRE_PROTOCOL_NAME; +} + +const uint8_t* mf_desfire_get_uid(const MfDesfireData* data, size_t* uid_len) { + furi_assert(data); + + return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len); +} + +bool mf_desfire_set_uid(MfDesfireData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len); +} + +Iso14443_4aData* mf_desfire_get_base_data(const MfDesfireData* data) { + furi_assert(data); + + return data->iso14443_4a_data; +} + +const MfDesfireApplication* + mf_desfire_get_application(const MfDesfireData* data, const MfDesfireApplicationId* app_id) { + MfDesfireApplication* app = NULL; + + for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) { + const MfDesfireApplicationId* current_app_id = simple_array_cget(data->application_ids, i); + if(memcmp(app_id, current_app_id, sizeof(MfDesfireApplicationId)) == 0) { + app = simple_array_get(data->applications, i); + } + } + + return app; +} + +const MfDesfireFileSettings* + mf_desfire_get_file_settings(const MfDesfireApplication* data, const MfDesfireFileId* file_id) { + MfDesfireFileSettings* file_settings = NULL; + + for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) { + const MfDesfireFileId* current_file_id = simple_array_cget(data->file_ids, i); + if(*file_id == *current_file_id) { + file_settings = simple_array_get(data->file_settings, i); + } + } + + return file_settings; +} + +const MfDesfireFileData* + mf_desfire_get_file_data(const MfDesfireApplication* data, const MfDesfireFileId* file_id) { + MfDesfireFileData* file_data = NULL; + + for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) { + const MfDesfireFileId* current_file_id = simple_array_cget(data->file_ids, i); + if(*file_id == *current_file_id) { + file_data = simple_array_get(data->file_data, i); + } + } + + return file_data; +} diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire.h b/lib/nfc/protocols/mf_desfire/mf_desfire.h new file mode 100644 index 000000000000..4d09dc851033 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire.h @@ -0,0 +1,191 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_DESFIRE_CMD_GET_VERSION (0x60) +#define MF_DESFIRE_CMD_GET_FREE_MEMORY (0x6E) +#define MF_DESFIRE_CMD_GET_KEY_SETTINGS (0x45) +#define MF_DESFIRE_CMD_GET_KEY_VERSION (0x64) +#define MF_DESFIRE_CMD_GET_APPLICATION_IDS (0x6A) +#define MF_DESFIRE_CMD_SELECT_APPLICATION (0x5A) +#define MF_DESFIRE_CMD_GET_FILE_IDS (0x6F) +#define MF_DESFIRE_CMD_GET_FILE_SETTINGS (0xF5) + +#define MF_DESFIRE_CMD_READ_DATA (0xBD) +#define MF_DESFIRE_CMD_GET_VALUE (0x6C) +#define MF_DESFIRE_CMD_READ_RECORDS (0xBB) + +#define MF_DESFIRE_FLAG_HAS_NEXT (0xAF) + +#define MF_DESFIRE_MAX_KEYS (14) +#define MF_DESFIRE_MAX_FILES (32) + +#define MF_DESFIRE_UID_SIZE (7) +#define MF_DESFIRE_BATCH_SIZE (5) +#define MF_DESFIRE_APP_ID_SIZE (3) + +typedef struct { + uint8_t hw_vendor; + uint8_t hw_type; + uint8_t hw_subtype; + uint8_t hw_major; + uint8_t hw_minor; + uint8_t hw_storage; + uint8_t hw_proto; + + uint8_t sw_vendor; + uint8_t sw_type; + uint8_t sw_subtype; + uint8_t sw_major; + uint8_t sw_minor; + uint8_t sw_storage; + uint8_t sw_proto; + + uint8_t uid[MF_DESFIRE_UID_SIZE]; + uint8_t batch[MF_DESFIRE_BATCH_SIZE]; + uint8_t prod_week; + uint8_t prod_year; +} MfDesfireVersion; + +typedef struct { + uint32_t bytes_free; + bool is_present; +} MfDesfireFreeMemory; // EV1+ only + +typedef struct { + bool is_master_key_changeable; + bool is_free_directory_list; + bool is_free_create_delete; + bool is_config_changeable; + uint8_t change_key_id; + uint8_t max_keys; + uint8_t flags; +} MfDesfireKeySettings; + +typedef uint8_t MfDesfireKeyVersion; + +typedef struct { + MfDesfireKeySettings key_settings; + SimpleArray* key_versions; +} MfDesfireKeyConfiguration; + +typedef enum { + MfDesfireFileTypeStandard = 0, + MfDesfireFileTypeBackup = 1, + MfDesfireFileTypeValue = 2, + MfDesfireFileTypeLinearRecord = 3, + MfDesfireFileTypeCyclicRecord = 4, +} MfDesfireFileType; + +typedef enum { + MfDesfireFileCommunicationSettingsPlaintext = 0, + MfDesfireFileCommunicationSettingsAuthenticated = 1, + MfDesfireFileCommunicationSettingsEnciphered = 3, +} MfDesfireFileCommunicationSettings; + +typedef uint8_t MfDesfireFileId; +typedef uint16_t MfDesfireFileAccessRights; + +typedef struct { + MfDesfireFileType type; + MfDesfireFileCommunicationSettings comm; + MfDesfireFileAccessRights access_rights; + union { + struct { + uint32_t size; + } data; + struct { + uint32_t lo_limit; + uint32_t hi_limit; + uint32_t limited_credit_value; + bool limited_credit_enabled; + } value; + struct { + uint32_t size; + uint32_t max; + uint32_t cur; + } record; + }; +} MfDesfireFileSettings; + +typedef struct { + SimpleArray* data; +} MfDesfireFileData; + +typedef struct { + uint8_t data[MF_DESFIRE_APP_ID_SIZE]; +} MfDesfireApplicationId; + +typedef struct MfDesfireApplication { + MfDesfireKeySettings key_settings; + SimpleArray* key_versions; + SimpleArray* file_ids; + SimpleArray* file_settings; + SimpleArray* file_data; +} MfDesfireApplication; + +typedef enum { + MfDesfireErrorNone, + MfDesfireErrorNotPresent, + MfDesfireErrorProtocol, + MfDesfireErrorTimeout, +} MfDesfireError; + +typedef struct { + Iso14443_4aData* iso14443_4a_data; + MfDesfireVersion version; + MfDesfireFreeMemory free_memory; + MfDesfireKeySettings master_key_settings; + SimpleArray* master_key_versions; + SimpleArray* application_ids; + SimpleArray* applications; +} MfDesfireData; + +extern const NfcDeviceBase nfc_device_mf_desfire; + +// Virtual methods + +MfDesfireData* mf_desfire_alloc(); + +void mf_desfire_free(MfDesfireData* data); + +void mf_desfire_reset(MfDesfireData* data); + +void mf_desfire_copy(MfDesfireData* data, const MfDesfireData* other); + +bool mf_desfire_verify(MfDesfireData* data, const FuriString* device_type); + +bool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version); + +bool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff); + +bool mf_desfire_is_equal(const MfDesfireData* data, const MfDesfireData* other); + +const char* mf_desfire_get_device_name(const MfDesfireData* data, NfcDeviceNameType name_type); + +const uint8_t* mf_desfire_get_uid(const MfDesfireData* data, size_t* uid_len); + +bool mf_desfire_set_uid(MfDesfireData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_4aData* mf_desfire_get_base_data(const MfDesfireData* data); + +// Getters and tests + +const MfDesfireApplication* + mf_desfire_get_application(const MfDesfireData* data, const MfDesfireApplicationId* app_id); + +const MfDesfireFileSettings* + mf_desfire_get_file_settings(const MfDesfireApplication* data, const MfDesfireFileId* file_id); + +const MfDesfireFileData* + mf_desfire_get_file_data(const MfDesfireApplication* data, const MfDesfireFileId* file_id); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c new file mode 100644 index 000000000000..3907556e89a4 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c @@ -0,0 +1,794 @@ +#include "mf_desfire_i.h" + +#define BITS_IN_BYTE (8U) + +#define MF_DESFIRE_FFF_VERSION_KEY \ + MF_DESFIRE_FFF_PICC_PREFIX " " \ + "Version" +#define MF_DESFIRE_FFF_FREE_MEM_KEY \ + MF_DESFIRE_FFF_PICC_PREFIX " " \ + "Free Memory" + +#define MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY "Change Key ID" +#define MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY "Config Changeable" +#define MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY "Free Create Delete" +#define MF_DESFIRE_FFF_FREE_DIR_LIST_KEY "Free Directory List" +#define MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY "Key Changeable" +#define MF_DESFIRE_FFF_FLAGS_KEY "Flags" +#define MF_DESFIRE_FFF_MAX_KEYS_KEY "Max Keys" + +#define MF_DESFIRE_FFF_KEY_SUB_PREFIX "Key" +#define MF_DESFIRE_FFF_KEY_VERSION_KEY "Version" + +#define MF_DESFIRE_FFF_APPLICATION_COUNT_KEY \ + MF_DESFIRE_FFF_APP_PREFIX " " \ + "Count" +#define MF_DESFIRE_FFF_APPLICATION_IDS_KEY \ + MF_DESFIRE_FFF_APP_PREFIX " " \ + "IDs" + +#define MF_DESFIRE_FFF_FILE_SUB_PREFIX "File" +#define MF_DESFIRE_FFF_FILE_IDS_KEY \ + MF_DESFIRE_FFF_FILE_SUB_PREFIX " " \ + "IDs" +#define MF_DESFIRE_FFF_FILE_TYPE_KEY "Type" +#define MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY "Communication Settings" +#define MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY "Access Rights" + +#define MF_DESFIRE_FFF_FILE_SIZE_KEY "Size" + +#define MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY "Hi Limit" +#define MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY "Lo Limit" +#define MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY "Limited Credit Value" +#define MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY "Limited Credit Enabled" + +#define MF_DESFIRE_FFF_FILE_MAX_KEY "Max" +#define MF_DESFIRE_FFF_FILE_CUR_KEY "Cur" + +bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) { + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireVersion); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(MfDesfireVersion)); + } + + return can_parse; +} + +bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf) { + typedef struct __attribute__((packed)) { + uint32_t bytes_free : 3 * BITS_IN_BYTE; + } MfDesfireFreeMemoryLayout; + + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireFreeMemoryLayout); + + if(can_parse) { + MfDesfireFreeMemoryLayout layout; + bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFreeMemoryLayout)); + data->bytes_free = layout.bytes_free; + } + + data->is_present = can_parse; + + return can_parse; +} + +bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf) { + typedef struct __attribute__((packed)) { + bool is_master_key_changeable : 1; + bool is_free_directory_list : 1; + bool is_free_create_delete : 1; + bool is_config_changeable : 1; + uint8_t change_key_id : 4; + uint8_t max_keys : 4; + uint8_t flags : 4; + } MfDesfireKeySettingsLayout; + + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeySettingsLayout); + + if(can_parse) { + MfDesfireKeySettingsLayout layout; + bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireKeySettingsLayout)); + + data->is_master_key_changeable = layout.is_master_key_changeable; + data->is_free_directory_list = layout.is_free_directory_list; + data->is_free_create_delete = layout.is_free_create_delete; + data->is_config_changeable = layout.is_config_changeable; + + data->change_key_id = layout.change_key_id; + data->max_keys = layout.max_keys; + data->flags = layout.flags; + } + + return can_parse; +} + +bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf) { + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeyVersion); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(MfDesfireKeyVersion)); + } + + return can_parse; +} + +bool mf_desfire_application_id_parse( + MfDesfireApplicationId* data, + uint32_t index, + const BitBuffer* buf) { + const bool can_parse = + bit_buffer_get_size_bytes(buf) >= + (index * sizeof(MfDesfireApplicationId) + sizeof(MfDesfireApplicationId)); + + if(can_parse) { + bit_buffer_write_bytes_mid( + buf, data, index * sizeof(MfDesfireApplicationId), sizeof(MfDesfireApplicationId)); + } + + return can_parse; +} + +bool mf_desfire_file_id_parse(MfDesfireFileId* data, uint32_t index, const BitBuffer* buf) { + const bool can_parse = bit_buffer_get_size_bytes(buf) >= + (index * sizeof(MfDesfireFileId) + sizeof(MfDesfireFileId)); + if(can_parse) { + bit_buffer_write_bytes_mid( + buf, data, index * sizeof(MfDesfireFileId), sizeof(MfDesfireFileId)); + } + + return can_parse; +} + +bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer* buf) { + bool parsed = false; + + typedef struct __attribute__((packed)) { + uint8_t type; + uint8_t comm; + uint16_t access_rights; + } MfDesfireFileSettingsHeader; + + typedef struct __attribute__((packed)) { + uint32_t size : 3 * BITS_IN_BYTE; + } MfDesfireFileSettingsData; + + typedef struct __attribute__((packed)) { + uint32_t lo_limit; + uint32_t hi_limit; + uint32_t limited_credit_value; + uint8_t limited_credit_enabled; + } MfDesfireFileSettingsValue; + + typedef struct __attribute__((packed)) { + uint32_t size : 3 * BITS_IN_BYTE; + uint32_t max : 3 * BITS_IN_BYTE; + uint32_t cur : 3 * BITS_IN_BYTE; + } MfDesfireFileSettingsRecord; + + typedef struct __attribute__((packed)) { + MfDesfireFileSettingsHeader header; + union { + MfDesfireFileSettingsData data; + MfDesfireFileSettingsValue value; + MfDesfireFileSettingsRecord record; + }; + } MfDesfireFileSettingsLayout; + + do { + const size_t data_size = bit_buffer_get_size_bytes(buf); + const size_t min_data_size = + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData); + + if(data_size < min_data_size) break; + + MfDesfireFileSettingsLayout layout; + bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout)); + + data->type = layout.header.type; + data->comm = layout.header.comm; + data->access_rights = layout.header.access_rights; + + if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { + if(data_size != min_data_size) break; + + data->data.size = layout.data.size; + + } else if(data->type == MfDesfireFileTypeValue) { + if(data_size != + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue)) + break; + + data->value.lo_limit = layout.value.lo_limit; + data->value.hi_limit = layout.value.hi_limit; + data->value.limited_credit_value = layout.value.hi_limit; + data->value.limited_credit_enabled = layout.value.limited_credit_enabled; + + } else if( + data->type == MfDesfireFileTypeLinearRecord || + data->type == MfDesfireFileTypeCyclicRecord) { + if(data_size != + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord)) + break; + + data->record.size = layout.record.size; + data->record.max = layout.record.max; + data->record.cur = layout.record.cur; + + } else { + break; + } + + parsed = true; + } while(false); + + return parsed; +} + +bool mf_desfire_file_data_parse(MfDesfireFileData* data, const BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + + if(data_size > 0) { + simple_array_init(data->data, data_size); + bit_buffer_write_bytes(buf, simple_array_get_data(data->data), data_size); + } + + // Success no matter whether there is data or not + return true; +} + +void mf_desfire_file_data_init(MfDesfireFileData* data) { + data->data = simple_array_alloc(&simple_array_config_uint8_t); +} + +void mf_desfire_application_init(MfDesfireApplication* data) { + data->key_versions = simple_array_alloc(&mf_desfire_key_version_array_config); + data->file_ids = simple_array_alloc(&mf_desfire_file_id_array_config); + data->file_settings = simple_array_alloc(&mf_desfire_file_settings_array_config); + data->file_data = simple_array_alloc(&mf_desfire_file_data_array_config); +} + +void mf_desfire_file_data_reset(MfDesfireFileData* data) { + simple_array_free(data->data); + memset(data, 0, sizeof(MfDesfireFileData)); +} + +void mf_desfire_application_reset(MfDesfireApplication* data) { + simple_array_free(data->key_versions); + simple_array_free(data->file_ids); + simple_array_free(data->file_settings); + simple_array_free(data->file_data); + memset(data, 0, sizeof(MfDesfireApplication)); +} + +void mf_desfire_file_data_copy(MfDesfireFileData* data, const MfDesfireFileData* other) { + simple_array_copy(data->data, other->data); +} + +void mf_desfire_application_copy(MfDesfireApplication* data, const MfDesfireApplication* other) { + data->key_settings = other->key_settings; + simple_array_copy(data->key_versions, other->key_versions); + simple_array_copy(data->file_ids, other->file_ids); + simple_array_copy(data->file_settings, other->file_settings); + simple_array_copy(data->file_data, other->file_data); +} + +bool mf_desfire_version_load(MfDesfireVersion* data, FlipperFormat* ff) { + return flipper_format_read_hex( + ff, MF_DESFIRE_FFF_VERSION_KEY, (uint8_t*)data, sizeof(MfDesfireVersion)); +} + +bool mf_desfire_free_memory_load(MfDesfireFreeMemory* data, FlipperFormat* ff) { + data->is_present = flipper_format_key_exist(ff, MF_DESFIRE_FFF_FREE_MEM_KEY); + return data->is_present ? + flipper_format_read_uint32(ff, MF_DESFIRE_FFF_FREE_MEM_KEY, &data->bytes_free, 1) : + true; +} + +bool mf_desfire_key_settings_load( + MfDesfireKeySettings* data, + const char* prefix, + FlipperFormat* ff) { + bool success = false; + + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY); + if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->change_key_id, 1)) break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY); + if(!flipper_format_read_bool(ff, furi_string_get_cstr(key), &data->is_config_changeable, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY); + if(!flipper_format_read_bool( + ff, furi_string_get_cstr(key), &data->is_free_create_delete, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_DIR_LIST_KEY); + if(!flipper_format_read_bool( + ff, furi_string_get_cstr(key), &data->is_free_directory_list, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY); + if(!flipper_format_read_bool( + ff, furi_string_get_cstr(key), &data->is_master_key_changeable, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FLAGS_KEY); + if(flipper_format_key_exist(ff, furi_string_get_cstr(key))) { + if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->flags, 1)) break; + } + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_MAX_KEYS_KEY); + if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->max_keys, 1)) break; + + // TODO: Whaaa + // ks->flags |= ks->max_keys >> 4; + // ks->max_keys &= 0xF; + + success = true; + } while(false); + + furi_string_free(key); + return success; +} + +bool mf_desfire_key_version_load( + MfDesfireKeyVersion* data, + const char* prefix, + uint32_t index, + FlipperFormat* ff) { + FuriString* key = furi_string_alloc_printf( + "%s %s %lu %s", + prefix, + MF_DESFIRE_FFF_KEY_SUB_PREFIX, + index, + MF_DESFIRE_FFF_KEY_VERSION_KEY); + const bool success = flipper_format_read_hex(ff, furi_string_get_cstr(key), data, 1); + furi_string_free(key); + return success; +} + +bool mf_desfire_file_count_load(uint32_t* data, const char* prefix, FlipperFormat* ff) { + FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY); + const bool success = flipper_format_get_value_count(ff, furi_string_get_cstr(key), data); + furi_string_free(key); + return success; +} + +bool mf_desfire_file_ids_load( + MfDesfireFileId* data, + uint32_t count, + const char* prefix, + FlipperFormat* ff) { + FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY); + const bool success = flipper_format_read_hex(ff, furi_string_get_cstr(key), data, count); + furi_string_free(key); + return success; +} + +bool mf_desfire_file_settings_load( + MfDesfireFileSettings* data, + const char* prefix, + FlipperFormat* ff) { + bool success = false; + + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_TYPE_KEY); + if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), (uint8_t*)&data->type, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY); + if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), (uint8_t*)&data->comm, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY); + if(!flipper_format_read_hex( + ff, + furi_string_get_cstr(key), + (uint8_t*)&data->access_rights, + sizeof(MfDesfireFileAccessRights))) + break; + + if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1)) + break; + + } else if(data->type == MfDesfireFileTypeValue) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.hi_limit, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.lo_limit, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY); + if(!flipper_format_read_uint32( + ff, furi_string_get_cstr(key), &data->value.limited_credit_value, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY); + if(!flipper_format_read_bool( + ff, furi_string_get_cstr(key), &data->value.limited_credit_enabled, 1)) + break; + } else if( + data->type == MfDesfireFileTypeLinearRecord || + data->type == MfDesfireFileTypeCyclicRecord) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.size, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_MAX_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.max, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1)) + break; + } + + success = true; + } while(false); + + furi_string_free(key); + + return success; +} + +bool mf_desfire_file_data_load(MfDesfireFileData* data, const char* prefix, FlipperFormat* ff) { + bool success = false; + do { + if(!flipper_format_key_exist(ff, prefix)) { + success = true; + break; + } + + uint32_t data_size; + if(!flipper_format_get_value_count(ff, prefix, &data_size)) break; + + simple_array_init(data->data, data_size); + + if(!flipper_format_read_hex(ff, prefix, simple_array_get_data(data->data), data_size)) + break; + + success = true; + } while(false); + + return success; +} + +bool mf_desfire_application_count_load(uint32_t* data, FlipperFormat* ff) { + return flipper_format_read_uint32(ff, MF_DESFIRE_FFF_APPLICATION_COUNT_KEY, data, 1); +} + +bool mf_desfire_application_ids_load( + MfDesfireApplicationId* data, + uint32_t count, + FlipperFormat* ff) { + return flipper_format_read_hex( + ff, MF_DESFIRE_FFF_APPLICATION_IDS_KEY, data->data, count * sizeof(MfDesfireApplicationId)); +} + +bool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, FlipperFormat* ff) { + FuriString* sub_prefix = furi_string_alloc(); + bool success = false; + + do { + if(!mf_desfire_key_settings_load(&data->key_settings, prefix, ff)) break; + + const uint32_t key_version_count = data->key_settings.max_keys; + simple_array_init(data->key_versions, key_version_count); + + uint32_t i; + for(i = 0; i < key_version_count; ++i) { + if(!mf_desfire_key_version_load(simple_array_get(data->key_versions, i), prefix, i, ff)) + break; + } + + if(i != key_version_count) break; + + uint32_t file_count; + if(!mf_desfire_file_count_load(&file_count, prefix, ff)) break; + + simple_array_init(data->file_ids, file_count); + if(!mf_desfire_file_ids_load(simple_array_get_data(data->file_ids), file_count, prefix, ff)) + break; + + simple_array_init(data->file_settings, file_count); + simple_array_init(data->file_data, file_count); + + for(i = 0; i < file_count; ++i) { + const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i); + furi_string_printf( + sub_prefix, "%s %s %u", prefix, MF_DESFIRE_FFF_FILE_SUB_PREFIX, *file_id); + + MfDesfireFileSettings* file_settings = simple_array_get(data->file_settings, i); + if(!mf_desfire_file_settings_load(file_settings, furi_string_get_cstr(sub_prefix), ff)) + break; + + MfDesfireFileData* file_data = simple_array_get(data->file_data, i); + if(!mf_desfire_file_data_load(file_data, furi_string_get_cstr(sub_prefix), ff)) break; + } + + if(i != file_count) break; + + success = true; + } while(false); + + furi_string_free(sub_prefix); + return success; +} + +bool mf_desfire_version_save(const MfDesfireVersion* data, FlipperFormat* ff) { + return flipper_format_write_hex( + ff, MF_DESFIRE_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(MfDesfireVersion)); +} + +bool mf_desfire_free_memory_save(const MfDesfireFreeMemory* data, FlipperFormat* ff) { + return data->is_present ? + flipper_format_write_uint32(ff, MF_DESFIRE_FFF_FREE_MEM_KEY, &data->bytes_free, 1) : + true; +} + +bool mf_desfire_key_settings_save( + const MfDesfireKeySettings* data, + const char* prefix, + FlipperFormat* ff) { + bool success = false; + + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY); + if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->change_key_id, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY); + if(!flipper_format_write_bool( + ff, furi_string_get_cstr(key), &data->is_config_changeable, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY); + if(!flipper_format_write_bool( + ff, furi_string_get_cstr(key), &data->is_free_create_delete, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_DIR_LIST_KEY); + if(!flipper_format_write_bool( + ff, furi_string_get_cstr(key), &data->is_free_directory_list, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY); + if(!flipper_format_write_bool( + ff, furi_string_get_cstr(key), &data->is_master_key_changeable, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FLAGS_KEY); + if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->flags, 1)) break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_MAX_KEYS_KEY); + if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->max_keys, 1)) break; + + success = true; + } while(false); + + furi_string_free(key); + return success; +} + +bool mf_desfire_key_version_save( + const MfDesfireKeyVersion* data, + const char* prefix, + uint32_t index, + FlipperFormat* ff) { + FuriString* key = furi_string_alloc_printf( + "%s %s %lu %s", + prefix, + MF_DESFIRE_FFF_KEY_SUB_PREFIX, + index, + MF_DESFIRE_FFF_KEY_VERSION_KEY); + const bool success = flipper_format_write_hex(ff, furi_string_get_cstr(key), data, 1); + furi_string_free(key); + return success; +} + +bool mf_desfire_file_ids_save( + const MfDesfireFileId* data, + uint32_t count, + const char* prefix, + FlipperFormat* ff) { + FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY); + const bool success = flipper_format_write_hex(ff, furi_string_get_cstr(key), data, count); + furi_string_free(key); + return success; +} + +bool mf_desfire_file_settings_save( + const MfDesfireFileSettings* data, + const char* prefix, + FlipperFormat* ff) { + bool success = false; + + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_TYPE_KEY); + if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), (const uint8_t*)&data->type, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY); + if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), (const uint8_t*)&data->comm, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY); + if(!flipper_format_write_hex( + ff, + furi_string_get_cstr(key), + (const uint8_t*)&data->access_rights, + sizeof(MfDesfireFileAccessRights))) + break; + + if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY); + if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1)) + break; + + } else if(data->type == MfDesfireFileTypeValue) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY); + if(!flipper_format_write_uint32( + ff, furi_string_get_cstr(key), &data->value.hi_limit, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY); + if(!flipper_format_write_uint32( + ff, furi_string_get_cstr(key), &data->value.lo_limit, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY); + if(!flipper_format_write_uint32( + ff, furi_string_get_cstr(key), &data->value.limited_credit_value, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY); + if(!flipper_format_write_bool( + ff, furi_string_get_cstr(key), &data->value.limited_credit_enabled, 1)) + break; + } else if( + data->type == MfDesfireFileTypeLinearRecord || + data->type == MfDesfireFileTypeCyclicRecord) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY); + if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.size, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_MAX_KEY); + if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.max, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY); + if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1)) + break; + } + + success = true; + } while(false); + + furi_string_free(key); + return success; +} + +bool mf_desfire_file_data_save( + const MfDesfireFileData* data, + const char* prefix, + FlipperFormat* ff) { + const uint32_t data_size = simple_array_get_count(data->data); + return data_size > 0 ? flipper_format_write_hex( + ff, prefix, simple_array_cget_data(data->data), data_size) : + true; +} + +bool mf_desfire_application_count_save(const uint32_t* data, FlipperFormat* ff) { + return flipper_format_write_uint32(ff, MF_DESFIRE_FFF_APPLICATION_COUNT_KEY, data, 1); +} + +bool mf_desfire_application_ids_save( + const MfDesfireApplicationId* data, + uint32_t count, + FlipperFormat* ff) { + return flipper_format_write_hex( + ff, MF_DESFIRE_FFF_APPLICATION_IDS_KEY, data->data, count * sizeof(MfDesfireApplicationId)); +} + +bool mf_desfire_application_save( + const MfDesfireApplication* data, + const char* prefix, + FlipperFormat* ff) { + FuriString* sub_prefix = furi_string_alloc(); + bool success = false; + + do { + if(!mf_desfire_key_settings_save(&data->key_settings, prefix, ff)) break; + + const uint32_t key_version_count = data->key_settings.max_keys; + + uint32_t i; + for(i = 0; i < key_version_count; ++i) { + if(!mf_desfire_key_version_save( + simple_array_cget(data->key_versions, i), prefix, i, ff)) + break; + } + + if(i != key_version_count) break; + + const uint32_t file_count = simple_array_get_count(data->file_ids); + if(!mf_desfire_file_ids_save(simple_array_get_data(data->file_ids), file_count, prefix, ff)) + break; + + for(i = 0; i < file_count; ++i) { + const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i); + furi_string_printf( + sub_prefix, "%s %s %u", prefix, MF_DESFIRE_FFF_FILE_SUB_PREFIX, *file_id); + + const MfDesfireFileSettings* file_settings = simple_array_cget(data->file_settings, i); + if(!mf_desfire_file_settings_save(file_settings, furi_string_get_cstr(sub_prefix), ff)) + break; + + const MfDesfireFileData* file_data = simple_array_cget(data->file_data, i); + if(!mf_desfire_file_data_save(file_data, furi_string_get_cstr(sub_prefix), ff)) break; + } + + if(i != file_count) break; + + success = true; + } while(false); + + furi_string_free(sub_prefix); + return success; +} + +const SimpleArrayConfig mf_desfire_key_version_array_config = { + .init = NULL, + .copy = NULL, + .reset = NULL, + .type_size = sizeof(MfDesfireKeyVersion), +}; + +const SimpleArrayConfig mf_desfire_app_id_array_config = { + .init = NULL, + .copy = NULL, + .reset = NULL, + .type_size = sizeof(MfDesfireApplicationId), +}; + +const SimpleArrayConfig mf_desfire_file_id_array_config = { + .init = NULL, + .copy = NULL, + .reset = NULL, + .type_size = sizeof(MfDesfireFileId), +}; + +const SimpleArrayConfig mf_desfire_file_settings_array_config = { + .init = NULL, + .copy = NULL, + .reset = NULL, + .type_size = sizeof(MfDesfireFileSettings), +}; + +const SimpleArrayConfig mf_desfire_file_data_array_config = { + .init = (SimpleArrayInit)mf_desfire_file_data_init, + .copy = (SimpleArrayCopy)mf_desfire_file_data_copy, + .reset = (SimpleArrayReset)mf_desfire_file_data_reset, + .type_size = sizeof(MfDesfireData), +}; + +const SimpleArrayConfig mf_desfire_application_array_config = { + .init = (SimpleArrayInit)mf_desfire_application_init, + .copy = (SimpleArrayCopy)mf_desfire_application_copy, + .reset = (SimpleArrayReset)mf_desfire_application_reset, + .type_size = sizeof(MfDesfireApplication), +}; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h new file mode 100644 index 000000000000..05381096d169 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h @@ -0,0 +1,140 @@ +#pragma once + +#include "mf_desfire.h" + +#define MF_DESFIRE_FFF_PICC_PREFIX "PICC" +#define MF_DESFIRE_FFF_APP_PREFIX "Application" + +// SimpleArray configurations + +extern const SimpleArrayConfig mf_desfire_key_version_array_config; +extern const SimpleArrayConfig mf_desfire_app_id_array_config; +extern const SimpleArrayConfig mf_desfire_file_id_array_config; +extern const SimpleArrayConfig mf_desfire_file_settings_array_config; +extern const SimpleArrayConfig mf_desfire_file_data_array_config; +extern const SimpleArrayConfig mf_desfire_application_array_config; + +// Parse internal MfDesfire structures + +bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf); + +bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf); + +bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf); + +bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf); + +bool mf_desfire_application_id_parse( + MfDesfireApplicationId* data, + uint32_t index, + const BitBuffer* buf); + +bool mf_desfire_file_id_parse(MfDesfireFileId* data, uint32_t index, const BitBuffer* buf); + +bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer* buf); + +bool mf_desfire_file_data_parse(MfDesfireFileData* data, const BitBuffer* buf); + +// Init internal MfDesfire structures + +void mf_desfire_file_data_init(MfDesfireFileData* data); + +void mf_desfire_application_init(MfDesfireApplication* data); + +// Reset internal MfDesfire structures + +void mf_desfire_file_data_reset(MfDesfireFileData* data); + +void mf_desfire_application_reset(MfDesfireApplication* data); + +// Copy internal MfDesfire structures + +void mf_desfire_file_data_copy(MfDesfireFileData* data, const MfDesfireFileData* other); + +void mf_desfire_application_copy(MfDesfireApplication* data, const MfDesfireApplication* other); + +// Load internal MfDesfire structures + +bool mf_desfire_version_load(MfDesfireVersion* data, FlipperFormat* ff); + +bool mf_desfire_free_memory_load(MfDesfireFreeMemory* data, FlipperFormat* ff); + +bool mf_desfire_key_settings_load( + MfDesfireKeySettings* data, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_key_version_load( + MfDesfireKeyVersion* data, + const char* prefix, + uint32_t index, + FlipperFormat* ff); + +bool mf_desfire_file_count_load(uint32_t* data, const char* prefix, FlipperFormat* ff); + +bool mf_desfire_file_ids_load( + MfDesfireFileId* data, + uint32_t count, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_file_settings_load( + MfDesfireFileSettings* data, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_file_data_load(MfDesfireFileData* data, const char* prefix, FlipperFormat* ff); + +bool mf_desfire_application_count_load(uint32_t* data, FlipperFormat* ff); + +bool mf_desfire_application_ids_load( + MfDesfireApplicationId* data, + uint32_t count, + FlipperFormat* ff); + +bool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, FlipperFormat* ff); + +// Save internal MFDesfire structures + +bool mf_desfire_version_save(const MfDesfireVersion* data, FlipperFormat* ff); + +bool mf_desfire_free_memory_save(const MfDesfireFreeMemory* data, FlipperFormat* ff); + +bool mf_desfire_key_settings_save( + const MfDesfireKeySettings* data, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_key_version_save( + const MfDesfireKeyVersion* data, + const char* prefix, + uint32_t index, + FlipperFormat* ff); + +bool mf_desfire_file_ids_save( + const MfDesfireFileId* data, + uint32_t count, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_file_settings_save( + const MfDesfireFileSettings* data, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_file_data_save( + const MfDesfireFileData* data, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_application_count_save(const uint32_t* data, FlipperFormat* ff); + +bool mf_desfire_application_ids_save( + const MfDesfireApplicationId* data, + uint32_t count, + FlipperFormat* ff); + +bool mf_desfire_application_save( + const MfDesfireApplication* data, + const char* prefix, + FlipperFormat* ff); diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c new file mode 100644 index 000000000000..6fb07091c2ac --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c @@ -0,0 +1,243 @@ +#include "mf_desfire_poller_i.h" + +#include + +#include + +#define TAG "MfDesfirePoller" + +#define MF_DESFIRE_BUF_SIZE_MAX (64U) + +typedef NfcCommand (*MfDesfirePollerReadHandler)(MfDesfirePoller* instance); + +const MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static MfDesfirePoller* mf_desfire_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) { + MfDesfirePoller* instance = malloc(sizeof(MfDesfirePoller)); + instance->iso14443_4a_poller = iso14443_4a_poller; + instance->data = mf_desfire_alloc(); + instance->tx_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX); + instance->rx_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX); + instance->input_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX); + instance->result_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE_MAX); + + instance->mf_desfire_event.data = &instance->mf_desfire_event_data; + + instance->general_event.protocol = NfcProtocolMfDesfire; + instance->general_event.data = &instance->mf_desfire_event; + instance->general_event.instance = instance; + + return instance; +} + +static void mf_desfire_poller_free(MfDesfirePoller* instance) { + furi_assert(instance); + + mf_desfire_free(instance->data); + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + bit_buffer_free(instance->input_buffer); + bit_buffer_free(instance->result_buffer); + free(instance); +} + +static NfcCommand mf_desfire_poller_handler_idle(MfDesfirePoller* instance) { + bit_buffer_reset(instance->input_buffer); + bit_buffer_reset(instance->result_buffer); + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + iso14443_4a_copy( + instance->data->iso14443_4a_data, + iso14443_4a_poller_get_data(instance->iso14443_4a_poller)); + + instance->state = MfDesfirePollerStateReadVersion; + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_version(MfDesfirePoller* instance) { + instance->error = mf_desfire_poller_async_read_version(instance, &instance->data->version); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read version success"); + instance->state = MfDesfirePollerStateReadFreeMemory; + } else { + FURI_LOG_E(TAG, "Failed to read version"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_free_memory(MfDesfirePoller* instance) { + instance->error = + mf_desfire_poller_async_read_free_memory(instance, &instance->data->free_memory); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read free memory success"); + instance->state = MfDesfirePollerStateReadMasterKeySettings; + } else { + FURI_LOG_E(TAG, "Failed to read free memory"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_master_key_settings(MfDesfirePoller* instance) { + instance->error = + mf_desfire_poller_async_read_key_settings(instance, &instance->data->master_key_settings); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read master key settings success"); + instance->state = MfDesfirePollerStateReadMasterKeyVersion; + } else { + FURI_LOG_E(TAG, "Failed to read master key settings"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_master_key_version(MfDesfirePoller* instance) { + instance->error = mf_desfire_poller_async_read_key_versions( + instance, + instance->data->master_key_versions, + instance->data->master_key_settings.max_keys); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read master key version success"); + instance->state = MfDesfirePollerStateReadApplicationIds; + } else { + FURI_LOG_E(TAG, "Failed to read master key version"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_application_ids(MfDesfirePoller* instance) { + instance->error = + mf_desfire_poller_async_read_application_ids(instance, instance->data->application_ids); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read application ids success"); + instance->state = MfDesfirePollerStateReadApplications; + } else { + FURI_LOG_E(TAG, "Failed to read application ids"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_applications(MfDesfirePoller* instance) { + instance->error = mf_desfire_poller_async_read_applications( + instance, instance->data->application_ids, instance->data->applications); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read applications success"); + instance->state = MfDesfirePollerStateReadSuccess; + } else { + FURI_LOG_E(TAG, "Failed to read applications"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_fail(MfDesfirePoller* instance) { + FURI_LOG_D(TAG, "Read Failed"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->mf_desfire_event.data->error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->state = MfDesfirePollerStateIdle; + return command; +} + +static NfcCommand mf_desfire_poller_handler_read_success(MfDesfirePoller* instance) { + FURI_LOG_D(TAG, "Read success."); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->mf_desfire_event.type = MfDesfirePollerEventTypeReadSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const MfDesfirePollerReadHandler mf_desfire_poller_read_handler[MfDesfirePollerStateNum] = { + [MfDesfirePollerStateIdle] = mf_desfire_poller_handler_idle, + [MfDesfirePollerStateReadVersion] = mf_desfire_poller_handler_read_version, + [MfDesfirePollerStateReadFreeMemory] = mf_desfire_poller_handler_read_free_memory, + [MfDesfirePollerStateReadMasterKeySettings] = + mf_desfire_poller_handler_read_master_key_settings, + [MfDesfirePollerStateReadMasterKeyVersion] = mf_desfire_poller_handler_read_master_key_version, + [MfDesfirePollerStateReadApplicationIds] = mf_desfire_poller_handler_read_application_ids, + [MfDesfirePollerStateReadApplications] = mf_desfire_poller_handler_read_applications, + [MfDesfirePollerStateReadFailed] = mf_desfire_poller_handler_read_fail, + [MfDesfirePollerStateReadSuccess] = mf_desfire_poller_handler_read_success, +}; + +static void mf_desfire_poller_set_callback( + MfDesfirePoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand mf_desfire_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + MfDesfirePoller* instance = context; + furi_assert(instance); + furi_assert(instance->callback); + + const Iso14443_4aPollerEvent* iso14443_4a_event = event.data; + furi_assert(iso14443_4a_event); + + NfcCommand command = NfcCommandContinue; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + command = mf_desfire_poller_read_handler[instance->state](instance); + } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) { + instance->mf_desfire_event.type = MfDesfirePollerEventTypeReadFailed; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool mf_desfire_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + MfDesfirePoller* instance = context; + furi_assert(instance); + + const Iso14443_4aPollerEvent* iso14443_4a_event = event.data; + furi_assert(iso14443_4a_event); + + bool protocol_detected = false; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + MfDesfireVersion version = {}; + const MfDesfireError error = mf_desfire_poller_async_read_version(instance, &version); + protocol_detected = (error == MfDesfireErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase mf_desfire_poller = { + .alloc = (NfcPollerAlloc)mf_desfire_poller_alloc, + .free = (NfcPollerFree)mf_desfire_poller_free, + .set_callback = (NfcPollerSetCallback)mf_desfire_poller_set_callback, + .run = (NfcPollerRun)mf_desfire_poller_run, + .detect = (NfcPollerDetect)mf_desfire_poller_detect, + .get_data = (NfcPollerGetData)mf_desfire_poller_get_data, +}; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h new file mode 100644 index 000000000000..360b0508ff00 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h @@ -0,0 +1,31 @@ +#pragma once + +#include "mf_desfire.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfDesfirePoller MfDesfirePoller; + +typedef enum { + MfDesfirePollerEventTypeReadSuccess, + MfDesfirePollerEventTypeReadFailed, +} MfDesfirePollerEventType; + +typedef struct { + union { + MfDesfireError error; + }; +} MfDesfirePollerEventData; + +typedef struct { + MfDesfirePollerEventType type; + MfDesfirePollerEventData* data; +} MfDesfirePollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_defs.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_defs.h new file mode 100644 index 000000000000..0c14ceee4d89 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase mf_desfire_poller; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c new file mode 100644 index 000000000000..64868d07fc3c --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -0,0 +1,518 @@ +#include "mf_desfire_poller_i.h" + +#include + +#include "mf_desfire_i.h" + +#define TAG "MfDesfirePoller" + +MfDesfireError mf_desfire_process_error(Iso14443_4aError error) { + switch(error) { + case Iso14443_4aErrorNone: + return MfDesfireErrorNone; + case Iso14443_4aErrorNotPresent: + return MfDesfireErrorNotPresent; + case Iso14443_4aErrorTimeout: + return MfDesfireErrorTimeout; + default: + return MfDesfireErrorProtocol; + } +} + +MfDesfireError mf_desfire_send_chunks( + MfDesfirePoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(instance->iso14443_4a_poller); + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + MfDesfireError error = MfDesfireErrorNone; + + do { + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer, fwt); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + error = mf_desfire_process_error(iso14443_4a_error); + break; + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_FLAG_HAS_NEXT); + + if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) { + bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t)); + } else { + bit_buffer_reset(rx_buffer); + } + + while(bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_FLAG_HAS_NEXT)) { + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer, fwt); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + error = mf_desfire_process_error(iso14443_4a_error); + break; + } + + bit_buffer_append_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t)); + } + } while(false); + + return error; +} + +MfDesfireError + mf_desfire_poller_async_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VERSION); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks( + instance, + instance->input_buffer, + instance->result_buffer, + MF_DESFIRE_POLLER_STANDARD_FWT_FC); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_version_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError + mf_desfire_poller_async_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FREE_MEMORY); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks( + instance, + instance->input_buffer, + instance->result_buffer, + MF_DESFIRE_POLLER_STANDARD_FWT_FC); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_free_memory_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_async_read_key_settings( + MfDesfirePoller* instance, + MfDesfireKeySettings* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_KEY_SETTINGS); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks( + instance, + instance->input_buffer, + instance->result_buffer, + MF_DESFIRE_POLLER_STANDARD_FWT_FC); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_key_settings_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_async_read_key_versions( + MfDesfirePoller* instance, + SimpleArray* data, + uint32_t count) { + furi_assert(instance); + furi_assert(count > 0); + + simple_array_init(data, count); + + bit_buffer_set_size_bytes(instance->input_buffer, sizeof(uint8_t) * 2); + bit_buffer_set_byte(instance->input_buffer, 0, MF_DESFIRE_CMD_GET_KEY_VERSION); + + MfDesfireError error = MfDesfireErrorNone; + + for(uint32_t i = 0; i < count; ++i) { + bit_buffer_set_byte(instance->input_buffer, 1, i); + + error = mf_desfire_send_chunks( + instance, + instance->input_buffer, + instance->result_buffer, + MF_DESFIRE_POLLER_STANDARD_FWT_FC); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_key_version_parse(simple_array_get(data, i), instance->result_buffer)) { + error = MfDesfireErrorProtocol; + break; + } + } + + return error; +} + +MfDesfireError + mf_desfire_poller_async_read_application_ids(MfDesfirePoller* instance, SimpleArray* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_APPLICATION_IDS); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks( + instance, + instance->input_buffer, + instance->result_buffer, + MF_DESFIRE_POLLER_STANDARD_FWT_FC); + + if(error != MfDesfireErrorNone) break; + + const uint32_t app_id_count = + bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireApplicationId); + if(app_id_count == 0) break; + + simple_array_init(data, app_id_count); + + for(uint32_t i = 0; i < app_id_count; ++i) { + if(!mf_desfire_application_id_parse( + simple_array_get(data, i), i, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + break; + } + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_async_select_application( + MfDesfirePoller* instance, + const MfDesfireApplicationId* id) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_SELECT_APPLICATION); + bit_buffer_append_bytes( + instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId)); + + MfDesfireError error = mf_desfire_send_chunks( + instance, + instance->input_buffer, + instance->result_buffer, + MF_DESFIRE_POLLER_STANDARD_FWT_FC); + + return error; +} + +MfDesfireError + mf_desfire_poller_async_read_file_ids(MfDesfirePoller* instance, SimpleArray* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_IDS); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks( + instance, + instance->input_buffer, + instance->result_buffer, + MF_DESFIRE_POLLER_STANDARD_FWT_FC); + + if(error != MfDesfireErrorNone) break; + + const uint32_t id_count = + bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireFileId); + + if(id_count == 0) break; + simple_array_init(data, id_count); + + for(uint32_t i = 0; i < id_count; ++i) { + if(!mf_desfire_file_id_parse(simple_array_get(data, i), i, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + break; + } + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_async_read_file_settings( + MfDesfirePoller* instance, + MfDesfireFileId id, + MfDesfireFileSettings* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_SETTINGS); + bit_buffer_append_byte(instance->input_buffer, id); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks( + instance, + instance->input_buffer, + instance->result_buffer, + MF_DESFIRE_POLLER_STANDARD_FWT_FC); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_file_settings_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_async_read_file_settings_multi( + MfDesfirePoller* instance, + const SimpleArray* file_ids, + SimpleArray* data) { + furi_assert(instance); + + MfDesfireError error = MfDesfireErrorNone; + + const uint32_t file_id_count = simple_array_get_count(file_ids); + if(file_id_count > 0) { + simple_array_init(data, file_id_count); + } + + for(uint32_t i = 0; i < file_id_count; ++i) { + const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i); + error = mf_desfire_poller_async_read_file_settings( + instance, file_id, simple_array_get(data, i)); + if(error != MfDesfireErrorNone) break; + } + + return error; +} + +MfDesfireError mf_desfire_poller_async_read_file_data( + MfDesfirePoller* instance, + MfDesfireFileId id, + uint32_t offset, + size_t size, + MfDesfireFileData* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_DATA); + bit_buffer_append_byte(instance->input_buffer, id); + bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3); + bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks( + instance, + instance->input_buffer, + instance->result_buffer, + MF_DESFIRE_POLLER_STANDARD_FWT_FC); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_file_data_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_async_read_file_value( + MfDesfirePoller* instance, + MfDesfireFileId id, + MfDesfireFileData* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VALUE); + bit_buffer_append_byte(instance->input_buffer, id); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks( + instance, + instance->input_buffer, + instance->result_buffer, + MF_DESFIRE_POLLER_STANDARD_FWT_FC); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_file_data_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_async_read_file_records( + MfDesfirePoller* instance, + MfDesfireFileId id, + uint32_t offset, + size_t size, + MfDesfireFileData* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_DATA); + bit_buffer_append_byte(instance->input_buffer, id); + bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3); + bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks( + instance, + instance->input_buffer, + instance->result_buffer, + MF_DESFIRE_POLLER_STANDARD_FWT_FC); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_file_data_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_async_read_file_data_multi( + MfDesfirePoller* instance, + const SimpleArray* file_ids, + const SimpleArray* file_settings, + SimpleArray* data) { + furi_assert(instance); + furi_assert(simple_array_get_count(file_ids) == simple_array_get_count(file_settings)); + + MfDesfireError error = MfDesfireErrorNone; + + const uint32_t file_id_count = simple_array_get_count(file_ids); + if(file_id_count > 0) { + simple_array_init(data, file_id_count); + } + + for(uint32_t i = 0; i < file_id_count; ++i) { + const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i); + const MfDesfireFileSettings* file_settings_cur = simple_array_cget(file_settings, i); + const MfDesfireFileType file_type = file_settings_cur->type; + + MfDesfireFileData* file_data = simple_array_get(data, i); + + if(file_type == MfDesfireFileTypeStandard || file_type == MfDesfireFileTypeBackup) { + error = mf_desfire_poller_async_read_file_data( + instance, file_id, 0, file_settings_cur->data.size, file_data); + } else if(file_type == MfDesfireFileTypeValue) { + error = mf_desfire_poller_async_read_file_value(instance, file_id, file_data); + } else if( + file_type == MfDesfireFileTypeLinearRecord || + file_type == MfDesfireFileTypeCyclicRecord) { + error = mf_desfire_poller_async_read_file_records( + instance, file_id, 0, file_settings_cur->data.size, file_data); + } + + if(error != MfDesfireErrorNone) break; + } + + return error; +} + +MfDesfireError mf_desfire_poller_async_read_application( + MfDesfirePoller* instance, + MfDesfireApplication* data) { + furi_assert(instance); + furi_assert(data); + + MfDesfireError error; + + do { + error = mf_desfire_poller_async_read_key_settings(instance, &data->key_settings); + if(error != MfDesfireErrorNone) break; + + error = mf_desfire_poller_async_read_key_versions( + instance, data->key_versions, data->key_settings.max_keys); + if(error != MfDesfireErrorNone) break; + + error = mf_desfire_poller_async_read_file_ids(instance, data->file_ids); + if(error != MfDesfireErrorNone) break; + + error = mf_desfire_poller_async_read_file_settings_multi( + instance, data->file_ids, data->file_settings); + if(error != MfDesfireErrorNone) break; + + error = mf_desfire_poller_async_read_file_data_multi( + instance, data->file_ids, data->file_settings, data->file_data); + if(error != MfDesfireErrorNone) break; + + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_async_read_applications( + MfDesfirePoller* instance, + const SimpleArray* app_ids, + SimpleArray* data) { + furi_assert(instance); + + MfDesfireError error = MfDesfireErrorNone; + + const uint32_t app_id_count = simple_array_get_count(app_ids); + if(app_id_count > 0) { + simple_array_init(data, app_id_count); + } + + for(uint32_t i = 0; i < app_id_count; ++i) { + do { + error = mf_desfire_poller_async_select_application( + instance, simple_array_cget(app_ids, i)); + if(error != MfDesfireErrorNone) break; + + MfDesfireApplication* current_app = simple_array_get(data, i); + error = mf_desfire_poller_async_read_application(instance, current_app); + + } while(false); + } + + return error; +} diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h new file mode 100644 index 000000000000..1f01e65d2716 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h @@ -0,0 +1,129 @@ +#pragma once + +#include "mf_desfire_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_DESFIRE_POLLER_STANDARD_FWT_FC (200000) + +typedef enum { + MfDesfirePollerStateIdle, + MfDesfirePollerStateReadVersion, + MfDesfirePollerStateReadFreeMemory, + MfDesfirePollerStateReadMasterKeySettings, + MfDesfirePollerStateReadMasterKeyVersion, + MfDesfirePollerStateReadApplicationIds, + MfDesfirePollerStateReadApplications, + MfDesfirePollerStateReadFailed, + MfDesfirePollerStateReadSuccess, + + MfDesfirePollerStateNum, +} MfDesfirePollerState; + +typedef enum { + MfDesfirePollerSessionStateIdle, + MfDesfirePollerSessionStateActive, + MfDesfirePollerSessionStateStopRequest, +} MfDesfirePollerSessionState; + +struct MfDesfirePoller { + Iso14443_4aPoller* iso14443_4a_poller; + MfDesfirePollerSessionState session_state; + MfDesfirePollerState state; + MfDesfireError error; + MfDesfireData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + BitBuffer* input_buffer; + BitBuffer* result_buffer; + MfDesfirePollerEventData mf_desfire_event_data; + MfDesfirePollerEvent mf_desfire_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +MfDesfireError mf_desfire_process_error(Iso14443_4aError error); + +const MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance); + +MfDesfireError mf_desfire_send_chunks( + MfDesfirePoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +MfDesfireError + mf_desfire_poller_async_read_version(MfDesfirePoller* instance, MfDesfireVersion* data); + +MfDesfireError + mf_desfire_poller_async_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data); + +MfDesfireError mf_desfire_poller_async_read_key_settings( + MfDesfirePoller* instance, + MfDesfireKeySettings* data); + +MfDesfireError mf_desfire_poller_async_read_key_versions( + MfDesfirePoller* instance, + SimpleArray* data, + uint32_t count); + +MfDesfireError + mf_desfire_poller_async_read_application_ids(MfDesfirePoller* instance, SimpleArray* data); + +MfDesfireError mf_desfire_poller_async_select_application( + MfDesfirePoller* instance, + const MfDesfireApplicationId* id); + +MfDesfireError mf_desfire_poller_async_read_file_ids(MfDesfirePoller* instance, SimpleArray* data); + +MfDesfireError mf_desfire_poller_async_read_file_settings( + MfDesfirePoller* instance, + MfDesfireFileId id, + MfDesfireFileSettings* data); + +MfDesfireError mf_desfire_poller_async_read_file_settings_multi( + MfDesfirePoller* instance, + const SimpleArray* file_ids, + SimpleArray* data); + +MfDesfireError mf_desfire_poller_async_read_file_data( + MfDesfirePoller* instance, + MfDesfireFileId id, + uint32_t offset, + size_t size, + MfDesfireFileData* data); + +MfDesfireError mf_desfire_poller_async_read_file_value( + MfDesfirePoller* instance, + MfDesfireFileId id, + MfDesfireFileData* data); + +MfDesfireError mf_desfire_poller_async_read_file_records( + MfDesfirePoller* instance, + MfDesfireFileId id, + uint32_t offset, + size_t size, + MfDesfireFileData* data); + +MfDesfireError mf_desfire_poller_async_read_file_data_multi( + MfDesfirePoller* instance, + const SimpleArray* file_ids, + const SimpleArray* file_settings, + SimpleArray* data); + +MfDesfireError + mf_desfire_poller_async_read_application(MfDesfirePoller* instance, MfDesfireApplication* data); + +MfDesfireError mf_desfire_poller_async_read_applications( + MfDesfirePoller* instance, + const SimpleArray* app_ids, + SimpleArray* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c new file mode 100644 index 000000000000..2d0774c4d902 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c @@ -0,0 +1,600 @@ +#include "mf_ultralight.h" + +#include + +#define MF_ULTRALIGHT_PROTOCOL_NAME "NTAG/Ultralight" + +#define MF_ULTRALIGHT_FORMAT_VERSION_KEY "Data format version" +#define MF_ULTRALIGHT_TYPE_KEY MF_ULTRALIGHT_PROTOCOL_NAME " type" +#define MF_ULTRALIGHT_SIGNATURE_KEY "Signature" +#define MF_ULTRALIGHT_MIFARE_VERSION_KEY "Mifare version" +#define MF_ULTRALIGHT_COUNTER_KEY "Counter" +#define MF_ULTRALIGHT_TEARING_KEY "Tearing" +#define MF_ULTRALIGHT_PAGES_TOTAL_KEY "Pages total" +#define MF_ULTRALIGHT_PAGES_READ_KEY "Pages read" +#define MF_ULTRALIGHT_PAGE_KEY "Page" +#define MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY "Failed authentication attempts" + +typedef struct { + const char* device_name; + uint16_t total_pages; + uint16_t config_page; + uint32_t feature_set; +} MfUltralightFeatures; + +static const uint32_t mf_ultralight_data_format_version = 2; + +static const MfUltralightFeatures mf_ultralight_features[MfUltralightTypeNum] = { + [MfUltralightTypeUnknown] = + { + .device_name = "Mifare Ultralight", + .total_pages = 16, + .config_page = 0, + .feature_set = MfUltralightFeatureSupportCompatibleWrite, + }, + [MfUltralightTypeNTAG203] = + { + .device_name = "NTAG203", + .total_pages = 42, + .config_page = 0, + .feature_set = MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportCounterInMemory, + }, + [MfUltralightTypeUL11] = + { + .device_name = "Mifare Ultralight 11", + .total_pages = 20, + .config_page = 16, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportReadCounter | + MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportIncCounter | MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportAuthentication | MfUltralightFeatureSupportVcsl, + }, + [MfUltralightTypeUL21] = + { + .device_name = "Mifare Ultralight 21", + .total_pages = 41, + .config_page = 37, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportReadCounter | + MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportIncCounter | MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportAuthentication | MfUltralightFeatureSupportVcsl, + }, + [MfUltralightTypeNTAG213] = + { + .device_name = "NTAG213", + .total_pages = 45, + .config_page = 41, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportAuthentication | + MfUltralightFeatureSupportSingleCounter | MfUltralightFeatureSupportAsciiMirror, + }, + [MfUltralightTypeNTAG215] = + { + .device_name = "NTAG215", + .total_pages = 135, + .config_page = 131, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportAuthentication | + MfUltralightFeatureSupportSingleCounter | MfUltralightFeatureSupportAsciiMirror, + }, + [MfUltralightTypeNTAG216] = + { + .device_name = "NTAG216", + .total_pages = 231, + .config_page = 227, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportAuthentication | + MfUltralightFeatureSupportSingleCounter | MfUltralightFeatureSupportAsciiMirror, + }, + [MfUltralightTypeNTAGI2C1K] = + { + .device_name = "NTAG I2C 1K", + .total_pages = 231, + .config_page = 0, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportFastWrite | MfUltralightFeatureSupportSectorSelect, + }, + [MfUltralightTypeNTAGI2C2K] = + { + .device_name = "NTAG I2C 2K", + .total_pages = 485, + .config_page = 0, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportFastWrite | MfUltralightFeatureSupportSectorSelect, + }, + [MfUltralightTypeNTAGI2CPlus1K] = + { + .device_name = "NTAG I2C Plus 1K", + .total_pages = 236, + .config_page = 227, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportFastRead | MfUltralightFeatureSupportAuthentication | + MfUltralightFeatureSupportSectorSelect, + }, + [MfUltralightTypeNTAGI2CPlus2K] = + { + .device_name = "NTAG I2C Plus 2K", + .total_pages = 492, + .config_page = 227, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportFastRead | MfUltralightFeatureSupportAuthentication | + MfUltralightFeatureSupportSectorSelect, + }, +}; + +const NfcDeviceBase nfc_device_mf_ultralight = { + .protocol_name = MF_ULTRALIGHT_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)mf_ultralight_alloc, + .free = (NfcDeviceFree)mf_ultralight_free, + .reset = (NfcDeviceReset)mf_ultralight_reset, + .copy = (NfcDeviceCopy)mf_ultralight_copy, + .verify = (NfcDeviceVerify)mf_ultralight_verify, + .load = (NfcDeviceLoad)mf_ultralight_load, + .save = (NfcDeviceSave)mf_ultralight_save, + .is_equal = (NfcDeviceEqual)mf_ultralight_is_equal, + .get_name = (NfcDeviceGetName)mf_ultralight_get_device_name, + .get_uid = (NfcDeviceGetUid)mf_ultralight_get_uid, + .set_uid = (NfcDeviceSetUid)mf_ultralight_set_uid, + .get_base_data = (NfcDeviceGetBaseData)mf_ultralight_get_base_data, +}; + +MfUltralightData* mf_ultralight_alloc() { + MfUltralightData* data = malloc(sizeof(MfUltralightData)); + data->iso14443_3a_data = iso14443_3a_alloc(); + return data; +} + +void mf_ultralight_free(MfUltralightData* data) { + furi_assert(data); + + iso14443_3a_free(data->iso14443_3a_data); + free(data); +} + +void mf_ultralight_reset(MfUltralightData* data) { + furi_assert(data); + + iso14443_3a_reset(data->iso14443_3a_data); +} + +void mf_ultralight_copy(MfUltralightData* data, const MfUltralightData* other) { + furi_assert(data); + furi_assert(other); + + iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data); + for(size_t i = 0; i < COUNT_OF(data->counter); i++) { + data->counter[i] = other->counter[i]; + } + for(size_t i = 0; i < COUNT_OF(data->tearing_flag); i++) { + data->tearing_flag[i] = other->tearing_flag[i]; + } + for(size_t i = 0; i < COUNT_OF(data->page); i++) { + data->page[i] = other->page[i]; + } + + data->type = other->type; + data->version = other->version; + data->signature = other->signature; + + data->pages_read = other->pages_read; + data->pages_total = other->pages_total; + data->auth_attempts = other->auth_attempts; +} + +static const char* + mf_ultralight_get_device_name_by_type(MfUltralightType type, NfcDeviceNameType name_type) { + if(name_type == NfcDeviceNameTypeShort && + (type == MfUltralightTypeUL11 || type == MfUltralightTypeUL21)) { + type = MfUltralightTypeUnknown; + } + + return mf_ultralight_features[type].device_name; +} + +bool mf_ultralight_verify(MfUltralightData* data, const FuriString* device_type) { + furi_assert(data); + + bool verified = false; + + for(MfUltralightType i = 0; i < MfUltralightTypeNum; i++) { + const char* name = mf_ultralight_get_device_name_by_type(i, NfcDeviceNameTypeFull); + verified = furi_string_equal(device_type, name); + if(verified) { + data->type = i; + break; + } + } + + return verified; +} + +bool mf_ultralight_load(MfUltralightData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + FuriString* temp_str = furi_string_alloc(); + bool parsed = false; + + do { + // Read ISO14443_3A data + if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break; + + // Read Ultralight specific data + // Read Mifare Ultralight format version + uint32_t data_format_version = 0; + if(!flipper_format_read_uint32( + ff, MF_ULTRALIGHT_FORMAT_VERSION_KEY, &data_format_version, 1)) { + if(!flipper_format_rewind(ff)) break; + } + + // Read Mifare Ultralight type + if(data_format_version > 1) { + if(!flipper_format_read_string(ff, MF_ULTRALIGHT_TYPE_KEY, temp_str)) break; + if(!mf_ultralight_verify(data, temp_str)) break; + } + + // Read signature + if(!flipper_format_read_hex( + ff, + MF_ULTRALIGHT_SIGNATURE_KEY, + data->signature.data, + sizeof(MfUltralightSignature))) + break; + // Read Mifare version + if(!flipper_format_read_hex( + ff, + MF_ULTRALIGHT_MIFARE_VERSION_KEY, + (uint8_t*)&data->version, + sizeof(MfUltralightVersion))) + break; + // Read counters and tearing flags + bool counters_parsed = true; + for(size_t i = 0; i < 3; i++) { + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_COUNTER_KEY, i); + if(!flipper_format_read_uint32( + ff, furi_string_get_cstr(temp_str), &data->counter[i].counter, 1)) { + counters_parsed = false; + break; + } + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_TEARING_KEY, i); + if(!flipper_format_read_hex( + ff, furi_string_get_cstr(temp_str), data->tearing_flag[i].data, 1)) { + counters_parsed = false; + break; + } + } + if(!counters_parsed) break; + // Read pages + uint32_t pages_total = 0; + if(!flipper_format_read_uint32(ff, MF_ULTRALIGHT_PAGES_TOTAL_KEY, &pages_total, 1)) break; + uint32_t pages_read = 0; + if(data_format_version < mf_ultralight_data_format_version) { + pages_read = pages_total; + } else { + if(!flipper_format_read_uint32(ff, MF_ULTRALIGHT_PAGES_READ_KEY, &pages_read, 1)) + break; + } + data->pages_total = pages_total; + data->pages_read = pages_read; + + if((pages_read > MF_ULTRALIGHT_MAX_PAGE_NUM) || (pages_total > MF_ULTRALIGHT_MAX_PAGE_NUM)) + break; + + bool pages_parsed = true; + for(size_t i = 0; i < pages_total; i++) { + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_PAGE_KEY, i); + if(!flipper_format_read_hex( + ff, + furi_string_get_cstr(temp_str), + data->page[i].data, + sizeof(MfUltralightPage))) { + pages_parsed = false; + break; + } + } + if(!pages_parsed) break; + + // Read authentication counter + if(!flipper_format_read_uint32( + ff, MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY, &data->auth_attempts, 1)) { + data->auth_attempts = 0; + } + + parsed = true; + } while(false); + + furi_string_free(temp_str); + + return parsed; +} + +bool mf_ultralight_save(const MfUltralightData* data, FlipperFormat* ff) { + furi_assert(data); + + FuriString* temp_str = furi_string_alloc(); + bool saved = false; + + do { + if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break; + + if(!flipper_format_write_comment_cstr(ff, MF_ULTRALIGHT_PROTOCOL_NAME " specific data")) + break; + if(!flipper_format_write_uint32( + ff, MF_ULTRALIGHT_FORMAT_VERSION_KEY, &mf_ultralight_data_format_version, 1)) + break; + + const char* device_type_name = + mf_ultralight_get_device_name_by_type(data->type, NfcDeviceNameTypeFull); + if(!flipper_format_write_string_cstr(ff, MF_ULTRALIGHT_TYPE_KEY, device_type_name)) break; + if(!flipper_format_write_hex( + ff, + MF_ULTRALIGHT_SIGNATURE_KEY, + data->signature.data, + sizeof(MfUltralightSignature))) + break; + if(!flipper_format_write_hex( + ff, + MF_ULTRALIGHT_MIFARE_VERSION_KEY, + (uint8_t*)&data->version, + sizeof(MfUltralightVersion))) + break; + + // Write conters and tearing flags data + bool counters_saved = true; + for(size_t i = 0; i < 3; i++) { + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_COUNTER_KEY, i); + if(!flipper_format_write_uint32( + ff, furi_string_get_cstr(temp_str), &data->counter[i].counter, 1)) { + counters_saved = false; + break; + } + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_TEARING_KEY, i); + if(!flipper_format_write_hex( + ff, furi_string_get_cstr(temp_str), data->tearing_flag[i].data, 1)) { + counters_saved = false; + break; + } + } + if(!counters_saved) break; + + // Write pages data + uint32_t pages_total = data->pages_total; + uint32_t pages_read = data->pages_read; + if(!flipper_format_write_uint32(ff, MF_ULTRALIGHT_PAGES_TOTAL_KEY, &pages_total, 1)) break; + if(!flipper_format_write_uint32(ff, MF_ULTRALIGHT_PAGES_READ_KEY, &pages_read, 1)) break; + bool pages_saved = true; + for(size_t i = 0; i < data->pages_total; i++) { + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_PAGE_KEY, i); + if(!flipper_format_write_hex( + ff, + furi_string_get_cstr(temp_str), + data->page[i].data, + sizeof(MfUltralightPage))) { + pages_saved = false; + break; + } + } + if(!pages_saved) break; + + // Write authentication counter + if(!flipper_format_write_uint32( + ff, MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY, &data->auth_attempts, 1)) + break; + + saved = true; + } while(false); + + furi_string_free(temp_str); + + return saved; +} + +bool mf_ultralight_is_equal(const MfUltralightData* data, const MfUltralightData* other) { + bool is_equal = false; + bool data_array_is_equal = true; + + do { + if(!iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data)) break; + if(data->type != other->type) break; + if(data->pages_read != other->pages_read) break; + if(data->pages_total != other->pages_total) break; + if(data->auth_attempts != other->auth_attempts) break; + + if(memcmp(&data->version, &other->version, sizeof(data->version))) break; + if(memcmp(&data->signature, &other->signature, sizeof(data->signature))) break; + + for(size_t i = 0; i < COUNT_OF(data->counter); i++) { + if(memcmp(&data->counter[i], &other->counter[i], sizeof(data->counter[i]))) { + data_array_is_equal = false; + break; + } + } + if(!data_array_is_equal) break; + + for(size_t i = 0; i < COUNT_OF(data->tearing_flag); i++) { + if(memcmp( + &data->tearing_flag[i], + &other->tearing_flag[i], + sizeof(data->tearing_flag[i]))) { + data_array_is_equal = false; + break; + } + } + if(!data_array_is_equal) break; + + for(size_t i = 0; i < COUNT_OF(data->page); i++) { + if(memcmp(&data->page[i], &other->page[i], sizeof(data->page[i]))) { + data_array_is_equal = false; + break; + } + } + if(!data_array_is_equal) break; + + is_equal = true; + } while(false); + + return is_equal; +} + +// TODO: Improve this function +const char* + mf_ultralight_get_device_name(const MfUltralightData* data, NfcDeviceNameType name_type) { + furi_assert(data); + furi_assert(data->type < MfUltralightTypeNum); + + return mf_ultralight_get_device_name_by_type(data->type, name_type); +} + +const uint8_t* mf_ultralight_get_uid(const MfUltralightData* data, size_t* uid_len) { + furi_assert(data); + + return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len); +} + +bool mf_ultralight_set_uid(MfUltralightData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len); +} + +Iso14443_3aData* mf_ultralight_get_base_data(const MfUltralightData* data) { + furi_assert(data); + + return data->iso14443_3a_data; +} + +MfUltralightType mf_ultralight_get_type_by_version(MfUltralightVersion* version) { + furi_assert(version); + + MfUltralightType type = MfUltralightTypeUnknown; + + if(version->storage_size == 0x0B || version->storage_size == 0x00) { + type = MfUltralightTypeUL11; + } else if(version->storage_size == 0x0E) { + type = MfUltralightTypeUL21; + } else if(version->storage_size == 0x0F) { + type = MfUltralightTypeNTAG213; + } else if(version->storage_size == 0x11) { + type = MfUltralightTypeNTAG215; + } else if(version->prod_subtype == 5 && version->prod_ver_major == 2) { + if(version->prod_ver_minor == 1) { + if(version->storage_size == 0x13) { + type = MfUltralightTypeNTAGI2C1K; + } else if(version->storage_size == 0x15) { + type = MfUltralightTypeNTAGI2C2K; + } + } else if(version->prod_ver_minor == 2) { + if(version->storage_size == 0x13) { + type = MfUltralightTypeNTAGI2CPlus1K; + } else if(version->storage_size == 0x15) { + type = MfUltralightTypeNTAGI2CPlus2K; + } + } + } else if(version->storage_size == 0x13) { + type = MfUltralightTypeNTAG216; + } + + return type; +} + +uint16_t mf_ultralight_get_pages_total(MfUltralightType type) { + return mf_ultralight_features[type].total_pages; +} + +uint32_t mf_ultralight_get_feature_support_set(MfUltralightType type) { + return mf_ultralight_features[type].feature_set; +} + +bool mf_ultralight_detect_protocol(const Iso14443_3aData* iso14443_3a_data) { + furi_assert(iso14443_3a_data); + + bool mfu_detected = (iso14443_3a_data->atqa[0] == 0x44) && + (iso14443_3a_data->atqa[1] == 0x00) && (iso14443_3a_data->sak == 0x00); + + return mfu_detected; +} + +uint16_t mf_ultralight_get_config_page_num(MfUltralightType type) { + return mf_ultralight_features[type].config_page; +} + +bool mf_ultralight_support_feature(const uint32_t feature_set, const uint32_t features_to_check) { + return (feature_set & features_to_check) != 0; +} + +bool mf_ultralight_get_config_page(const MfUltralightData* data, MfUltralightConfigPages** config) { + furi_assert(data); + furi_assert(config); + + bool config_pages_found = false; + + uint16_t config_page = mf_ultralight_features[data->type].config_page; + if(config_page != 0) { + *config = (MfUltralightConfigPages*)&data->page[config_page]; + config_pages_found = true; + } + + return config_pages_found; +} + +bool mf_ultralight_is_all_data_read(const MfUltralightData* data) { + furi_assert(data); + + bool all_read = false; + if(data->pages_read == data->pages_total) { + // Having read all the pages doesn't mean that we've got everything. + // By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000, + // so a default read on an auth-supported NTAG is never complete. + uint32_t feature_set = mf_ultralight_get_feature_support_set(data->type); + if(feature_set & MfUltralightFeatureSupportAuthentication) { + all_read = true; + } else { + MfUltralightConfigPages* config = NULL; + if(mf_ultralight_get_config_page(data, &config)) { + all_read = ((config->password.pass != 0) || (config->pack.pack != 0)); + } + } + } + + return all_read; +} + +bool mf_ultralight_is_counter_configured(const MfUltralightData* data) { + furi_assert(data); + + MfUltralightConfigPages* config = NULL; + bool configured = false; + + switch(data->type) { + case MfUltralightTypeNTAG213: + case MfUltralightTypeNTAG215: + case MfUltralightTypeNTAG216: + if(mf_ultralight_get_config_page(data, &config)) { + configured = config->access.nfc_cnt_en; + } + break; + + default: + configured = true; + break; + } + + return configured; +} diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h new file mode 100644 index 000000000000..0727adc07064 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h @@ -0,0 +1,218 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_ULTRALIGHT_TEARING_FLAG_DEFAULT (0xBD) + +#define MF_ULTRALIGHT_CMD_GET_VERSION (0x60) +#define MF_ULTRALIGHT_CMD_READ_PAGE (0x30) +#define MF_ULTRALIGHT_CMD_SECTOR_SELECT (0xC2) +#define MF_ULTRALIGHT_CMD_COMP_WRITE (0xA0) +#define MF_ULTRALIGHT_CMD_WRITE_PAGE (0xA2) +#define MF_ULTRALIGTH_CMD_READ_SIG (0x3C) +#define MF_ULTRALIGHT_CMD_READ_CNT (0x39) +#define MF_ULTRALIGHT_CMD_INCR_CNT (0xA5) +#define MF_ULTRALIGHT_CMD_CHECK_TEARING (0x3E) +#define MF_ULTRALIGHT_CMD_AUTH (0x1B) + +#define MF_ULTRALIGHT_CMD_ACK (0x0A) +#define MF_ULTRALIGHT_CMD_NACK (0x00) + +#define MF_ULTRALIGHT_MAX_CNTR_VAL (0x00FFFFFF) +#define MF_ULTRALIGHT_MAX_PAGE_NUM (510) +#define MF_ULTRALIGHT_PAGE_SIZE (4U) +#define MF_ULTRALIGHT_SIGNATURE_SIZE (32) +#define MF_ULTRALIGHT_COUNTER_SIZE (3) +#define MF_ULTRALIGHT_COUNTER_NUM (3) +#define MF_ULTRALIGHT_TEARING_FLAG_SIZE (1) +#define MF_ULTRALIGHT_TEARING_FLAG_NUM (3) +#define MF_ULTRALIGHT_AUTH_PASSWORD_SIZE (4) +#define MF_ULTRALIGHT_AUTH_PACK_SIZE (2) + +typedef enum { + MfUltralightErrorNone, + MfUltralightErrorNotPresent, + MfUltralightErrorProtocol, + MfUltralightErrorAuth, + MfUltralightErrorTimeout, +} MfUltralightError; + +typedef enum { + MfUltralightTypeUnknown, + MfUltralightTypeNTAG203, + MfUltralightTypeUL11, + MfUltralightTypeUL21, + MfUltralightTypeNTAG213, + MfUltralightTypeNTAG215, + MfUltralightTypeNTAG216, + MfUltralightTypeNTAGI2C1K, + MfUltralightTypeNTAGI2C2K, + MfUltralightTypeNTAGI2CPlus1K, + MfUltralightTypeNTAGI2CPlus2K, + + MfUltralightTypeNum, +} MfUltralightType; + +typedef enum { + MfUltralightFeatureSupportReadVersion = (1U << 0), + MfUltralightFeatureSupportReadSignature = (1U << 1), + MfUltralightFeatureSupportReadCounter = (1U << 2), + MfUltralightFeatureSupportCheckTearingFlag = (1U << 3), + MfUltralightFeatureSupportFastRead = (1U << 4), + MfUltralightFeatureSupportIncCounter = (1U << 5), + MfUltralightFeatureSupportFastWrite = (1U << 6), + MfUltralightFeatureSupportCompatibleWrite = (1U << 7), + MfUltralightFeatureSupportAuthentication = (1U << 8), + MfUltralightFeatureSupportVcsl = (1U << 9), + MfUltralightFeatureSupportSectorSelect = (1U << 10), + MfUltralightFeatureSupportSingleCounter = (1U << 11), + MfUltralightFeatureSupportAsciiMirror = (1U << 12), + MfUltralightFeatureSupportCounterInMemory = (1U << 13), +} MfUltralightFeatureSupport; + +typedef struct { + uint8_t data[MF_ULTRALIGHT_PAGE_SIZE]; +} MfUltralightPage; + +typedef struct { + MfUltralightPage page[4]; +} MfUltralightPageReadCommandData; + +typedef struct { + uint8_t header; + uint8_t vendor_id; + uint8_t prod_type; + uint8_t prod_subtype; + uint8_t prod_ver_major; + uint8_t prod_ver_minor; + uint8_t storage_size; + uint8_t protocol_type; +} MfUltralightVersion; + +typedef struct { + uint8_t data[MF_ULTRALIGHT_SIGNATURE_SIZE]; +} MfUltralightSignature; + +typedef union { + uint32_t counter; + uint8_t data[MF_ULTRALIGHT_COUNTER_SIZE]; +} MfUltralightCounter; + +typedef struct { + uint8_t data[MF_ULTRALIGHT_TEARING_FLAG_SIZE]; +} MfUltralightTearingFlag; + +typedef union { + uint8_t data[MF_ULTRALIGHT_AUTH_PASSWORD_SIZE]; + uint32_t pass; +} MfUltralightAuthPassword; + +typedef union { + uint8_t data[MF_ULTRALIGHT_AUTH_PACK_SIZE]; + uint16_t pack; +} MfUltralightAuthPack; + +typedef enum { + MfUltralightMirrorNone, + MfUltralightMirrorUid, + MfUltralightMirrorCounter, + MfUltralightMirrorUidCounter, +} MfUltralightMirrorConf; + +typedef struct __attribute__((packed)) { + union { + uint8_t value; + struct { + uint8_t rfui1 : 2; + bool strg_mod_en : 1; + bool rfui2 : 1; + uint8_t mirror_byte : 2; + MfUltralightMirrorConf mirror_conf : 2; + }; + } mirror; + uint8_t rfui1; + uint8_t mirror_page; + uint8_t auth0; + union { + uint8_t value; + struct { + uint8_t authlim : 3; + bool nfc_cnt_pwd_prot : 1; + bool nfc_cnt_en : 1; + bool nfc_dis_sec1 : 1; // NTAG I2C Plus only + bool cfglck : 1; + bool prot : 1; + }; + } access; + uint8_t vctid; + uint8_t rfui2[2]; + MfUltralightAuthPassword password; + MfUltralightAuthPack pack; + uint8_t rfui3[2]; +} MfUltralightConfigPages; + +typedef struct { + Iso14443_3aData* iso14443_3a_data; + MfUltralightType type; + MfUltralightVersion version; + MfUltralightSignature signature; + MfUltralightCounter counter[MF_ULTRALIGHT_COUNTER_NUM]; + MfUltralightTearingFlag tearing_flag[MF_ULTRALIGHT_TEARING_FLAG_NUM]; + MfUltralightPage page[MF_ULTRALIGHT_MAX_PAGE_NUM]; + uint16_t pages_read; + uint16_t pages_total; + uint32_t auth_attempts; +} MfUltralightData; + +extern const NfcDeviceBase nfc_device_mf_ultralight; + +MfUltralightData* mf_ultralight_alloc(); + +void mf_ultralight_free(MfUltralightData* data); + +void mf_ultralight_reset(MfUltralightData* data); + +void mf_ultralight_copy(MfUltralightData* data, const MfUltralightData* other); + +bool mf_ultralight_verify(MfUltralightData* data, const FuriString* device_type); + +bool mf_ultralight_load(MfUltralightData* data, FlipperFormat* ff, uint32_t version); + +bool mf_ultralight_save(const MfUltralightData* data, FlipperFormat* ff); + +bool mf_ultralight_is_equal(const MfUltralightData* data, const MfUltralightData* other); + +const char* + mf_ultralight_get_device_name(const MfUltralightData* data, NfcDeviceNameType name_type); + +const uint8_t* mf_ultralight_get_uid(const MfUltralightData* data, size_t* uid_len); + +bool mf_ultralight_set_uid(MfUltralightData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_3aData* mf_ultralight_get_base_data(const MfUltralightData* data); + +MfUltralightType mf_ultralight_get_type_by_version(MfUltralightVersion* version); + +uint16_t mf_ultralight_get_pages_total(MfUltralightType type); + +uint32_t mf_ultralight_get_feature_support_set(MfUltralightType type); + +uint16_t mf_ultralight_get_config_page_num(MfUltralightType type); + +bool mf_ultralight_support_feature(const uint32_t feature_set, const uint32_t features_to_check); + +bool mf_ultralight_get_config_page(const MfUltralightData* data, MfUltralightConfigPages** config); + +bool mf_ultralight_is_all_data_read(const MfUltralightData* data); + +bool mf_ultralight_detect_protocol(const Iso14443_3aData* iso14443_3a_data); + +bool mf_ultralight_is_counter_configured(const MfUltralightData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c new file mode 100644 index 000000000000..97d8376a1fae --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c @@ -0,0 +1,569 @@ +#include "mf_ultralight_listener_i.h" +#include "mf_ultralight_listener_defs.h" + +#include + +#include + +#define TAG "MfUltralightListener" + +#define MF_ULTRALIGHT_LISTENER_MAX_TX_BUFF_SIZE (32) + +typedef enum { + MfUltralightListenerAccessTypeRead, + MfUltralightListenerAccessTypeWrite, +} MfUltralightListenerAccessType; + +typedef struct { + uint8_t cmd; + size_t cmd_len_bits; + MfUltralightListenerCommandCallback callback; +} MfUltralightListenerCmdHandler; + +static bool mf_ultralight_listener_check_access( + MfUltralightListener* instance, + uint8_t start_page, + MfUltralightListenerAccessType access_type) { + bool access_success = false; + bool is_write_op = (access_type == MfUltralightListenerAccessTypeWrite); + + do { + if((instance->features & MfUltralightFeatureSupportAuthentication) == 0) { + access_success = true; + break; + } + if(instance->auth_state != MfUltralightListenerAuthStateSuccess) { + if((instance->config->auth0 <= start_page) && + (instance->config->access.prot || is_write_op)) { + break; + } + } + if(instance->config->access.cfglck && is_write_op) { + uint16_t config_page_start = instance->data->pages_total - 4; + if((start_page == config_page_start) || (start_page == config_page_start + 1)) { + break; + } + } + + access_success = true; + } while(false); + + return access_success; +} + +static void mf_ultralight_listener_send_short_resp(MfUltralightListener* instance, uint8_t data) { + furi_assert(instance->tx_buffer); + + bit_buffer_set_size(instance->tx_buffer, 4); + bit_buffer_set_byte(instance->tx_buffer, 0, data); + iso14443_3a_listener_tx(instance->iso14443_3a_listener, instance->tx_buffer); +}; + +static MfUltralightCommand + mf_ultralight_listener_read_page_handler(MfUltralightListener* instance, BitBuffer* buffer) { + uint8_t start_page = bit_buffer_get_byte(buffer, 1); + uint16_t pages_total = instance->data->pages_total; + MfUltralightPageReadCommandData read_cmd_data = {}; + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + + FURI_LOG_D(TAG, "CMD_READ: %d", start_page); + + if(pages_total < start_page) { + instance->state = MfUltraligthListenerStateIdle; + instance->auth_state = MfUltralightListenerAuthStateIdle; + } else if(!mf_ultralight_listener_check_access( + instance, start_page, MfUltralightListenerAccessTypeRead)) { + instance->state = MfUltraligthListenerStateIdle; + instance->auth_state = MfUltralightListenerAuthStateIdle; + } else { + mf_ultralight_mirror_read_prepare(start_page, instance); + + uint16_t config_page = mf_ultralight_get_config_page_num(instance->data->type); + for(size_t i = 0; i < 4; i++) { + bool hide_data = + ((config_page != 0) && ((i == config_page + 1U) || (i == config_page + 2U))); + if(hide_data) { + memset(read_cmd_data.page[i].data, 0, sizeof(MfUltralightPage)); + } else { + uint8_t current_page = start_page + i; + read_cmd_data.page[i] = instance->data->page[current_page % pages_total]; + + mf_ultralight_mirror_read_handler( + current_page, read_cmd_data.page[i].data, instance); + } + } + + bit_buffer_copy_bytes( + instance->tx_buffer, + (uint8_t*)&read_cmd_data, + sizeof(MfUltralightPageReadCommandData)); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + } + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_write_page_handler(MfUltralightListener* instance, BitBuffer* buffer) { + uint8_t start_page = bit_buffer_get_byte(buffer, 1); + uint16_t pages_total = instance->data->pages_total; + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + + FURI_LOG_D(TAG, "CMD_WRITE"); + + if(pages_total < start_page) { + instance->state = MfUltraligthListenerStateIdle; + instance->auth_state = MfUltralightListenerAuthStateIdle; + } else if(!mf_ultralight_listener_check_access( + instance, start_page, MfUltralightListenerAccessTypeWrite)) { + instance->state = MfUltraligthListenerStateIdle; + instance->auth_state = MfUltralightListenerAuthStateIdle; + } else { + const uint8_t* rx_data = bit_buffer_get_data(buffer); + memcpy(instance->data->page[start_page].data, &rx_data[2], sizeof(MfUltralightPage)); + mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_ACK); + command = MfUltralightCommandProcessed; + } + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_read_version_handler(MfUltralightListener* instance, BitBuffer* buffer) { + UNUSED(buffer); + MfUltralightCommand command = MfUltralightCommandNotProcessedSilent; + + FURI_LOG_D(TAG, "CMD_GET_VERSION"); + + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportReadVersion)) { + bit_buffer_copy_bytes( + instance->tx_buffer, (uint8_t*)&instance->data->version, sizeof(MfUltralightVersion)); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + } else { + instance->state = MfUltraligthListenerStateIdle; + } + + return command; +} + +static MfUltralightCommand mf_ultralight_listener_read_signature_handler( + MfUltralightListener* instance, + BitBuffer* buffer) { + UNUSED(buffer); + MfUltralightCommand command = MfUltralightCommandNotProcessedSilent; + + FURI_LOG_D(TAG, "CMD_READ_SIG"); + + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportReadSignature)) { + bit_buffer_copy_bytes( + instance->tx_buffer, instance->data->signature.data, sizeof(MfUltralightSignature)); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + } else { + instance->state = MfUltraligthListenerStateIdle; + } + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_read_counter_handler(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + + FURI_LOG_D(TAG, "CMD_READ_CNT"); + + do { + uint8_t counter_num = bit_buffer_get_byte(buffer, 1); + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportReadCounter)) + break; + + if(mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportSingleCounter)) { + if(instance->config == NULL) break; + + if(!instance->config->access.nfc_cnt_en || counter_num != 2) break; + + if(instance->config->access.nfc_cnt_pwd_prot) { + if(instance->auth_state != MfUltralightListenerAuthStateSuccess) { + break; + } + } + } + + if(counter_num > 2) break; + uint8_t cnt_value[3] = { + (instance->data->counter[counter_num].counter >> 0) & 0xff, + (instance->data->counter[counter_num].counter >> 8) & 0xff, + (instance->data->counter[counter_num].counter >> 16) & 0xff, + }; + bit_buffer_copy_bytes(instance->tx_buffer, cnt_value, sizeof(cnt_value)); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + } while(false); + + return command; +} + +static MfUltralightCommand mf_ultralight_listener_increase_counter_handler( + MfUltralightListener* instance, + BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + + FURI_LOG_D(TAG, "CMD_INCR_CNT"); + + do { + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportIncCounter)) { + command = MfUltralightCommandNotProcessedSilent; + break; + } + + uint8_t counter_num = bit_buffer_get_byte(buffer, 1); + if(counter_num > 2) break; + + if(instance->data->counter[counter_num].counter == MF_ULTRALIGHT_MAX_CNTR_VAL) { + command = MfUltralightCommandProcessed; + break; + } + + MfUltralightCounter buf_counter = {}; + bit_buffer_write_bytes_mid(buffer, buf_counter.data, 2, sizeof(buf_counter.data)); + uint32_t incr_value = buf_counter.counter; + + if(instance->data->counter[counter_num].counter + incr_value > MF_ULTRALIGHT_MAX_CNTR_VAL) + break; + + instance->data->counter[counter_num].counter += incr_value; + mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_ACK); + command = MfUltralightCommandProcessed; + } while(false); + + return command; +} + +static MfUltralightCommand mf_ultralight_listener_check_tearing_handler( + MfUltralightListener* instance, + BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + + FURI_LOG_D(TAG, "CMD_CHECK_TEARING"); + + do { + uint8_t tearing_flag_num = bit_buffer_get_byte(buffer, 1); + if(!mf_ultralight_support_feature( + instance->features, + MfUltralightFeatureSupportCheckTearingFlag | + MfUltralightFeatureSupportSingleCounter)) { + break; + } + if(mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportSingleCounter) && + (tearing_flag_num != 2)) { + break; + } + + bit_buffer_set_size_bytes(instance->tx_buffer, 1); + bit_buffer_set_byte( + instance->tx_buffer, 0, instance->data->tearing_flag->data[tearing_flag_num]); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_auth_handler(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedSilent; + + FURI_LOG_D(TAG, "CMD_AUTH"); + + do { + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportAuthentication)) + break; + + const uint8_t* rx_data = bit_buffer_get_data(buffer); + MfUltralightAuthPassword password = {}; + memcpy(password.data, &rx_data[1], sizeof(MfUltralightAuthPassword)); + if(instance->callback) { + instance->mfu_event_data.password = password; + instance->mfu_event.type = MfUltralightListenerEventTypeAuth; + instance->callback(instance->generic_event, instance->context); + } + if(password.pass != instance->config->password.pass) break; + + bit_buffer_copy_bytes( + instance->tx_buffer, instance->config->pack.data, sizeof(MfUltralightAuthPack)); + instance->auth_state = MfUltralightListenerAuthStateSuccess; + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + + command = MfUltralightCommandProcessed; + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_comp_write_handler_p2(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + FURI_LOG_D(TAG, "CMD_CM_WR_2"); + + do { + if(bit_buffer_get_size_bytes(buffer) != 16) break; + + const uint8_t* rx_data = bit_buffer_get_data(buffer); + uint8_t start_page = instance->composite_cmd.data; + memcpy(instance->data->page[start_page].data, &rx_data[0], sizeof(MfUltralightPage)); + mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_ACK); + + command = MfUltralightCommandProcessed; + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_comp_write_handler_p1(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedSilent; + + FURI_LOG_D(TAG, "CMD_CM_WR_1"); + + do { + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportCompatibleWrite)) + break; + + uint8_t start_page = bit_buffer_get_byte(buffer, 1); + uint16_t pages_total = instance->data->pages_total; + + if(start_page < 2 || start_page > pages_total) { + command = MfUltralightCommandNotProcessedNAK; + break; + } + + instance->composite_cmd.data = start_page; + mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_ACK); + command = MfUltralightCommandProcessed; + mf_ultralight_composite_command_set_next(instance, mf_ultralight_comp_write_handler_p2); + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_sector_select_handler_p2(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + UNUSED(instance); + UNUSED(buffer); + FURI_LOG_D(TAG, "CMD_SEC_SEL_2"); + + do { + if(bit_buffer_get_size_bytes(buffer) != 4) break; + uint8_t sector = bit_buffer_get_byte(buffer, 0); + if(sector == 0xFF) break; + + instance->sector = sector; + command = MfUltralightCommandProcessedSilent; + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_sector_select_handler_p1(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + UNUSED(buffer); + FURI_LOG_D(TAG, "CMD_SEC_SEL_1"); + + do { + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportSectorSelect) && + bit_buffer_get_byte(buffer, 1) == 0xFF) + break; + + command = MfUltralightCommandProcessed; + mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_ACK); + mf_ultralight_composite_command_set_next(instance, mf_ultralight_sector_select_handler_p2); + } while(false); + + return command; +} + +static const MfUltralightListenerCmdHandler mf_ultralight_command[] = { + { + .cmd = MF_ULTRALIGHT_CMD_READ_PAGE, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_listener_read_page_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_WRITE_PAGE, + .cmd_len_bits = 6 * 8, + .callback = mf_ultralight_listener_write_page_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_GET_VERSION, + .cmd_len_bits = 8, + .callback = mf_ultralight_listener_read_version_handler, + }, + { + .cmd = MF_ULTRALIGTH_CMD_READ_SIG, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_listener_read_signature_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_READ_CNT, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_listener_read_counter_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_CHECK_TEARING, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_listener_check_tearing_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_AUTH, + .cmd_len_bits = 5 * 8, + .callback = mf_ultralight_listener_auth_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_INCR_CNT, + .cmd_len_bits = 6 * 8, + .callback = mf_ultralight_listener_increase_counter_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_SECTOR_SELECT, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_sector_select_handler_p1, + }, + { + .cmd = MF_ULTRALIGHT_CMD_COMP_WRITE, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_comp_write_handler_p1, + }, +}; + +static void mf_ultralight_listener_prepare_emulation(MfUltralightListener* instance) { + MfUltralightData* data = instance->data; + instance->features = mf_ultralight_get_feature_support_set(data->type); + mf_ultralight_get_config_page(data, &instance->config); + mf_ultraligt_mirror_prepare_emulation(instance); +} + +MfUltralightListener* mf_ultralight_listener_alloc( + Iso14443_3aListener* iso14443_3a_listener, + MfUltralightData* data) { + furi_assert(iso14443_3a_listener); + + MfUltralightListener* instance = malloc(sizeof(MfUltralightListener)); + instance->mirror.ascii_mirror_data = furi_string_alloc(); + instance->iso14443_3a_listener = iso14443_3a_listener; + instance->data = data; + mf_ultralight_listener_prepare_emulation(instance); + mf_ultralight_composite_command_reset(instance); + instance->sector = 0; + instance->tx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_LISTENER_MAX_TX_BUFF_SIZE); + + instance->mfu_event.data = &instance->mfu_event_data; + instance->generic_event.protocol = NfcProtocolMfUltralight; + instance->generic_event.instance = instance; + instance->generic_event.data = &instance->mfu_event; + + return instance; +} + +void mf_ultralight_listener_free(MfUltralightListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->tx_buffer); + + bit_buffer_free(instance->tx_buffer); + furi_string_free(instance->mirror.ascii_mirror_data); + free(instance); +} + +const MfUltralightData* mf_ultralight_listener_get_data(MfUltralightListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +void mf_ultralight_listener_set_callback( + MfUltralightListener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +NfcCommand mf_ultralight_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(event.data); + + MfUltralightListener* instance = context; + Iso14443_3aListenerEvent* iso14443_3a_event = event.data; + BitBuffer* rx_buffer = iso14443_3a_event->data->buffer; + NfcCommand command = NfcCommandContinue; + + if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) { + MfUltralightCommand mfu_command = MfUltralightCommandNotFound; + size_t size = bit_buffer_get_size(rx_buffer); + uint8_t cmd = bit_buffer_get_byte(rx_buffer, 0); + + if(mf_ultralight_composite_command_in_progress(instance)) { + mfu_command = mf_ultralight_composite_command_run(instance, rx_buffer); + } else { + for(size_t i = 0; i < COUNT_OF(mf_ultralight_command); i++) { + if(size != mf_ultralight_command[i].cmd_len_bits) continue; + if(cmd != mf_ultralight_command[i].cmd) continue; + mfu_command = mf_ultralight_command[i].callback(instance, rx_buffer); + + if(mfu_command != MfUltralightCommandNotFound) break; + } + } + if(mfu_command == MfUltralightCommandProcessedSilent) { + command = NfcCommandReset; + } else if(mfu_command != MfUltralightCommandProcessed) { + instance->state = MfUltraligthListenerStateIdle; + instance->auth_state = MfUltralightListenerAuthStateIdle; + command = NfcCommandSleep; + + if(mfu_command == MfUltralightCommandNotProcessedNAK) { + mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_NACK); + } + } + } else if( + iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedData || + iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff || + iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted) { + // TODO generic state reset ? + mf_ultralight_composite_command_reset(instance); + instance->sector = 0; + instance->auth_state = MfUltralightListenerAuthStateIdle; + command = NfcCommandSleep; + } + + return command; +} + +const NfcListenerBase mf_ultralight_listener = { + .alloc = (NfcListenerAlloc)mf_ultralight_listener_alloc, + .free = (NfcListenerFree)mf_ultralight_listener_free, + .get_data = (NfcListenerGetData)mf_ultralight_listener_get_data, + .set_callback = (NfcListenerSetCallback)mf_ultralight_listener_set_callback, + .run = (NfcListenerRun)mf_ultralight_listener_run, +}; diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h new file mode 100644 index 000000000000..fa6a6bd7e70a --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h @@ -0,0 +1,28 @@ +#pragma once + +#include "mf_ultralight.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfUltralightListener MfUltralightListener; + +typedef enum { + MfUltralightListenerEventTypeAuth, +} MfUltralightListenerEventType; + +typedef struct { + union { + MfUltralightAuthPassword password; + }; +} MfUltralightListenerEventData; + +typedef struct { + MfUltralightListenerEventType type; + MfUltralightListenerEventData* data; +} MfUltralightListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_defs.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_defs.h new file mode 100644 index 000000000000..aa3e11b8c4df --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcListenerBase mf_ultralight_listener; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c new file mode 100644 index 000000000000..43e54309e362 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c @@ -0,0 +1,186 @@ +#include "mf_ultralight_listener_i.h" + +#include + +static MfUltralightMirrorConf mf_ultralight_mirror_check_mode( + const MfUltralightConfigPages* const config, + const MfUltralightListenerAuthState auth_state) { + MfUltralightMirrorConf mirror_mode = config->mirror.mirror_conf; + + if(mirror_mode == MfUltralightMirrorNone || mirror_mode == MfUltralightMirrorUid) + return mirror_mode; + + if(!config->access.nfc_cnt_en || + (config->access.nfc_cnt_pwd_prot && auth_state != MfUltralightListenerAuthStateSuccess)) { + mirror_mode = mirror_mode == MfUltralightMirrorCounter ? MfUltralightMirrorNone : + MfUltralightMirrorUid; + } + return mirror_mode; +} + +static bool mf_ultralight_mirror_check_boundaries(MfUltralightListener* instance) { + const MfUltralightConfigPages* const conf = instance->config; + + uint8_t last_user_page = mf_ultralight_get_config_page_num(instance->data->type) - 2; + + uint8_t max_page_offset = 0; + uint8_t max_byte_offset = 2; + + MfUltralightMirrorConf mode = mf_ultralight_mirror_check_mode(conf, instance->auth_state); + + bool result = false; + bool again = false; + do { + if(mode == MfUltralightMirrorNone) { + break; + } else if(mode == MfUltralightMirrorUid) { + max_page_offset = 3; + } else if(mode == MfUltralightMirrorCounter) { + max_page_offset = 1; + } else if(mode == MfUltralightMirrorUidCounter) { + max_page_offset = 5; + max_byte_offset = 3; + } + + instance->mirror.actual_mode = mode; + + if(conf->mirror_page <= 3) break; + if(conf->mirror_page < last_user_page - max_page_offset) { + result = true; + break; + } + if(conf->mirror_page == last_user_page - max_page_offset) { + result = (conf->mirror.mirror_byte <= max_byte_offset); + break; + } + + if(conf->mirror_page > last_user_page - max_page_offset && + mode == MfUltralightMirrorUidCounter) { + mode = MfUltralightMirrorUid; + again = true; + } + } while(again); + + return result; +} + +static bool mf_ultralight_mirror_enabled(MfUltralightListener* instance) { + bool mirror_enabled = false; + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportAsciiMirror) && + (instance->config != NULL) && mf_ultralight_mirror_check_boundaries(instance)) { + mirror_enabled = true; + } + instance->mirror.enabled = mirror_enabled; + return instance->mirror.enabled; +} + +static uint8_t mf_ultralight_get_mirror_data_size(MfUltralightMirrorConf mode) { + switch(mode) { + case MfUltralightMirrorUid: + return 14; + case MfUltralightMirrorCounter: + return 6; + case MfUltralightMirrorUidCounter: + return 21; + default: + return 0; + } +} + +static uint8_t mf_ultralight_get_mirror_last_page(MfUltralightListener* instance) { + uint8_t strSize = mf_ultralight_get_mirror_data_size(instance->mirror.actual_mode); + return (instance->config->mirror_page + 1U + strSize / 4); +} + +static uint8_t mf_ultralight_get_ascii_offset(uint8_t start_page, MfUltralightListener* instance) { + uint8_t start_offset = 0; + if(instance->config->mirror.mirror_conf == MfUltralightMirrorCounter) start_offset = 15; + + uint8_t ascii_offset = start_offset; + + if(start_page > instance->config->mirror_page) + ascii_offset = (start_page - instance->config->mirror_page) * 4 - + instance->config->mirror.mirror_byte + start_offset; + + return ascii_offset; +} + +static uint8_t mf_ultralight_get_ascii_end(MfUltralightMirrorConf mode) { + return (mode == MfUltralightMirrorUid) ? 14 : 21; +} + +static uint8_t mf_ultralight_get_byte_offset( + uint8_t current_page, + const MfUltralightConfigPages* const config) { + return (current_page > config->mirror_page) ? 0 : config->mirror.mirror_byte; +} + +static void mf_ultraligt_format_mirror_data( + FuriString* str, + const uint8_t* const data, + const uint8_t data_len) { + for(uint8_t i = 0; i < data_len; i++) furi_string_cat_printf(str, "%02X", data[i]); +} + +void mf_ultralight_mirror_read_prepare(uint8_t start_page, MfUltralightListener* instance) { + if(mf_ultralight_mirror_enabled(instance)) { + instance->mirror.ascii_offset = mf_ultralight_get_ascii_offset(start_page, instance); + instance->mirror.ascii_end = mf_ultralight_get_ascii_end(instance->mirror.actual_mode); + + instance->mirror.mirror_last_page = mf_ultralight_get_mirror_last_page(instance); + } +} + +void mf_ultralight_mirror_read_handler( + uint8_t mirror_page_num, + uint8_t* dest, + MfUltralightListener* instance) { + if(instance->mirror.enabled && mirror_page_num >= instance->config->mirror_page && + mirror_page_num <= instance->mirror.mirror_last_page) { + uint8_t byte_offset = mf_ultralight_get_byte_offset(mirror_page_num, instance->config); + + uint8_t ascii_offset = instance->mirror.ascii_offset; + uint8_t ascii_end = instance->mirror.ascii_end; + uint8_t* source = (uint8_t*)furi_string_get_cstr(instance->mirror.ascii_mirror_data); + for(uint8_t j = byte_offset; (j < 4) && (ascii_offset < ascii_end); j++) { + dest[j] = source[ascii_offset]; + ascii_offset++; + } + } +} + +void mf_ultraligt_mirror_prepare_emulation(MfUltralightListener* instance) { + mf_ultraligt_format_mirror_data( + instance->mirror.ascii_mirror_data, + instance->data->iso14443_3a_data->uid, + instance->data->iso14443_3a_data->uid_len); + + furi_string_push_back(instance->mirror.ascii_mirror_data, 'x'); + + mf_ultraligt_format_mirror_data( + instance->mirror.ascii_mirror_data, + instance->data->counter[2].data, + sizeof(instance->data->counter[2].data)); +} + +bool mf_ultralight_composite_command_in_progress(MfUltralightListener* instance) { + return (instance->composite_cmd.callback != NULL); +} + +MfUltralightCommand + mf_ultralight_composite_command_run(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = (instance->composite_cmd.callback)(instance, buffer); + mf_ultralight_composite_command_reset(instance); + return command; +} + +void mf_ultralight_composite_command_reset(MfUltralightListener* instance) { + instance->composite_cmd.callback = NULL; + instance->composite_cmd.data = 0; +} + +void mf_ultralight_composite_command_set_next( + MfUltralightListener* instance, + const MfUltralightListenerCommandCallback handler) { + instance->composite_cmd.callback = handler; +} \ No newline at end of file diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h new file mode 100644 index 000000000000..ac390a800e29 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h @@ -0,0 +1,83 @@ +#pragma once + +#include "mf_ultralight_listener.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MfUltralightListenerAuthStateIdle, + MfUltralightListenerAuthStateSuccess, +} MfUltralightListenerAuthState; + +typedef enum { + MfUltraligthListenerStateIdle, +} MfUltraligthListenerState; + +typedef enum { + MfUltralightCommandNotFound, + MfUltralightCommandProcessed, + MfUltralightCommandProcessedSilent, + MfUltralightCommandNotProcessedNAK, + MfUltralightCommandNotProcessedSilent, +} MfUltralightCommand; + +typedef MfUltralightCommand ( + *MfUltralightListenerCommandCallback)(MfUltralightListener* instance, BitBuffer* buf); + +typedef uint8_t MfUltralightListenerCompositeCommandData; + +typedef struct { + MfUltralightListenerCompositeCommandData data; + MfUltralightListenerCommandCallback callback; +} MfUltralightListenerCompositeCommandContext; + +typedef struct { + uint8_t enabled; + uint8_t ascii_offset; + uint8_t ascii_end; + uint8_t mirror_last_page; + MfUltralightMirrorConf actual_mode; + FuriString* ascii_mirror_data; +} MfUltralightMirrorMode; + +struct MfUltralightListener { + Iso14443_3aListener* iso14443_3a_listener; + MfUltralightListenerAuthState auth_state; + MfUltraligthListenerState state; + + MfUltralightData* data; + BitBuffer* tx_buffer; + MfUltralightFeatureSupport features; + MfUltralightConfigPages* config; + + NfcGenericEvent generic_event; + MfUltralightListenerEvent mfu_event; + MfUltralightListenerEventData mfu_event_data; + NfcGenericCallback callback; + uint8_t sector; + MfUltralightMirrorMode mirror; + MfUltralightListenerCompositeCommandContext composite_cmd; + void* context; +}; + +void mf_ultraligt_mirror_prepare_emulation(MfUltralightListener* instance); +void mf_ultralight_mirror_read_prepare(uint8_t start_page, MfUltralightListener* instance); +void mf_ultralight_mirror_read_handler( + uint8_t mirror_page_num, + uint8_t* dest, + MfUltralightListener* instance); + +void mf_ultralight_composite_command_set_next( + MfUltralightListener* instance, + const MfUltralightListenerCommandCallback handler); +void mf_ultralight_composite_command_reset(MfUltralightListener* instance); +bool mf_ultralight_composite_command_in_progress(MfUltralightListener* instance); +MfUltralightCommand + mf_ultralight_composite_command_run(MfUltralightListener* instance, BitBuffer* buffer); +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c new file mode 100644 index 000000000000..ead34defc7f1 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c @@ -0,0 +1,558 @@ +#include "mf_ultralight_poller_i.h" + +#include + +#include + +#define TAG "MfUltralightPoller" + +typedef NfcCommand (*MfUltralightPollerReadHandler)(MfUltralightPoller* instance); + +static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_1k( + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left) { + bool tag_calculated = false; + // 0 - 226: sector 0 + // 227 - 228: config registers + // 229 - 230: session registers + + if(lin_addr > 230) { + *pages_left = 0; + } else if(lin_addr >= 229) { + *sector = 3; + *pages_left = 2 - (lin_addr - 229); + *tag = lin_addr - 229 + 248; + tag_calculated = true; + } else if(lin_addr >= 227) { + *sector = 0; + *pages_left = 2 - (lin_addr - 227); + *tag = lin_addr - 227 + 232; + tag_calculated = true; + } else { + *sector = 0; + *pages_left = 227 - lin_addr; + *tag = lin_addr; + tag_calculated = true; + } + + return tag_calculated; +} + +static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_2k( + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left) { + bool tag_calculated = false; + // 0 - 255: sector 0 + // 256 - 480: sector 1 + // 481 - 482: config registers + // 483 - 484: session registers + + if(lin_addr > 484) { + *pages_left = 0; + } else if(lin_addr >= 483) { + *sector = 3; + *pages_left = 2 - (lin_addr - 483); + *tag = lin_addr - 483 + 248; + tag_calculated = true; + } else if(lin_addr >= 481) { + *sector = 1; + *pages_left = 2 - (lin_addr - 481); + *tag = lin_addr - 481 + 232; + tag_calculated = true; + } else if(lin_addr >= 256) { + *sector = 1; + *pages_left = 225 - (lin_addr - 256); + *tag = lin_addr - 256; + tag_calculated = true; + } else { + *sector = 0; + *pages_left = 256 - lin_addr; + *tag = lin_addr; + tag_calculated = true; + } + + return tag_calculated; +} + +static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_1k( + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left) { + bool tag_calculated = false; + // 0 - 233: sector 0 + registers + // 234 - 235: session registers + + if(lin_addr > 235) { + *pages_left = 0; + } else if(lin_addr >= 234) { + *sector = 0; + *pages_left = 2 - (lin_addr - 234); + *tag = lin_addr - 234 + 236; + tag_calculated = true; + } else { + *sector = 0; + *pages_left = 234 - lin_addr; + *tag = lin_addr; + tag_calculated = true; + } + + return tag_calculated; +} + +static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_2k( + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left) { + bool tag_calculated = false; + // 0 - 233: sector 0 + registers + // 234 - 235: session registers + // 236 - 491: sector 1 + + if(lin_addr > 491) { + *pages_left = 0; + } else if(lin_addr >= 236) { + *sector = 1; + *pages_left = 256 - (lin_addr - 236); + *tag = lin_addr - 236; + tag_calculated = true; + } else if(lin_addr >= 234) { + *sector = 0; + *pages_left = 2 - (lin_addr - 234); + *tag = lin_addr - 234 + 236; + tag_calculated = true; + } else { + *sector = 0; + *pages_left = 234 - lin_addr; + *tag = lin_addr; + tag_calculated = true; + } + + return tag_calculated; +} + +bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag( + MfUltralightPoller* instance, + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left) { + furi_assert(instance); + furi_assert(sector); + furi_assert(tag); + furi_assert(pages_left); + + bool tag_calculated = false; + + if(instance->data->type == MfUltralightTypeNTAGI2C1K) { + tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_1k( + lin_addr, sector, tag, pages_left); + } else if(instance->data->type == MfUltralightTypeNTAGI2C2K) { + tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_2k( + lin_addr, sector, tag, pages_left); + } else if(instance->data->type == MfUltralightTypeNTAGI2CPlus1K) { + tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_1k( + lin_addr, sector, tag, pages_left); + } else if(instance->data->type == MfUltralightTypeNTAGI2CPlus2K) { + tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_2k( + lin_addr, sector, tag, pages_left); + } + + return tag_calculated; +} + +MfUltralightPoller* mf_ultralight_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) { + furi_assert(iso14443_3a_poller); + + MfUltralightPoller* instance = malloc(sizeof(MfUltralightPoller)); + instance->iso14443_3a_poller = iso14443_3a_poller; + instance->tx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_MAX_BUFF_SIZE); + instance->rx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_MAX_BUFF_SIZE); + instance->data = mf_ultralight_alloc(); + + instance->mfu_event.data = &instance->mfu_event_data; + + instance->general_event.protocol = NfcProtocolMfUltralight; + instance->general_event.data = &instance->mfu_event; + instance->general_event.instance = instance; + + return instance; +} + +void mf_ultralight_poller_free(MfUltralightPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + mf_ultralight_free(instance->data); + free(instance); +} + +static void mf_ultralight_poller_set_callback( + MfUltralightPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +const MfUltralightData* mf_ultralight_poller_get_data(MfUltralightPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static NfcCommand mf_ultralight_poller_handler_idle(MfUltralightPoller* instance) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + iso14443_3a_copy( + instance->data->iso14443_3a_data, + iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); + instance->counters_read = 0; + instance->counters_total = 3; + instance->tearing_flag_read = 0; + instance->tearing_flag_total = 3; + instance->pages_read = 0; + instance->state = MfUltralightPollerStateReadVersion; + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_read_version(MfUltralightPoller* instance) { + instance->error = mf_ultralight_poller_async_read_version(instance, &instance->data->version); + if(instance->error == MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Read version success"); + instance->data->type = mf_ultralight_get_type_by_version(&instance->data->version); + instance->state = MfUltralightPollerStateGetFeatureSet; + } else { + FURI_LOG_D(TAG, "Didn't response. Check NTAG 203"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->state = MfUltralightPollerStateDetectNtag203; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_check_ntag_203(MfUltralightPoller* instance) { + MfUltralightPageReadCommandData data = {}; + instance->error = mf_ultralight_poller_async_read_page(instance, 41, &data); + if(instance->error == MfUltralightErrorNone) { + FURI_LOG_D(TAG, "NTAG203 detected"); + instance->data->type = MfUltralightTypeNTAG203; + } else { + FURI_LOG_D(TAG, "Original Ultralight detected"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->data->type = MfUltralightTypeUnknown; + } + instance->state = MfUltralightPollerStateGetFeatureSet; + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_get_feature_set(MfUltralightPoller* instance) { + instance->feature_set = mf_ultralight_get_feature_support_set(instance->data->type); + instance->pages_total = mf_ultralight_get_pages_total(instance->data->type); + instance->data->pages_total = instance->pages_total; + FURI_LOG_D( + TAG, + "%s detected. Total pages: %d", + mf_ultralight_get_device_name(instance->data, NfcDeviceNameTypeFull), + instance->pages_total); + + instance->state = MfUltralightPollerStateReadSignature; + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_read_signature(MfUltralightPoller* instance) { + MfUltralightPollerState next_state = MfUltralightPollerStateAuth; + if(mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportReadSignature)) { + FURI_LOG_D(TAG, "Reading signature"); + instance->error = + mf_ultralight_poller_async_read_signature(instance, &instance->data->signature); + if(instance->error != MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Read signature failed"); + next_state = MfUltralightPollerStateReadFailed; + } + } else { + FURI_LOG_D(TAG, "Skip reading signature"); + } + instance->state = next_state; + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_read_counters(MfUltralightPoller* instance) { + do { + if(!mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportReadCounter) || + !mf_ultralight_is_counter_configured(instance->data)) { + FURI_LOG_D(TAG, "Skip reading counters"); + instance->state = MfUltralightPollerStateReadTearingFlags; + break; + } + + if(instance->counters_read == instance->counters_total) { + instance->state = MfUltralightPollerStateReadTearingFlags; + break; + } + + if(mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportSingleCounter)) { + instance->counters_read = 2; + } + + FURI_LOG_D(TAG, "Reading counter %d", instance->counters_read); + instance->error = mf_ultralight_poller_async_read_counter( + instance, instance->counters_read, &instance->data->counter[instance->counters_read]); + if(instance->error != MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Failed to read %d counter", instance->counters_read); + instance->state = MfUltralightPollerStateReadTearingFlags; + } else { + instance->counters_read++; + } + + } while(false); + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPoller* instance) { + if(mf_ultralight_support_feature( + instance->feature_set, + MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportSingleCounter)) { + if(instance->tearing_flag_read == instance->tearing_flag_total) { + instance->state = MfUltralightPollerStateTryDefaultPass; + } else { + if(mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportSingleCounter)) { + instance->tearing_flag_read = 2; + } + FURI_LOG_D(TAG, "Reading tearing flag %d", instance->tearing_flag_read); + instance->error = mf_ultralight_poller_async_read_tearing_flag( + instance, + instance->tearing_flag_read, + &instance->data->tearing_flag[instance->tearing_flag_read]); + if(instance->error != MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Reading tearing flag %d failed", instance->tearing_flag_read); + instance->state = MfUltralightPollerStateReadFailed; + } else { + instance->tearing_flag_read++; + } + } + } else { + FURI_LOG_D(TAG, "Skip reading tearing flags"); + instance->state = MfUltralightPollerStateTryDefaultPass; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance) { + NfcCommand command = NfcCommandContinue; + if(mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportAuthentication)) { + instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest; + + command = instance->callback(instance->general_event, instance->context); + if(!instance->mfu_event.data->auth_context.skip_auth) { + instance->auth_context.password = instance->mfu_event.data->auth_context.password; + FURI_LOG_D( + TAG, + "Trying to authenticate with password %08lX", + instance->auth_context.password.pass); + instance->error = mf_ultralight_poller_async_auth(instance, &instance->auth_context); + if(instance->error == MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Auth success"); + instance->auth_context.auth_success = true; + instance->mfu_event.data->auth_context = instance->auth_context; + instance->mfu_event.type = MfUltralightPollerEventTypeAuthSuccess; + command = instance->callback(instance->general_event, instance->context); + } else { + FURI_LOG_D(TAG, "Auth failed"); + instance->auth_context.auth_success = false; + instance->mfu_event.type = MfUltralightPollerEventTypeAuthFailed; + command = instance->callback(instance->general_event, instance->context); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + } + } + } + instance->state = MfUltralightPollerStateReadPages; + + return command; +} + +static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* instance) { + MfUltralightPageReadCommandData data = {}; + uint16_t start_page = instance->pages_read; + if(MF_ULTRALIGHT_IS_NTAG_I2C(instance->data->type)) { + uint8_t tag = 0; + uint8_t sector = 0; + uint8_t pages_left = 0; + if(mf_ultralight_poller_ntag_i2c_addr_lin_to_tag( + instance, start_page, §or, &tag, &pages_left)) { + instance->error = + mf_ultralight_poller_async_read_page_from_sector(instance, sector, tag, &data); + } else { + FURI_LOG_D(TAG, "Failed to calculate sector and tag from %d page", start_page); + instance->error = MfUltralightErrorProtocol; + } + } else { + instance->error = mf_ultralight_poller_async_read_page(instance, start_page, &data); + } + + if(instance->error == MfUltralightErrorNone) { + for(size_t i = 0; i < 4; i++) { + if(start_page + i < instance->pages_total) { + FURI_LOG_D(TAG, "Read page %d success", start_page + i); + instance->data->page[start_page + i] = data.page[i]; + instance->pages_read++; + instance->data->pages_read = instance->pages_read; + } + } + if(instance->pages_read == instance->pages_total) { + instance->state = MfUltralightPollerStateReadCounters; + } + } else { + FURI_LOG_D(TAG, "Read page %d failed", instance->pages_read); + if(instance->pages_read) { + instance->state = MfUltralightPollerStateReadCounters; + } else { + instance->state = MfUltralightPollerStateReadFailed; + } + } + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoller* instance) { + do { + if(!mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportAuthentication)) + break; + + MfUltralightConfigPages* config = NULL; + mf_ultralight_get_config_page(instance->data, &config); + if(instance->auth_context.auth_success) { + config->password = instance->auth_context.password; + config->pack = instance->auth_context.pack; + } else if(config->access.authlim == 0) { + FURI_LOG_D(TAG, "No limits in authentication. Trying default password"); + instance->auth_context.password.pass = MF_ULTRALIGHT_DEFAULT_PASSWORD; + instance->error = mf_ultralight_poller_async_auth(instance, &instance->auth_context); + if(instance->error == MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Default password detected"); + config->password.pass = MF_ULTRALIGHT_DEFAULT_PASSWORD; + config->pack = instance->auth_context.pack; + } + } + + if(instance->pages_read != instance->pages_total) { + // Probably password protected, fix AUTH0 and PROT so before AUTH0 + // can be written and since AUTH0 won't be readable, like on the + // original card + config->auth0 = instance->pages_read; + config->access.prot = true; + } + } while(false); + + instance->state = MfUltralightPollerStateReadSuccess; + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_read_fail(MfUltralightPoller* instance) { + FURI_LOG_D(TAG, "Read Failed"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->mfu_event.data->error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->state = MfUltralightPollerStateIdle; + return command; +} + +static NfcCommand mf_ultralight_poller_handler_read_success(MfUltralightPoller* instance) { + FURI_LOG_D(TAG, "Read success"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->mfu_event.type = MfUltralightPollerEventTypeReadSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const MfUltralightPollerReadHandler + mf_ultralight_poller_read_handler[MfUltralightPollerStateNum] = { + [MfUltralightPollerStateIdle] = mf_ultralight_poller_handler_idle, + [MfUltralightPollerStateReadVersion] = mf_ultralight_poller_handler_read_version, + [MfUltralightPollerStateDetectNtag203] = mf_ultralight_poller_handler_check_ntag_203, + [MfUltralightPollerStateGetFeatureSet] = mf_ultralight_poller_handler_get_feature_set, + [MfUltralightPollerStateReadSignature] = mf_ultralight_poller_handler_read_signature, + [MfUltralightPollerStateReadCounters] = mf_ultralight_poller_handler_read_counters, + [MfUltralightPollerStateReadTearingFlags] = + mf_ultralight_poller_handler_read_tearing_flags, + [MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth, + [MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass, + [MfUltralightPollerStateReadPages] = mf_ultralight_poller_handler_read_pages, + [MfUltralightPollerStateReadFailed] = mf_ultralight_poller_handler_read_fail, + [MfUltralightPollerStateReadSuccess] = mf_ultralight_poller_handler_read_success, + +}; + +static NfcCommand mf_ultralight_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + MfUltralightPoller* instance = context; + furi_assert(instance->callback); + + const Iso14443_3aPollerEvent* iso14443_3a_event = event.data; + + NfcCommand command = NfcCommandContinue; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + command = mf_ultralight_poller_read_handler[instance->state](instance); + } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + instance->mfu_event.type = MfUltralightPollerEventTypeReadFailed; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool mf_ultralight_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + bool protocol_detected = false; + MfUltralightPoller* instance = context; + const Iso14443_3aPollerEvent* iso14443_3a_event = event.data; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + MfUltralightPageReadCommandData read_page_cmd_data = {}; + MfUltralightError error = + mf_ultralight_poller_async_read_page(instance, 0, &read_page_cmd_data); + protocol_detected = (error == MfUltralightErrorNone); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + } + + return protocol_detected; +} + +const NfcPollerBase mf_ultralight_poller = { + .alloc = (NfcPollerAlloc)mf_ultralight_poller_alloc, + .free = (NfcPollerFree)mf_ultralight_poller_free, + .set_callback = (NfcPollerSetCallback)mf_ultralight_poller_set_callback, + .run = (NfcPollerRun)mf_ultralight_poller_run, + .detect = (NfcPollerDetect)mf_ultralight_poller_detect, + .get_data = (NfcPollerGetData)mf_ultralight_poller_get_data, +}; diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h new file mode 100644 index 000000000000..2d4ef33ea947 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h @@ -0,0 +1,41 @@ +#pragma once + +#include "mf_ultralight.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfUltralightPoller MfUltralightPoller; + +typedef enum { + MfUltralightPollerEventTypeAuthRequest, + MfUltralightPollerEventTypeAuthSuccess, + MfUltralightPollerEventTypeAuthFailed, + MfUltralightPollerEventTypeReadSuccess, + MfUltralightPollerEventTypeReadFailed, +} MfUltralightPollerEventType; + +typedef struct { + MfUltralightAuthPassword password; + MfUltralightAuthPack pack; + bool auth_success; + bool skip_auth; +} MfUltralightPollerAuthContext; + +typedef struct { + union { + MfUltralightPollerAuthContext auth_context; + MfUltralightError error; + }; +} MfUltralightPollerEventData; + +typedef struct { + MfUltralightPollerEventType type; + MfUltralightPollerEventData* data; +} MfUltralightPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h new file mode 100644 index 000000000000..80b0d7b6eaa9 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase mf_ultralight_poller; diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c new file mode 100644 index 000000000000..8e362cd66f24 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c @@ -0,0 +1,281 @@ +#include "mf_ultralight_poller_i.h" + +#include + +#define TAG "MfUltralightPoller" + +MfUltralightError mf_ultralight_process_error(Iso14443_3aError error) { + MfUltralightError ret = MfUltralightErrorNone; + + switch(error) { + case Iso14443_3aErrorNone: + ret = MfUltralightErrorNone; + break; + case Iso14443_3aErrorNotPresent: + ret = MfUltralightErrorNotPresent; + break; + case Iso14443_3aErrorColResFailed: + case Iso14443_3aErrorCommunication: + case Iso14443_3aErrorWrongCrc: + ret = MfUltralightErrorProtocol; + break; + case Iso14443_3aErrorTimeout: + ret = MfUltralightErrorTimeout; + break; + default: + ret = MfUltralightErrorProtocol; + break; + } + + return ret; +} + +MfUltralightError mf_ultralight_poller_async_auth( + MfUltralightPoller* instance, + MfUltralightPollerAuthContext* data) { + uint8_t auth_cmd[5] = {MF_ULTRALIGHT_CMD_AUTH}; + nfc_util_num2bytes(data->password.pass, MF_ULTRALIGHT_AUTH_PASSWORD_SIZE, &auth_cmd[1]); + bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd)); + + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + do { + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDART_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != MF_ULTRALIGHT_AUTH_PACK_SIZE) { + ret = MfUltralightErrorAuth; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data->pack.data, MF_ULTRALIGHT_AUTH_PACK_SIZE); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_async_read_page_from_sector( + MfUltralightPoller* instance, + uint8_t sector, + uint8_t tag, + MfUltralightPageReadCommandData* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + const uint8_t select_sector_cmd[2] = {MF_ULTRALIGHT_CMD_SECTOR_SELECT, 0xff}; + bit_buffer_copy_bytes(instance->tx_buffer, select_sector_cmd, sizeof(select_sector_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDART_FWT_FC); + if(error != Iso14443_3aErrorWrongCrc) { + FURI_LOG_D(TAG, "Failed to issue sector select command"); + ret = mf_ultralight_process_error(error); + break; + } + + const uint8_t read_sector_cmd[4] = {sector, 0x00, 0x00, 0x00}; + bit_buffer_copy_bytes(instance->tx_buffer, read_sector_cmd, sizeof(read_sector_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDART_FWT_FC); + if(error != Iso14443_3aErrorTimeout) { + // This is NOT a typo! The tag ACKs by not sending a response within 1ms. + FURI_LOG_D(TAG, "Sector %u select NAK'd", sector); + ret = MfUltralightErrorProtocol; + break; + } + + ret = mf_ultralight_poller_async_read_page(instance, tag, data); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_async_read_page( + MfUltralightPoller* instance, + uint8_t start_page, + MfUltralightPageReadCommandData* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t read_page_cmd[2] = {MF_ULTRALIGHT_CMD_READ_PAGE, start_page}; + bit_buffer_copy_bytes(instance->tx_buffer, read_page_cmd, sizeof(read_page_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDART_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != + sizeof(MfUltralightPageReadCommandData)) { + ret = MfUltralightErrorProtocol; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightPageReadCommandData)); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_async_write_page( + MfUltralightPoller* instance, + uint8_t page, + MfUltralightPage* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t write_page_cmd[MF_ULTRALIGHT_PAGE_SIZE + 2] = {MF_ULTRALIGHT_CMD_WRITE_PAGE, page}; + memcpy(&write_page_cmd[2], data->data, MF_ULTRALIGHT_PAGE_SIZE); + bit_buffer_copy_bytes(instance->tx_buffer, write_page_cmd, sizeof(write_page_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDART_FWT_FC); + if(error != Iso14443_3aErrorWrongCrc) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size(instance->rx_buffer) != 4) { + ret = MfUltralightErrorProtocol; + break; + } + if(!bit_buffer_starts_with_byte(instance->rx_buffer, MF_ULTRALIGHT_CMD_ACK)) { + ret = MfUltralightErrorProtocol; + break; + } + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_async_read_version( + MfUltralightPoller* instance, + MfUltralightVersion* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + const uint8_t get_version_cmd = MF_ULTRALIGHT_CMD_GET_VERSION; + bit_buffer_copy_bytes(instance->tx_buffer, &get_version_cmd, sizeof(get_version_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDART_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(MfUltralightVersion)) { + FURI_LOG_I( + TAG, "Read Version failed: %d", bit_buffer_get_size_bytes(instance->rx_buffer)); + ret = MfUltralightErrorProtocol; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightVersion)); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_async_read_signature( + MfUltralightPoller* instance, + MfUltralightSignature* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + const uint8_t read_signature_cmd[2] = {MF_ULTRALIGTH_CMD_READ_SIG, 0x00}; + bit_buffer_copy_bytes(instance->tx_buffer, read_signature_cmd, sizeof(read_signature_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDART_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(MfUltralightSignature)) { + ret = MfUltralightErrorProtocol; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightSignature)); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_async_read_counter( + MfUltralightPoller* instance, + uint8_t counter_num, + MfUltralightCounter* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t read_counter_cmd[2] = {MF_ULTRALIGHT_CMD_READ_CNT, counter_num}; + bit_buffer_copy_bytes(instance->tx_buffer, read_counter_cmd, sizeof(read_counter_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDART_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != MF_ULTRALIGHT_COUNTER_SIZE) { + ret = MfUltralightErrorProtocol; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data->data, MF_ULTRALIGHT_COUNTER_SIZE); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_async_read_tearing_flag( + MfUltralightPoller* instance, + uint8_t tearing_falg_num, + MfUltralightTearingFlag* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t check_tearing_cmd[2] = {MF_ULTRALIGHT_CMD_CHECK_TEARING, tearing_falg_num}; + bit_buffer_copy_bytes(instance->tx_buffer, check_tearing_cmd, sizeof(check_tearing_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDART_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(MfUltralightTearingFlag)) { + ret = MfUltralightErrorProtocol; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightTearingFlag)); + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h new file mode 100644 index 000000000000..b6b540cbcb97 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h @@ -0,0 +1,145 @@ +#pragma once + +#include "mf_ultralight_poller.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_ULTRALIGHT_POLLER_STANDART_FWT_FC (60000) +#define MF_ULTRALIGHT_MAX_BUFF_SIZE (64) + +#define MF_ULTRALIGHT_DEFAULT_PASSWORD (0xffffffffUL) + +#define MF_ULTRALIGHT_IS_NTAG_I2C(type) \ + (((type) == MfUltralightTypeNTAGI2C1K) || ((type) == MfUltralightTypeNTAGI2C2K) || \ + ((type) == MfUltralightTypeNTAGI2CPlus1K) || ((type) == MfUltralightTypeNTAGI2CPlus2K)) + +typedef struct { + MfUltralightPage page; + uint8_t page_to_write; +} MfUltralightPollerWritePageCommand; + +typedef struct { + MfUltralightPageReadCommandData data; + uint8_t start_page; +} MfUltralightPollerReadPageCommand; + +typedef struct { + MfUltralightCounter data; + uint8_t counter_num; +} MfUltralightPollerReadCounterCommand; + +typedef struct { + MfUltralightTearingFlag data; + uint8_t tearing_flag_num; +} MfUltralightPollerReadTearingFlagCommand; + +typedef union { + MfUltralightPollerWritePageCommand write_cmd; + MfUltralightPollerReadPageCommand read_cmd; + MfUltralightVersion version; + MfUltralightSignature signature; + MfUltralightPollerReadCounterCommand counter_cmd; + MfUltralightPollerReadTearingFlagCommand tearing_flag_cmd; + MfUltralightData* data; +} MfUltralightPollerContextData; + +typedef enum { + MfUltralightPollerStateIdle, + MfUltralightPollerStateReadVersion, + MfUltralightPollerStateDetectNtag203, + MfUltralightPollerStateGetFeatureSet, + MfUltralightPollerStateReadSignature, + MfUltralightPollerStateReadCounters, + MfUltralightPollerStateReadTearingFlags, + MfUltralightPollerStateAuth, + MfUltralightPollerStateReadPages, + MfUltralightPollerStateTryDefaultPass, + MfUltralightPollerStateReadFailed, + MfUltralightPollerStateReadSuccess, + + MfUltralightPollerStateNum, +} MfUltralightPollerState; + +struct MfUltralightPoller { + Iso14443_3aPoller* iso14443_3a_poller; + MfUltralightPollerState state; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + MfUltralightData* data; + MfUltralightPollerAuthContext auth_context; + uint32_t feature_set; + uint16_t pages_read; + uint16_t pages_total; + uint8_t counters_read; + uint8_t counters_total; + uint8_t tearing_flag_read; + uint8_t tearing_flag_total; + MfUltralightError error; + + NfcGenericEvent general_event; + MfUltralightPollerEvent mfu_event; + MfUltralightPollerEventData mfu_event_data; + NfcGenericCallback callback; + void* context; +}; + +MfUltralightError mf_ultralight_process_error(Iso14443_3aError error); + +MfUltralightPoller* mf_ultralight_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller); + +void mf_ultralight_poller_free(MfUltralightPoller* instance); + +const MfUltralightData* mf_ultralight_poller_get_data(MfUltralightPoller* instance); + +bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag( + MfUltralightPoller* instance, + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left); + +MfUltralightError mf_ultralight_poller_async_auth( + MfUltralightPoller* instance, + MfUltralightPollerAuthContext* data); + +MfUltralightError mf_ultralight_poller_async_read_page( + MfUltralightPoller* instance, + uint8_t start_page, + MfUltralightPageReadCommandData* data); + +MfUltralightError mf_ultralight_poller_async_read_page_from_sector( + MfUltralightPoller* instnace, + uint8_t sector, + uint8_t tag, + MfUltralightPageReadCommandData* data); + +MfUltralightError mf_ultralight_poller_async_write_page( + MfUltralightPoller* instance, + uint8_t page, + MfUltralightPage* data); + +MfUltralightError mf_ultralight_poller_async_read_version( + MfUltralightPoller* instance, + MfUltralightVersion* data); + +MfUltralightError mf_ultralight_poller_async_read_signature( + MfUltralightPoller* instance, + MfUltralightSignature* data); + +MfUltralightError mf_ultralight_poller_async_read_counter( + MfUltralightPoller* instance, + uint8_t counter_num, + MfUltralightCounter* data); + +MfUltralightError mf_ultralight_poller_async_read_tearing_flag( + MfUltralightPoller* instance, + uint8_t tearing_falg_num, + MfUltralightTearingFlag* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync_api.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync_api.c new file mode 100644 index 000000000000..72041c6c5408 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync_api.c @@ -0,0 +1,287 @@ +#include "mf_ultralight_poller_i.h" + +#include + +#include + +#define MF_ULTRALIGHT_POLLER_COMPLETE_EVENT (1UL << 0) + +typedef enum { + MfUltralightPollerCmdTypeReadPage, + MfUltralightPollerCmdTypeWritePage, + MfUltralightPollerCmdTypeReadVersion, + MfUltralightPollerCmdTypeReadSignature, + MfUltralightPollerCmdTypeReadCounter, + MfUltralightPollerCmdTypeReadTearingFlag, + + MfUltralightPollerCmdTypeNum, +} MfUltralightPollerCmdType; + +typedef struct { + MfUltralightPollerCmdType cmd_type; + FuriThreadId thread_id; + MfUltralightError error; + MfUltralightPollerContextData data; +} MfUltralightPollerContext; + +typedef MfUltralightError (*MfUltralightPollerCmdHandler)( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data); + +MfUltralightError mf_ultralight_poller_read_page_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_async_read_page( + poller, data->read_cmd.start_page, &data->read_cmd.data); +} + +MfUltralightError mf_ultralight_poller_write_page_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_async_write_page( + poller, data->write_cmd.page_to_write, &data->write_cmd.page); +} + +MfUltralightError mf_ultralight_poller_read_version_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_async_read_version(poller, &data->version); +} + +MfUltralightError mf_ultralight_poller_read_signature_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_async_read_signature(poller, &data->signature); +} + +MfUltralightError mf_ultralight_poller_read_counter_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_async_read_counter( + poller, data->counter_cmd.counter_num, &data->counter_cmd.data); +} + +MfUltralightError mf_ultralight_poller_read_tearing_flag_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_async_read_tearing_flag( + poller, data->tearing_flag_cmd.tearing_flag_num, &data->tearing_flag_cmd.data); +} + +static const MfUltralightPollerCmdHandler + mf_ultralight_poller_cmd_handlers[MfUltralightPollerCmdTypeNum] = { + [MfUltralightPollerCmdTypeReadPage] = mf_ultralight_poller_read_page_handler, + [MfUltralightPollerCmdTypeWritePage] = mf_ultralight_poller_write_page_handler, + [MfUltralightPollerCmdTypeReadVersion] = mf_ultralight_poller_read_version_handler, + [MfUltralightPollerCmdTypeReadSignature] = mf_ultralight_poller_read_signature_handler, + [MfUltralightPollerCmdTypeReadCounter] = mf_ultralight_poller_read_counter_handler, + [MfUltralightPollerCmdTypeReadTearingFlag] = + mf_ultralight_poller_read_tearing_flag_handler, +}; + +static NfcCommand mf_ultralgiht_poller_cmd_callback(NfcGenericEvent event, void* context) { + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(event.data); + furi_assert(context); + + MfUltralightPollerContext* poller_context = context; + Iso14443_3aPollerEvent* iso14443_3a_event = event.data; + Iso14443_3aPoller* iso14443_3a_poller = event.instance; + MfUltralightPoller* mfu_poller = mf_ultralight_poller_alloc(iso14443_3a_poller); + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + poller_context->error = mf_ultralight_poller_cmd_handlers[poller_context->cmd_type]( + mfu_poller, &poller_context->data); + } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + poller_context->error = mf_ultralight_process_error(iso14443_3a_event->data->error); + } + + furi_thread_flags_set(poller_context->thread_id, MF_ULTRALIGHT_POLLER_COMPLETE_EVENT); + + mf_ultralight_poller_free(mfu_poller); + + return NfcCommandStop; +} + +static MfUltralightError + mf_ultralight_poller_cmd_execute(Nfc* nfc, MfUltralightPollerContext* poller_ctx) { + furi_assert(poller_ctx->cmd_type < MfUltralightPollerCmdTypeNum); + + poller_ctx->thread_id = furi_thread_get_current_id(); + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolIso14443_3a); + nfc_poller_start(poller, mf_ultralgiht_poller_cmd_callback, poller_ctx); + furi_thread_flags_wait(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + return poller_ctx->error; +} + +MfUltralightError mf_ultralight_poller_read_page(Nfc* nfc, uint16_t page, MfUltralightPage* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeReadPage, + .data.read_cmd.start_page = page, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + if(error == MfUltralightErrorNone) { + *data = poller_context.data.read_cmd.data.page[0]; + } + + return error; +} + +MfUltralightError + mf_ultralight_poller_write_page(Nfc* nfc, uint16_t page, MfUltralightPage* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeWritePage, + .data.write_cmd = + { + .page_to_write = page, + .page = *data, + }, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + return error; +} + +MfUltralightError mf_ultralight_poller_read_version(Nfc* nfc, MfUltralightVersion* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeReadVersion, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + if(error == MfUltralightErrorNone) { + *data = poller_context.data.version; + } + + return error; +} + +MfUltralightError mf_ultralight_poller_read_signature(Nfc* nfc, MfUltralightSignature* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeReadSignature, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + if(error == MfUltralightErrorNone) { + *data = poller_context.data.signature; + } + + return error; +} + +MfUltralightError + mf_ultralight_poller_read_counter(Nfc* nfc, uint8_t counter_num, MfUltralightCounter* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeReadCounter, + .data.counter_cmd.counter_num = counter_num, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + if(error == MfUltralightErrorNone) { + *data = poller_context.data.counter_cmd.data; + } + + return error; +} + +MfUltralightError mf_ultralight_poller_read_tearing_flag( + Nfc* nfc, + uint8_t flag_num, + MfUltralightTearingFlag* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeReadTearingFlag, + .data.tearing_flag_cmd.tearing_flag_num = flag_num, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + if(error == MfUltralightErrorNone) { + *data = poller_context.data.tearing_flag_cmd.data; + } + + return error; +} + +static NfcCommand mf_ultralight_poller_read_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.instance); + furi_assert(event.data); + furi_assert(event.protocol == NfcProtocolMfUltralight); + + NfcCommand command = NfcCommandContinue; + MfUltralightPollerContext* poller_context = context; + MfUltralightPoller* mfu_poller = event.instance; + MfUltralightPollerEvent* mfu_event = event.data; + + if(mfu_event->type == MfUltralightPollerEventTypeReadSuccess) { + mf_ultralight_copy(poller_context->data.data, mf_ultralight_poller_get_data(mfu_poller)); + poller_context->error = MfUltralightErrorNone; + command = NfcCommandStop; + } else if(mfu_event->type == MfUltralightPollerEventTypeReadFailed) { + poller_context->error = mfu_event->data->error; + command = NfcCommandStop; + } else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) { + mfu_event->data->auth_context.skip_auth = true; + } + + if(command == NfcCommandStop) { + furi_thread_flags_set(poller_context->thread_id, MF_ULTRALIGHT_POLLER_COMPLETE_EVENT); + } + + return command; +} + +MfUltralightError mf_ultralight_poller_read_card(Nfc* nfc, MfUltralightData* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = {}; + poller_context.thread_id = furi_thread_get_current_id(); + poller_context.data.data = mf_ultralight_alloc(); + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfUltralight); + nfc_poller_start(poller, mf_ultralight_poller_read_callback, &poller_context); + furi_thread_flags_wait(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + if(poller_context.error == MfUltralightErrorNone) { + mf_ultralight_copy(data, poller_context.data.data); + } + + mf_ultralight_free(poller_context.data.data); + + return poller_context.error; +} diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync_api.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync_api.h new file mode 100644 index 000000000000..a0124ae09218 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync_api.h @@ -0,0 +1,30 @@ +#pragma once + +#include "mf_ultralight.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +MfUltralightError mf_ultralight_poller_read_page(Nfc* nfc, uint16_t page, MfUltralightPage* data); + +MfUltralightError mf_ultralight_poller_write_page(Nfc* nfc, uint16_t page, MfUltralightPage* data); + +MfUltralightError mf_ultralight_poller_read_version(Nfc* nfc, MfUltralightVersion* data); + +MfUltralightError mf_ultralight_poller_read_signature(Nfc* nfc, MfUltralightSignature* data); + +MfUltralightError + mf_ultralight_poller_read_counter(Nfc* nfc, uint8_t counter_num, MfUltralightCounter* data); + +MfUltralightError mf_ultralight_poller_read_tearing_flag( + Nfc* nfc, + uint8_t flag_num, + MfUltralightTearingFlag* data); + +MfUltralightError mf_ultralight_poller_read_card(Nfc* nfc, MfUltralightData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_device_base.h b/lib/nfc/protocols/nfc_device_base.h new file mode 100644 index 000000000000..11905e30b115 --- /dev/null +++ b/lib/nfc/protocols/nfc_device_base.h @@ -0,0 +1,16 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + NfcDeviceNameTypeFull, + NfcDeviceNameTypeShort, +} NfcDeviceNameType; + +typedef void NfcDeviceData; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_device_base_i.h b/lib/nfc/protocols/nfc_device_base_i.h new file mode 100644 index 000000000000..99b0f1d23532 --- /dev/null +++ b/lib/nfc/protocols/nfc_device_base_i.h @@ -0,0 +1,42 @@ +#pragma once + +#include "nfc_device_base.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef NfcDeviceData* (*NfcDeviceAlloc)(); +typedef void (*NfcDeviceFree)(NfcDeviceData* data); +typedef void (*NfcDeviceReset)(NfcDeviceData* data); +typedef void (*NfcDeviceCopy)(NfcDeviceData* data, const NfcDeviceData* other); +typedef bool (*NfcDeviceVerify)(NfcDeviceData* data, const FuriString* device_type); +typedef bool (*NfcDeviceLoad)(NfcDeviceData* data, FlipperFormat* ff, uint32_t version); +typedef bool (*NfcDeviceSave)(const NfcDeviceData* data, FlipperFormat* ff); +typedef bool (*NfcDeviceEqual)(const NfcDeviceData* data, const NfcDeviceData* other); +typedef const char* (*NfcDeviceGetName)(const NfcDeviceData* data, NfcDeviceNameType name_type); +typedef const uint8_t* (*NfcDeviceGetUid)(const NfcDeviceData* data, size_t* uid_len); +typedef bool (*NfcDeviceSetUid)(NfcDeviceData* data, const uint8_t* uid, size_t uid_len); +typedef NfcDeviceData* (*NfcDeviceGetBaseData)(const NfcDeviceData* data); + +typedef struct { + const char* protocol_name; + NfcDeviceAlloc alloc; + NfcDeviceFree free; + NfcDeviceReset reset; + NfcDeviceCopy copy; + NfcDeviceVerify verify; + NfcDeviceLoad load; + NfcDeviceSave save; + NfcDeviceEqual is_equal; + NfcDeviceGetName get_name; + NfcDeviceGetUid get_uid; + NfcDeviceSetUid set_uid; + NfcDeviceGetBaseData get_base_data; +} NfcDeviceBase; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c new file mode 100644 index 000000000000..6c44601cd256 --- /dev/null +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -0,0 +1,26 @@ +#include "nfc_device_defs.h" + +#include "nfc_protocol.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { + [NfcProtocolIso14443_3a] = &nfc_device_iso14443_3a, + [NfcProtocolIso14443_3b] = &nfc_device_iso14443_3b, + [NfcProtocolIso14443_4a] = &nfc_device_iso14443_4a, + [NfcProtocolIso15693_3] = &nfc_device_iso15693_3, + [NfcProtocolFelica] = &nfc_device_felica, + [NfcProtocolMfUltralight] = &nfc_device_mf_ultralight, + [NfcProtocolMfClassic] = &nfc_device_mf_classic, + [NfcProtocolMfDesfire] = &nfc_device_mf_desfire, + [NfcProtocolSlix] = &nfc_device_slix, + /* Add new protocols here */ +}; diff --git a/lib/nfc/protocols/nfc_device_defs.h b/lib/nfc/protocols/nfc_device_defs.h new file mode 100644 index 000000000000..f5ba2563f461 --- /dev/null +++ b/lib/nfc/protocols/nfc_device_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include "nfc_device_base_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcDeviceBase* nfc_devices[]; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_generic_event.h b/lib/nfc/protocols/nfc_generic_event.h new file mode 100644 index 000000000000..cee465eb8a2c --- /dev/null +++ b/lib/nfc/protocols/nfc_generic_event.h @@ -0,0 +1,25 @@ +#pragma once + +#include "nfc_protocol.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void NfcGenericInstance; + +typedef void NfcGenericEventData; + +typedef struct { + NfcProtocol protocol; + NfcGenericInstance* instance; + // TODO change to event + NfcGenericEventData* data; +} NfcGenericEvent; + +typedef NfcCommand (*NfcGenericCallback)(NfcGenericEvent event, void* context); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_listener_base.h b/lib/nfc/protocols/nfc_listener_base.h new file mode 100644 index 000000000000..8336b60e29ed --- /dev/null +++ b/lib/nfc/protocols/nfc_listener_base.h @@ -0,0 +1,32 @@ +#pragma once + +#include "nfc_generic_event.h" +#include "nfc_device_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef NfcGenericInstance* ( + *NfcListenerAlloc)(NfcGenericInstance* base_listener, NfcDeviceData* data); +typedef void (*NfcListenerFree)(NfcGenericInstance* instance); + +typedef void (*NfcListenerSetCallback)( + NfcGenericInstance* listener, + NfcGenericCallback callback, + void* context); +typedef NfcCommand (*NfcListenerRun)(NfcGenericEvent event, void* context); + +typedef const NfcDeviceData* (*NfcListenerGetData)(const NfcGenericInstance* instance); + +typedef struct { + NfcListenerAlloc alloc; + NfcListenerFree free; + NfcListenerSetCallback set_callback; + NfcListenerRun run; + NfcListenerGetData get_data; +} NfcListenerBase; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c new file mode 100644 index 000000000000..d0ccd6bbb8f5 --- /dev/null +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -0,0 +1,19 @@ +#include "nfc_listener_defs.h" + +#include +#include +#include +#include +#include +#include + +const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { + [NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a, + [NfcProtocolIso14443_3b] = NULL, + [NfcProtocolIso14443_4a] = &nfc_listener_iso14443_4a, + [NfcProtocolIso15693_3] = &nfc_listener_iso15693_3, + [NfcProtocolMfUltralight] = &mf_ultralight_listener, + [NfcProtocolMfClassic] = &mf_classic_listener, + [NfcProtocolMfDesfire] = NULL, + [NfcProtocolSlix] = &nfc_listener_slix, +}; diff --git a/lib/nfc/protocols/nfc_listener_defs.h b/lib/nfc/protocols/nfc_listener_defs.h new file mode 100644 index 000000000000..4d88cc09830d --- /dev/null +++ b/lib/nfc/protocols/nfc_listener_defs.h @@ -0,0 +1,14 @@ +#pragma once + +#include "nfc_listener_base.h" +#include "nfc_protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcListenerBase* nfc_listeners_api[NfcProtocolNum]; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_poller_base.h b/lib/nfc/protocols/nfc_poller_base.h new file mode 100644 index 000000000000..7f05ea33f5b3 --- /dev/null +++ b/lib/nfc/protocols/nfc_poller_base.h @@ -0,0 +1,30 @@ +#pragma once + +#include "nfc_generic_event.h" +#include "nfc_device_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef NfcGenericInstance* (*NfcPollerAlloc)(NfcGenericInstance* base_poller); +typedef void (*NfcPollerFree)(NfcGenericInstance* instance); + +typedef void ( + *NfcPollerSetCallback)(NfcGenericInstance* poller, NfcGenericCallback callback, void* context); +typedef NfcCommand (*NfcPollerRun)(NfcGenericEvent event, void* context); +typedef bool (*NfcPollerDetect)(NfcGenericEvent event, void* context); +typedef const NfcDeviceData* (*NfcPollerGetData)(const NfcGenericInstance* instance); + +typedef struct { + NfcPollerAlloc alloc; + NfcPollerFree free; + NfcPollerSetCallback set_callback; + NfcPollerRun run; + NfcPollerDetect detect; + NfcPollerGetData get_data; +} NfcPollerBase; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c new file mode 100644 index 000000000000..2ad5cdc38311 --- /dev/null +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -0,0 +1,24 @@ +#include "nfc_poller_defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { + [NfcProtocolIso14443_3a] = &nfc_poller_iso14443_3a, + [NfcProtocolIso14443_3b] = &nfc_poller_iso14443_3b, + [NfcProtocolIso14443_4a] = &nfc_poller_iso14443_4a, + [NfcProtocolIso15693_3] = &nfc_poller_iso15693_3, + [NfcProtocolFelica] = &nfc_poller_felica, + [NfcProtocolMfUltralight] = &mf_ultralight_poller, + [NfcProtocolMfClassic] = &mf_classic_poller, + [NfcProtocolMfDesfire] = &mf_desfire_poller, + [NfcProtocolSlix] = &nfc_poller_slix, + /* Add new pollers here */ +}; diff --git a/lib/nfc/protocols/nfc_poller_defs.h b/lib/nfc/protocols/nfc_poller_defs.h new file mode 100644 index 000000000000..a406a5f08b42 --- /dev/null +++ b/lib/nfc/protocols/nfc_poller_defs.h @@ -0,0 +1,14 @@ +#pragma once + +#include "nfc_poller_base.h" +#include "nfc_protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase* nfc_pollers_api[NfcProtocolNum]; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c new file mode 100644 index 000000000000..80d176aff9cb --- /dev/null +++ b/lib/nfc/protocols/nfc_protocol.c @@ -0,0 +1,121 @@ +#include "nfc_protocol.h" + +#include + +typedef struct { + NfcProtocol parent_protocol; + size_t children_num; + const NfcProtocol* children_protocol; +} NfcProtocolTreeNode; + +/**************************** Protocol tree structure **************************** + * + * (Start) + * | + * +------------------------+-----------+---------+ + * | | | | + * ISO14443-3A ISO14443-3B Felica ISO15693-3 + * | | + * +---------------+-------------+ SLIX + * | | | + * ISO14443-4A Mf Ultralight Mf Classic + * | + * +-----+-----+ + * | | + * Mf Desfire Bank Card + * + */ + +static const NfcProtocol nfc_protocol_iso14443_3a_children_protocol[] = { + NfcProtocolIso14443_4a, + NfcProtocolMfUltralight, +}; + +static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { + NfcProtocolMfDesfire, +}; + +static const NfcProtocol nfc_protocol_iso15693_3_children_protocol[] = { + NfcProtocolSlix, +}; + +static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { + [NfcProtocolIso14443_3a] = + { + .parent_protocol = NfcProtocolInvalid, + .children_num = COUNT_OF(nfc_protocol_iso14443_3a_children_protocol), + .children_protocol = nfc_protocol_iso14443_3a_children_protocol, + }, + [NfcProtocolIso14443_3b] = + { + .parent_protocol = NfcProtocolInvalid, + .children_num = 0, + .children_protocol = NULL, + }, + [NfcProtocolIso14443_4a] = + { + .parent_protocol = NfcProtocolIso14443_3a, + .children_num = COUNT_OF(nfc_protocol_iso14443_4a_children_protocol), + .children_protocol = nfc_protocol_iso14443_4a_children_protocol, + }, + [NfcProtocolIso15693_3] = + { + .parent_protocol = NfcProtocolInvalid, + .children_num = COUNT_OF(nfc_protocol_iso15693_3_children_protocol), + .children_protocol = nfc_protocol_iso15693_3_children_protocol, + }, + [NfcProtocolFelica] = + { + .parent_protocol = NfcProtocolInvalid, + .children_num = 0, + .children_protocol = NULL, + }, + [NfcProtocolMfUltralight] = + { + .parent_protocol = NfcProtocolIso14443_3a, + .children_num = 0, + .children_protocol = NULL, + }, + [NfcProtocolMfClassic] = + { + .parent_protocol = NfcProtocolIso14443_3a, + .children_num = 0, + .children_protocol = NULL, + }, + [NfcProtocolMfDesfire] = + { + .parent_protocol = NfcProtocolIso14443_4a, + .children_num = 0, + .children_protocol = NULL, + }, + [NfcProtocolSlix] = + { + .parent_protocol = NfcProtocolIso15693_3, + .children_num = 0, + .children_protocol = NULL, + }, +}; + +NfcProtocol nfc_protocol_get_parent(NfcProtocol protocol) { + furi_assert(protocol < NfcProtocolNum); + + return nfc_protocol_nodes[protocol].parent_protocol; +} + +bool nfc_protocol_has_parent(NfcProtocol protocol, NfcProtocol parent_protocol) { + furi_assert(protocol < NfcProtocolNum); + furi_assert(parent_protocol < NfcProtocolNum); + + bool parent_found = false; + const NfcProtocolTreeNode* iter = &nfc_protocol_nodes[protocol]; + + while(iter->parent_protocol != NfcProtocolInvalid) { + if(iter->parent_protocol == parent_protocol) { + parent_found = true; + break; + } + iter = &nfc_protocol_nodes[iter->parent_protocol]; + } + + return parent_found; +} diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h new file mode 100644 index 000000000000..f54435a81552 --- /dev/null +++ b/lib/nfc/protocols/nfc_protocol.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + NfcProtocolIso14443_3a, + NfcProtocolIso14443_3b, + NfcProtocolIso14443_4a, + NfcProtocolIso15693_3, + NfcProtocolFelica, + NfcProtocolMfUltralight, + NfcProtocolMfClassic, + NfcProtocolMfDesfire, + NfcProtocolSlix, + /* Add new protocols here */ + + NfcProtocolNum, + + NfcProtocolInvalid, +} NfcProtocol; + +NfcProtocol nfc_protocol_get_parent(NfcProtocol protocol); + +bool nfc_protocol_has_parent(NfcProtocol protocol, NfcProtocol parent_protocol); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix.c b/lib/nfc/protocols/slix/slix.c new file mode 100644 index 000000000000..12dc6750dd16 --- /dev/null +++ b/lib/nfc/protocols/slix/slix.c @@ -0,0 +1,433 @@ +#include "slix_i.h" +#include "slix_device_defs.h" + +#include +#include + +#define SLIX_PROTOCOL_NAME "SLIX" +#define SLIX_DEVICE_NAME "SLIX" + +#define SLIX_TYPE_SLIX_SLIX2 (0x01U) +#define SLIX_TYPE_SLIX_S (0x02U) +#define SLIX_TYPE_SLIX_L (0x03U) + +#define SLIX_TYPE_INDICATOR_SLIX (0x02U) +#define SLIX_TYPE_INDICATOR_SLIX2 (0x01U) + +#define SLIX_PASSWORD_READ_KEY "Password Read" +#define SLIX_PASSWORD_WRITE_KEY "Password Write" +#define SLIX_PASSWORD_PRIVACY_KEY "Password Privacy" +#define SLIX_PASSWORD_DESTROY_KEY "Password Destroy" +#define SLIX_PASSWORD_EAS_KEY "Password EAS" +#define SLIX_SIGNATURE_KEY "Signature" +#define SLIX_PRIVACY_MODE_KEY "Privacy Mode" +#define SLIX_PROTECTION_POINTER_KEY "Protection Pointer" +#define SLIX_PROTECTION_CONDITION_KEY "Protection Condition" +#define SLIX_LOCK_EAS_KEY "Lock EAS" +#define SLIX_LOCK_PPL_KEY "Lock PPL" + +typedef struct { + uint8_t iso15693_3[2]; + uint8_t icode_type; + union { + struct { + uint8_t unused_1 : 3; + uint8_t type_indicator : 2; + uint8_t unused_2 : 3; + }; + uint8_t serial_num[5]; + }; +} SlixUidLayout; + +const NfcDeviceBase nfc_device_slix = { + .protocol_name = SLIX_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)slix_alloc, + .free = (NfcDeviceFree)slix_free, + .reset = (NfcDeviceReset)slix_reset, + .copy = (NfcDeviceCopy)slix_copy, + .verify = (NfcDeviceVerify)slix_verify, + .load = (NfcDeviceLoad)slix_load, + .save = (NfcDeviceSave)slix_save, + .is_equal = (NfcDeviceEqual)slix_is_equal, + .get_name = (NfcDeviceGetName)slix_get_device_name, + .get_uid = (NfcDeviceGetUid)slix_get_uid, + .set_uid = (NfcDeviceSetUid)slix_set_uid, + .get_base_data = (NfcDeviceGetBaseData)slix_get_base_data, +}; + +static const char* slix_nfc_device_name[] = { + [SlixTypeSlix] = SLIX_DEVICE_NAME, + [SlixTypeSlixS] = SLIX_DEVICE_NAME "-S", + [SlixTypeSlixL] = SLIX_DEVICE_NAME "-L", + [SlixTypeSlix2] = SLIX_DEVICE_NAME "2", +}; + +static const SlixTypeFeatures slix_type_features[] = { + [SlixTypeSlix] = SLIX_TYPE_FEATURES_SLIX, + [SlixTypeSlixS] = SLIX_TYPE_FEATURES_SLIX_S, + [SlixTypeSlixL] = SLIX_TYPE_FEATURES_SLIX_L, + [SlixTypeSlix2] = SLIX_TYPE_FEATURES_SLIX2, +}; + +typedef struct { + const char* key; + SlixTypeFeatures feature_flag; + SlixPassword default_value; +} SlixPasswordConfig; + +static const SlixPasswordConfig slix_password_configs[] = { + [SlixPasswordTypeRead] = {SLIX_PASSWORD_READ_KEY, SLIX_TYPE_FEATURE_READ, 0x00000000U}, + [SlixPasswordTypeWrite] = {SLIX_PASSWORD_WRITE_KEY, SLIX_TYPE_FEATURE_WRITE, 0x00000000U}, + [SlixPasswordTypePrivacy] = {SLIX_PASSWORD_PRIVACY_KEY, SLIX_TYPE_FEATURE_PRIVACY, 0xFFFFFFFFU}, + [SlixPasswordTypeDestroy] = {SLIX_PASSWORD_DESTROY_KEY, SLIX_TYPE_FEATURE_DESTROY, 0xFFFFFFFFU}, + [SlixPasswordTypeEasAfi] = {SLIX_PASSWORD_EAS_KEY, SLIX_TYPE_FEATURE_EAS, 0x00000000U}, +}; + +static void slix_password_set_defaults(SlixPassword* passwords) { + for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) { + passwords[i] = slix_password_configs[i].default_value; + } +} + +SlixData* slix_alloc() { + SlixData* data = malloc(sizeof(SlixData)); + + data->iso15693_3_data = iso15693_3_alloc(); + slix_password_set_defaults(data->passwords); + + return data; +} + +void slix_free(SlixData* data) { + furi_assert(data); + + iso15693_3_free(data->iso15693_3_data); + + free(data); +} + +void slix_reset(SlixData* data) { + furi_assert(data); + + iso15693_3_reset(data->iso15693_3_data); + slix_password_set_defaults(data->passwords); + + memset(&data->system_info, 0, sizeof(SlixSystemInfo)); + memset(data->signature, 0, sizeof(SlixSignature)); + + data->privacy = false; +} + +void slix_copy(SlixData* data, const SlixData* other) { + furi_assert(data); + furi_assert(other); + + iso15693_3_copy(data->iso15693_3_data, other->iso15693_3_data); + + memcpy(data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount); + memcpy(data->signature, other->signature, sizeof(SlixSignature)); + + data->system_info = other->system_info; + data->privacy = other->privacy; +} + +bool slix_verify(SlixData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + // No backward compatibility, unified format only + return false; +} + +static bool slix_load_passwords(SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) { + bool ret = true; + + for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) { + const SlixPasswordConfig* password_config = &slix_password_configs[i]; + + if(!slix_type_has_features(slix_type, password_config->feature_flag)) continue; + if(!flipper_format_key_exist(ff, password_config->key)) { + passwords[i] = password_config->default_value; + continue; + } + if(!flipper_format_read_hex( + ff, password_config->key, (uint8_t*)&passwords[i], sizeof(SlixPassword))) { + ret = false; + break; + } + } + + return ret; +} + +bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool loaded = false; + + do { + if(!iso15693_3_load(data->iso15693_3_data, ff, version)) break; + + const SlixType slix_type = slix_get_type(data); + if(slix_type >= SlixTypeCount) break; + + if(!slix_load_passwords(data->passwords, slix_type, ff)) break; + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) { + if(flipper_format_key_exist(ff, SLIX_SIGNATURE_KEY)) { + if(!flipper_format_read_hex( + ff, SLIX_SIGNATURE_KEY, data->signature, SLIX_SIGNATURE_SIZE)) + break; + } + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) { + if(flipper_format_key_exist(ff, SLIX_PRIVACY_MODE_KEY)) { + if(!flipper_format_read_bool(ff, SLIX_PRIVACY_MODE_KEY, &data->privacy, 1)) break; + } + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + SlixProtection* protection = &data->system_info.protection; + if(flipper_format_key_exist(ff, SLIX_PROTECTION_POINTER_KEY) && + flipper_format_key_exist(ff, SLIX_PROTECTION_CONDITION_KEY)) { + if(!flipper_format_read_hex( + ff, SLIX_PROTECTION_POINTER_KEY, &protection->pointer, 1)) + break; + if(!flipper_format_read_hex( + ff, SLIX_PROTECTION_CONDITION_KEY, &protection->condition, 1)) + break; + } + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) { + if(flipper_format_key_exist(ff, SLIX_LOCK_EAS_KEY)) { + SlixLockBits* lock_bits = &data->system_info.lock_bits; + if(!flipper_format_read_bool(ff, SLIX_LOCK_EAS_KEY, &lock_bits->eas, 1)) break; + } + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + if(flipper_format_key_exist(ff, SLIX_LOCK_PPL_KEY)) { + SlixLockBits* lock_bits = &data->system_info.lock_bits; + if(!flipper_format_read_bool(ff, SLIX_LOCK_PPL_KEY, &lock_bits->ppl, 1)) break; + } + } + + loaded = true; + } while(false); + + return loaded; +} + +static bool + slix_save_passwords(const SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) { + bool ret = true; + + for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) { + const SlixPasswordConfig* password_config = &slix_password_configs[i]; + + if(!slix_type_has_features(slix_type, password_config->feature_flag)) continue; + if(!flipper_format_write_hex( + ff, password_config->key, (uint8_t*)&passwords[i], sizeof(SlixPassword))) { + ret = false; + break; + } + } + + return ret; +} + +bool slix_save(const SlixData* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + const SlixType slix_type = slix_get_type(data); + if(slix_type >= SlixTypeCount) break; + + if(!iso15693_3_save(data->iso15693_3_data, ff)) break; + if(!flipper_format_write_comment_cstr(ff, SLIX_PROTOCOL_NAME " specific data")) break; + + if(!flipper_format_write_comment_cstr( + ff, + "Passwords are optional. If a password is omitted, a default value will be used")) + break; + + if(!slix_save_passwords(data->passwords, slix_type, ff)) break; + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) { + if(!flipper_format_write_comment_cstr( + ff, + "This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.")) + break; + if(!flipper_format_write_hex( + ff, SLIX_SIGNATURE_KEY, data->signature, SLIX_SIGNATURE_SIZE)) + break; + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) { + if(!flipper_format_write_bool(ff, SLIX_PRIVACY_MODE_KEY, &data->privacy, 1)) break; + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + const SlixProtection* protection = &data->system_info.protection; + if(!flipper_format_write_comment_cstr(ff, "Protection pointer configuration")) break; + if(!flipper_format_write_hex( + ff, SLIX_PROTECTION_POINTER_KEY, &protection->pointer, sizeof(uint8_t))) + break; + if(!flipper_format_write_hex( + ff, SLIX_PROTECTION_CONDITION_KEY, &protection->condition, sizeof(uint8_t))) + break; + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS) || + slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + if(!flipper_format_write_comment_cstr(ff, "SLIX Lock Bits")) break; + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) { + const SlixLockBits* lock_bits = &data->system_info.lock_bits; + if(!flipper_format_write_bool(ff, SLIX_LOCK_EAS_KEY, &lock_bits->eas, 1)) break; + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + const SlixLockBits* lock_bits = &data->system_info.lock_bits; + if(!flipper_format_write_bool(ff, SLIX_LOCK_PPL_KEY, &lock_bits->ppl, 1)) break; + } + + saved = true; + } while(false); + + return saved; +} + +bool slix_is_equal(const SlixData* data, const SlixData* other) { + return iso15693_3_is_equal(data->iso15693_3_data, other->iso15693_3_data) && + memcmp(&data->system_info, &other->system_info, sizeof(SlixSystemInfo)) == 0 && + memcmp( + data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount) == + 0 && + memcmp(&data->signature, &other->signature, sizeof(SlixSignature)) == 0 && + data->privacy == other->privacy; +} + +const char* slix_get_device_name(const SlixData* data, NfcDeviceNameType name_type) { + UNUSED(name_type); + + const SlixType slix_type = slix_get_type(data); + furi_assert(slix_type < SlixTypeCount); + + return slix_nfc_device_name[slix_type]; +} + +const uint8_t* slix_get_uid(const SlixData* data, size_t* uid_len) { + return iso15693_3_get_uid(data->iso15693_3_data, uid_len); +} + +bool slix_set_uid(SlixData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso15693_3_set_uid(data->iso15693_3_data, uid, uid_len); +} + +const Iso15693_3Data* slix_get_base_data(const SlixData* data) { + furi_assert(data); + + return data->iso15693_3_data; +} + +SlixType slix_get_type(const SlixData* data) { + SlixType type = SlixTypeCount; + + do { + if(iso15693_3_get_manufacturer_id(data->iso15693_3_data) != SLIX_NXP_MANUFACTURER_CODE) + break; + + const SlixUidLayout* uid = (const SlixUidLayout*)data->iso15693_3_data->uid; + + if(uid->icode_type == SLIX_TYPE_SLIX_SLIX2) { + if(uid->type_indicator == SLIX_TYPE_INDICATOR_SLIX) { + type = SlixTypeSlix; + } else if(uid->type_indicator == SLIX_TYPE_INDICATOR_SLIX2) { + type = SlixTypeSlix2; + } + } else if(uid->icode_type == SLIX_TYPE_SLIX_S) { + type = SlixTypeSlixS; + } else if(uid->icode_type == SLIX_TYPE_SLIX_L) { + type = SlixTypeSlixL; + } + + } while(false); + + return type; +} + +SlixPassword slix_get_password(const SlixData* data, SlixPasswordType password_type) { + furi_assert(data); + furi_assert(password_type < SlixPasswordTypeCount); + + return data->passwords[password_type]; +} + +uint16_t slix_get_counter(const SlixData* data) { + furi_assert(data); + const SlixCounter* counter = (const SlixCounter*)iso15693_3_get_block_data( + data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM); + + return counter->value; +} + +bool slix_is_privacy_mode(const SlixData* data) { + furi_assert(data); + + return data->privacy; +} + +bool slix_is_block_protected( + const SlixData* data, + SlixPasswordType password_type, + uint8_t block_num) { + furi_assert(data); + furi_assert(password_type < SlixPasswordTypeCount); + + bool ret = false; + + do { + if(password_type != SlixPasswordTypeRead && password_type != SlixPasswordTypeWrite) break; + if(block_num >= iso15693_3_get_block_count(data->iso15693_3_data)) break; + if(block_num == SLIX_COUNTER_BLOCK_NUM) break; + + const bool high = block_num >= data->system_info.protection.pointer; + const bool read = password_type == SlixPasswordTypeRead; + + const uint8_t condition = high ? (read ? SLIX_PP_CONDITION_RH : SLIX_PP_CONDITION_WH) : + (read ? SLIX_PP_CONDITION_RL : SLIX_PP_CONDITION_WL); + + ret = data->system_info.protection.condition & condition; + } while(false); + + return ret; +} + +bool slix_is_counter_increment_protected(const SlixData* data) { + furi_assert(data); + + const SlixCounter* counter = (const SlixCounter*)iso15693_3_get_block_data( + data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM); + + return counter->protection != 0; +} + +bool slix_type_has_features(SlixType slix_type, SlixTypeFeatures features) { + furi_assert(slix_type < SlixTypeCount); + + return (slix_type_features[slix_type] & features) == features; +} + +bool slix_type_supports_password(SlixType slix_type, SlixPasswordType password_type) { + furi_assert(slix_type < SlixTypeCount); + furi_assert(password_type < SlixPasswordTypeCount); + + return slix_type_features[slix_type] & slix_password_configs[password_type].feature_flag; +} diff --git a/lib/nfc/protocols/slix/slix.h b/lib/nfc/protocols/slix/slix.h new file mode 100644 index 000000000000..f6c1453c5ddd --- /dev/null +++ b/lib/nfc/protocols/slix/slix.h @@ -0,0 +1,146 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SLIX_BLOCK_SIZE (4U) +#define SLIX_SIGNATURE_SIZE (32U) + +#define SLIX_COUNTER_BLOCK_NUM (79U) + +#define SLIX_PP_CONDITION_RL (1U << 0) +#define SLIX_PP_CONDITION_WL (1U << 1) +#define SLIX_PP_CONDITION_RH (1U << 4) +#define SLIX_PP_CONDITION_WH (1U << 5) + +#define SLIX_FEATURE_FLAG_UM_PP (1UL << 0) +#define SLIX_FEATURE_FLAG_COUNTER (1UL << 1) +#define SLIX_FEATURE_FLAG_EAS_ID (1UL << 2) +#define SLIX_FEATURE_FLAG_EAS_PP (1UL << 3) +#define SLIX_FEATURE_FLAG_AFI_PP (1UL << 4) +#define SLIX_FEATURE_FLAG_INVENTORY_READ_EXT (1UL << 5) +#define SLIX_FEATURE_FLAG_EAS_IR (1UL << 6) +#define SLIX_FEATURE_FLAG_ORIGINALITY_SIG (1UL << 8) +#define SLIX_FEATURE_FLAG_ORIGINALITY_SIG_PP (1UL << 9) +#define SLIX_FEATURE_FLAG_PERSISTENT_QUIET (1UL << 10) +#define SLIX_FEATURE_FLAG_PRIVACY (1UL << 12) +#define SLIX_FEATURE_FLAG_DESTROY (1UL << 13) +#define SLIX_FEATURE_EXT (1UL << 31) + +#define SLIX_TYPE_FEATURE_READ (1U << 0) +#define SLIX_TYPE_FEATURE_WRITE (1U << 1) +#define SLIX_TYPE_FEATURE_PRIVACY (1U << 2) +#define SLIX_TYPE_FEATURE_DESTROY (1U << 3) +#define SLIX_TYPE_FEATURE_EAS (1U << 4) +#define SLIX_TYPE_FEATURE_SIGNATURE (1U << 5) +#define SLIX_TYPE_FEATURE_PROTECTION (1U << 6) + +typedef uint32_t SlixTypeFeatures; + +typedef enum { + SlixErrorNone, + SlixErrorTimeout, + SlixErrorFormat, + SlixErrorNotSupported, + SlixErrorInternal, + SlixErrorWrongPassword, + SlixErrorUidMismatch, + SlixErrorUnknown, +} SlixError; + +typedef enum { + SlixTypeSlix, + SlixTypeSlixS, + SlixTypeSlixL, + SlixTypeSlix2, + SlixTypeCount, +} SlixType; + +typedef enum { + SlixPasswordTypeRead, + SlixPasswordTypeWrite, + SlixPasswordTypePrivacy, + SlixPasswordTypeDestroy, + SlixPasswordTypeEasAfi, + SlixPasswordTypeCount, +} SlixPasswordType; + +typedef uint32_t SlixPassword; +typedef uint8_t SlixSignature[SLIX_SIGNATURE_SIZE]; +typedef bool SlixPrivacy; + +typedef struct { + uint8_t pointer; + uint8_t condition; +} SlixProtection; + +typedef struct { + bool eas; + bool ppl; +} SlixLockBits; + +typedef struct { + SlixProtection protection; + SlixLockBits lock_bits; +} SlixSystemInfo; + +typedef struct { + Iso15693_3Data* iso15693_3_data; + SlixSystemInfo system_info; + SlixSignature signature; + SlixPassword passwords[SlixPasswordTypeCount]; + SlixPrivacy privacy; +} SlixData; + +SlixData* slix_alloc(); + +void slix_free(SlixData* data); + +void slix_reset(SlixData* data); + +void slix_copy(SlixData* data, const SlixData* other); + +bool slix_verify(SlixData* data, const FuriString* device_type); + +bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version); + +bool slix_save(const SlixData* data, FlipperFormat* ff); + +bool slix_is_equal(const SlixData* data, const SlixData* other); + +const char* slix_get_device_name(const SlixData* data, NfcDeviceNameType name_type); + +const uint8_t* slix_get_uid(const SlixData* data, size_t* uid_len); + +bool slix_set_uid(SlixData* data, const uint8_t* uid, size_t uid_len); + +const Iso15693_3Data* slix_get_base_data(const SlixData* data); + +// Getters and tests + +SlixType slix_get_type(const SlixData* data); + +SlixPassword slix_get_password(const SlixData* data, SlixPasswordType password_type); + +uint16_t slix_get_counter(const SlixData* data); + +bool slix_is_privacy_mode(const SlixData* data); + +bool slix_is_block_protected( + const SlixData* data, + SlixPasswordType password_type, + uint8_t block_num); + +bool slix_is_counter_increment_protected(const SlixData* data); + +// Static methods +bool slix_type_has_features(SlixType slix_type, SlixTypeFeatures features); + +bool slix_type_supports_password(SlixType slix_type, SlixPasswordType password_type); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_device_defs.h b/lib/nfc/protocols/slix/slix_device_defs.h new file mode 100644 index 000000000000..90d826afcb12 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_device_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcDeviceBase nfc_device_slix; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_i.c b/lib/nfc/protocols/slix/slix_i.c new file mode 100644 index 000000000000..97d66484c01b --- /dev/null +++ b/lib/nfc/protocols/slix/slix_i.c @@ -0,0 +1,127 @@ +#include "slix_i.h" + +#include + +bool slix_error_response_parse(SlixError* error, const BitBuffer* buf) { + Iso15693_3Error iso15693_3_error; + const bool ret = iso15693_3_error_response_parse(&iso15693_3_error, buf); + + if(ret) { + *error = slix_process_iso15693_3_error(iso15693_3_error); + } + + return ret; +} + +SlixError slix_process_iso15693_3_error(Iso15693_3Error iso15693_3_error) { + switch(iso15693_3_error) { + case Iso15693_3ErrorNone: + return SlixErrorNone; + case Iso15693_3ErrorTimeout: + return SlixErrorTimeout; + case Iso15693_3ErrorFormat: + return SlixErrorFormat; + case Iso15693_3ErrorInternal: + return SlixErrorInternal; + default: + return SlixErrorUnknown; + } +} + +SlixError slix_get_nxp_system_info_response_parse(SlixData* data, const BitBuffer* buf) { + furi_assert(data); + SlixError error = SlixErrorNone; + + do { + if(slix_error_response_parse(&error, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t pp_pointer; + uint8_t pp_condition; + uint8_t lock_bits; + uint32_t feature_flags; + } SlixGetNxpSystemInfoResponseLayout; + + const size_t size_received = bit_buffer_get_size_bytes(buf); + const size_t size_required = sizeof(SlixGetNxpSystemInfoResponseLayout); + + if(size_received != size_required) { + error = SlixErrorFormat; + break; + } + + const SlixGetNxpSystemInfoResponseLayout* response = + (const SlixGetNxpSystemInfoResponseLayout*)bit_buffer_get_data(buf); + + SlixProtection* protection = &data->system_info.protection; + protection->pointer = response->pp_pointer; + protection->condition = response->pp_condition; + + Iso15693_3LockBits* iso15693_3_lock_bits = &data->iso15693_3_data->settings.lock_bits; + iso15693_3_lock_bits->dsfid = response->lock_bits & SLIX_LOCK_BITS_DSFID; + iso15693_3_lock_bits->afi = response->lock_bits & SLIX_LOCK_BITS_AFI; + + SlixLockBits* slix_lock_bits = &data->system_info.lock_bits; + slix_lock_bits->eas = response->lock_bits & SLIX_LOCK_BITS_EAS; + slix_lock_bits->ppl = response->lock_bits & SLIX_LOCK_BITS_PPL; + + } while(false); + + return error; +} + +SlixError slix_read_signature_response_parse(SlixSignature data, const BitBuffer* buf) { + SlixError error = SlixErrorNone; + + do { + if(slix_error_response_parse(&error, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t signature[SLIX_SIGNATURE_SIZE]; + } SlixReadSignatureResponseLayout; + + const size_t size_received = bit_buffer_get_size_bytes(buf); + const size_t size_required = sizeof(SlixReadSignatureResponseLayout); + + if(size_received != size_required) { + error = SlixErrorFormat; + break; + } + + const SlixReadSignatureResponseLayout* response = + (const SlixReadSignatureResponseLayout*)bit_buffer_get_data(buf); + + memcpy(data, response->signature, sizeof(SlixSignature)); + } while(false); + + return error; +} + +void slix_set_password(SlixData* data, SlixPasswordType password_type, SlixPassword password) { + furi_assert(data); + furi_assert(password_type < SlixPasswordTypeCount); + + data->passwords[password_type] = password; +} + +void slix_set_privacy_mode(SlixData* data, bool set) { + furi_assert(data); + + data->privacy = set; +} + +void slix_increment_counter(SlixData* data) { + furi_assert(data); + + const uint8_t* block_data = + iso15693_3_get_block_data(data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM); + + SlixCounter counter; + memcpy(counter.bytes, block_data, SLIX_BLOCK_SIZE); + counter.value += 1; + + iso15693_3_set_block_data( + data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM, counter.bytes, sizeof(SlixCounter)); +} diff --git a/lib/nfc/protocols/slix/slix_i.h b/lib/nfc/protocols/slix/slix_i.h new file mode 100644 index 000000000000..b5e445f31d0e --- /dev/null +++ b/lib/nfc/protocols/slix/slix_i.h @@ -0,0 +1,86 @@ +#pragma once + +#include "slix.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SLIX_NXP_MANUFACTURER_CODE (0x04U) + +#define SLIX_LOCK_BITS_AFI (1U << 0) +#define SLIX_LOCK_BITS_EAS (1U << 1) +#define SLIX_LOCK_BITS_DSFID (1U << 2) +#define SLIX_LOCK_BITS_PPL (1U << 3) + +#define SLIX_CMD_CUSTOM_START (0xA2U) +#define SLIX_CMD_SET_EAS (0xA2U) +#define SLIX_CMD_RESET_EAS (0xA3U) +#define SLIX_CMD_LOCK_EAS (0xA4U) +#define SLIX_CMD_EAS_ALARM (0xA5U) +#define SLIX_CMD_PASSWORD_PROTECT_EAS_AFI (0xA6U) +#define SLIX_CMD_WRITE_EAS_ID (0xA7U) +#define SLIX_CMD_GET_NXP_SYSTEM_INFORMATION (0xABU) +#define SLIX_CMD_INVENTORY_PAGE_READ (0xB0U) +#define SLIX_CMD_INVENTORY_PAGE_READ_FAST (0xB1U) +#define SLIX_CMD_GET_RANDOM_NUMBER (0xB2U) +#define SLIX_CMD_SET_PASSWORD (0xB3U) +#define SLIX_CMD_WRITE_PASSWORD (0xB4U) +#define SLIX_CMD_64_BIT_PASSWORD_PROTECTION (0xB5U) +#define SLIX_CMD_PROTECT_PAGE (0xB6U) +#define SLIX_CMD_LOCK_PAGE_PROTECTION_CONDITION (0xB7U) +#define SLIX_CMD_DESTROY (0xB9U) +#define SLIX_CMD_ENABLE_PRIVACY (0xBAU) +#define SLIX_CMD_STAY_QUIET_PERSISTENT (0xBCU) +#define SLIX_CMD_READ_SIGNATURE (0xBDU) +#define SLIX_CMD_CUSTOM_END (0xBEU) +#define SLIX_CMD_CUSTOM_COUNT (SLIX_CMD_CUSTOM_END - SLIX_CMD_CUSTOM_START) + +#define SLIX_TYPE_FEATURES_SLIX (SLIX_TYPE_FEATURE_EAS) +#define SLIX_TYPE_FEATURES_SLIX_S \ + (SLIX_TYPE_FEATURE_READ | SLIX_TYPE_FEATURE_WRITE | SLIX_TYPE_FEATURE_PRIVACY | \ + SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS) +#define SLIX_TYPE_FEATURES_SLIX_L \ + (SLIX_TYPE_FEATURE_PRIVACY | SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS) +#define SLIX_TYPE_FEATURES_SLIX2 \ + (SLIX_TYPE_FEATURE_READ | SLIX_TYPE_FEATURE_WRITE | SLIX_TYPE_FEATURE_PRIVACY | \ + SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS | SLIX_TYPE_FEATURE_SIGNATURE | \ + SLIX_TYPE_FEATURE_PROTECTION) + +#define SLIX2_FEATURE_FLAGS \ + (SLIX_FEATURE_FLAG_UM_PP | SLIX_FEATURE_FLAG_COUNTER | SLIX_FEATURE_FLAG_EAS_ID | \ + SLIX_FEATURE_FLAG_EAS_PP | SLIX_FEATURE_FLAG_AFI_PP | SLIX_FEATURE_FLAG_INVENTORY_READ_EXT | \ + SLIX_FEATURE_FLAG_EAS_IR | SLIX_FEATURE_FLAG_ORIGINALITY_SIG | \ + SLIX_FEATURE_FLAG_PERSISTENT_QUIET | SLIX_FEATURE_FLAG_PRIVACY | SLIX_FEATURE_FLAG_DESTROY) + +typedef union { + struct { + uint16_t value; + uint8_t reserved; + uint8_t protection; + }; + uint8_t bytes[SLIX_BLOCK_SIZE]; +} SlixCounter; + +// Same behaviour as iso15693_3_error_response_parse +bool slix_error_response_parse(SlixError* error, const BitBuffer* buf); + +SlixError slix_process_iso15693_3_error(Iso15693_3Error iso15693_3_error); + +SlixError slix_get_nxp_system_info_response_parse(SlixData* data, const BitBuffer* buf); + +SlixError slix_read_signature_response_parse(SlixSignature data, const BitBuffer* buf); + +// Setters +void slix_set_password(SlixData* data, SlixPasswordType password_type, SlixPassword password); + +void slix_set_privacy_mode(SlixData* data, bool set); + +void slix_increment_counter(SlixData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_listener.c b/lib/nfc/protocols/slix/slix_listener.c new file mode 100644 index 000000000000..f65892deea6b --- /dev/null +++ b/lib/nfc/protocols/slix/slix_listener.c @@ -0,0 +1,79 @@ +#include "slix_listener_i.h" + +#include +#include + +#define TAG "SlixListener" + +#define SLIX_LISTENER_BUF_SIZE (64U) + +static SlixListener* slix_listener_alloc(Iso15693_3Listener* iso15693_3_listener, SlixData* data) { + furi_assert(iso15693_3_listener); + + SlixListener* instance = malloc(sizeof(SlixListener)); + instance->iso15693_3_listener = iso15693_3_listener; + instance->data = data; + + instance->tx_buffer = bit_buffer_alloc(SLIX_LISTENER_BUF_SIZE); + + instance->slix_event.data = &instance->slix_event_data; + instance->generic_event.protocol = NfcProtocolSlix; + instance->generic_event.instance = instance; + instance->generic_event.data = &instance->slix_event; + + slix_listener_init_iso15693_3_extensions(instance); + + return instance; +} + +static void slix_listener_free(SlixListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->tx_buffer); + + bit_buffer_free(instance->tx_buffer); + free(instance); +} + +static void + slix_listener_set_callback(SlixListener* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +static const SlixData* slix_listener_get_data(SlixListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static NfcCommand slix_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso15693_3); + furi_assert(event.data); + + SlixListener* instance = context; + Iso15693_3ListenerEvent* iso15693_3_event = event.data; + BitBuffer* rx_buffer = iso15693_3_event->data->buffer; + NfcCommand command = NfcCommandContinue; + + if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) { + const SlixError error = slix_listener_process_request(instance, rx_buffer); + if(error == SlixErrorWrongPassword) { + command = NfcCommandStop; + } + } + + return command; +} + +const NfcListenerBase nfc_listener_slix = { + .alloc = (NfcListenerAlloc)slix_listener_alloc, + .free = (NfcListenerFree)slix_listener_free, + .set_callback = (NfcListenerSetCallback)slix_listener_set_callback, + .get_data = (NfcListenerGetData)slix_listener_get_data, + .run = (NfcListenerRun)slix_listener_run, +}; diff --git a/lib/nfc/protocols/slix/slix_listener.h b/lib/nfc/protocols/slix/slix_listener.h new file mode 100644 index 000000000000..7b8518a5bb71 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_listener.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "slix.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SlixListener SlixListener; + +typedef enum { + SlixListenerEventTypeFieldOff, + SlixListenerEventTypeCustomCommand, +} SlixListenerEventType; + +typedef struct { + BitBuffer* buffer; +} SlixListenerEventData; + +typedef struct { + SlixListenerEventType type; + SlixListenerEventData* data; +} SlixListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_listener_defs.h b/lib/nfc/protocols/slix/slix_listener_defs.h new file mode 100644 index 000000000000..415c284830ca --- /dev/null +++ b/lib/nfc/protocols/slix/slix_listener_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcListenerBase nfc_listener_slix; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_listener_i.c b/lib/nfc/protocols/slix/slix_listener_i.c new file mode 100644 index 000000000000..4f075fd3f5df --- /dev/null +++ b/lib/nfc/protocols/slix/slix_listener_i.c @@ -0,0 +1,617 @@ +#include "slix_listener_i.h" + +#include + +#include + +#define TAG "SlixListener" + +typedef SlixError (*SlixRequestHandler)( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags); + +// Helper functions + +static bool + slix_listener_is_password_lock_enabled(SlixListener* instance, SlixPasswordType password_type) { + return !instance->session_state.password_match[password_type]; +} + +static SlixPasswordType slix_listener_get_password_type_by_id(uint8_t id) { + uint32_t type; + + for(type = 0; type < SlixPasswordTypeCount; ++type) { + if(id >> type == 0x01U) break; + } + + return type; +} + +static SlixPassword + slix_listener_unxor_password(const SlixPassword password_xored, uint16_t random) { + return password_xored ^ ((SlixPassword)random << 16 | random); +} + +static SlixError slix_listener_set_password( + SlixListener* instance, + SlixPasswordType password_type, + SlixPassword password) { + SlixError error = SlixErrorNone; + + do { + if(password_type >= SlixPasswordTypeCount) { + error = SlixErrorInternal; + break; + } + + SlixData* slix_data = instance->data; + + if(!slix_type_supports_password(slix_get_type(slix_data), password_type)) { + error = SlixErrorNotSupported; + break; + } + + SlixListenerSessionState* session_state = &instance->session_state; + session_state->password_match[password_type] = + (password == slix_get_password(slix_data, password_type)); + + if(!session_state->password_match[password_type]) { + error = SlixErrorWrongPassword; + break; + } + } while(false); + + return error; +} + +static SlixError slix_listener_write_password( + SlixListener* instance, + SlixPasswordType password_type, + SlixPassword password) { + SlixError error = SlixErrorNone; + + do { + if(password_type >= SlixPasswordTypeCount) { + error = SlixErrorInternal; + break; + } + + SlixData* slix_data = instance->data; + + if(!slix_type_supports_password(slix_get_type(slix_data), password_type)) { + error = SlixErrorNotSupported; + break; + } + + SlixListenerSessionState* session_state = &instance->session_state; + + if(session_state->password_match[password_type]) { + // TODO: check for password lock + slix_set_password(slix_data, password_type, password); + // Require another SET_PASSWORD command with the new password + session_state->password_match[password_type] = false; + } else { + error = SlixErrorWrongPassword; + break; + } + } while(false); + + return error; +} + +// Custom SLIX request handlers +static SlixError slix_listener_default_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(instance); + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + // Empty placeholder handler + return SlixErrorNotSupported; +} + +static SlixError slix_listener_get_nxp_system_info_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + const SlixData* slix_data = instance->data; + const Iso15693_3Data* iso15693_data = instance->data->iso15693_3_data; + + const SlixProtection* protection = &slix_data->system_info.protection; + bit_buffer_append_byte(instance->tx_buffer, protection->pointer); + bit_buffer_append_byte(instance->tx_buffer, protection->condition); + + uint8_t lock_bits = 0; + if(iso15693_data->settings.lock_bits.dsfid) { + lock_bits |= SLIX_LOCK_BITS_DSFID; + } + if(iso15693_data->settings.lock_bits.afi) { + lock_bits |= SLIX_LOCK_BITS_AFI; + } + if(slix_data->system_info.lock_bits.eas) { + lock_bits |= SLIX_LOCK_BITS_EAS; + } + if(slix_data->system_info.lock_bits.ppl) { + lock_bits |= SLIX_LOCK_BITS_PPL; + } + bit_buffer_append_byte(instance->tx_buffer, lock_bits); + + const uint32_t feature_flags = SLIX2_FEATURE_FLAGS; + bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&feature_flags, sizeof(uint32_t)); + + return SlixErrorNone; +} + +static SlixError slix_listener_get_random_number_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + SlixListenerSessionState* session_state = &instance->session_state; + session_state->random = furi_hal_random_get(); + bit_buffer_append_bytes( + instance->tx_buffer, (uint8_t*)&session_state->random, sizeof(uint16_t)); + + return SlixErrorNone; +} + +static SlixError slix_listener_set_password_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(flags); + SlixError error = SlixErrorNone; + + do { +#pragma pack(push, 1) + typedef struct { + uint8_t password_id; + SlixPassword password_xored; + } SlixSetPasswordRequestLayout; +#pragma pack(pop) + + if(data_size != sizeof(SlixSetPasswordRequestLayout)) { + error = SlixErrorFormat; + break; + } + + const SlixSetPasswordRequestLayout* request = (const SlixSetPasswordRequestLayout*)data; + const SlixPasswordType password_type = + slix_listener_get_password_type_by_id(request->password_id); + const SlixPassword password_received = + slix_listener_unxor_password(request->password_xored, instance->session_state.random); + + error = slix_listener_set_password(instance, password_type, password_received); + if(error != SlixErrorNone) break; + + if(password_type == SlixPasswordTypePrivacy) { + slix_set_privacy_mode(instance->data, false); + } + } while(false); + + return error; +} + +static SlixError slix_listener_write_password_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(flags); + SlixError error = SlixErrorNone; + + do { +#pragma pack(push, 1) + typedef struct { + uint8_t password_id; + SlixPassword password; + } SlixWritePasswordRequestLayout; +#pragma pack(pop) + + if(data_size != sizeof(SlixWritePasswordRequestLayout)) { + error = SlixErrorFormat; + break; + } + + const SlixWritePasswordRequestLayout* request = + (const SlixWritePasswordRequestLayout*)data; + const SlixPasswordType password_type = + slix_listener_get_password_type_by_id(request->password_id); + + error = slix_listener_write_password(instance, password_type, request->password); + if(error != SlixErrorNone) break; + + } while(false); + + return error; +} + +static SlixError slix_listener_protect_page_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(flags); + SlixError error = SlixErrorNone; + + do { + typedef struct { + uint8_t pointer; + uint8_t condition; + } SlixProtectPageRequestLayout; + + if(data_size != sizeof(SlixProtectPageRequestLayout)) { + error = SlixErrorFormat; + break; + } + + SlixData* slix_data = instance->data; + + if(slix_data->system_info.lock_bits.ppl) { + error = SlixErrorInternal; + break; + } + + const SlixListenerSessionState* session_state = &instance->session_state; + if(!session_state->password_match[SlixPasswordTypeRead] || + !session_state->password_match[SlixPasswordTypeWrite]) { + error = SlixErrorInternal; + break; + } + + const SlixProtectPageRequestLayout* request = (const SlixProtectPageRequestLayout*)data; + + if(request->pointer >= SLIX_COUNTER_BLOCK_NUM) { + error = SlixErrorInternal; + break; + } + + SlixProtection* protection = &slix_data->system_info.protection; + + protection->pointer = request->pointer; + protection->condition = request->condition; + } while(false); + + return error; +} + +static SlixError slix_listener_enable_privacy_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(flags); + SlixError error = SlixErrorNone; + + do { + typedef struct { + SlixPassword password_xored; + } SlixEnablePrivacyRequestLayout; + + if(data_size != sizeof(SlixEnablePrivacyRequestLayout)) { + error = SlixErrorFormat; + break; + } + + const SlixEnablePrivacyRequestLayout* request = + (const SlixEnablePrivacyRequestLayout*)data; + + const SlixPassword password_received = + slix_listener_unxor_password(request->password_xored, instance->session_state.random); + + error = slix_listener_set_password(instance, SlixPasswordTypePrivacy, password_received); + if(error != SlixErrorNone) break; + + slix_set_privacy_mode(instance->data, true); + } while(false); + + return error; +} + +static SlixError slix_listener_read_signature_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + bit_buffer_append_bytes(instance->tx_buffer, instance->data->signature, sizeof(SlixSignature)); + return SlixErrorNone; +} + +// Custom SLIX commands handler table +static const SlixRequestHandler slix_request_handler_table[SLIX_CMD_CUSTOM_COUNT] = { + slix_listener_default_handler, // SLIX_CMD_SET_EAS (0xA2U) + slix_listener_default_handler, // SLIX_CMD_RESET_EAS (0xA3U) + slix_listener_default_handler, // SLIX_CMD_LOCK_EAS (0xA4U) + slix_listener_default_handler, // SLIX_CMD_EAS_ALARM (0xA5U) + slix_listener_default_handler, // SLIX_CMD_PASSWORD_PROTECT_EAS_AFI (0xA6U) + slix_listener_default_handler, // SLIX_CMD_WRITE_EAS_ID (0xA7U) + slix_listener_default_handler, // UNUSED (0xA8U) + slix_listener_default_handler, // UNUSED (0xA9U) + slix_listener_default_handler, // UNUSED (0xAAU) + slix_listener_get_nxp_system_info_handler, + slix_listener_default_handler, // UNUSED (0xACU) + slix_listener_default_handler, // UNUSED (0xADU) + slix_listener_default_handler, // UNUSED (0xAEU) + slix_listener_default_handler, // UNUSED (0xAFU) + slix_listener_default_handler, // SLIX_CMD_INVENTORY_PAGE_READ (0xB0U) + slix_listener_default_handler, // SLIX_CMD_INVENTORY_PAGE_READ_FAST (0xB1U) + slix_listener_get_random_number_handler, + slix_listener_set_password_handler, + slix_listener_write_password_handler, + slix_listener_default_handler, // SLIX_CMD_64_BIT_PASSWORD_PROTECTION (0xB5U) + slix_listener_protect_page_handler, + slix_listener_default_handler, // SLIX_CMD_LOCK_PAGE_PROTECTION_CONDITION (0xB7U) + slix_listener_default_handler, // UNUSED (0xB8U) + slix_listener_default_handler, // SLIX_CMD_DESTROY (0xB9U) + slix_listener_enable_privacy_handler, + slix_listener_default_handler, // UNUSED (0xBBU) + slix_listener_default_handler, // SLIX_CMD_STAY_QUIET_PERSISTENT (0xBCU) + slix_listener_read_signature_handler, +}; + +// ISO15693-3 Protocol extension handlers + +static Iso15693_3Error + slix_listener_iso15693_3_inventory_extension_handler(SlixListener* instance, va_list args) { + UNUSED(args); + + return instance->data->privacy ? Iso15693_3ErrorIgnore : Iso15693_3ErrorNone; +} + +static Iso15693_3Error + slix_iso15693_3_read_block_extension_handler(SlixListener* instance, va_list args) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const uint32_t block_num = va_arg(args, uint32_t); + // SLIX Counter has no read protection + if(block_num == SLIX_COUNTER_BLOCK_NUM) break; + + if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) { + error = Iso15693_3ErrorInternal; + break; + } + } + } while(false); + + return error; +} + +static Iso15693_3Error + slix_listener_iso15693_3_write_block_extension_handler(SlixListener* instance, va_list args) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const uint32_t block_num = va_arg(args, uint32_t); + + if(block_num == SLIX_COUNTER_BLOCK_NUM) { + const uint32_t counter = *(va_arg(args, uint32_t*)); + if(counter == 0x00000001U) { + if(slix_is_counter_increment_protected(instance->data) && + slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) { + error = Iso15693_3ErrorInternal; + break; + } + slix_increment_counter(instance->data); + error = Iso15693_3ErrorFullyHandled; + break; + } + } else if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) { + error = Iso15693_3ErrorInternal; + break; + } + } + + if(slix_is_block_protected(instance->data, SlixPasswordTypeWrite, block_num)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeWrite)) { + error = Iso15693_3ErrorInternal; + break; + } + } + + } while(false); + + return error; +} + +static Iso15693_3Error + slix_listener_iso15693_3_lock_block_extension_handler(SlixListener* instance, va_list args) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const uint32_t block_num = va_arg(args, uint32_t); + + // SLIX counter cannot be locked + if(block_num == SLIX_COUNTER_BLOCK_NUM) { + error = Iso15693_3ErrorInternal; + break; + } + + if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) { + error = Iso15693_3ErrorInternal; + break; + } + } + + if(slix_is_block_protected(instance->data, SlixPasswordTypeWrite, block_num)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeWrite)) { + error = Iso15693_3ErrorInternal; + break; + } + } + + } while(false); + + return error; +} + +static Iso15693_3Error slix_listener_iso15693_3_read_multi_block_extension_handler( + SlixListener* instance, + va_list args) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + const uint32_t block_index_start = va_arg(args, uint32_t); + const uint32_t block_index_end = va_arg(args, uint32_t); + + for(uint32_t i = block_index_start; i <= block_index_end; ++i) { + // SLIX Counter has no read protection + if(i == SLIX_COUNTER_BLOCK_NUM) continue; + + if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, i)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) { + error = Iso15693_3ErrorInternal; + break; + } + } + } + + return error; +} + +static Iso15693_3Error slix_listener_iso15693_3_write_multi_block_extension_handler( + SlixListener* instance, + va_list args) { + UNUSED(instance); + UNUSED(args); + // No mention of this command in SLIX docs, assuming not supported + return Iso15693_3ErrorNotSupported; +} + +static Iso15693_3Error slix_listener_iso15693_3_write_lock_afi_extension_handler( + SlixListener* instance, + va_list args) { + UNUSED(args); + + return slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeEasAfi) ? + Iso15693_3ErrorInternal : + Iso15693_3ErrorNone; +} + +// Extended ISO15693-3 standard commands handler table (NULL = no extension) +static const Iso15693_3ExtensionHandlerTable slix_iso15693_extension_handler_table = { + .mandatory = + { + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_inventory_extension_handler, + (Iso15693_3ExtensionHandler)NULL // ISO15693_3_CMD_STAY_QUIET (0x02U) + }, + .optional = + { + (Iso15693_3ExtensionHandler)slix_iso15693_3_read_block_extension_handler, + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_block_extension_handler, + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_lock_block_extension_handler, + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_read_multi_block_extension_handler, + (Iso15693_3ExtensionHandler) + slix_listener_iso15693_3_write_multi_block_extension_handler, + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_SELECT (0x25U) + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_RESET_TO_READY (0x26U) + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_lock_afi_extension_handler, + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_lock_afi_extension_handler, + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_WRITE_DSFID (0x29U) + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_LOCK_DSFID (0x2AU) + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_GET_SYS_INFO (0x2BU) + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_GET_BLOCKS_SECURITY (0x2CU) + }, +}; + +SlixError slix_listener_init_iso15693_3_extensions(SlixListener* instance) { + iso15693_3_listener_set_extension_handler_table( + instance->iso15693_3_listener, &slix_iso15693_extension_handler_table, instance); + return SlixErrorNone; +} + +SlixError slix_listener_process_request(SlixListener* instance, const BitBuffer* rx_buffer) { + SlixError error = SlixErrorNone; + + do { + typedef struct { + uint8_t flags; + uint8_t command; + uint8_t manufacturer; + uint8_t data[]; + } SlixRequestLayout; + + const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer); + + if(buf_size < sizeof(SlixRequestLayout)) { + error = SlixErrorFormat; + break; + } + + const SlixRequestLayout* request = + (const SlixRequestLayout*)bit_buffer_get_data(rx_buffer); + + const bool addressed_mode = instance->iso15693_3_listener->session_state.addressed; + + const size_t uid_field_size = addressed_mode ? ISO15693_3_UID_SIZE : 0; + const size_t buf_size_min = sizeof(SlixRequestLayout) + uid_field_size; + + if(buf_size < buf_size_min) { + error = SlixErrorFormat; + break; + } + + if(addressed_mode) { + if(!iso15693_3_is_equal_uid(instance->data->iso15693_3_data, request->data)) { + error = SlixErrorUidMismatch; + break; + } + } + + const uint8_t command = request->command; + const bool is_valid_slix_command = command >= SLIX_CMD_CUSTOM_START && + command < SLIX_CMD_CUSTOM_END; + if(!is_valid_slix_command) { + error = SlixErrorNotSupported; + break; + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_NONE); + + const uint8_t* request_data = &request->data[uid_field_size]; + const size_t request_data_size = buf_size - buf_size_min; + + SlixRequestHandler handler = slix_request_handler_table[command - SLIX_CMD_CUSTOM_START]; + error = handler(instance, request_data, request_data_size, request->flags); + + // It's a trick! Send no reply. + if(error == SlixErrorFormat || error == SlixErrorWrongPassword || + error == SlixErrorNotSupported) + break; + + if(error != SlixErrorNone) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_ERROR); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_ERROR_UNKNOWN); + } + + const Iso15693_3Error iso15693_error = + iso15693_3_listener_send_frame(instance->iso15693_3_listener, instance->tx_buffer); + error = slix_process_iso15693_3_error(iso15693_error); + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/slix/slix_listener_i.h b/lib/nfc/protocols/slix/slix_listener_i.h new file mode 100644 index 000000000000..ce059c5a675e --- /dev/null +++ b/lib/nfc/protocols/slix/slix_listener_i.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "slix_listener.h" +#include "slix_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint16_t random; + bool password_match[SlixPasswordTypeCount]; +} SlixListenerSessionState; + +struct SlixListener { + Iso15693_3Listener* iso15693_3_listener; + SlixData* data; + SlixListenerSessionState session_state; + + BitBuffer* tx_buffer; + + NfcGenericEvent generic_event; + SlixListenerEvent slix_event; + SlixListenerEventData slix_event_data; + NfcGenericCallback callback; + void* context; +}; + +SlixError slix_listener_init_iso15693_3_extensions(SlixListener* instance); + +SlixError slix_listener_process_request(SlixListener* instance, const BitBuffer* rx_buffer); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_poller.c b/lib/nfc/protocols/slix/slix_poller.c new file mode 100644 index 000000000000..8950fe303c4e --- /dev/null +++ b/lib/nfc/protocols/slix/slix_poller.c @@ -0,0 +1,154 @@ +#include "slix_poller_i.h" + +#include + +#include + +#define TAG "SlixPoller" + +#define SLIX_POLLER_BUF_SIZE (64U) + +typedef NfcCommand (*SlixPollerStateHandler)(SlixPoller* instance); + +const SlixData* slix_poller_get_data(SlixPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static SlixPoller* slix_poller_alloc(Iso15693_3Poller* iso15693_3_poller) { + SlixPoller* instance = malloc(sizeof(SlixPoller)); + instance->iso15693_3_poller = iso15693_3_poller; + instance->data = slix_alloc(); + instance->tx_buffer = bit_buffer_alloc(SLIX_POLLER_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(SLIX_POLLER_BUF_SIZE); + + instance->slix_event.data = &instance->slix_event_data; + + instance->general_event.protocol = NfcProtocolSlix; + instance->general_event.data = &instance->slix_event; + instance->general_event.instance = instance; + + return instance; +} + +static void slix_poller_free(SlixPoller* instance) { + furi_assert(instance); + + slix_free(instance->data); + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + free(instance); +} + +static NfcCommand slix_poller_handler_idle(SlixPoller* instance) { + iso15693_3_copy( + instance->data->iso15693_3_data, iso15693_3_poller_get_data(instance->iso15693_3_poller)); + + instance->poller_state = SlixPollerStateGetNxpSysInfo; + return NfcCommandContinue; +} + +static NfcCommand slix_poller_handler_get_nfc_system_info(SlixPoller* instance) { + instance->error = + slix_poller_async_get_nxp_system_info(instance, &instance->data->system_info); + if(instance->error == SlixErrorNone) { + instance->poller_state = SlixPollerStateReadSignature; + } else { + instance->poller_state = SlixPollerStateError; + } + + return NfcCommandContinue; +} + +static NfcCommand slix_poller_handler_read_signature(SlixPoller* instance) { + instance->error = slix_poller_async_read_signature(instance, &instance->data->signature); + if(instance->error == SlixErrorNone) { + instance->poller_state = SlixPollerStateReady; + } else { + instance->poller_state = SlixPollerStateError; + } + + return NfcCommandContinue; +} + +static NfcCommand slix_poller_handler_error(SlixPoller* instance) { + instance->slix_event_data.error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->poller_state = SlixPollerStateIdle; + return command; +} + +static NfcCommand slix_poller_handler_ready(SlixPoller* instance) { + instance->slix_event.type = SlixPollerEventTypeReady; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const SlixPollerStateHandler slix_poller_state_handler[SlixPollerStateNum] = { + [SlixPollerStateIdle] = slix_poller_handler_idle, + [SlixPollerStateError] = slix_poller_handler_error, + [SlixPollerStateGetNxpSysInfo] = slix_poller_handler_get_nfc_system_info, + [SlixPollerStateReadSignature] = slix_poller_handler_read_signature, + [SlixPollerStateReady] = slix_poller_handler_ready, +}; + +static void + slix_poller_set_callback(SlixPoller* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand slix_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso15693_3); + + SlixPoller* instance = context; + furi_assert(instance); + furi_assert(instance->callback); + + Iso15693_3PollerEvent* iso15693_3_event = event.data; + furi_assert(iso15693_3_event); + + NfcCommand command = NfcCommandContinue; + + if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) { + command = slix_poller_state_handler[instance->poller_state](instance); + } else if(iso15693_3_event->type == Iso15693_3PollerEventTypeError) { + instance->slix_event.type = SlixPollerEventTypeError; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool slix_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso15693_3); + + const SlixPoller* instance = context; + furi_assert(instance); + + const Iso15693_3PollerEvent* iso15693_3_event = event.data; + furi_assert(iso15693_3_event); + iso15693_3_copy( + instance->data->iso15693_3_data, iso15693_3_poller_get_data(instance->iso15693_3_poller)); + + bool protocol_detected = false; + + if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) { + protocol_detected = slix_get_type(instance->data) < SlixTypeCount; + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_slix = { + .alloc = (NfcPollerAlloc)slix_poller_alloc, + .free = (NfcPollerFree)slix_poller_free, + .set_callback = (NfcPollerSetCallback)slix_poller_set_callback, + .run = (NfcPollerRun)slix_poller_run, + .detect = (NfcPollerDetect)slix_poller_detect, + .get_data = (NfcPollerGetData)slix_poller_get_data, +}; diff --git a/lib/nfc/protocols/slix/slix_poller.h b/lib/nfc/protocols/slix/slix_poller.h new file mode 100644 index 000000000000..f4c7214de4f5 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_poller.h @@ -0,0 +1,29 @@ +#pragma once + +#include "slix.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SlixPoller SlixPoller; + +typedef enum { + SlixPollerEventTypeError, + SlixPollerEventTypeReady, +} SlixPollerEventType; + +typedef struct { + SlixError error; +} SlixPollerEventData; + +typedef struct { + SlixPollerEventType type; + SlixPollerEventData* data; +} SlixPollerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_poller_defs.h b/lib/nfc/protocols/slix/slix_poller_defs.h new file mode 100644 index 000000000000..f619ad179d5d --- /dev/null +++ b/lib/nfc/protocols/slix/slix_poller_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase nfc_poller_slix; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_poller_i.c b/lib/nfc/protocols/slix/slix_poller_i.c new file mode 100644 index 000000000000..a36e7694a4f3 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_poller_i.c @@ -0,0 +1,69 @@ +#include "slix_poller_i.h" + +#include + +#include "slix_i.h" + +#define TAG "SlixPoller" + +static void slix_poller_prepare_request(SlixPoller* instance, uint8_t command) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_append_byte( + instance->tx_buffer, + ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI | + ISO15693_3_REQ_FLAG_T4_ADDRESSED); + bit_buffer_append_byte(instance->tx_buffer, command); + bit_buffer_append_byte(instance->tx_buffer, SLIX_NXP_MANUFACTURER_CODE); + + iso15693_3_append_uid(instance->data->iso15693_3_data, instance->tx_buffer); +} + +SlixError slix_poller_send_frame( + SlixPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + + const Iso15693_3Error iso15693_3_error = + iso15693_3_poller_send_frame(instance->iso15693_3_poller, tx_buffer, rx_buffer, fwt); + return slix_process_iso15693_3_error(iso15693_3_error); +} + +SlixError slix_poller_async_get_nxp_system_info(SlixPoller* instance, SlixSystemInfo* data) { + furi_assert(instance); + furi_assert(data); + + slix_poller_prepare_request(instance, SLIX_CMD_GET_NXP_SYSTEM_INFORMATION); + + SlixError error = SlixErrorNone; + + do { + error = slix_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); + if(error != SlixErrorNone) break; + error = slix_get_nxp_system_info_response_parse(instance->data, instance->rx_buffer); + } while(false); + + return error; +} + +SlixError slix_poller_async_read_signature(SlixPoller* instance, SlixSignature* data) { + furi_assert(instance); + furi_assert(data); + + slix_poller_prepare_request(instance, SLIX_CMD_READ_SIGNATURE); + + SlixError error = SlixErrorNone; + + do { + error = slix_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC * 2); + if(error != SlixErrorNone) break; + error = slix_read_signature_response_parse(instance->data->signature, instance->rx_buffer); + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/slix/slix_poller_i.h b/lib/nfc/protocols/slix/slix_poller_i.h new file mode 100644 index 000000000000..c6a8a3c33efa --- /dev/null +++ b/lib/nfc/protocols/slix/slix_poller_i.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include "slix_poller.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + SlixPollerStateIdle, + SlixPollerStateGetNxpSysInfo, + SlixPollerStateReadSignature, + SlixPollerStateReady, + SlixPollerStateError, + SlixPollerStateNum, +} SlixPollerState; + +struct SlixPoller { + Iso15693_3Poller* iso15693_3_poller; + SlixData* data; + SlixPollerState poller_state; + SlixError error; + + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + SlixPollerEventData slix_event_data; + SlixPollerEvent slix_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +SlixError slix_poller_send_frame( + SlixPoller* instance, + const BitBuffer* tx_data, + BitBuffer* rx_data, + uint32_t fwt); + +SlixError slix_poller_async_get_nxp_system_info(SlixPoller* instance, SlixSystemInfo* data); + +SlixError slix_poller_async_read_signature(SlixPoller* instance, SlixSignature* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 74d99fe801c2..1c3cb4a586f8 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -135,6 +135,7 @@ void pulse_reader_stop(PulseReader* signal) { LL_DMA_DisableChannel(DMA1, signal->dma_channel + 1); LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); LL_TIM_DisableCounter(TIM2); + furi_hal_bus_disable(FuriHalBusTIM2); furi_hal_gpio_init_simple(signal->gpio, GpioModeAnalog); } @@ -146,6 +147,8 @@ void pulse_reader_start(PulseReader* signal) { signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buffer; signal->dma_config_gpio.NbData = signal->size; + furi_hal_bus_enable(FuriHalBusTIM2); + /* start counter */ LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); diff --git a/lib/signal_reader/SConscript b/lib/signal_reader/SConscript new file mode 100644 index 000000000000..ea7314420189 --- /dev/null +++ b/lib/signal_reader/SConscript @@ -0,0 +1,20 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/signal_reader", + ], + SDK_HEADERS=[ + File("signal_reader.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="signal_reader") +libenv.ApplyLibFlags() +libenv.Append(CCFLAGS=["-O3", "-funroll-loops", "-Ofast"]) + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/signal_reader/parsers/iso15693/iso15693_parser.c b/lib/signal_reader/parsers/iso15693/iso15693_parser.c new file mode 100644 index 000000000000..4021e4ef98bf --- /dev/null +++ b/lib/signal_reader/parsers/iso15693/iso15693_parser.c @@ -0,0 +1,301 @@ +#include "iso15693_parser.h" + +#include + +#include + +#define ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE (2) +#define ISO15693_PARSER_BITSTREAM_BUFF_SIZE (32) +#define ISO15693_PARSER_BITRATE_F64MHZ (603U) + +#define TAG "Iso15693Parser" + +typedef enum { + Iso15693ParserStateParseSoF, + Iso15693ParserStateParseFrame, + Iso15693ParserStateFail, +} Iso15693ParserState; + +typedef enum { + Iso15693ParserMode1OutOf4, + Iso15693ParserMode1OutOf256, + + Iso15693ParserModeNum, +} Iso15693ParserMode; + +struct Iso15693Parser { + Iso15693ParserState state; + Iso15693ParserMode mode; + + SignalReader* signal_reader; + + uint8_t bitstream_buff[ISO15693_PARSER_BITSTREAM_BUFF_SIZE]; + size_t bitstream_idx; + uint8_t last_byte; + + bool signal_detected; + bool bit_offset_calculated; + uint8_t bit_offset; + size_t byte_idx; + size_t bytes_to_process; + + uint8_t next_byte; + uint16_t next_byte_part; + bool zero_found; + + BitBuffer* parsed_frame; + bool eof_received; + bool frame_parsed; + + Iso15693ParserCallback callback; + void* context; +}; + +typedef enum { + Iso15693ParserCommandProcessed, + Iso15693ParserCommandWaitData, + Iso15693ParserCommandFail, + Iso15693ParserCommandSuccess, +} Iso15693ParserCommand; + +typedef Iso15693ParserCommand (*Iso15693ParserStateHandler)(Iso15693Parser* instance); + +Iso15693Parser* iso15693_parser_alloc(const GpioPin* pin, size_t max_frame_size) { + Iso15693Parser* instance = malloc(sizeof(Iso15693Parser)); + instance->parsed_frame = bit_buffer_alloc(max_frame_size); + + instance->signal_reader = signal_reader_alloc(pin, ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE); + signal_reader_set_sample_rate( + instance->signal_reader, SignalReaderTimeUnit64Mhz, ISO15693_PARSER_BITRATE_F64MHZ); + signal_reader_set_pull(instance->signal_reader, GpioPullDown); + signal_reader_set_polarity(instance->signal_reader, SignalReaderPolarityInverted); + signal_reader_set_trigger(instance->signal_reader, SignalReaderTriggerRisingFallingEdge); + + return instance; +} + +void iso15693_parser_free(Iso15693Parser* instance) { + furi_assert(instance); + + bit_buffer_free(instance->parsed_frame); + signal_reader_free(instance->signal_reader); + free(instance); +} + +void iso15693_parser_reset(Iso15693Parser* instance) { + furi_assert(instance); + + instance->state = Iso15693ParserStateParseSoF; + instance->mode = Iso15693ParserMode1OutOf4; + memset(instance->bitstream_buff, 0x00, sizeof(instance->bitstream_buff)); + instance->bitstream_idx = 0; + + instance->next_byte = 0; + instance->next_byte_part = 0; + + instance->bit_offset = 0; + instance->byte_idx = 0; + instance->bytes_to_process = 0; + instance->signal_detected = false; + instance->bit_offset_calculated = false; + + instance->last_byte = 0x00; + instance->zero_found = false; + instance->eof_received = false; + + bit_buffer_reset(instance->parsed_frame); + instance->frame_parsed = false; +} + +static void signal_reader_callback(SignalReaderEvent event, void* context) { + furi_assert(context); + furi_assert(event.data->data); + furi_assert(event.data->len == ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE / 2); + + Iso15693Parser* instance = context; + furi_assert(instance->callback); + + const uint8_t sof_1_out_of_4 = 0x21; + const uint8_t sof_1_out_of_256 = 0x81; + const uint8_t eof_single = 0x01; + const uint8_t eof = 0x04; + + if(instance->state == Iso15693ParserStateParseSoF) { + if(event.data->data[0] == sof_1_out_of_4) { + instance->mode = Iso15693ParserMode1OutOf4; + instance->state = Iso15693ParserStateParseFrame; + } else if(event.data->data[0] == sof_1_out_of_256) { + instance->mode = Iso15693ParserMode1OutOf256; + instance->state = Iso15693ParserStateParseFrame; + } else if(event.data->data[0] == eof_single) { + instance->eof_received = true; + instance->callback(Iso15693ParserEventDataReceived, instance->context); + } else { + // TODO: Better garbage input handling + instance->state = Iso15693ParserStateFail; + instance->callback(Iso15693ParserEventDataReceived, instance->context); + } + } else { + if(instance->mode == Iso15693ParserMode1OutOf4) { + if(event.data->data[0] == eof) { + instance->eof_received = true; + instance->callback(Iso15693ParserEventDataReceived, instance->context); + } else { + instance->bitstream_buff[instance->bytes_to_process] = event.data->data[0]; + instance->bytes_to_process++; + if(instance->bytes_to_process == ISO15693_PARSER_BITSTREAM_BUFF_SIZE) { + instance->callback(Iso15693ParserEventDataReceived, instance->context); + } + } + } else { + instance->bitstream_buff[instance->bytes_to_process] = event.data->data[0]; + instance->bytes_to_process++; + if(instance->bytes_to_process == ISO15693_PARSER_BITSTREAM_BUFF_SIZE) { + instance->callback(Iso15693ParserEventDataReceived, instance->context); + } + } + } +} + +static void iso15693_parser_start_signal_reader(Iso15693Parser* instance) { + iso15693_parser_reset(instance); + signal_reader_start(instance->signal_reader, signal_reader_callback, instance); +} + +void iso15693_parser_start( + Iso15693Parser* instance, + Iso15693ParserCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; + iso15693_parser_start_signal_reader(instance); +} + +void iso15693_parser_stop(Iso15693Parser* instance) { + furi_assert(instance); + + signal_reader_stop(instance->signal_reader); +} + +static Iso15693ParserCommand iso15693_parser_parse_1_out_of_4(Iso15693Parser* instance) { + Iso15693ParserCommand command = Iso15693ParserCommandWaitData; + const uint8_t bit_patterns_1_out_of_4[] = {0x02, 0x08, 0x20, 0x80}; + + for(size_t i = 0; i < instance->bytes_to_process; i++) { + // Check next pattern + size_t j = 0; + for(j = 0; j < COUNT_OF(bit_patterns_1_out_of_4); j++) { + if(instance->bitstream_buff[i] == bit_patterns_1_out_of_4[j]) { + instance->next_byte |= j << (instance->next_byte_part * 2); + instance->next_byte_part++; + if(instance->next_byte_part == 4) { + instance->next_byte_part = 0; + bit_buffer_append_byte(instance->parsed_frame, instance->next_byte); + instance->next_byte = 0; + } + break; + } + } + if(j == COUNT_OF(bit_patterns_1_out_of_4)) { + command = Iso15693ParserCommandFail; + break; + } + } + + if(command != Iso15693ParserCommandFail) { + if(instance->eof_received) { + command = Iso15693ParserCommandSuccess; + instance->frame_parsed = true; + } + } + + instance->bytes_to_process = 0; + + return command; +} + +static Iso15693ParserCommand iso15693_parser_parse_1_out_of_256(Iso15693Parser* instance) { + Iso15693ParserCommand command = Iso15693ParserCommandWaitData; + const uint8_t eof = 0x04; + + for(size_t i = instance->byte_idx; i < instance->bytes_to_process; i++) { + // Check EoF + if(instance->next_byte_part == 0) { + if(instance->bitstream_buff[i] == eof) { + instance->frame_parsed = true; + command = Iso15693ParserCommandSuccess; + break; + } + } + + if(instance->zero_found) { + if(instance->bitstream_buff[i] != 0x00) { + command = Iso15693ParserCommandFail; + break; + } + } else { + if(instance->bitstream_buff[i] != 0x00) { + for(size_t j = 0; j < 8; j++) { + if(FURI_BIT(instance->bitstream_buff[i], j) == 1) { + bit_buffer_append_byte( + instance->parsed_frame, instance->next_byte_part * 4 + j / 2); + } + } + } + } + instance->next_byte_part = (instance->next_byte_part + 1) % 64; + } + instance->bytes_to_process = 0; + instance->byte_idx = 0; + + return command; +} + +static const Iso15693ParserStateHandler iso15693_parser_state_handlers[Iso15693ParserModeNum] = { + [Iso15693ParserMode1OutOf4] = iso15693_parser_parse_1_out_of_4, + [Iso15693ParserMode1OutOf256] = iso15693_parser_parse_1_out_of_256, +}; + +bool iso15693_parser_run(Iso15693Parser* instance) { + if(instance->state == Iso15693ParserStateFail) { + iso15693_parser_stop(instance); + iso15693_parser_start_signal_reader(instance); + } else if((instance->state == Iso15693ParserStateParseSoF) && (instance->eof_received)) { + instance->frame_parsed = true; + } else if(instance->bytes_to_process) { + Iso15693ParserCommand command = Iso15693ParserCommandProcessed; + while(command == Iso15693ParserCommandProcessed) { + command = iso15693_parser_state_handlers[instance->mode](instance); + } + + if(command == Iso15693ParserCommandFail) { + iso15693_parser_stop(instance); + iso15693_parser_start_signal_reader(instance); + FURI_LOG_D(TAG, "Frame parse failed"); + } + } + + return instance->frame_parsed; +} + +size_t iso15693_parser_get_data_size_bytes(Iso15693Parser* instance) { + furi_assert(instance); + + return bit_buffer_get_size_bytes(instance->parsed_frame); +} + +void iso15693_parser_get_data( + Iso15693Parser* instance, + uint8_t* buff, + size_t buff_size, + size_t* data_bits) { + furi_assert(instance); + furi_assert(buff); + furi_assert(data_bits); + + bit_buffer_write_bytes(instance->parsed_frame, buff, buff_size); + *data_bits = bit_buffer_get_size(instance->parsed_frame); +} diff --git a/lib/signal_reader/parsers/iso15693/iso15693_parser.h b/lib/signal_reader/parsers/iso15693/iso15693_parser.h new file mode 100644 index 000000000000..3017a96d79ac --- /dev/null +++ b/lib/signal_reader/parsers/iso15693/iso15693_parser.h @@ -0,0 +1,42 @@ +#pragma once + +#include "../../signal_reader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso15693Parser Iso15693Parser; + +typedef enum { + Iso15693ParserEventDataReceived, +} Iso15693ParserEvent; + +typedef void (*Iso15693ParserCallback)(Iso15693ParserEvent event, void* context); + +Iso15693Parser* iso15693_parser_alloc(const GpioPin* pin, size_t max_frame_size); + +void iso15693_parser_free(Iso15693Parser* instance); + +void iso15693_parser_reset(Iso15693Parser* instance); + +void iso15693_parser_start( + Iso15693Parser* instance, + Iso15693ParserCallback callback, + void* context); + +void iso15693_parser_stop(Iso15693Parser* instance); + +bool iso15693_parser_run(Iso15693Parser* instance); + +size_t iso15693_parser_get_data_size_bytes(Iso15693Parser* instance); + +void iso15693_parser_get_data( + Iso15693Parser* instance, + uint8_t* buff, + size_t buff_size, + size_t* data_bits); + +#ifdef __cplusplus +} +#endif diff --git a/lib/signal_reader/signal_reader.c b/lib/signal_reader/signal_reader.c new file mode 100644 index 000000000000..7c4d0bae7ed3 --- /dev/null +++ b/lib/signal_reader/signal_reader.c @@ -0,0 +1,317 @@ +#include "signal_reader.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define SIGNAL_READER_DMA DMA2 + +#define SIGNAL_READER_CAPTURE_TIM (TIM16) +#define SIGNAL_READER_CAPTURE_TIM_CHANNEL LL_TIM_CHANNEL_CH1 + +#define SIGNAL_READER_DMA_GPIO LL_DMA_CHANNEL_2 +#define SIGNAL_READER_DMA_GPIO_IRQ FuriHalInterruptIdDma2Ch2 +#define SIGNAL_READER_DMA_GPIO_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_GPIO + +#define SIGNAL_READER_DMA_TRIGGER LL_DMA_CHANNEL_3 +#define SIGNAL_READER_DMA_TRIGGER_IRQ FuriHalInterruptIdDma2Ch3 +#define SIGNAL_READER_DMA_TRIGGER_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_TRIGGER + +#define SIGNAL_READER_DMA_CNT_SYNC LL_DMA_CHANNEL_5 +#define SIGNAL_READER_DMA_CNT_SYNC_IRQ FuriHalInterruptIdDma2Ch5 +#define SIGNAL_READER_DMA_CNT_SYNC_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_CNT_SYNC + +struct SignalReader { + size_t buffer_size; + const GpioPin* pin; + GpioPull pull; + SignalReaderPolarity polarity; + SignalReaderTrigger trigger; + + uint16_t* gpio_buffer; + uint8_t* bitstream_buffer; + uint32_t cnt_en; + + uint32_t tim_cnt_compensation; + uint32_t tim_arr; + + SignalReaderEvent event; + SignalReaderEventData event_data; + + SignalReaderCallback callback; + void* context; +}; + +#define GPIO_PIN_MAP(pin, prefix) \ + (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ + ((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \ + ((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \ + ((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \ + ((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \ + ((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \ + ((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \ + ((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \ + ((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \ + ((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \ + ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \ + ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \ + ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \ + ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \ + ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \ + prefix##15) + +#define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE) + +SignalReader* signal_reader_alloc(const GpioPin* gpio_pin, uint32_t size) { + SignalReader* instance = malloc(sizeof(SignalReader)); + + instance->pin = gpio_pin; + instance->pull = GpioPullNo; + + instance->buffer_size = size; + instance->gpio_buffer = malloc(sizeof(uint16_t) * size * 8); + instance->bitstream_buffer = malloc(size); + + instance->event.data = &instance->event_data; + + return instance; +} + +void signal_reader_free(SignalReader* instance) { + furi_assert(instance); + furi_assert(instance->gpio_buffer); + furi_assert(instance->bitstream_buffer); + + free(instance->gpio_buffer); + free(instance->bitstream_buffer); + free(instance); +} + +void signal_reader_set_pull(SignalReader* instance, GpioPull pull) { + furi_assert(instance); + + instance->pull = pull; +} + +void signal_reader_set_polarity(SignalReader* instance, SignalReaderPolarity polarity) { + furi_assert(instance); + + instance->polarity = polarity; +} + +void signal_reader_set_sample_rate( + SignalReader* instance, + SignalReaderTimeUnit time_unit, + uint32_t time) { + furi_assert(instance); + UNUSED(time_unit); + + instance->tim_arr = time; +} + +void signal_reader_set_trigger(SignalReader* instance, SignalReaderTrigger trigger) { + furi_assert(instance); + + instance->trigger = trigger; +} + +static void furi_hal_sw_digital_pin_dma_rx_isr(void* context) { + SignalReader* instance = context; + + uint16_t* gpio_buff_start = NULL; + uint8_t* bitstream_buff_start = NULL; + + if(LL_DMA_IsActiveFlag_HT2(SIGNAL_READER_DMA)) { + LL_DMA_ClearFlag_HT2(SIGNAL_READER_DMA); + instance->event.type = SignalReaderEventTypeHalfBufferFilled; + gpio_buff_start = instance->gpio_buffer; + bitstream_buff_start = instance->bitstream_buffer; + + if(instance->callback) { + furi_assert(gpio_buff_start); + furi_assert(bitstream_buff_start); + + for(size_t i = 0; i < instance->buffer_size * 4; i++) { + if((i % 8) == 0) { + bitstream_buff_start[i / 8] = 0; + } + uint8_t bit = 0; + if(instance->polarity == SignalReaderPolarityNormal) { + bit = (gpio_buff_start[i] & instance->pin->pin) == instance->pin->pin; + } else { + bit = (gpio_buff_start[i] & instance->pin->pin) == 0; + } + bitstream_buff_start[i / 8] |= bit << (i % 8); + } + instance->event_data.data = bitstream_buff_start; + instance->event_data.len = instance->buffer_size / 2; + instance->callback(instance->event, instance->context); + } + } + if(LL_DMA_IsActiveFlag_TC2(SIGNAL_READER_DMA)) { + LL_DMA_ClearFlag_TC2(SIGNAL_READER_DMA); + instance->event.type = SignalReaderEventTypeFullBufferFilled; + gpio_buff_start = &instance->gpio_buffer[instance->buffer_size * 4]; + bitstream_buff_start = &instance->bitstream_buffer[instance->buffer_size / 2]; + + if(instance->callback) { + furi_assert(gpio_buff_start); + furi_assert(bitstream_buff_start); + + for(size_t i = 0; i < instance->buffer_size * 4; i++) { + if((i % 8) == 0) { + bitstream_buff_start[i / 8] = 0; + } + uint8_t bit = 0; + if(instance->polarity == SignalReaderPolarityNormal) { + bit = (gpio_buff_start[i] & instance->pin->pin) == instance->pin->pin; + } else { + bit = (gpio_buff_start[i] & instance->pin->pin) == 0; + } + bitstream_buff_start[i / 8] |= bit << (i % 8); + } + instance->event_data.data = bitstream_buff_start; + instance->event_data.len = instance->buffer_size / 2; + instance->callback(instance->event, instance->context); + } + } +} + +void signal_reader_start(SignalReader* instance, SignalReaderCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; + + // EXTI delay compensation + instance->tim_cnt_compensation = 9; + instance->cnt_en = SIGNAL_READER_CAPTURE_TIM->CR1; + instance->cnt_en |= TIM_CR1_CEN; + + furi_hal_bus_enable(FuriHalBusTIM16); + + // Capture timer config + LL_TIM_SetPrescaler(SIGNAL_READER_CAPTURE_TIM, 0); + LL_TIM_SetCounterMode(SIGNAL_READER_CAPTURE_TIM, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(SIGNAL_READER_CAPTURE_TIM, instance->tim_arr); + LL_TIM_SetClockDivision(SIGNAL_READER_CAPTURE_TIM, LL_TIM_CLOCKDIVISION_DIV1); + + LL_TIM_DisableARRPreload(SIGNAL_READER_CAPTURE_TIM); + LL_TIM_SetClockSource(SIGNAL_READER_CAPTURE_TIM, LL_TIM_CLOCKSOURCE_INTERNAL); + + // Configure TIM channel CC1 + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_FROZEN; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE; + TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE; + TIM_OC_InitStruct.CompareValue = (instance->tim_arr / 2); + TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH; + LL_TIM_OC_Init( + SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL, &TIM_OC_InitStruct); + LL_TIM_OC_DisableFast(SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL); + + LL_TIM_SetTriggerOutput(SIGNAL_READER_CAPTURE_TIM, LL_TIM_TRGO_RESET); + LL_TIM_DisableMasterSlaveMode(SIGNAL_READER_CAPTURE_TIM); + + // Start + LL_TIM_GenerateEvent_UPDATE(SIGNAL_READER_CAPTURE_TIM); + + /* We need the EXTI to be configured as interrupt generating line, but no ISR registered */ + furi_hal_gpio_init( + instance->pin, GpioModeInterruptRiseFall, instance->pull, GpioSpeedVeryHigh); + + /* Set DMAMUX request generation signal ID on specified DMAMUX channel */ + LL_DMAMUX_SetRequestSignalID( + DMAMUX1, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(instance->pin->pin)); + /* Set the polarity of the signal on which the DMA request is generated */ + LL_DMAMUX_SetRequestGenPolarity(DMAMUX1, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); + /* Set the number of DMA requests that will be authorized after a generation event */ + LL_DMAMUX_SetGenRequestNb(DMAMUX1, LL_DMAMUX_REQ_GEN_0, 1); + + // Configure DMA Sync + LL_DMA_SetMemoryAddress( + SIGNAL_READER_DMA_CNT_SYNC_DEF, (uint32_t)&instance->tim_cnt_compensation); + LL_DMA_SetPeriphAddress( + SIGNAL_READER_DMA_CNT_SYNC_DEF, (uint32_t) & (SIGNAL_READER_CAPTURE_TIM->CNT)); + LL_DMA_ConfigTransfer( + SIGNAL_READER_DMA_CNT_SYNC_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD | + LL_DMA_PRIORITY_VERYHIGH); + LL_DMA_SetDataLength(SIGNAL_READER_DMA_CNT_SYNC_DEF, 1); + LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_CNT_SYNC_DEF, LL_DMAMUX_REQ_GENERATOR0); + + // Configure DMA Sync + LL_DMA_SetMemoryAddress(SIGNAL_READER_DMA_TRIGGER_DEF, (uint32_t)&instance->cnt_en); + LL_DMA_SetPeriphAddress( + SIGNAL_READER_DMA_TRIGGER_DEF, (uint32_t) & (SIGNAL_READER_CAPTURE_TIM->CR1)); + LL_DMA_ConfigTransfer( + SIGNAL_READER_DMA_TRIGGER_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_NOINCREMENT | + LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD | LL_DMA_PRIORITY_VERYHIGH); + LL_DMA_SetDataLength(SIGNAL_READER_DMA_TRIGGER_DEF, 1); + LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_TRIGGER_DEF, LL_DMAMUX_REQ_GENERATOR0); + + // Configure DMA Rx pin + LL_DMA_SetMemoryAddress(SIGNAL_READER_DMA_GPIO_DEF, (uint32_t)instance->gpio_buffer); + LL_DMA_SetPeriphAddress(SIGNAL_READER_DMA_GPIO_DEF, (uint32_t) & (instance->pin->port->IDR)); + LL_DMA_ConfigTransfer( + SIGNAL_READER_DMA_GPIO_DEF, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength(SIGNAL_READER_DMA_GPIO_DEF, instance->buffer_size * 8); + LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_GPIO_DEF, LL_DMAMUX_REQ_TIM16_CH1); + + // Configure DMA Channel CC1 + LL_TIM_EnableDMAReq_CC1(SIGNAL_READER_CAPTURE_TIM); + LL_TIM_CC_EnableChannel(SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL); + + // Start DMA irq, higher priority than normal + furi_hal_interrupt_set_isr_ex( + SIGNAL_READER_DMA_GPIO_IRQ, 14, furi_hal_sw_digital_pin_dma_rx_isr, instance); + + // Start DMA Sync timer + LL_DMA_EnableChannel(SIGNAL_READER_DMA_CNT_SYNC_DEF); + + // Start DMA Rx pin + LL_DMA_EnableChannel(SIGNAL_READER_DMA_GPIO_DEF); + // Strat timer + LL_TIM_SetCounter(SIGNAL_READER_CAPTURE_TIM, 0); + if(instance->trigger == SignalReaderTriggerNone) { + LL_TIM_EnableCounter(SIGNAL_READER_CAPTURE_TIM); + } else { + LL_DMA_EnableChannel(SIGNAL_READER_DMA_TRIGGER_DEF); + } + + LL_DMAMUX_EnableRequestGen(DMAMUX1, LL_DMAMUX_REQ_GEN_0); + // Need to clear flags before enabling DMA !!!! + if(LL_DMA_IsActiveFlag_TC2(SIGNAL_READER_DMA)) LL_DMA_ClearFlag_TC1(SIGNAL_READER_DMA); + if(LL_DMA_IsActiveFlag_TE2(SIGNAL_READER_DMA)) LL_DMA_ClearFlag_TE1(SIGNAL_READER_DMA); + LL_DMA_EnableIT_TC(SIGNAL_READER_DMA_GPIO_DEF); + LL_DMA_EnableIT_HT(SIGNAL_READER_DMA_GPIO_DEF); +} + +void signal_reader_stop(SignalReader* instance) { + furi_assert(instance); + + furi_hal_interrupt_set_isr(SIGNAL_READER_DMA_GPIO_IRQ, NULL, NULL); + + // Deinit DMA Rx pin + LL_DMA_DeInit(SIGNAL_READER_DMA_GPIO_DEF); + // Deinit DMA Sync timer + LL_DMA_DeInit(SIGNAL_READER_DMA_CNT_SYNC_DEF); + // Deinit DMA Trigger timer + LL_DMA_DeInit(SIGNAL_READER_DMA_TRIGGER_DEF); + + furi_hal_bus_disable(FuriHalBusTIM16); +} diff --git a/lib/signal_reader/signal_reader.h b/lib/signal_reader/signal_reader.h new file mode 100644 index 000000000000..be18f295a448 --- /dev/null +++ b/lib/signal_reader/signal_reader.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + SignalReaderEventTypeHalfBufferFilled, + SignalReaderEventTypeFullBufferFilled, +} SignalReaderEventType; + +typedef struct { + uint8_t* data; + size_t len; +} SignalReaderEventData; + +typedef struct { + SignalReaderEventType type; + SignalReaderEventData* data; +} SignalReaderEvent; + +typedef enum { + SignalReaderTimeUnit64Mhz, +} SignalReaderTimeUnit; + +typedef enum { + SignalReaderPolarityNormal, + SignalReaderPolarityInverted, +} SignalReaderPolarity; + +typedef enum { + SignalReaderTriggerNone, + SignalReaderTriggerRisingFallingEdge, +} SignalReaderTrigger; + +typedef void (*SignalReaderCallback)(SignalReaderEvent event, void* context); + +typedef struct SignalReader SignalReader; + +SignalReader* signal_reader_alloc(const GpioPin* gpio_pin, uint32_t size); + +void signal_reader_free(SignalReader* instance); + +void signal_reader_set_pull(SignalReader* instance, GpioPull pull); + +void signal_reader_set_polarity(SignalReader* instance, SignalReaderPolarity polarity); + +void signal_reader_set_sample_rate( + SignalReader* instance, + SignalReaderTimeUnit time_unit, + uint32_t time); + +void signal_reader_set_trigger(SignalReader* instance, SignalReaderTrigger trigger); + +void signal_reader_start(SignalReader* instance, SignalReaderCallback callback, void* context); + +void signal_reader_stop(SignalReader* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 6d9c0cd06385..82c925c1a312 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -18,6 +18,9 @@ env.Append( File("blocks/generic.h"), File("blocks/math.h"), File("subghz_setting.h"), + File("subghz_protocol_registry.h"), + File("devices/cc1101_configs.h"), + File("devices/cc1101_int/cc1101_int_interconnect.h"), ], ) diff --git a/lib/subghz/devices/cc1101_configs.c b/lib/subghz/devices/cc1101_configs.c new file mode 100644 index 000000000000..35ddccd54f6d --- /dev/null +++ b/lib/subghz/devices/cc1101_configs.c @@ -0,0 +1,431 @@ +#include "cc1101_configs.h" +#include + +const uint8_t subghz_device_cc1101_preset_ook_270khz_async_regs[] = { + // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration + + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x0D, // GD0 as async serial data output/input + + /* FIFO and internals */ + CC1101_FIFOTHR, + 0x47, // The only important bit is ADC_RETENTION, FIFO Tx=33 Rx=32 + + /* Packet engine */ + CC1101_PKTCTRL0, + 0x32, // Async, continious, no whitening + + /* Frequency Synthesizer Control */ + CC1101_FSCTRL1, + 0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + // Modem Configuration + CC1101_MDMCFG0, + 0x00, // Channel spacing is 25kHz + CC1101_MDMCFG1, + 0x00, // Channel spacing is 25kHz + CC1101_MDMCFG2, + 0x30, // Format ASK/OOK, No preamble/sync + CC1101_MDMCFG3, + 0x32, // Data rate is 3.79372 kBaud + CC1101_MDMCFG4, + 0x67, // Rx BW filter is 270.833333kHz + + /* Main Radio Control State Machine */ + CC1101_MCSM0, + 0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + CC1101_FOCCFG, + 0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + CC1101_AGCCTRL0, + 0x40, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + CC1101_AGCCTRL1, + 0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + CC1101_AGCCTRL2, + 0x03, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + + /* Wake on radio and timeouts control */ + CC1101_WORCTRL, + 0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + CC1101_FREND0, + 0x11, // Adjusts current TX LO buffer + high is PATABLE[1] + CC1101_FREND1, + 0xB6, // + + /* End load reg */ + 0, + 0, + + //ook_async_patable[8] + 0x00, + 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_ook_650khz_async_regs[] = { + // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration + + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x0D, // GD0 as async serial data output/input + + /* FIFO and internals */ + CC1101_FIFOTHR, + 0x07, // The only important bit is ADC_RETENTION + + /* Packet engine */ + CC1101_PKTCTRL0, + 0x32, // Async, continious, no whitening + + /* Frequency Synthesizer Control */ + CC1101_FSCTRL1, + 0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + // Modem Configuration + CC1101_MDMCFG0, + 0x00, // Channel spacing is 25kHz + CC1101_MDMCFG1, + 0x00, // Channel spacing is 25kHz + CC1101_MDMCFG2, + 0x30, // Format ASK/OOK, No preamble/sync + CC1101_MDMCFG3, + 0x32, // Data rate is 3.79372 kBaud + CC1101_MDMCFG4, + 0x17, // Rx BW filter is 650.000kHz + + /* Main Radio Control State Machine */ + CC1101_MCSM0, + 0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + CC1101_FOCCFG, + 0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + // CC1101_AGCTRL0,0x40, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + // CC1101_AGCTRL1,0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + // CC1101_AGCCTRL2, 0x03, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. + CC1101_AGCCTRL0, + 0x91, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + CC1101_AGCCTRL1, + 0x0, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + CC1101_AGCCTRL2, + 0x07, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB + + /* Wake on radio and timeouts control */ + CC1101_WORCTRL, + 0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + CC1101_FREND0, + 0x11, // Adjusts current TX LO buffer + high is PATABLE[1] + CC1101_FREND1, + 0xB6, // + + /* End load reg */ + 0, + 0, + + //ook_async_patable[8] + 0x00, + 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs[] = { + + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x0D, // GD0 as async serial data output/input + + /* Frequency Synthesizer Control */ + CC1101_FSCTRL1, + 0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + /* Packet engine */ + CC1101_PKTCTRL0, + 0x32, // Async, continious, no whitening + CC1101_PKTCTRL1, + 0x04, + + // // Modem Configuration + CC1101_MDMCFG0, + 0x00, + CC1101_MDMCFG1, + 0x02, + CC1101_MDMCFG2, + 0x04, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) + CC1101_MDMCFG3, + 0x83, // Data rate is 4.79794 kBaud + CC1101_MDMCFG4, + 0x67, //Rx BW filter is 270.833333 kHz + CC1101_DEVIATN, + 0x04, //Deviation 2.380371 kHz + + /* Main Radio Control State Machine */ + CC1101_MCSM0, + 0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + CC1101_FOCCFG, + 0x16, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + CC1101_AGCCTRL0, + 0x91, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + CC1101_AGCCTRL1, + 0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + CC1101_AGCCTRL2, + 0x07, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB + + /* Wake on radio and timeouts control */ + CC1101_WORCTRL, + 0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + CC1101_FREND0, + 0x10, // Adjusts current TX LO buffer + CC1101_FREND1, + 0x56, + + /* End load reg */ + 0, + 0, + + // 2fsk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs[] = { + + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x0D, // GD0 as async serial data output/input + + /* Frequency Synthesizer Control */ + CC1101_FSCTRL1, + 0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + /* Packet engine */ + CC1101_PKTCTRL0, + 0x32, // Async, continious, no whitening + CC1101_PKTCTRL1, + 0x04, + + // // Modem Configuration + CC1101_MDMCFG0, + 0x00, + CC1101_MDMCFG1, + 0x02, + CC1101_MDMCFG2, + 0x04, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) + CC1101_MDMCFG3, + 0x83, // Data rate is 4.79794 kBaud + CC1101_MDMCFG4, + 0x67, //Rx BW filter is 270.833333 kHz + CC1101_DEVIATN, + 0x47, //Deviation 47.60742 kHz + + /* Main Radio Control State Machine */ + CC1101_MCSM0, + 0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + CC1101_FOCCFG, + 0x16, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + CC1101_AGCCTRL0, + 0x91, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + CC1101_AGCCTRL1, + 0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + CC1101_AGCCTRL2, + 0x07, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB + + /* Wake on radio and timeouts control */ + CC1101_WORCTRL, + 0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + CC1101_FREND0, + 0x10, // Adjusts current TX LO buffer + CC1101_FREND1, + 0x56, + + /* End load reg */ + 0, + 0, + + // 2fsk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_msk_99_97kb_async_regs[] = { + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x06, + + CC1101_FIFOTHR, + 0x07, // The only important bit is ADC_RETENTION + CC1101_SYNC1, + 0x46, + CC1101_SYNC0, + 0x4C, + CC1101_ADDR, + 0x00, + CC1101_PKTLEN, + 0x00, + CC1101_CHANNR, + 0x00, + + CC1101_PKTCTRL0, + 0x05, + + CC1101_FSCTRL0, + 0x23, + CC1101_FSCTRL1, + 0x06, + + CC1101_MDMCFG0, + 0xF8, + CC1101_MDMCFG1, + 0x22, + CC1101_MDMCFG2, + 0x72, + CC1101_MDMCFG3, + 0xF8, + CC1101_MDMCFG4, + 0x5B, + CC1101_DEVIATN, + 0x47, + + CC1101_MCSM0, + 0x18, + CC1101_FOCCFG, + 0x16, + + CC1101_AGCCTRL0, + 0xB2, + CC1101_AGCCTRL1, + 0x00, + CC1101_AGCCTRL2, + 0xC7, + + CC1101_FREND0, + 0x10, + CC1101_FREND1, + 0x56, + + CC1101_BSCFG, + 0x1C, + CC1101_FSTEST, + 0x59, + + /* End load reg */ + 0, + 0, + + // msk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_gfsk_9_99kb_async_regs[] = { + + CC1101_IOCFG0, + 0x06, //GDO0 Output Pin Configuration + CC1101_FIFOTHR, + 0x47, //RX FIFO and TX FIFO Thresholds + + //1 : CRC calculation in TX and CRC check in RX enabled, + //1 : Variable packet length mode. Packet length configured by the first byte after sync word + CC1101_PKTCTRL0, + 0x05, + + CC1101_FSCTRL1, + 0x06, //Frequency Synthesizer Control + + CC1101_SYNC1, + 0x46, + CC1101_SYNC0, + 0x4C, + CC1101_ADDR, + 0x00, + CC1101_PKTLEN, + 0x00, + + CC1101_MDMCFG4, + 0xC8, //Modem Configuration 9.99 + CC1101_MDMCFG3, + 0x93, //Modem Configuration + CC1101_MDMCFG2, + 0x12, // 2: 16/16 sync word bits detected + + CC1101_DEVIATN, + 0x34, //Deviation = 19.042969 + CC1101_MCSM0, + 0x18, //Main Radio Control State Machine Configuration + CC1101_FOCCFG, + 0x16, //Frequency Offset Compensation Configuration + + CC1101_AGCCTRL2, + 0x43, //AGC Control + CC1101_AGCCTRL1, + 0x40, + CC1101_AGCCTRL0, + 0x91, + + CC1101_WORCTRL, + 0xFB, //Wake On Radio Control + + /* End load reg */ + 0, + 0, + + // gfsk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; diff --git a/lib/subghz/devices/cc1101_configs.h b/lib/subghz/devices/cc1101_configs.h new file mode 100644 index 000000000000..eecab01d999e --- /dev/null +++ b/lib/subghz/devices/cc1101_configs.h @@ -0,0 +1,18 @@ +#pragma once +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const uint8_t subghz_device_cc1101_preset_ook_270khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_ook_650khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_msk_99_97kb_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_gfsk_9_99kb_async_regs[]; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c new file mode 100644 index 000000000000..41a0609df00c --- /dev/null +++ b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c @@ -0,0 +1,96 @@ +#include "cc1101_int_interconnect.h" +#include +#include "../cc1101_configs.h" + +#define TAG "SubGhzDeviceCC1101Int" + +static bool subghz_device_cc1101_int_interconnect_is_frequency_valid(uint32_t frequency) { + bool ret = furi_hal_subghz_is_frequency_valid(frequency); + if(!ret) { + furi_crash("SubGhz: Incorrect frequency."); + } + return ret; +} + +static uint32_t subghz_device_cc1101_int_interconnect_set_frequency(uint32_t frequency) { + subghz_device_cc1101_int_interconnect_is_frequency_valid(frequency); + return furi_hal_subghz_set_frequency_and_path(frequency); +} + +static bool subghz_device_cc1101_int_interconnect_start_async_tx(void* callback, void* context) { + return furi_hal_subghz_start_async_tx((FuriHalSubGhzAsyncTxCallback)callback, context); +} + +static void subghz_device_cc1101_int_interconnect_start_async_rx(void* callback, void* context) { + furi_hal_subghz_start_async_rx((FuriHalSubGhzCaptureCallback)callback, context); +} + +static void subghz_device_cc1101_int_interconnect_load_preset( + FuriHalSubGhzPreset preset, + uint8_t* preset_data) { + switch(preset) { + case FuriHalSubGhzPresetOok650Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); + break; + case FuriHalSubGhzPresetOok270Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_270khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev238Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev476Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs); + break; + case FuriHalSubGhzPresetMSK99_97KbAsync: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_msk_99_97kb_async_regs); + break; + case FuriHalSubGhzPresetGFSK9_99KbAsync: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_gfsk_9_99kb_async_regs); + break; + + default: + furi_hal_subghz_load_custom_preset(preset_data); + } +} + +static bool subghz_device_cc1101_int_interconnect_is_connect(void) { + return true; +} + +const SubGhzDeviceInterconnect subghz_device_cc1101_int_interconnect = { + .begin = NULL, + .end = furi_hal_subghz_shutdown, + .is_connect = subghz_device_cc1101_int_interconnect_is_connect, + .reset = furi_hal_subghz_reset, + .sleep = furi_hal_subghz_sleep, + .idle = furi_hal_subghz_idle, + .load_preset = subghz_device_cc1101_int_interconnect_load_preset, + .set_frequency = subghz_device_cc1101_int_interconnect_set_frequency, + .is_frequency_valid = furi_hal_subghz_is_frequency_valid, + .set_async_mirror_pin = furi_hal_subghz_set_async_mirror_pin, + .get_data_gpio = furi_hal_subghz_get_data_gpio, + + .set_tx = furi_hal_subghz_tx, + .flush_tx = furi_hal_subghz_flush_tx, + .start_async_tx = subghz_device_cc1101_int_interconnect_start_async_tx, + .is_async_complete_tx = furi_hal_subghz_is_async_tx_complete, + .stop_async_tx = furi_hal_subghz_stop_async_tx, + + .set_rx = furi_hal_subghz_rx, + .flush_rx = furi_hal_subghz_flush_rx, + .start_async_rx = subghz_device_cc1101_int_interconnect_start_async_rx, + .stop_async_rx = furi_hal_subghz_stop_async_rx, + + .get_rssi = furi_hal_subghz_get_rssi, + .get_lqi = furi_hal_subghz_get_lqi, + + .rx_pipe_not_empty = furi_hal_subghz_rx_pipe_not_empty, + .is_rx_data_crc_valid = furi_hal_subghz_is_rx_data_crc_valid, + .read_packet = furi_hal_subghz_read_packet, + .write_packet = furi_hal_subghz_write_packet, +}; + +const SubGhzDevice subghz_device_cc1101_int = { + .name = SUBGHZ_DEVICE_CC1101_INT_NAME, + .interconnect = &subghz_device_cc1101_int_interconnect, +}; diff --git a/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h new file mode 100644 index 000000000000..629d1264c093 --- /dev/null +++ b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h @@ -0,0 +1,8 @@ +#pragma once +#include "../types.h" + +#define SUBGHZ_DEVICE_CC1101_INT_NAME "cc1101_int" + +typedef struct SubGhzDeviceCC1101Int SubGhzDeviceCC1101Int; + +extern const SubGhzDevice subghz_device_cc1101_int; diff --git a/lib/subghz/devices/device_registry.h b/lib/subghz/devices/device_registry.h new file mode 100644 index 000000000000..70a0db4b2656 --- /dev/null +++ b/lib/subghz/devices/device_registry.h @@ -0,0 +1,13 @@ +#pragma once + +#include "registry.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const SubGhzDeviceRegistry subghz_device_registry; + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/devices/devices.c b/lib/subghz/devices/devices.c new file mode 100644 index 000000000000..55db61e11880 --- /dev/null +++ b/lib/subghz/devices/devices.c @@ -0,0 +1,236 @@ +#include "devices.h" + +#include "registry.h" + +void subghz_devices_init() { + furi_check(!subghz_device_registry_is_valid()); + subghz_device_registry_init(); +} + +void subghz_devices_deinit(void) { + furi_check(subghz_device_registry_is_valid()); + subghz_device_registry_deinit(); +} + +const SubGhzDevice* subghz_devices_get_by_name(const char* device_name) { + furi_check(subghz_device_registry_is_valid()); + const SubGhzDevice* device = subghz_device_registry_get_by_name(device_name); + return device; +} + +const char* subghz_devices_get_name(const SubGhzDevice* device) { + const char* ret = NULL; + if(device) { + ret = device->name; + } + return ret; +} + +bool subghz_devices_begin(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->begin) { + ret = device->interconnect->begin(); + } + return ret; +} + +void subghz_devices_end(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->end) { + device->interconnect->end(); + } +} + +bool subghz_devices_is_connect(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_connect) { + ret = device->interconnect->is_connect(); + } + return ret; +} + +void subghz_devices_reset(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->reset) { + device->interconnect->reset(); + } +} + +void subghz_devices_sleep(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->sleep) { + device->interconnect->sleep(); + } +} + +void subghz_devices_idle(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->idle) { + device->interconnect->idle(); + } +} + +void subghz_devices_load_preset( + const SubGhzDevice* device, + FuriHalSubGhzPreset preset, + uint8_t* preset_data) { + furi_assert(device); + if(device->interconnect->load_preset) { + device->interconnect->load_preset(preset, preset_data); + } +} + +uint32_t subghz_devices_set_frequency(const SubGhzDevice* device, uint32_t frequency) { + uint32_t ret = 0; + furi_assert(device); + if(device->interconnect->set_frequency) { + ret = device->interconnect->set_frequency(frequency); + } + return ret; +} + +bool subghz_devices_is_frequency_valid(const SubGhzDevice* device, uint32_t frequency) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_frequency_valid) { + ret = device->interconnect->is_frequency_valid(frequency); + } + return ret; +} + +void subghz_devices_set_async_mirror_pin(const SubGhzDevice* device, const GpioPin* gpio) { + furi_assert(device); + if(device->interconnect->set_async_mirror_pin) { + device->interconnect->set_async_mirror_pin(gpio); + } +} + +const GpioPin* subghz_devices_get_data_gpio(const SubGhzDevice* device) { + const GpioPin* ret = NULL; + furi_assert(device); + if(device->interconnect->get_data_gpio) { + ret = device->interconnect->get_data_gpio(); + } + return ret; +} + +bool subghz_devices_set_tx(const SubGhzDevice* device) { + bool ret = 0; + furi_assert(device); + if(device->interconnect->set_tx) { + ret = device->interconnect->set_tx(); + } + return ret; +} + +void subghz_devices_flush_tx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->flush_tx) { + device->interconnect->flush_tx(); + } +} + +bool subghz_devices_start_async_tx(const SubGhzDevice* device, void* callback, void* context) { + bool ret = false; + furi_assert(device); + if(device->interconnect->start_async_tx) { + ret = device->interconnect->start_async_tx(callback, context); + } + return ret; +} + +bool subghz_devices_is_async_complete_tx(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_async_complete_tx) { + ret = device->interconnect->is_async_complete_tx(); + } + return ret; +} + +void subghz_devices_stop_async_tx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->stop_async_tx) { + device->interconnect->stop_async_tx(); + } +} + +void subghz_devices_set_rx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->set_rx) { + device->interconnect->set_rx(); + } +} + +void subghz_devices_flush_rx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->flush_rx) { + device->interconnect->flush_rx(); + } +} + +void subghz_devices_start_async_rx(const SubGhzDevice* device, void* callback, void* context) { + furi_assert(device); + if(device->interconnect->start_async_rx) { + device->interconnect->start_async_rx(callback, context); + } +} + +void subghz_devices_stop_async_rx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->stop_async_rx) { + device->interconnect->stop_async_rx(); + } +} + +float subghz_devices_get_rssi(const SubGhzDevice* device) { + float ret = 0; + furi_assert(device); + if(device->interconnect->get_rssi) { + ret = device->interconnect->get_rssi(); + } + return ret; +} + +uint8_t subghz_devices_get_lqi(const SubGhzDevice* device) { + uint8_t ret = 0; + furi_assert(device); + if(device->interconnect->get_lqi) { + ret = device->interconnect->get_lqi(); + } + return ret; +} + +bool subghz_devices_rx_pipe_not_empty(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->rx_pipe_not_empty) { + ret = device->interconnect->rx_pipe_not_empty(); + } + return ret; +} + +bool subghz_devices_is_rx_data_crc_valid(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_rx_data_crc_valid) { + ret = device->interconnect->is_rx_data_crc_valid(); + } + return ret; +} + +void subghz_devices_read_packet(const SubGhzDevice* device, uint8_t* data, uint8_t* size) { + furi_assert(device); + if(device->interconnect->read_packet) { + device->interconnect->read_packet(data, size); + } +} + +void subghz_devices_write_packet(const SubGhzDevice* device, const uint8_t* data, uint8_t size) { + furi_assert(device); + if(device->interconnect->write_packet) { + device->interconnect->write_packet(data, size); + } +} diff --git a/lib/subghz/devices/devices.h b/lib/subghz/devices/devices.h new file mode 100644 index 000000000000..dad3c9aeb538 --- /dev/null +++ b/lib/subghz/devices/devices.h @@ -0,0 +1,52 @@ +#pragma once + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzDevice SubGhzDevice; + +void subghz_devices_init(); +void subghz_devices_deinit(void); + +const SubGhzDevice* subghz_devices_get_by_name(const char* device_name); +const char* subghz_devices_get_name(const SubGhzDevice* device); +bool subghz_devices_begin(const SubGhzDevice* device); +void subghz_devices_end(const SubGhzDevice* device); +bool subghz_devices_is_connect(const SubGhzDevice* device); +void subghz_devices_reset(const SubGhzDevice* device); +void subghz_devices_sleep(const SubGhzDevice* device); +void subghz_devices_idle(const SubGhzDevice* device); +void subghz_devices_load_preset( + const SubGhzDevice* device, + FuriHalSubGhzPreset preset, + uint8_t* preset_data); +uint32_t subghz_devices_set_frequency(const SubGhzDevice* device, uint32_t frequency); +bool subghz_devices_is_frequency_valid(const SubGhzDevice* device, uint32_t frequency); +void subghz_devices_set_async_mirror_pin(const SubGhzDevice* device, const GpioPin* gpio); +const GpioPin* subghz_devices_get_data_gpio(const SubGhzDevice* device); + +bool subghz_devices_set_tx(const SubGhzDevice* device); +void subghz_devices_flush_tx(const SubGhzDevice* device); +bool subghz_devices_start_async_tx(const SubGhzDevice* device, void* callback, void* context); +bool subghz_devices_is_async_complete_tx(const SubGhzDevice* device); +void subghz_devices_stop_async_tx(const SubGhzDevice* device); + +void subghz_devices_set_rx(const SubGhzDevice* device); +void subghz_devices_flush_rx(const SubGhzDevice* device); +void subghz_devices_start_async_rx(const SubGhzDevice* device, void* callback, void* context); +void subghz_devices_stop_async_rx(const SubGhzDevice* device); + +float subghz_devices_get_rssi(const SubGhzDevice* device); +uint8_t subghz_devices_get_lqi(const SubGhzDevice* device); + +bool subghz_devices_rx_pipe_not_empty(const SubGhzDevice* device); +bool subghz_devices_is_rx_data_crc_valid(const SubGhzDevice* device); +void subghz_devices_read_packet(const SubGhzDevice* device, uint8_t* data, uint8_t* size); +void subghz_devices_write_packet(const SubGhzDevice* device, const uint8_t* data, uint8_t size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/devices/preset.h b/lib/subghz/devices/preset.h new file mode 100644 index 000000000000..8716f2e233c5 --- /dev/null +++ b/lib/subghz/devices/preset.h @@ -0,0 +1,13 @@ +#pragma once + +/** Radio Presets */ +typedef enum { + FuriHalSubGhzPresetIDLE, /**< default configuration */ + FuriHalSubGhzPresetOok270Async, /**< OOK, bandwidth 270kHz, asynchronous */ + FuriHalSubGhzPresetOok650Async, /**< OOK, bandwidth 650kHz, asynchronous */ + FuriHalSubGhzPreset2FSKDev238Async, /**< FM, deviation 2.380371 kHz, asynchronous */ + FuriHalSubGhzPreset2FSKDev476Async, /**< FM, deviation 47.60742 kHz, asynchronous */ + FuriHalSubGhzPresetMSK99_97KbAsync, /**< MSK, deviation 47.60742 kHz, 99.97Kb/s, asynchronous */ + FuriHalSubGhzPresetGFSK9_99KbAsync, /**< GFSK, deviation 19.042969 kHz, 9.996Kb/s, asynchronous */ + FuriHalSubGhzPresetCustom, /**Custom Preset*/ +} FuriHalSubGhzPreset; diff --git a/lib/subghz/devices/registry.c b/lib/subghz/devices/registry.c new file mode 100644 index 000000000000..c0d5bb292dce --- /dev/null +++ b/lib/subghz/devices/registry.c @@ -0,0 +1,76 @@ +#include "registry.h" + +#include "cc1101_int/cc1101_int_interconnect.h" +#include +#include + +#define TAG "SubGhzDeviceRegistry" + +struct SubGhzDeviceRegistry { + const SubGhzDevice** items; + size_t size; + PluginManager* manager; +}; + +static SubGhzDeviceRegistry* subghz_device_registry = NULL; + +void subghz_device_registry_init(void) { + SubGhzDeviceRegistry* subghz_device = + (SubGhzDeviceRegistry*)malloc(sizeof(SubGhzDeviceRegistry)); + subghz_device->manager = plugin_manager_alloc( + SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID, + SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION, + firmware_api_interface); + + //ToDo: fix path to plugins + if(plugin_manager_load_all(subghz_device->manager, "/any/apps_data/subghz/plugins") != + //if(plugin_manager_load_all(subghz_device->manager, APP_DATA_PATH("plugins")) != + PluginManagerErrorNone) { + FURI_LOG_E(TAG, "Failed to load all libs"); + } + + subghz_device->size = plugin_manager_get_count(subghz_device->manager) + 1; + subghz_device->items = + (const SubGhzDevice**)malloc(sizeof(SubGhzDevice*) * subghz_device->size); + subghz_device->items[0] = &subghz_device_cc1101_int; + for(uint32_t i = 1; i < subghz_device->size; i++) { + const SubGhzDevice* plugin = plugin_manager_get_ep(subghz_device->manager, i - 1); + subghz_device->items[i] = plugin; + } + + FURI_LOG_I(TAG, "Loaded %zu radio device", subghz_device->size); + subghz_device_registry = subghz_device; +} + +void subghz_device_registry_deinit(void) { + plugin_manager_free(subghz_device_registry->manager); + free(subghz_device_registry->items); + free(subghz_device_registry); + subghz_device_registry = NULL; +} + +bool subghz_device_registry_is_valid(void) { + return subghz_device_registry != NULL; +} + +const SubGhzDevice* subghz_device_registry_get_by_name(const char* name) { + furi_assert(subghz_device_registry); + + if(name != NULL) { + for(size_t i = 0; i < subghz_device_registry->size; i++) { + if(strcmp(name, subghz_device_registry->items[i]->name) == 0) { + return subghz_device_registry->items[i]; + } + } + } + return NULL; +} + +const SubGhzDevice* subghz_device_registry_get_by_index(size_t index) { + furi_assert(subghz_device_registry); + if(index < subghz_device_registry->size) { + return subghz_device_registry->items[index]; + } else { + return NULL; + } +} diff --git a/lib/subghz/devices/registry.h b/lib/subghz/devices/registry.h new file mode 100644 index 000000000000..5200589205bd --- /dev/null +++ b/lib/subghz/devices/registry.h @@ -0,0 +1,40 @@ +#pragma once + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzDevice SubGhzDevice; + +void subghz_device_registry_init(void); + +void subghz_device_registry_deinit(void); + +bool subghz_device_registry_is_valid(void); + +/** + * Registration by name SubGhzDevice. + * @param name SubGhzDevice name + * @return SubGhzDevice* pointer to a SubGhzDevice instance + */ +const SubGhzDevice* subghz_device_registry_get_by_name(const char* name); + +/** + * Registration subghzdevice by index in array SubGhzDevice. + * @param index SubGhzDevice by index in array + * @return SubGhzDevice* pointer to a SubGhzDevice instance + */ +const SubGhzDevice* subghz_device_registry_get_by_index(size_t index); + +/** + * Getting the number of registered subghzdevices. + * @param subghz_device SubGhzDeviceRegistry + * @return Number of subghzdevices + */ +size_t subghz_device_registry_count(void); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/devices/types.h b/lib/subghz/devices/types.h new file mode 100644 index 000000000000..8a41984263fc --- /dev/null +++ b/lib/subghz/devices/types.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "preset.h" + +#include + +#define SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID "subghz_radio_device" +#define SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION 1 + +typedef struct SubGhzDeviceRegistry SubGhzDeviceRegistry; +typedef struct SubGhzDevice SubGhzDevice; + +typedef bool (*SubGhzBegin)(void); +typedef void (*SubGhzEnd)(void); +typedef bool (*SubGhzIsConnect)(void); +typedef void (*SubGhzReset)(void); +typedef void (*SubGhzSleep)(void); +typedef void (*SubGhzIdle)(void); +typedef void (*SubGhzLoadPreset)(FuriHalSubGhzPreset preset, uint8_t* preset_data); +typedef uint32_t (*SubGhzSetFrequency)(uint32_t frequency); +typedef bool (*SubGhzIsFrequencyValid)(uint32_t frequency); + +typedef void (*SubGhzSetAsyncMirrorPin)(const GpioPin* gpio); +typedef const GpioPin* (*SubGhzGetDataGpio)(void); + +typedef bool (*SubGhzSetTx)(void); +typedef void (*SubGhzFlushTx)(void); +typedef bool (*SubGhzStartAsyncTx)(void* callback, void* context); +typedef bool (*SubGhzIsAsyncCompleteTx)(void); +typedef void (*SubGhzStopAsyncTx)(void); + +typedef void (*SubGhzSetRx)(void); +typedef void (*SubGhzFlushRx)(void); +typedef void (*SubGhzStartAsyncRx)(void* callback, void* context); +typedef void (*SubGhzStopAsyncRx)(void); + +typedef float (*SubGhzGetRSSI)(void); +typedef uint8_t (*SubGhzGetLQI)(void); + +typedef bool (*SubGhzRxPipeNotEmpty)(void); +typedef bool (*SubGhzRxIsDataCrcValid)(void); +typedef void (*SubGhzReadPacket)(uint8_t* data, uint8_t* size); +typedef void (*SubGhzWritePacket)(const uint8_t* data, uint8_t size); + +typedef struct { + SubGhzBegin begin; + SubGhzEnd end; + + SubGhzIsConnect is_connect; + SubGhzReset reset; + SubGhzSleep sleep; + SubGhzIdle idle; + + SubGhzLoadPreset load_preset; + SubGhzSetFrequency set_frequency; + SubGhzIsFrequencyValid is_frequency_valid; + SubGhzSetAsyncMirrorPin set_async_mirror_pin; + SubGhzGetDataGpio get_data_gpio; + + SubGhzSetTx set_tx; + SubGhzFlushTx flush_tx; + SubGhzStartAsyncTx start_async_tx; + SubGhzIsAsyncCompleteTx is_async_complete_tx; + SubGhzStopAsyncTx stop_async_tx; + + SubGhzSetRx set_rx; + SubGhzFlushRx flush_rx; + SubGhzStartAsyncRx start_async_rx; + SubGhzStopAsyncRx stop_async_rx; + + SubGhzGetRSSI get_rssi; + SubGhzGetLQI get_lqi; + + SubGhzRxPipeNotEmpty rx_pipe_not_empty; + SubGhzRxIsDataCrcValid is_rx_data_crc_valid; + SubGhzReadPacket read_packet; + SubGhzWritePacket write_packet; + +} SubGhzDeviceInterconnect; + +struct SubGhzDevice { + const char* name; + const SubGhzDeviceInterconnect* interconnect; +}; diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index 5ded243c4168..3794dbad841c 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -92,16 +92,17 @@ const char* void subghz_environment_set_protocol_registry( SubGhzEnvironment* instance, - void* protocol_registry_items) { + const SubGhzProtocolRegistry* protocol_registry_items) { furi_assert(instance); const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items; instance->protocol_registry = protocol_registry; } -void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { +const SubGhzProtocolRegistry* + subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { furi_assert(instance); furi_assert(instance->protocol_registry); - return (void*)instance->protocol_registry; + return instance->protocol_registry; } const char* diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index 7bd38ba2fe57..c15b8b211b00 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -1,6 +1,7 @@ #pragma once #include +#include "registry.h" #include "subghz_keystore.h" @@ -9,6 +10,7 @@ extern "C" { #endif typedef struct SubGhzEnvironment SubGhzEnvironment; +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; /** * Allocate SubGhzEnvironment. @@ -93,14 +95,15 @@ const char* */ void subghz_environment_set_protocol_registry( SubGhzEnvironment* instance, - void* protocol_registry_items); + const SubGhzProtocolRegistry* protocol_registry_items); /** * Get list of protocols to work. * @param instance Pointer to a SubGhzEnvironment instance * @return Pointer to a SubGhzProtocolRegistry */ -void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); +const SubGhzProtocolRegistry* + subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); /** * Get list of protocols names. diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c index 123a4ba9d31f..717d7a0c9aa5 100644 --- a/lib/subghz/protocols/bin_raw.c +++ b/lib/subghz/protocols/bin_raw.c @@ -9,6 +9,8 @@ #include #include +#include + #define TAG "SubGhzProtocolBinRAW" //change very carefully, RAM ends at the most inopportune moment diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 4ca1c4679d15..f1a28ac9b514 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -1,5 +1,6 @@ #pragma once #include "../registry.h" +#include "../subghz_protocol_registry.h" #include "princeton.h" #include "keeloq.h" @@ -43,5 +44,3 @@ #include "alutech_at_4n.h" #include "kinggates_stylo_4k.h" #include "bin_raw.h" - -extern const SubGhzProtocolRegistry subghz_protocol_registry; diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index 66358698ac93..1288c957371f 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -40,6 +40,7 @@ struct SubGhzProtocolEncoderRAW { bool is_running; FuriString* file_name; + FuriString* radio_device_name; SubGhzFileEncoderWorker* file_worker_encoder; }; @@ -282,6 +283,7 @@ void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment) { instance->base.protocol = &subghz_protocol_raw; instance->file_name = furi_string_alloc(); + instance->radio_device_name = furi_string_alloc(); instance->is_running = false; return instance; } @@ -300,6 +302,7 @@ void subghz_protocol_encoder_raw_free(void* context) { SubGhzProtocolEncoderRAW* instance = context; subghz_protocol_encoder_raw_stop(instance); furi_string_free(instance->file_name); + furi_string_free(instance->radio_device_name); free(instance); } @@ -318,7 +321,9 @@ static bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* in instance->file_worker_encoder = subghz_file_encoder_worker_alloc(); if(subghz_file_encoder_worker_start( - instance->file_worker_encoder, furi_string_get_cstr(instance->file_name))) { + instance->file_worker_encoder, + furi_string_get_cstr(instance->file_name), + furi_string_get_cstr(instance->radio_device_name))) { //the worker needs a file in order to open and read part of the file furi_delay_ms(100); instance->is_running = true; @@ -328,7 +333,10 @@ static bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* in return instance->is_running; } -void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path) { +void subghz_protocol_raw_gen_fff_data( + FlipperFormat* flipper_format, + const char* file_path, + const char* radio_device_name) { do { stream_clean(flipper_format_get_raw_stream(flipper_format)); if(!flipper_format_write_string_cstr(flipper_format, "Protocol", "RAW")) { @@ -340,6 +348,12 @@ void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* FURI_LOG_E(TAG, "Unable to add File_name"); break; } + + if(!flipper_format_write_string_cstr( + flipper_format, "Radio_device_name", radio_device_name)) { + FURI_LOG_E(TAG, "Unable to add Radio_device_name"); + break; + } } while(false); } @@ -364,6 +378,13 @@ SubGhzProtocolStatus } furi_string_set(instance->file_name, temp_str); + if(!flipper_format_read_string(flipper_format, "Radio_device_name", temp_str)) { + FURI_LOG_E(TAG, "Missing Radio_device_name"); + res = SubGhzProtocolStatusErrorParserOthers; + break; + } + furi_string_set(instance->radio_device_name, temp_str); + if(!subghz_protocol_encoder_raw_worker_init(instance)) { res = SubGhzProtocolStatusErrorEncoderGetUpload; break; diff --git a/lib/subghz/protocols/raw.h b/lib/subghz/protocols/raw.h index 4f67a4e2f24a..6d791bb3663e 100644 --- a/lib/subghz/protocols/raw.h +++ b/lib/subghz/protocols/raw.h @@ -126,8 +126,12 @@ void subghz_protocol_raw_file_encoder_worker_set_callback_end( * File generation for RAW work. * @param flipper_format Pointer to a FlipperFormat instance * @param file_path File path + * @param radio_dev_name Radio device name */ -void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path); +void subghz_protocol_raw_gen_fff_data( + FlipperFormat* flipper_format, + const char* file_path, + const char* radio_dev_name); /** * Deserialize and generating an upload to send. diff --git a/lib/subghz/registry.h b/lib/subghz/registry.h index 91027807e898..8529c1097051 100644 --- a/lib/subghz/registry.h +++ b/lib/subghz/registry.h @@ -9,6 +9,7 @@ extern "C" { typedef struct SubGhzEnvironment SubGhzEnvironment; typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; +typedef struct SubGhzProtocol SubGhzProtocol; struct SubGhzProtocolRegistry { const SubGhzProtocol** items; diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 5c4d36f78e88..519ff3fdcc35 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -3,6 +3,7 @@ #include #include #include +#include #define TAG "SubGhzFileEncoderWorker" @@ -21,6 +22,7 @@ struct SubGhzFileEncoderWorker { bool is_storage_slow; FuriString* str_data; FuriString* file_path; + const SubGhzDevice* device; SubGhzFileEncoderWorkerCallbackEnd callback_end; void* context_end; @@ -156,10 +158,13 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { if(instance->is_storage_slow) { FURI_LOG_E(TAG, "Storage is slow"); } + FURI_LOG_I(TAG, "End read file"); - while(!furi_hal_subghz_is_async_tx_complete() && instance->worker_running) { + while(instance->device && !subghz_devices_is_async_complete_tx(instance->device) && + instance->worker_running) { furi_delay_ms(5); } + FURI_LOG_I(TAG, "End transmission"); while(instance->worker_running) { if(instance->worker_stoping) { @@ -206,12 +211,16 @@ void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance) { free(instance); } -bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path) { +bool subghz_file_encoder_worker_start( + SubGhzFileEncoderWorker* instance, + const char* file_path, + const char* radio_device_name) { furi_assert(instance); furi_assert(!instance->worker_running); furi_stream_buffer_reset(instance->stream); furi_string_set(instance->file_path, file_path); + instance->device = subghz_devices_get_by_name(radio_device_name); instance->worker_running = true; furi_thread_start(instance->thread); diff --git a/lib/subghz/subghz_file_encoder_worker.h b/lib/subghz/subghz_file_encoder_worker.h index a87be5cd6f3a..e66c66d76eab 100644 --- a/lib/subghz/subghz_file_encoder_worker.h +++ b/lib/subghz/subghz_file_encoder_worker.h @@ -38,9 +38,14 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context); /** * Start SubGhzFileEncoderWorker. * @param instance Pointer to a SubGhzFileEncoderWorker instance + * @param file_path File path + * @param radio_device_name Radio device name * @return bool - true if ok */ -bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path); +bool subghz_file_encoder_worker_start( + SubGhzFileEncoderWorker* instance, + const char* file_path, + const char* radio_device_name); /** * Stop SubGhzFileEncoderWorker diff --git a/lib/subghz/subghz_protocol_registry.h b/lib/subghz/subghz_protocol_registry.h new file mode 100644 index 000000000000..8e80071b5185 --- /dev/null +++ b/lib/subghz/subghz_protocol_registry.h @@ -0,0 +1,38 @@ +#pragma once + +#include "registry.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const SubGhzProtocolRegistry subghz_protocol_registry; + +typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW; + +bool subghz_protocol_secplus_v2_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint32_t cnt, + SubGhzRadioPreset* preset); + +bool subghz_protocol_keeloq_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + const char* manufacture_name, + SubGhzRadioPreset* preset); + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi); + +bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 656580043f05..9804f827733c 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -4,7 +4,7 @@ #include #include -#include +#include #define TAG "SubGhzSetting" @@ -218,8 +218,7 @@ void subghz_setting_free(SubGhzSetting* instance) { static void subghz_setting_load_default_preset( SubGhzSetting* instance, const char* preset_name, - const uint8_t* preset_data, - const uint8_t preset_pa_table[8]) { + const uint8_t* preset_data) { furi_assert(instance); furi_assert(preset_data); uint32_t preset_data_count = 0; @@ -235,10 +234,8 @@ static void subghz_setting_load_default_preset( preset_data_count += 2; item->custom_preset_data_size = sizeof(uint8_t) * preset_data_count + sizeof(uint8_t) * 8; item->custom_preset_data = malloc(item->custom_preset_data_size); - //load preset register - memcpy(&item->custom_preset_data[0], &preset_data[0], preset_data_count); - //load pa table - memcpy(&item->custom_preset_data[preset_data_count], &preset_pa_table[0], 8); + //load preset register + pa table + memcpy(&item->custom_preset_data[0], &preset_data[0], item->custom_preset_data_size); } static void subghz_setting_load_default_region( @@ -262,25 +259,13 @@ static void subghz_setting_load_default_region( } subghz_setting_load_default_preset( - instance, - "AM270", - (uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs, - furi_hal_subghz_preset_ook_async_patable); + instance, "AM270", subghz_device_cc1101_preset_ook_270khz_async_regs); subghz_setting_load_default_preset( - instance, - "AM650", - (uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs, - furi_hal_subghz_preset_ook_async_patable); + instance, "AM650", subghz_device_cc1101_preset_ook_650khz_async_regs); subghz_setting_load_default_preset( - instance, - "FM238", - (uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs, - furi_hal_subghz_preset_2fsk_async_patable); + instance, "FM238", subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs); subghz_setting_load_default_preset( - instance, - "FM476", - (uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs, - furi_hal_subghz_preset_2fsk_async_patable); + instance, "FM476", subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs); } void subghz_setting_load_default(SubGhzSetting* instance) { @@ -359,6 +344,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { } while(flipper_format_read_uint32( fff_data_file, "Frequency", (uint32_t*)&temp_data32, 1)) { + //Todo: add a frequency support check depending on the selected radio device if(furi_hal_subghz_is_frequency_valid(temp_data32)) { FURI_LOG_I(TAG, "Frequency loaded %lu", temp_data32); FrequencyList_push_back(instance->frequencies, temp_data32); diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 42124bebc1c8..250e6666f407 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -21,6 +21,8 @@ struct SubGhzTxRxWorker { SubGhzTxRxWorkerStatus status; uint32_t frequency; + const SubGhzDevice* device; + const GpioPin* device_data_gpio; SubGhzTxRxWorkerCallbackHaveRead callback_have_read; void* context_have_read; @@ -65,33 +67,33 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* uint8_t timeout = 100; bool ret = false; if(instance->status != SubGhzTxRxWorkerStatusRx) { - furi_hal_subghz_rx(); + subghz_devices_set_rx(instance->device); instance->status = SubGhzTxRxWorkerStatusRx; furi_delay_tick(1); } //waiting for reception to complete - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { + while(furi_hal_gpio_read(instance->device_data_gpio)) { furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); - furi_hal_subghz_flush_rx(); - furi_hal_subghz_rx(); + subghz_devices_flush_rx(instance->device); + subghz_devices_set_rx(instance->device); break; } } - if(furi_hal_subghz_rx_pipe_not_empty()) { + if(subghz_devices_rx_pipe_not_empty(instance->device)) { FURI_LOG_I( TAG, "RSSI: %03.1fdbm LQI: %d", - (double)furi_hal_subghz_get_rssi(), - furi_hal_subghz_get_lqi()); - if(furi_hal_subghz_is_rx_data_crc_valid()) { - furi_hal_subghz_read_packet(data, size); + (double)subghz_devices_get_rssi(instance->device), + subghz_devices_get_lqi(instance->device)); + if(subghz_devices_is_rx_data_crc_valid(instance->device)) { + subghz_devices_read_packet(instance->device, data, size); ret = true; } - furi_hal_subghz_flush_rx(); - furi_hal_subghz_rx(); + subghz_devices_flush_rx(instance->device); + subghz_devices_set_rx(instance->device); } return ret; } @@ -99,26 +101,28 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { uint8_t timeout = 200; if(instance->status != SubGhzTxRxWorkerStatusIDLE) { - furi_hal_subghz_idle(); + subghz_devices_idle(instance->device); } - furi_hal_subghz_write_packet(data, size); - furi_hal_subghz_tx(); //start send + subghz_devices_write_packet(instance->device, data, size); + subghz_devices_set_tx(instance->device); //start send instance->status = SubGhzTxRxWorkerStatusTx; - while(!furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted + while(!furi_hal_gpio_read( + instance->device_data_gpio)) { // Wait for GDO0 to be set -> sync transmitted furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); break; } } - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet + while(furi_hal_gpio_read( + instance->device_data_gpio)) { // Wait for GDO0 to be cleared -> end of packet furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); break; } } - furi_hal_subghz_idle(); + subghz_devices_idle(instance->device); instance->status = SubGhzTxRxWorkerStatusIDLE; } /** Worker thread @@ -128,16 +132,19 @@ void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t si */ static int32_t subghz_tx_rx_worker_thread(void* context) { SubGhzTxRxWorker* instance = context; + furi_assert(instance->device); FURI_LOG_I(TAG, "Worker start"); - furi_hal_subghz_reset(); - furi_hal_subghz_idle(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); - //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + subghz_devices_begin(instance->device); + instance->device_data_gpio = subghz_devices_get_data_gpio(instance->device); + subghz_devices_reset(instance->device); + subghz_devices_idle(instance->device); + subghz_devices_load_preset(instance->device, FuriHalSubGhzPresetGFSK9_99KbAsync, NULL); - furi_hal_subghz_set_frequency_and_path(instance->frequency); - furi_hal_subghz_flush_rx(); + furi_hal_gpio_init(instance->device_data_gpio, GpioModeInput, GpioPullNo, GpioSpeedLow); + + subghz_devices_set_frequency(instance->device, instance->frequency); + subghz_devices_flush_rx(instance->device); uint8_t data[SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE + 1] = {0}; size_t size_tx = 0; @@ -191,8 +198,8 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { furi_delay_tick(1); } - furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); - furi_hal_subghz_sleep(); + subghz_devices_sleep(instance->device); + subghz_devices_end(instance->device); FURI_LOG_I(TAG, "Worker stop"); return 0; @@ -224,7 +231,10 @@ void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance) { free(instance); } -bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { +bool subghz_tx_rx_worker_start( + SubGhzTxRxWorker* instance, + const SubGhzDevice* device, + uint32_t frequency) { furi_assert(instance); furi_assert(!instance->worker_running); bool res = false; @@ -235,6 +245,7 @@ bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { if(furi_hal_region_is_frequency_allowed(frequency)) { instance->frequency = frequency; + instance->device = device; res = true; } diff --git a/lib/subghz/subghz_tx_rx_worker.h b/lib/subghz/subghz_tx_rx_worker.h index ddc02e749d8f..56bdb0a1febd 100644 --- a/lib/subghz/subghz_tx_rx_worker.h +++ b/lib/subghz/subghz_tx_rx_worker.h @@ -1,6 +1,7 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { @@ -67,9 +68,13 @@ void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance); /** * Start SubGhzTxRxWorker * @param instance Pointer to a SubGhzTxRxWorker instance + * @param device Pointer to a SubGhzDevice instance * @return bool - true if ok */ -bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency); +bool subghz_tx_rx_worker_start( + SubGhzTxRxWorker* instance, + const SubGhzDevice* device, + uint32_t frequency); /** * Stop SubGhzTxRxWorker diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 09eb07eeaada..d87a0dc760c0 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -21,6 +21,15 @@ #define SUBGHZ_RAW_FILE_VERSION 1 #define SUBGHZ_RAW_FILE_TYPE "Flipper SubGhz RAW File" +#define SUBGHZ_KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes") +#define SUBGHZ_KEYSTORE_DIR_USER_NAME EXT_PATH("subghz/assets/keeloq_mfcodes_user") +#define SUBGHZ_CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") +#define SUBGHZ_NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") +#define SUBGHZ_ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n") + +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; +typedef struct SubGhzEnvironment SubGhzEnvironment; + // Radio Preset typedef struct { FuriString* name; @@ -115,11 +124,11 @@ typedef enum { SubGhzProtocolFlag_BinRAW = (1 << 10), } SubGhzProtocolFlag; -typedef struct { +struct SubGhzProtocol { const char* name; SubGhzProtocolType type; SubGhzProtocolFlag flag; const SubGhzProtocolEncoder* encoder; const SubGhzProtocolDecoder* decoder; -} SubGhzProtocol; +}; diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 4e158e30ea79..3aaae2fd3416 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -8,6 +8,7 @@ env.Append( "#/lib/toolbox", ], SDK_HEADERS=[ + File("api_lock.h"), File("manchester_decoder.h"), File("manchester_encoder.h"), File("path.h"), @@ -29,6 +30,7 @@ env.Append( File("protocols/protocol_dict.h"), File("pretty_format.h"), File("hex.h"), + File("simple_array.h"), ], ) diff --git a/lib/toolbox/bit_buffer.c b/lib/toolbox/bit_buffer.c new file mode 100644 index 000000000000..cac949cd62b5 --- /dev/null +++ b/lib/toolbox/bit_buffer.c @@ -0,0 +1,338 @@ +#include "bit_buffer.h" + +#include + +#define BITS_IN_BYTE (8) + +struct BitBuffer { + uint8_t* data; + uint8_t* parity; + size_t capacity_bytes; + size_t size_bits; +}; + +BitBuffer* bit_buffer_alloc(size_t capacity_bytes) { + furi_assert(capacity_bytes); + + BitBuffer* buf = malloc(sizeof(BitBuffer)); + + buf->data = malloc(capacity_bytes); + size_t parity_buf_size = (capacity_bytes + BITS_IN_BYTE - 1) / BITS_IN_BYTE; + buf->parity = malloc(parity_buf_size); + buf->capacity_bytes = capacity_bytes; + buf->size_bits = 0; + + return buf; +} + +void bit_buffer_free(BitBuffer* buf) { + furi_assert(buf); + + free(buf->data); + free(buf->parity); + free(buf); +} + +void bit_buffer_reset(BitBuffer* buf) { + furi_assert(buf); + + memset(buf->data, 0, buf->capacity_bytes); + size_t parity_buf_size = (buf->capacity_bytes + BITS_IN_BYTE - 1) / BITS_IN_BYTE; + memset(buf->parity, 0, parity_buf_size); + buf->size_bits = 0; +} + +void bit_buffer_copy(BitBuffer* buf, const BitBuffer* other) { + furi_assert(buf); + furi_assert(other); + + if(buf == other) return; + + furi_assert(buf->capacity_bytes * BITS_IN_BYTE >= other->size_bits); + + memcpy(buf->data, other->data, bit_buffer_get_size_bytes(other)); + buf->size_bits = other->size_bits; +} + +void bit_buffer_copy_right(BitBuffer* buf, const BitBuffer* other, size_t start_index) { + furi_assert(buf); + furi_assert(other); + furi_assert(bit_buffer_get_size_bytes(other) > start_index); + furi_assert(buf->capacity_bytes >= bit_buffer_get_size_bytes(other) - start_index); + + memcpy(buf->data, other->data + start_index, bit_buffer_get_size_bytes(other) - start_index); + buf->size_bits = other->size_bits - start_index * BITS_IN_BYTE; +} + +void bit_buffer_copy_left(BitBuffer* buf, const BitBuffer* other, size_t end_index) { + furi_assert(buf); + furi_assert(other); + furi_assert(bit_buffer_get_capacity_bytes(buf) >= end_index); + furi_assert(bit_buffer_get_size_bytes(other) >= end_index); + + memcpy(buf->data, other->data, end_index); + buf->size_bits = end_index * BITS_IN_BYTE; +} + +void bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) { + furi_assert(buf); + furi_assert(data); + furi_assert(buf->capacity_bytes >= size_bytes); + + memcpy(buf->data, data, size_bytes); + buf->size_bits = size_bytes * BITS_IN_BYTE; +} + +void bit_buffer_copy_bits(BitBuffer* buf, const uint8_t* data, size_t size_bits) { + furi_assert(buf); + furi_assert(data); + furi_assert(buf->capacity_bytes * BITS_IN_BYTE >= size_bits); + + size_t size_bytes = (size_bits + BITS_IN_BYTE - 1) / BITS_IN_BYTE; + memcpy(buf->data, data, size_bytes); + buf->size_bits = size_bits; +} + +void bit_buffer_copy_bytes_with_parity(BitBuffer* buf, const uint8_t* data, size_t size_bits) { + furi_assert(buf); + furi_assert(data); + + size_t bit_processed = 0; + size_t curr_byte = 0; + + if(size_bits < BITS_IN_BYTE + 1) { + buf->size_bits = size_bits; + buf->data[0] = data[0]; + } else { + furi_assert(size_bits % (BITS_IN_BYTE + 1) == 0); + buf->size_bits = size_bits / (BITS_IN_BYTE + 1) * BITS_IN_BYTE; + while(bit_processed < size_bits) { + buf->data[curr_byte] = data[bit_processed / BITS_IN_BYTE] >> + (bit_processed % BITS_IN_BYTE); + buf->data[curr_byte] |= data[bit_processed / BITS_IN_BYTE + 1] + << (BITS_IN_BYTE - bit_processed % BITS_IN_BYTE); + uint8_t bit = + FURI_BIT(data[bit_processed / BITS_IN_BYTE + 1], bit_processed % BITS_IN_BYTE); + + if(bit_processed % BITS_IN_BYTE) { + buf->parity[curr_byte / BITS_IN_BYTE] = bit; + } else { + buf->parity[curr_byte / BITS_IN_BYTE] |= bit << (bit_processed % BITS_IN_BYTE); + } + bit_processed += BITS_IN_BYTE + 1; + curr_byte++; + } + buf->size_bits = curr_byte * BITS_IN_BYTE; + } +} + +void bit_buffer_write_bytes(const BitBuffer* buf, void* dest, size_t size_bytes) { + furi_assert(buf); + furi_assert(dest); + furi_assert(bit_buffer_get_size_bytes(buf) <= size_bytes); + + memcpy(dest, buf->data, bit_buffer_get_size_bytes(buf)); +} + +void bit_buffer_write_bytes_with_parity( + const BitBuffer* buf, + void* dest, + size_t size_bytes, + size_t* bits_written) { + furi_assert(buf); + furi_assert(dest); + furi_assert(bits_written); + + size_t buf_size_bytes = bit_buffer_get_size_bytes(buf); + size_t buf_size_with_parity_bytes = + (buf_size_bytes * (BITS_IN_BYTE + 1) + BITS_IN_BYTE) / BITS_IN_BYTE; + furi_assert(buf_size_with_parity_bytes <= size_bytes); + + uint8_t next_par_bit = 0; + uint16_t curr_bit_pos = 0; + uint8_t* bitstream = dest; + + for(size_t i = 0; i < buf_size_bytes; i++) { + next_par_bit = FURI_BIT(buf->parity[i / BITS_IN_BYTE], i % BITS_IN_BYTE); + if(curr_bit_pos % BITS_IN_BYTE == 0) { + bitstream[curr_bit_pos / BITS_IN_BYTE] = buf->data[i]; + curr_bit_pos += BITS_IN_BYTE; + bitstream[curr_bit_pos / BITS_IN_BYTE] = next_par_bit; + curr_bit_pos++; + } else { + bitstream[curr_bit_pos / BITS_IN_BYTE] |= buf->data[i] + << (curr_bit_pos % BITS_IN_BYTE); + bitstream[curr_bit_pos / BITS_IN_BYTE + 1] = + buf->data[i] >> (BITS_IN_BYTE - curr_bit_pos % BITS_IN_BYTE); + bitstream[curr_bit_pos / BITS_IN_BYTE + 1] |= next_par_bit + << (curr_bit_pos % BITS_IN_BYTE); + curr_bit_pos += BITS_IN_BYTE + 1; + } + } + + *bits_written = curr_bit_pos; +} + +void bit_buffer_write_bytes_mid( + const BitBuffer* buf, + void* dest, + size_t start_index, + size_t size_bytes) { + furi_assert(buf); + furi_assert(dest); + furi_assert(start_index + size_bytes <= bit_buffer_get_size_bytes(buf)); + + memcpy(dest, buf->data + start_index, size_bytes); +} + +bool bit_buffer_has_partial_byte(const BitBuffer* buf) { + furi_assert(buf); + + return (buf->size_bits % BITS_IN_BYTE) != 0; +} + +bool bit_buffer_starts_with_byte(const BitBuffer* buf, uint8_t byte) { + furi_assert(buf); + + return bit_buffer_get_size_bytes(buf) && (buf->data[0] == byte); +} + +size_t bit_buffer_get_capacity_bytes(const BitBuffer* buf) { + furi_assert(buf); + + return buf->capacity_bytes; +} + +size_t bit_buffer_get_size(const BitBuffer* buf) { + furi_assert(buf); + + return buf->size_bits; +} + +size_t bit_buffer_get_size_bytes(const BitBuffer* buf) { + furi_assert(buf); + + return (buf->size_bits / BITS_IN_BYTE) + (buf->size_bits % BITS_IN_BYTE ? 1 : 0); +} + +uint8_t bit_buffer_get_byte(const BitBuffer* buf, size_t index) { + furi_assert(buf); + furi_assert(buf->capacity_bytes > index); + + return buf->data[index]; +} + +uint8_t bit_buffer_get_byte_from_bit(const BitBuffer* buf, size_t index_bits) { + furi_assert(buf); + furi_assert(buf->capacity_bytes * BITS_IN_BYTE > index_bits); + + const size_t byte_index = index_bits / BITS_IN_BYTE; + const size_t bit_offset = index_bits % BITS_IN_BYTE; + + const uint8_t lo = buf->data[byte_index] >> bit_offset; + const uint8_t hi = buf->data[byte_index + 1] << (BITS_IN_BYTE - bit_offset); + + return lo | hi; +} + +const uint8_t* bit_buffer_get_data(const BitBuffer* buf) { + furi_assert(buf); + + return buf->data; +} + +const uint8_t* bit_buffer_get_parity(const BitBuffer* buf) { + furi_assert(buf); + + return buf->parity; +} + +void bit_buffer_set_byte(BitBuffer* buf, size_t index, uint8_t byte) { + furi_assert(buf); + + const size_t size_bytes = bit_buffer_get_size_bytes(buf); + furi_assert(size_bytes > index); + + buf->data[index] = byte; +} + +void bit_buffer_set_byte_with_parity(BitBuffer* buff, size_t index, uint8_t byte, bool parity) { + furi_assert(buff); + furi_assert(buff->size_bits / BITS_IN_BYTE > index); + + buff->data[index] = byte; + if((index % BITS_IN_BYTE) == 0) { + buff->parity[index / BITS_IN_BYTE] = parity; + } else { + buff->parity[index / BITS_IN_BYTE] |= parity << (index % BITS_IN_BYTE); + } +} + +void bit_buffer_set_size(BitBuffer* buf, size_t new_size) { + furi_assert(buf); + furi_assert(buf->capacity_bytes * BITS_IN_BYTE >= new_size); + + buf->size_bits = new_size; +} + +void bit_buffer_set_size_bytes(BitBuffer* buf, size_t new_size_bytes) { + furi_assert(buf); + furi_assert(buf->capacity_bytes >= new_size_bytes); + + buf->size_bits = new_size_bytes * BITS_IN_BYTE; +} + +void bit_buffer_append(BitBuffer* buf, const BitBuffer* other) { + bit_buffer_append_right(buf, other, 0); +} + +void bit_buffer_append_right(BitBuffer* buf, const BitBuffer* other, size_t start_index) { + furi_assert(buf); + furi_assert(other); + + const size_t size_bytes = bit_buffer_get_size_bytes(buf); + const size_t other_size_bytes = bit_buffer_get_size_bytes(other) - start_index; + + furi_assert(buf->capacity_bytes >= size_bytes + other_size_bytes); + + memcpy(buf->data + size_bytes, other->data + start_index, other_size_bytes); + buf->size_bits += other->size_bits - start_index * BITS_IN_BYTE; +} + +void bit_buffer_append_byte(BitBuffer* buf, uint8_t byte) { + furi_assert(buf); + + const size_t data_size_bytes = bit_buffer_get_size_bytes(buf); + const size_t new_data_size_bytes = data_size_bytes + 1; + furi_assert(new_data_size_bytes <= buf->capacity_bytes); + + buf->data[data_size_bytes] = byte; + buf->size_bits = new_data_size_bytes * BITS_IN_BYTE; +} + +void bit_buffer_append_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) { + furi_assert(buf); + furi_assert(data); + + size_t buf_size_bytes = bit_buffer_get_size_bytes(buf); + furi_assert(buf->capacity_bytes >= buf_size_bytes + size_bytes); + + // TODO: Correct size + memcpy(&buf->data[buf_size_bytes], data, size_bytes); + buf->size_bits += size_bytes * BITS_IN_BYTE; +} + +void bit_buffer_append_bit(BitBuffer* buf, bool bit) { + furi_assert(buf); + furi_assert( + bit_buffer_get_size_bytes(buf) <= + (buf->capacity_bytes - (bit_buffer_has_partial_byte(buf) ? 0 : 1))); + + if(bit) { + const size_t byte_index = buf->size_bits / BITS_IN_BYTE; + const size_t bit_offset = (buf->size_bits % BITS_IN_BYTE); + buf->data[byte_index] |= 1U << bit_offset; + } + + buf->size_bits++; +} diff --git a/lib/toolbox/bit_buffer.h b/lib/toolbox/bit_buffer.h new file mode 100644 index 000000000000..5c50e729c605 --- /dev/null +++ b/lib/toolbox/bit_buffer.h @@ -0,0 +1,325 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BitBuffer BitBuffer; + +// Construction, deletion, reset + +/** + * Allocate a BitBuffer instance. + * + * @param [in] capacity_bytes maximum buffer capacity, in bytes + * @return pointer to the allocated BitBuffer instance + */ +BitBuffer* bit_buffer_alloc(size_t capacity_bytes); + +/** + * Delete a BitBuffer instance. + * + * @param [in,out] buf pointer to a BitBuffer instance + */ +void bit_buffer_free(BitBuffer* buf); + +/** + * Clear all data from a BitBuffer instance. + * + * @param [in,out] buf pointer to a BitBuffer instance + */ +void bit_buffer_reset(BitBuffer* buf); + +// Copy and write + +/** + * Copy another BitBuffer instance's contents to this one, replacing + * all of the original data. + * The destination capacity must be no less than the source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] other pointer to a BitBuffer instance to copy from + * @note + */ +void bit_buffer_copy(BitBuffer* buf, const BitBuffer* other); + +/** + * Copy all BitBuffer instance's contents to this one, starting from start_index, + * replacing all of the original data. + * The destination capacity must be no less than the source data size + * counting from start_index. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] other pointer to a BitBuffer instance to copy from + * @param [in] start_index index to begin copying source data from + */ +void bit_buffer_copy_right(BitBuffer* buf, const BitBuffer* other, size_t start_index); + +/** + * Copy all BitBuffer instance's contents to this one, ending with end_index, + * replacing all of the original data. + * The destination capacity must be no less than the source data size + * counting to end_index. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] other pointer to a BitBuffer instance to copy from + * @param [in] end_index index to end copying source data at + */ +void bit_buffer_copy_left(BitBuffer* buf, const BitBuffer* other, size_t end_index); + +/** + * Copy a byte array to a BitBuffer instance, replacing all of the original data. + * The destination capacity must be no less than the source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] data pointer to the byte array to be copied + * @param [in] size_bytes size of the data to be copied, in bytes + */ +void bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes); + +/** + * Copy a byte array to a BitBuffer instance, replacing all of the original data. + * The destination capacity must be no less than the source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] data pointer to the byte array to be copied + * @param [in] size_bits size of the data to be copied, in bits + */ +void bit_buffer_copy_bits(BitBuffer* buf, const uint8_t* data, size_t size_bits); + +/** + * Copy a byte with parity array to a BitBuffer instance, replacing all of the original data. + * The destination capacity must be no less than the source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] data pointer to the byte array to be copied + * @param [in] size_bitss size of the data to be copied, in bits + */ +void bit_buffer_copy_bytes_with_parity(BitBuffer* buf, const uint8_t* data, size_t size_bits); + +/** + * Write a BitBuffer instance's entire contents to an arbitrary memory location. + * The destination memory must be allocated. Additionally, the destination + * capacity must be no less than the source data size. + * + * @param [in] buf pointer to a BitBuffer instance to write from + * @param [out] dest pointer to the destination memory location + * @param [in] size_bytes maximum destination data size, in bytes + */ +void bit_buffer_write_bytes(const BitBuffer* buf, void* dest, size_t size_bytes); + +/** + * Write a BitBuffer instance's entire contents to an arbitrary memory location. + * Additionally, place a parity bit after each byte. + * The destination memory must be allocated. Additionally, the destination + * capacity must be no less than the source data size plus parity. + * + * @param [in] buf pointer to a BitBuffer instance to write from + * @param [out] dest pointer to the destination memory location + * @param [in] size_bytes maximum destination data size, in bytes + * @param [out] bits_written actual number of bits writen, in bits + */ +void bit_buffer_write_bytes_with_parity( + const BitBuffer* buf, + void* dest, + size_t size_bytes, + size_t* bits_written); + +/** + * Write a slice of BitBuffer instance's contents to an arbitrary memory location. + * The destination memory must be allocated. Additionally, the destination + * capacity must be no less than the requested slice size. + * + * @param [in] buf pointer to a BitBuffer instance to write from + * @param [out] dest pointer to the destination memory location + * @param [in] start_index index to begin copying source data from + * @param [in] size_bytes data slice size, in bytes + */ +void bit_buffer_write_bytes_mid( + const BitBuffer* buf, + void* dest, + size_t start_index, + size_t size_bytes); + +// Checks + +/** + * Check whether a BitBuffer instance contains a partial byte (i.e. the bit count + * is not divisible by 8). + * + * @param [in] buf pointer to a BitBuffer instance to be checked + * @return true if the instance contains a partial byte, false otherwise + */ +bool bit_buffer_has_partial_byte(const BitBuffer* buf); + +/** + * Check whether a BitBuffer instance's contents start with the designated byte. + * + * @param [in] buf pointer to a BitBuffer instance to be checked + * @param [in] byte byte value to be checked against + * @return true if data starts with designated byte, false otherwise + */ +bool bit_buffer_starts_with_byte(const BitBuffer* buf, uint8_t byte); + +// Getters + +/** + * Get a BitBuffer instance's capacity (i.e. the maximum possible amount of data), in bytes. + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @return capacity, in bytes + */ +size_t bit_buffer_get_capacity_bytes(const BitBuffer* buf); + +/** + * Get a BitBuffer instance's data size (i.e. the amount of stored data), in bits. + * Might be not divisible by 8 (see bit_buffer_is_partial_byte). + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @return data size, in bits. + */ +size_t bit_buffer_get_size(const BitBuffer* buf); + +/** + * Get a BitBuffer instance's data size (i.e. the amount of stored data), in bytes. + * If a partial byte is present, it is also counted. + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @return data size, in bytes. + */ +size_t bit_buffer_get_size_bytes(const BitBuffer* buf); + +/** + * Get a byte value at a specified index in a BitBuffer instance. + * The index must be valid (i.e. less than the instance's data size in bytes). + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @param [in] index index of the byte in question + */ +uint8_t bit_buffer_get_byte(const BitBuffer* buf, size_t index); + +/** + * Get a byte value starting from the specified bit index in a BitBuffer instance. + * The resulting byte might correspond to a single byte (if the index is a multiple + * of 8), or two overlapping bytes combined. + * The index must be valid (i.e. less than the instance's data size in bits). + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @param [in] index bit index of the byte in question + */ +uint8_t bit_buffer_get_byte_from_bit(const BitBuffer* buf, size_t index_bits); + +/** + * Get the pointer to a BitBuffer instance's underlying data. + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @return pointer to the underlying data + */ +const uint8_t* bit_buffer_get_data(const BitBuffer* buf); + +/** + * Get the pointer to a BitBuffer instance's underlying data. + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @return pointer to the underlying data + */ +const uint8_t* bit_buffer_get_parity(const BitBuffer* buf); + +// Setters + +/** + * Set byte value at a specified index in a BitBuffer instance. + * The index must be valid (i.e. less than the instance's data size in bytes). + * + * @param [in,out] buf pointer to a BitBuffer instance to be modified + * @param [in] index index of the byte in question + * @param [in] byte byte value to be set at index + */ +void bit_buffer_set_byte(BitBuffer* buf, size_t index, uint8_t byte); + +/** + * Set byte and parity bit value at a specified index in a BitBuffer instance. + * The index must be valid (i.e. less than the instance's data size in bytes). + * + * @param [in,out] buf pointer to a BitBuffer instance to be modified + * @param [in] index index of the byte in question + * @param [in] byte byte value to be set at index + * @param [in] parity parity bit value to be set at index + */ +void bit_buffer_set_byte_with_parity(BitBuffer* buff, size_t index, uint8_t byte, bool parity); + +/** + * Resize a BitBuffer instance to a new size, in bits. + * @warning May cause bugs. Use only if absolutely necessary. + * + * @param [in,out] buf pointer to a BitBuffer instance to be resized + * @param [in] new_size the new size of the buffer, in bits + */ +void bit_buffer_set_size(BitBuffer* buf, size_t new_size); + +/** + * Resize a BitBuffer instance to a new size, in bytes. + * @warning May cause bugs. Use only if absolutely necessary. + * + * @param [in,out] buf pointer to a BitBuffer instance to be resized + * @param [in] new_size_bytes the new size of the buffer, in bytes + */ +void bit_buffer_set_size_bytes(BitBuffer* buf, size_t new_size_bytes); + +// Modification + +/** + * Append all BitBuffer's instance contents to this one. The destination capacity + * must be no less than its original data size plus source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to be appended to + * @param [in] other pointer to a BitBuffer instance to be appended + */ +void bit_buffer_append(BitBuffer* buf, const BitBuffer* other); + +/** + * Append a BitBuffer's instance contents to this one, starting from start_index. + * The destination capacity must be no less than the source data size + * counting from start_index. + * + * @param [in,out] buf pointer to a BitBuffer instance to be appended to + * @param [in] other pointer to a BitBuffer instance to be appended + * @param [in] start_index index to begin copying source data from + */ +void bit_buffer_append_right(BitBuffer* buf, const BitBuffer* other, size_t start_index); + +/** + * Append a byte to a BitBuffer instance. + * The destination capacity must be no less its original data size plus one. + * + * @param [in,out] buf pointer to a BitBuffer instance to be appended to + * @param [in] byte byte value to be appended + */ +void bit_buffer_append_byte(BitBuffer* buf, uint8_t byte); + +/** + * Append a byte array to a BitBuffer instance. + * The destination capacity must be no less its original data size plus source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to be appended to + * @param [in] data pointer to the byte array to be appended + * @param [in] size_bytes size of the data to be appended, in bytes + */ +void bit_buffer_append_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes); + +/** + * Append a bit to a BitBuffer instance. + * The destination capacity must be sufficient to accomodate the additional bit. + * + * @param [in,out] buf pointer to a BitBuffer instance to be appended to + * @param [in] bit bit value to be appended + */ +void bit_buffer_append_bit(BitBuffer* buf, bool bit); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/m_cstr_dup.h b/lib/toolbox/m_cstr_dup.h index 0555f72c651c..11b7fe35adac 100644 --- a/lib/toolbox/m_cstr_dup.h +++ b/lib/toolbox/m_cstr_dup.h @@ -2,15 +2,16 @@ #include #define M_INIT_DUP(a) ((a) = strdup("")) -#define M_SET_DUP(a, b) (M_CHECK_DEFAULT_TYPE(a), free((void*)a), (a) = strdup(b)) +#define M_INIT_SET_DUP(a, b) ((a) = strdup(b)) +#define M_SET_DUP(a, b) (free((void*)a), (a) = strdup(b)) #define M_CLEAR_DUP(a) (free((void*)a)) -#define M_CSTR_DUP_OPLIST \ - (INIT(M_INIT_DUP), \ - INIT_SET(M_SET_DUP), \ - SET(M_SET_DUP), \ - CLEAR(M_CLEAR_DUP), \ - HASH(m_core_cstr_hash), \ - EQUAL(M_CSTR_EQUAL), \ - CMP(strcmp), \ +#define M_CSTR_DUP_OPLIST \ + (INIT(M_INIT_DUP), \ + INIT_SET(M_INIT_SET_DUP), \ + SET(M_SET_DUP), \ + CLEAR(M_CLEAR_DUP), \ + HASH(m_core_cstr_hash), \ + EQUAL(M_CSTR_EQUAL), \ + CMP(strcmp), \ TYPE(const char*)) diff --git a/lib/toolbox/simple_array.c b/lib/toolbox/simple_array.c new file mode 100644 index 000000000000..7aed8e34be3a --- /dev/null +++ b/lib/toolbox/simple_array.c @@ -0,0 +1,127 @@ +#include "simple_array.h" + +#include + +struct SimpleArray { + const SimpleArrayConfig* config; + SimpleArrayElement* data; + uint32_t count; +}; + +SimpleArray* simple_array_alloc(const SimpleArrayConfig* config) { + SimpleArray* instance = malloc(sizeof(SimpleArray)); + instance->config = config; + return instance; +} + +void simple_array_free(SimpleArray* instance) { + furi_assert(instance); + + simple_array_reset(instance); + free(instance); +} + +void simple_array_init(SimpleArray* instance, uint32_t count) { + furi_assert(instance); + furi_assert(count > 0); + + simple_array_reset(instance); + + instance->data = malloc(count * instance->config->type_size); + instance->count = count; + + SimpleArrayInit init = instance->config->init; + if(init) { + for(uint32_t i = 0; i < instance->count; ++i) { + init(simple_array_get(instance, i)); + } + } +} + +void simple_array_reset(SimpleArray* instance) { + furi_assert(instance); + + if(instance->data) { + SimpleArrayReset reset = instance->config->reset; + + if(reset) { + for(uint32_t i = 0; i < instance->count; ++i) { + reset(simple_array_get(instance, i)); + } + } + + free(instance->data); + + instance->count = 0; + instance->data = NULL; + } +} + +void simple_array_copy(SimpleArray* instance, const SimpleArray* other) { + furi_assert(instance); + furi_assert(other); + furi_assert(instance->config == other->config); + + simple_array_reset(instance); + + if(other->count == 0) { + return; + } + + simple_array_init(instance, other->count); + + SimpleArrayCopy copy = instance->config->copy; + if(copy) { + for(uint32_t i = 0; i < other->count; ++i) { + copy(simple_array_get(instance, i), simple_array_cget(other, i)); + } + } else { + memcpy(instance->data, other->data, other->count * instance->config->type_size); + } +} + +bool simple_array_is_equal(const SimpleArray* instance, const SimpleArray* other) { + furi_assert(instance); + furi_assert(other); + + // Equal if the same object + if(instance == other) return true; + + return (instance->config == other->config) && (instance->count == other->count) && + ((instance->data == other->data) || (instance->data == NULL) || (other->data == NULL) || + (memcmp(instance->data, other->data, other->count) == 0)); +} + +uint32_t simple_array_get_count(const SimpleArray* instance) { + furi_assert(instance); + return instance->count; +} + +SimpleArrayElement* simple_array_get(SimpleArray* instance, uint32_t index) { + furi_assert(instance); + furi_assert(index < instance->count); + + return instance->data + index * instance->config->type_size; +} + +const SimpleArrayElement* simple_array_cget(const SimpleArray* instance, uint32_t index) { + return simple_array_get((SimpleArrayElement*)instance, index); +} + +SimpleArrayData* simple_array_get_data(SimpleArray* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +const SimpleArrayData* simple_array_cget_data(const SimpleArray* instance) { + return simple_array_get_data((SimpleArray*)instance); +} + +const SimpleArrayConfig simple_array_config_uint8_t = { + .init = NULL, + .copy = NULL, + .reset = NULL, + .type_size = sizeof(uint8_t), +}; diff --git a/lib/toolbox/simple_array.h b/lib/toolbox/simple_array.h new file mode 100644 index 000000000000..a608db08d876 --- /dev/null +++ b/lib/toolbox/simple_array.h @@ -0,0 +1,148 @@ +/** + * @file simple_array.h + * + * @brief This file provides a simple (non-type safe) array for elements with + * (optional) in-place construction support. + * + * No append, remove or take operations are supported. + * Instead, the user must call simple_array_init() in order to reserve the memory for n elements. + * In case if init() is specified in the configuration, then the elements are constructed in-place. + * + * To clear all elements from the array, call simple_array_reset(), which will also call reset() + * for each element in case if it was specified in the configuration. This is useful if a custom + * destructor is required for a particular type. Calling simple_array_free() will also result in a + * simple_array_reset() call automatically. + * + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SimpleArray SimpleArray; + +typedef void SimpleArrayData; +typedef void SimpleArrayElement; + +typedef void (*SimpleArrayInit)(SimpleArrayElement* elem); +typedef void (*SimpleArrayReset)(SimpleArrayElement* elem); +typedef void (*SimpleArrayCopy)(SimpleArrayElement* elem, const SimpleArrayElement* other); + +/** Simple Array configuration structure. Defined per type. */ +typedef struct { + SimpleArrayInit init; /**< Initialisation (in-place constructor) method. */ + SimpleArrayReset reset; /**< Reset (custom destructor) method. */ + SimpleArrayCopy copy; /**< Copy (custom copy-constructor) method. */ + const size_t type_size; /** Type size, in bytes. */ +} SimpleArrayConfig; + +/** + * Allocate a SimpleArray instance with the given configuration. + * + * @param [in] config Pointer to the type-specific configuration + * @return Pointer to the allocated SimpleArray instance + */ +SimpleArray* simple_array_alloc(const SimpleArrayConfig* config); + +/** + * Free a SimpleArray instance and release its contents. + * + * @param [in] instance Pointer to the SimpleArray instance to be freed + */ +void simple_array_free(SimpleArray* instance); + +/** + * Initialise a SimpleArray instance by allocating additional space to contain + * the requested number of elements. + * If init() is specified in the config, then it is called for each element, + * otherwise the data is filled with zeroes. + * + * @param [in] instance Pointer to the SimpleArray instance to be init'd + * @param [in] count Number of elements to be allocated and init'd + */ +void simple_array_init(SimpleArray* instance, uint32_t count); + +/** + * Reset a SimpleArray instance and delete all of its elements. + * If reset() is specified in the config, then it is called for each element, + * otherwise the data is simply free()'d. + * + * @param [in] instance Pointer to the SimpleArray instance to be reset + */ +void simple_array_reset(SimpleArray* instance); + +/** + * Copy (duplicate) another SimpleArray instance to this one. + * If copy() is specified in the config, then it is called for each element, + * otherwise the data is simply memcpy()'d. + * + * @param [in] instance Pointer to the SimpleArray instance to copy to + * @param [in] other Pointer to the SimpleArray instance to copy from + */ +void simple_array_copy(SimpleArray* instance, const SimpleArray* other); + +/** + * Check if another SimpleArray instance is equal (the same object or holds the + * same data) to this one. + * + * @param [in] instance Pointer to the SimpleArray instance to be compared + * @param [in] other Pointer to the SimpleArray instance to be compared + * @return True if instances are considered equal, false otherwise + */ +bool simple_array_is_equal(const SimpleArray* instance, const SimpleArray* other); + +/** + * Get the count of elements currently contained in a SimpleArray instance. + * + * @param [in] instance Pointer to the SimpleArray instance to query the count from + * @return Count of elements contained in the instance + */ +uint32_t simple_array_get_count(const SimpleArray* instance); + +/** + * Get a pointer to an element contained in a SimpleArray instance. + * + * @param [in] instance Pointer to the SimpleArray instance to get an element from + * @param [in] index Index of the element in question. MUST be less than total element count + * @return Pointer to the element specified by index + */ +SimpleArrayElement* simple_array_get(SimpleArray* instance, uint32_t index); + +/** + * Get a const pointer to an element contained in a SimpleArray instance. + * + * @param [in] instance Pointer to the SimpleArray instance to get an element from + * @param [in] index Index of the element in question. MUST be less than total element count + * @return Const pointer to the element specified by index + */ +const SimpleArrayElement* simple_array_cget(const SimpleArray* instance, uint32_t index); + +/** + * Get a pointer to the internal data of a SimpleArray instance. + * + * @param [in] instance Pointer to the SimpleArray instance to get the data of + * @return Pointer to the instance's internal data + */ +SimpleArrayData* simple_array_get_data(SimpleArray* instance); + +/** + * Get a constant pointer to the internal data of a SimpleArray instance. + * + * @param [in] instance Pointer to the SimpleArray instance to get the data of + * @return Constant pointer to the instance's internal data + */ +const SimpleArrayData* simple_array_cget_data(const SimpleArray* instance); + +// Standard preset configurations + +// Preset configuration for a byte(uint8_t) array. +extern const SimpleArrayConfig simple_array_config_uint8_t; + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/value_index.c b/lib/toolbox/value_index.c index e0745e434107..5ec0fb96287a 100644 --- a/lib/toolbox/value_index.c +++ b/lib/toolbox/value_index.c @@ -1,5 +1,18 @@ #include "value_index.h" +uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count) { + int64_t last_value = INT64_MIN; + uint8_t index = 0; + for(uint8_t i = 0; i < values_count; i++) { + if((value >= last_value) && (value <= values[i])) { + index = i; + break; + } + last_value = values[i]; + } + return index; +} + uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count) { int64_t last_value = INT64_MIN; uint8_t index = 0; diff --git a/lib/toolbox/value_index.h b/lib/toolbox/value_index.h index 9459292a75b7..5aa768e3d1d9 100644 --- a/lib/toolbox/value_index.h +++ b/lib/toolbox/value_index.h @@ -7,6 +7,19 @@ extern "C" { #endif +/** Get the index of a int32_t array element which is closest to the given value. + * + * Returned index corresponds to the first element found. + * If no suitable elements were found, the function returns 0. + * + * @param value value to be searched. + * @param values pointer to the array to perform the search in. + * @param values_count array size. + * + * @return value's index. + */ +uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count); + /** Get the index of a uint32_t array element which is closest to the given value. * * Returned index corresponds to the first element found. diff --git a/lib/toolbox/version.c b/lib/toolbox/version.c index 6ba68e364d93..876695f07852 100644 --- a/lib/toolbox/version.c +++ b/lib/toolbox/version.c @@ -5,7 +5,7 @@ #define VERSION_MAGIC (0xBE40u) #define VERSION_MAJOR (0x1u) -#define VERSION_MINOR (0x0u) +#define VERSION_MINOR (0x1u) struct Version { // Header @@ -20,6 +20,9 @@ struct Version { // Payload bits and pieces const uint8_t target; const bool build_is_dirty; + // v 1.1 + const char* firmware_origin; + const char* git_origin; }; /* version of current running firmware (bootloader/flipper) */ @@ -37,6 +40,8 @@ static const Version version = { , .target = TARGET, .build_is_dirty = BUILD_DIRTY, + .firmware_origin = FIRMWARE_ORIGIN, + .git_origin = GIT_ORIGIN, }; const Version* version_get(void) { @@ -71,3 +76,11 @@ uint8_t version_get_target(const Version* v) { bool version_get_dirty_flag(const Version* v) { return v ? v->build_is_dirty : version.build_is_dirty; } + +const char* version_get_firmware_origin(const Version* v) { + return v ? v->firmware_origin : version.firmware_origin; +} + +const char* version_get_git_origin(const Version* v) { + return v ? v->git_origin : version.git_origin; +} diff --git a/lib/toolbox/version.h b/lib/toolbox/version.h index 652ff3feace8..0c04e5c75964 100644 --- a/lib/toolbox/version.h +++ b/lib/toolbox/version.h @@ -82,6 +82,17 @@ uint8_t version_get_target(const Version* v); */ bool version_get_dirty_flag(const Version* v); +/** + * Get firmware origin. "Official" for mainline firmware, fork name for forks. + * Set by FIRMWARE_ORIGIN fbt argument. +*/ +const char* version_get_firmware_origin(const Version* v); + +/** + * Get git repo origin +*/ +const char* version_get_git_origin(const Version* v); + #ifdef __cplusplus } #endif diff --git a/lib/u8g2/u8g2_glue.c b/lib/u8g2/u8g2_glue.c index 17a702b50fc2..0142e3e2fdc7 100644 --- a/lib/u8g2/u8g2_glue.c +++ b/lib/u8g2/u8g2_glue.c @@ -2,6 +2,9 @@ #include +#define CONTRAST_ERC 31 +#define CONTRAST_MGG 31 + uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { UNUSED(u8x8); UNUSED(arg_ptr); @@ -207,6 +210,19 @@ void u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio u8x8_cad_EndTransfer(u8x8); } +void u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset) { + uint8_t contrast = (furi_hal_version_get_hw_display() == FuriHalVersionDisplayMgg) ? + CONTRAST_MGG : + CONTRAST_ERC; + contrast += contrast_offset; + contrast = contrast & 0b00111111; + + u8x8_cad_StartTransfer(u8x8); + u8x8_cad_SendCmd(u8x8, ST756X_CMD_SET_EV); + u8x8_cad_SendArg(u8x8, contrast); + u8x8_cad_EndTransfer(u8x8); +} + uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { /* call common procedure first and handle messages there */ if(u8x8_d_st756x_common(u8x8, msg, arg_int, arg_ptr) == 0) { @@ -225,7 +241,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 10 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.88 is 6 (0b110) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 31, 0b110, false); + u8x8_d_st756x_init(u8x8, CONTRAST_MGG, 0b110, false); } else { /* ERC v1(ST7565) and v2(ST7567) * EV = 33 @@ -233,7 +249,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 9.3 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.47 is 5.5 (0b101) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 32, 0b101, false); + u8x8_d_st756x_init(u8x8, CONTRAST_ERC, 0b101, false); } break; case U8X8_MSG_DISPLAY_SET_FLIP_MODE: diff --git a/lib/u8g2/u8g2_glue.h b/lib/u8g2/u8g2_glue.h index 91ba2980a5c5..af236279ecec 100644 --- a/lib/u8g2/u8g2_glue.h +++ b/lib/u8g2/u8g2_glue.h @@ -14,3 +14,5 @@ void u8g2_Setup_st756x_flipper( u8x8_msg_cb gpio_and_delay_cb); void u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias); + +void u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset); diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index 6e05b0233178..0cecfc016a38 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -20,7 +20,8 @@ static const char* update_prepare_result_descr[] = { [UpdatePrepareResultManifestInvalid] = "Invalid manifest data", [UpdatePrepareResultStageMissing] = "Missing Stage2 loader", [UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader", - [UpdatePrepareResultManifestPointerError] = "Failed to create update pointer file", + [UpdatePrepareResultManifestPointerCreateError] = "Failed to create update pointer file", + [UpdatePrepareResultManifestPointerCheckError] = "Update pointer file error (corrupted FS?)", [UpdatePrepareResultTargetMismatch] = "Hardware target mismatch", [UpdatePrepareResultOutdatedManifestVersion] = "Update package is too old", [UpdatePrepareResultIntFull] = "Need more free space in internal storage", @@ -142,8 +143,8 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { File* file = storage_file_alloc(storage); uint64_t free_int_space; - FuriString* stage_path; - stage_path = furi_string_alloc(); + FuriString* stage_path = furi_string_alloc(); + FuriString* manifest_path_check = furi_string_alloc(); do { if((storage_common_fs_info(storage, STORAGE_INT_PATH_PREFIX, NULL, &free_int_space) != FSE_OK) || @@ -188,7 +189,18 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { } if(!update_operation_persist_manifest_path(storage, manifest_file_path)) { - result = UpdatePrepareResultManifestPointerError; + result = UpdatePrepareResultManifestPointerCreateError; + break; + } + + if(!update_operation_get_current_package_manifest_path(storage, manifest_path_check) || + (furi_string_cmpi_str(manifest_path_check, manifest_file_path) != 0)) { + FURI_LOG_E( + "update", + "Manifest pointer check failed: '%s' != '%s'", + furi_string_get_cstr(manifest_path_check), + manifest_file_path); + result = UpdatePrepareResultManifestPointerCheckError; break; } @@ -197,6 +209,7 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { } while(false); furi_string_free(stage_path); + furi_string_free(manifest_path_check); storage_file_free(file); update_manifest_free(manifest); diff --git a/lib/update_util/update_operation.h b/lib/update_util/update_operation.h index 65abf8e1504d..8e36b5a1370c 100644 --- a/lib/update_util/update_operation.h +++ b/lib/update_util/update_operation.h @@ -28,7 +28,8 @@ typedef enum { UpdatePrepareResultManifestInvalid, UpdatePrepareResultStageMissing, UpdatePrepareResultStageIntegrityError, - UpdatePrepareResultManifestPointerError, + UpdatePrepareResultManifestPointerCreateError, + UpdatePrepareResultManifestPointerCheckError, UpdatePrepareResultTargetMismatch, UpdatePrepareResultOutdatedManifestVersion, UpdatePrepareResultIntFull, diff --git a/scripts/debug/flipperapps.py b/scripts/debug/flipperapps.py index 90582c1e44ad..6dba89a5640e 100644 --- a/scripts/debug/flipperapps.py +++ b/scripts/debug/flipperapps.py @@ -188,6 +188,7 @@ def attach_to_fw(self) -> None: ) self.app_type_ptr = gdb.lookup_type("FlipperApplication").pointer() self.app_list_entry_type = gdb.lookup_type("struct FlipperApplicationList_s") + self._sync_apps() def handle_stop(self, event) -> None: self._sync_apps() @@ -196,7 +197,10 @@ def handle_exit(self, event) -> None: self.set_debug_mode(False) def set_debug_mode(self, mode: bool) -> None: - gdb.execute(f"set variable furi_hal_debug_gdb_session_active = {int(mode)}") + try: + gdb.execute(f"set variable furi_hal_debug_gdb_session_active = {int(mode)}") + except gdb.error as e: + print(f"Failed to set debug mode: {e}") # Init additional 'fap-set-debug-elf-root' command and set up hooks diff --git a/scripts/debug/flipperversion.py b/scripts/debug/flipperversion.py index 4ac3bd200d1e..56915c0f28e1 100644 --- a/scripts/debug/flipperversion.py +++ b/scripts/debug/flipperversion.py @@ -23,6 +23,10 @@ class VersionData: version: str target: int build_is_dirty: bool + # Since version 1.1 + firmware_origin: str = "" + git_origin: str = "" + # More fields may be added in the future extra: Optional[Dict[str, str]] = field(default_factory=dict) @@ -52,7 +56,7 @@ def load_versioned(self, major, minor): # Struct version 1.0 extra_data = int(self.version_ptr[5].cast(self._uint_type)) - return VersionData( + version_data = VersionData( git_hash=self.version_ptr[1].cast(self._cstr_type).string(), git_branch=self.version_ptr[2].cast(self._cstr_type).string(), build_date=self.version_ptr[3].cast(self._cstr_type).string(), @@ -60,6 +64,12 @@ def load_versioned(self, major, minor): target=extra_data & 0xF, build_is_dirty=bool((extra_data >> 8) & 0xF), ) + if minor >= 1: + version_data.firmware_origin = ( + self.version_ptr[6].cast(self._cstr_type).string() + ) + version_data.git_origin = self.version_ptr[7].cast(self._cstr_type).string() + return version_data def load_unversioned(self): """Parse an early version of the version struct.""" @@ -104,6 +114,10 @@ def invoke(self, arg, from_tty): print(f"\tGit commit: {v.version.git_hash}") print(f"\tDirty: {v.version.build_is_dirty}") print(f"\tHW Target: {v.version.target}") + if v.version.firmware_origin: + print(f"\tOrigin: {v.version.firmware_origin}") + if v.version.git_origin: + print(f"\tGit origin: {v.version.git_origin}") FlipperFwVersion() diff --git a/scripts/distfap.py b/scripts/distfap.py old mode 100644 new mode 100755 index d330988b51e7..b1c5587906b7 --- a/scripts/distfap.py +++ b/scripts/distfap.py @@ -52,9 +52,7 @@ def install(self): if not self.args.launch_app: return 0 - storage.send_and_wait_eol( - f'loader open "Applications" {fap_dst_path}\r' - ) + storage.send_and_wait_eol(f"loader open {fap_dst_path}\r") if len(result := storage.read.until(storage.CLI_EOL)): self.logger.error(f"Unexpected response: {result.decode('ascii')}") diff --git a/scripts/fastfap.py b/scripts/fastfap.py new file mode 100755 index 000000000000..95e32c37be99 --- /dev/null +++ b/scripts/fastfap.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +import hashlib +import os +import struct +import subprocess +import tempfile +from collections import defaultdict +from dataclasses import dataclass + +from elftools.elf.elffile import ELFFile +from elftools.elf.relocation import RelocationSection +from elftools.elf.sections import SymbolTableSection +from fbt.sdk.hashes import gnu_sym_hash +from flipper.app import App + +VERSION = 1 + + +@dataclass +class RelData: + section: int + section_value: int + type: int + offset: int + name: str + + +@dataclass(frozen=True) +class UniqueRelData: + section: int + section_value: int + type: int + name: str + + +@dataclass +class RelSection: + name: str + oringinal_name: str + data: dict[UniqueRelData, list[int]] + + +def serialize_relsection_data(data: dict[UniqueRelData, list[int]]) -> bytes: + result = struct.pack(" 0: + result += struct.pack("> 8) & 0xFF, (offset >> 16) & 0xFF + ) + + return result + + +class Main(App): + def init(self): + self.parser.add_argument("fap_src_path", help="App file to upload") + self.parser.add_argument("objcopy_path", help="Objcopy path") + self.parser.set_defaults(func=self.process) + + def process(self): + fap_path = self.args.fap_src_path + objcopy_path = self.args.objcopy_path + + sections: list[RelSection] = [] + + with open(fap_path, "rb") as f: + elf_file = ELFFile(f) + + relocation_sections: list[RelocationSection] = [] + symtab_section: SymbolTableSection | None = None + + for section in elf_file.iter_sections(): + if isinstance(section, RelocationSection): + relocation_sections.append(section) + + if isinstance(section, SymbolTableSection): + symtab_section = section + + if not symtab_section: + self.logger.error("No symbol table found") + return 1 + + if not relocation_sections: + self.logger.info("No relocation sections found") + return 0 + + for section in relocation_sections: + section_relocations: list[RelData] = [] + + for relocation in section.iter_relocations(): + symbol_id: int = relocation.entry["r_info_sym"] + offset: int = relocation.entry["r_offset"] + type: int = relocation.entry["r_info_type"] + symbol = symtab_section.get_symbol(symbol_id) + section_index: int = symbol["st_shndx"] + section_value: int = symbol["st_value"] + if section_index == "SHN_UNDEF": + section_index = 0 + + section_relocations.append( + RelData(section_index, section_value, type, offset, symbol.name) + ) + + unique_relocations: dict[UniqueRelData, list[int]] = defaultdict(list) + for relocation in section_relocations: + unique = UniqueRelData( + relocation.section, + relocation.section_value, + relocation.type, + relocation.name, + ) + + unique_relocations[unique].append(relocation.offset) + + section_name = section.name + if section_name.startswith(".rel"): + section_name = ".fast.rel" + section_name[4:] + else: + self.logger.error( + "Unknown relocation section name: %s", section_name + ) + return 1 + + sections.append( + RelSection(section_name, section.name, unique_relocations) + ) + + with tempfile.TemporaryDirectory() as temp_dir: + for section in sections: + data = serialize_relsection_data(section.data) + hash_name = hashlib.md5(section.name.encode()).hexdigest() + filename = f"{temp_dir}/{hash_name}.bin" + + if os.path.isfile(filename): + self.logger.error(f"File {filename} already exists") + return 1 + + with open(filename, "wb") as f: + f.write(data) + + exit_code = subprocess.run( + [ + objcopy_path, + "--add-section", + f"{section.name}={filename}", + fap_path, + ], + check=True, + ) + + if exit_code.returncode != 0: + self.logger.error("objcopy failed") + return 1 + + return 0 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 5e41f4c0e305..067b4a26f09f 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -1,7 +1,8 @@ import os +import re from dataclasses import dataclass, field from enum import Enum -from typing import Callable, List, Optional, Tuple +from typing import Callable, ClassVar, List, Optional, Tuple, Union class FlipperManifestException(Exception): @@ -17,12 +18,15 @@ class FlipperAppType(Enum): SETTINGS = "Settings" STARTUP = "StartupHook" EXTERNAL = "External" + MENUEXTERNAL = "MenuExternal" METAPACKAGE = "Package" PLUGIN = "Plugin" @dataclass class FlipperApplication: + APP_ID_REGEX: ClassVar[re.Pattern] = re.compile(r"^[a-z0-9_]+$") + @dataclass class ExternallyBuiltFile: path: str @@ -56,7 +60,7 @@ class Library: # .fap-specific sources: List[str] = field(default_factory=lambda: ["*.c*"]) - fap_version: Tuple[int] = field(default_factory=lambda: (0, 1)) + fap_version: Union[str, Tuple[int]] = "0.1" fap_icon: Optional[str] = None fap_libs: List[str] = field(default_factory=list) fap_category: str = "" @@ -84,6 +88,17 @@ def is_default_deployable(self): def __post_init__(self): if self.apptype == FlipperAppType.PLUGIN: self.stack_size = 0 + if not self.APP_ID_REGEX.match(self.appid): + raise FlipperManifestException( + f"Invalid appid '{self.appid}'. Must match regex '{self.APP_ID_REGEX}'" + ) + if isinstance(self.fap_version, str): + try: + self.fap_version = tuple(int(v) for v in self.fap_version.split(".")) + except ValueError: + raise FlipperManifestException( + f"Invalid version string '{self.fap_version}'. Must be in the form 'major.minor'" + ) class AppManager: @@ -199,7 +214,7 @@ def __init__( appmgr: AppManager, appnames: List[str], hw_target: str, - message_writer: Callable = None, + message_writer: Callable | None = None, ): self.appmgr = appmgr self.appnames = set(appnames) @@ -310,7 +325,13 @@ def get_apps_cdefs(self): def get_sdk_headers(self): sdk_headers = [] for app in self.apps: - sdk_headers.extend([app._appdir.File(header) for header in app.sdk_headers]) + sdk_headers.extend( + [ + src._appdir.File(header) + for src in [app, *app._plugins] + for header in src.sdk_headers + ] + ) return sdk_headers def get_apps_of_type(self, apptype: FlipperAppType, all_known: bool = False): @@ -339,14 +360,25 @@ def get_builtin_app_folders(self): class ApplicationsCGenerator: APP_TYPE_MAP = { - FlipperAppType.SERVICE: ("FlipperApplication", "FLIPPER_SERVICES"), - FlipperAppType.SYSTEM: ("FlipperApplication", "FLIPPER_SYSTEM_APPS"), - FlipperAppType.APP: ("FlipperApplication", "FLIPPER_APPS"), - FlipperAppType.DEBUG: ("FlipperApplication", "FLIPPER_DEBUG_APPS"), - FlipperAppType.SETTINGS: ("FlipperApplication", "FLIPPER_SETTINGS_APPS"), - FlipperAppType.STARTUP: ("FlipperOnStartHook", "FLIPPER_ON_SYSTEM_START"), + FlipperAppType.SERVICE: ("FlipperInternalApplication", "FLIPPER_SERVICES"), + FlipperAppType.SYSTEM: ("FlipperInternalApplication", "FLIPPER_SYSTEM_APPS"), + FlipperAppType.APP: ("FlipperInternalApplication", "FLIPPER_APPS"), + FlipperAppType.DEBUG: ("FlipperInternalApplication", "FLIPPER_DEBUG_APPS"), + FlipperAppType.SETTINGS: ( + "FlipperInternalApplication", + "FLIPPER_SETTINGS_APPS", + ), + FlipperAppType.STARTUP: ( + "FlipperInternalOnStartHook", + "FLIPPER_ON_SYSTEM_START", + ), } + APP_EXTERNAL_TYPE = ( + "FlipperExternalApplication", + "FLIPPER_EXTERNAL_APPS", + ) + def __init__(self, buildset: AppBuildset, autorun_app: str = ""): self.buildset = buildset self.autorun = autorun_app @@ -365,7 +397,18 @@ def get_app_descr(self, app: FlipperApplication): .appid = "{app.appid}", .stack_size = {app.stack_size}, .icon = {f"&{app.icon}" if app.icon else "NULL"}, - .flags = {'|'.join(f"FlipperApplicationFlag{flag}" for flag in app.flags)} }}""" + .flags = {'|'.join(f"FlipperInternalApplicationFlag{flag}" for flag in app.flags)} }}""" + + def get_external_app_descr(self, app: FlipperApplication): + app_path = "/ext/apps" + if app.fap_category: + app_path += f"/{app.fap_category}" + app_path += f"/{app.appid}.fap" + return f""" + {{ + .name = "{app.name}", + .icon = {f"&{app.icon}" if app.icon else "NULL"}, + .path = "{app_path}" }}""" def generate(self): contents = [ @@ -394,8 +437,15 @@ def generate(self): contents.extend( [ self.get_app_ep_forward(archive_app[0]), - f"const FlipperApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};", + f"const FlipperInternalApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};", ] ) + entry_type, entry_block = self.APP_EXTERNAL_TYPE + external_apps = self.buildset.get_apps_of_type(FlipperAppType.MENUEXTERNAL) + contents.append(f"const {entry_type} {entry_block}[] = {{") + contents.append(",\n".join(map(self.get_external_app_descr, external_apps))) + contents.append("};") + contents.append(f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});") + return "\n".join(contents) diff --git a/scripts/fbt/sdk/collector.py b/scripts/fbt/sdk/collector.py index 578a8c7a62b9..1dd3bc4ebb99 100644 --- a/scripts/fbt/sdk/collector.py +++ b/scripts/fbt/sdk/collector.py @@ -1,4 +1,5 @@ from typing import List +from .hashes import gnu_sym_hash from cxxheaderparser.parser import CxxParser from . import ( @@ -72,13 +73,6 @@ def add_header(self, header: str): self.api.headers.add(ApiHeader(header)) -def gnu_sym_hash(name: str): - h = 0x1505 - for c in name: - h = (h << 5) + h + ord(c) - return str(hex(h))[-8:] - - class SdkCollector: def __init__(self): self.symbol_manager = SymbolManager() diff --git a/scripts/fbt/sdk/hashes.py b/scripts/fbt/sdk/hashes.py new file mode 100644 index 000000000000..fef88ddb5ee0 --- /dev/null +++ b/scripts/fbt/sdk/hashes.py @@ -0,0 +1,5 @@ +def gnu_sym_hash(name: str) -> int: + h = 0x1505 + for c in name: + h = ((h << 5) + h + ord(c)) & 0xFFFFFFFF + return h diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index a43d62e9dc94..e47898bd9ed0 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -132,7 +132,7 @@ def generate(env): "UsbInstall": Builder( action=[ Action( - '${PYTHON3} "${SELFUPDATE_SCRIPT}" ${UPDATE_BUNDLE_DIR}/update.fuf' + '${PYTHON3} "${SELFUPDATE_SCRIPT}" -p ${FLIP_PORT} ${UPDATE_BUNDLE_DIR}/update.fuf' ), Touch("${TARGET}"), ] diff --git a/scripts/fbt_tools/fbt_envhooks.py b/scripts/fbt_tools/fbt_envhooks.py new file mode 100644 index 000000000000..0538e173c655 --- /dev/null +++ b/scripts/fbt_tools/fbt_envhooks.py @@ -0,0 +1,67 @@ +""" + +To introduce changes to firmware build environment that are specific to your fork: + + create a file "scripts/fbt/fbt_hooks.py" + +With it, you can define functions that will be called at specific points of +firmware build configuration, with environment as an argument. + +For example, you can define a function `PreConfigureFwEnvionment(env)` that +defines that will be a part of SDK build, so applications can +use them for conditional compilation. + +Here is a list of all available hooks: + + PreConfigureFwEnvionment(env): + This function is called on firmware environment (incl. updater) + before any major configuration is done. + + PostConfigureFwEnvionment(env): + This function is called on firmware environment (incl. updater) + after all configuration is done. + + PreConfigureUfbtEnvionment(env): + This function is called on ufbt environment at the beginning of + its configuration, before dist environment is created. + + PostConfigureUfbtEnvionment(env): + This function is called on ufbt dist_env environment after all + configuration and target creation is done. +""" + + +class DefaultFbtHooks: + pass + + +try: + from fbt import fbt_hooks +except ImportError: + fbt_hooks = DefaultFbtHooks() + + +def generate(env): + stub_hook = lambda env: None + control_hooks = [ + "PreConfigureFwEnvionment", + "PostConfigureFwEnvionment", + "PreConfigureUfbtEnvionment", + "PostConfigureUfbtEnvionment", + ] + + if ( + isinstance(fbt_hooks, DefaultFbtHooks) + and env.subst("${FIRMWARE_ORIGIN}") != "Official" + ): + # If fbt_hooks.py is not present, but we are not building official firmware, + # create "scripts/fbt/fbt_hooks.py" to implement changes to firmware build environment. + pass + + for hook_name in control_hooks: + hook_fn = getattr(fbt_hooks, hook_name, stub_hook) + env.AddMethod(hook_fn, hook_name) + + +def exists(): + return True diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 1a1bad29e718..1766d4c44475 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -3,7 +3,7 @@ import pathlib import shutil from dataclasses import dataclass, field -from typing import Optional +from typing import Optional, Dict, List import SCons.Warnings from ansi.color import fg @@ -384,32 +384,42 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature): "${SOURCES} ${TARGET}" ) - actions.append( - Action( - objcopy_str, - "$APPMETAEMBED_COMSTR", + actions.extend( + ( + Action( + objcopy_str, + "$APPMETAEMBED_COMSTR", + ), + Action( + "${PYTHON3} ${FBT_SCRIPT_DIR}/fastfap.py ${TARGET} ${OBJCOPY}", + "$FASTFAP_COMSTR", + ), ) ) return Action(actions) -def AddAppLaunchTarget(env, appname, launch_target_name): - deploy_sources, flipp_dist_paths, validators = [], [], [] - run_script_extra_ars = "" +@dataclass +class AppDeploymentComponents: + deploy_sources: Dict[str, object] = field(default_factory=dict) + validators: List[object] = field(default_factory=list) + extra_launch_args: str = "" - def _add_dist_targets(app_artifacts): - validators.append(app_artifacts.validator) + def add_app(self, app_artifacts): for _, ext_path in app_artifacts.dist_entries: - deploy_sources.append(app_artifacts.compact) - flipp_dist_paths.append(f"/ext/{ext_path}") - return app_artifacts + self.deploy_sources[f"/ext/{ext_path}"] = app_artifacts.compact + self.validators.append(app_artifacts.validator) + + +def _gather_app_components(env, appname) -> AppDeploymentComponents: + components = AppDeploymentComponents() def _add_host_app_to_targets(host_app): artifacts_app_to_run = env["EXT_APPS"].get(host_app.appid, None) - _add_dist_targets(artifacts_app_to_run) + components.add_app(artifacts_app_to_run) for plugin in host_app._plugins: - _add_dist_targets(env["EXT_APPS"].get(plugin.appid, None)) + components.add_app(env["EXT_APPS"].get(plugin.appid, None)) artifacts_app_to_run = env.GetExtAppByIdOrPath(appname) if artifacts_app_to_run.app.apptype == FlipperAppType.PLUGIN: @@ -417,33 +427,45 @@ def _add_host_app_to_targets(host_app): host_app = env["APPMGR"].get(artifacts_app_to_run.app.requires[0]) if host_app: - if host_app.apptype == FlipperAppType.EXTERNAL: - _add_host_app_to_targets(host_app) + if host_app.apptype in [ + FlipperAppType.EXTERNAL, + FlipperAppType.MENUEXTERNAL, + ]: + components.add_app(host_app) else: # host app is a built-in app - run_script_extra_ars = f"-a {host_app.name}" - _add_dist_targets(artifacts_app_to_run) + components.add_app(artifacts_app_to_run) + components.extra_launch_args = f"-a {host_app.name}" else: raise UserError("Host app is unknown") else: _add_host_app_to_targets(artifacts_app_to_run.app) + return components + - # print(deploy_sources, flipp_dist_paths) - env.PhonyTarget( +def AddAppLaunchTarget(env, appname, launch_target_name): + components = _gather_app_components(env, appname) + target = env.PhonyTarget( launch_target_name, - '${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', - source=deploy_sources, - FLIPPER_FILE_TARGETS=flipp_dist_paths, - EXTRA_ARGS=run_script_extra_ars, + '${PYTHON3} "${APP_RUN_SCRIPT}" -p ${FLIP_PORT} ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', + source=components.deploy_sources.values(), + FLIPPER_FILE_TARGETS=components.deploy_sources.keys(), + EXTRA_ARGS=components.extra_launch_args, ) - env.Alias(launch_target_name, validators) + env.Alias(launch_target_name, components.validators) + return target + + +def AddAppBuildTarget(env, appname, build_target_name): + components = _gather_app_components(env, appname) + env.Alias(build_target_name, components.validators) + env.Alias(build_target_name, components.deploy_sources.values()) def generate(env, **kw): env.SetDefault( EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}", APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", - STORAGE_SCRIPT="${FBT_SCRIPT_DIR}/storage.py", ) if not env["VERBOSE"]: env.SetDefault( @@ -451,6 +473,7 @@ def generate(env, **kw): APPMETA_COMSTR="\tAPPMETA\t${TARGET}", APPFILE_COMSTR="\tAPPFILE\t${TARGET}", APPMETAEMBED_COMSTR="\tFAP\t${TARGET}", + FASTFAP_COMSTR="\tFASTFAP\t${TARGET}", APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", ) @@ -465,6 +488,7 @@ def generate(env, **kw): env.AddMethod(BuildAppElf) env.AddMethod(GetExtAppByIdOrPath) env.AddMethod(AddAppLaunchTarget) + env.AddMethod(AddAppBuildTarget) env.Append( BUILDERS={ diff --git a/scripts/fbt_tools/fbt_help.py b/scripts/fbt_tools/fbt_help.py index c7452af98840..68fc2aaf9ff5 100644 --- a/scripts/fbt_tools/fbt_help.py +++ b/scripts/fbt_tools/fbt_help.py @@ -4,15 +4,16 @@ tail_help = """ TASKS: -Building: +Firmware & apps: firmware_all, fw_dist: Build firmware; create distribution package faps, fap_dist: Build all FAP apps - fap_{APPID}, launch_app APPSRC={APPID}: + fap_{APPID}, build APPSRC={APPID}; launch APPSRC={APPID}: Build FAP app with appid={APPID}; upload & start it over USB fap_deploy: Build and upload all FAP apps over USB + Flashing & debugging: flash, flash_blackmagic, jflash: diff --git a/scripts/fbt_tools/fbt_version.py b/scripts/fbt_tools/fbt_version.py index 8469e181a32a..aead13b29fa5 100644 --- a/scripts/fbt_tools/fbt_version.py +++ b/scripts/fbt_tools/fbt_version.py @@ -19,7 +19,9 @@ def generate(env): BUILDERS={ "VersionBuilder": Builder( action=Action( - '${PYTHON3} "${VERSION_SCRIPT}" generate -t ${TARGET_HW} -o ${TARGET.dir.posix} --dir "${ROOT_DIR}"', + '${PYTHON3} "${VERSION_SCRIPT}" generate ' + "-t ${TARGET_HW} -fw-origin ${FIRMWARE_ORIGIN} " + '-o ${TARGET.dir.posix} --dir "${ROOT_DIR}"', "${VERSIONCOMSTR}", ), emitter=version_emitter, diff --git a/scripts/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index f176e3b2e82c..25c072899ea8 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -58,14 +58,23 @@ def loadCubeInfo(self, cube_dir, reference_cube_version): def _getFileName(self, name): return posixpath.join(self.COPRO_TAR_DIR, name) + def _addFileReadPermission(self, tarinfo): + tarinfo.mode = 0o644 + return tarinfo + def addFile(self, array, filename, **kwargs): source_file = os.path.join(self.mcu_copro, filename) - self.output_tar.add(source_file, arcname=self._getFileName(filename)) + self.output_tar.add( + source_file, + arcname=self._getFileName(filename), + filter=self._addFileReadPermission, + ) array.append({"name": filename, "sha256": file_sha256(source_file), **kwargs}) def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None): self.output_tar = tarfile.open(output_file, "w:gz", format=tarfile.USTAR_FORMAT) fw_directory = tarfile.TarInfo(self.COPRO_TAR_DIR) + fw_directory.mode = 0o755 fw_directory.type = tarfile.DIRTYPE self.output_tar.addfile(fw_directory) diff --git a/scripts/flipper/assets/coprobin.py b/scripts/flipper/assets/coprobin.py index 75bf76d766dd..84f52fbb3eb5 100644 --- a/scripts/flipper/assets/coprobin.py +++ b/scripts/flipper/assets/coprobin.py @@ -46,7 +46,10 @@ class CoproFooterBase: _SIG_BIN_COMMON_SIZE = 2 * 4 def get_version(self): - return f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" + return ( + f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, " + f"branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" + ) def get_details(self): raise CoproException("Not implemented") diff --git a/scripts/flipper/assets/dolphin.py b/scripts/flipper/assets/dolphin.py index ebe9fd889866..b4a53a62df5f 100644 --- a/scripts/flipper/assets/dolphin.py +++ b/scripts/flipper/assets/dolphin.py @@ -49,11 +49,11 @@ def __init__( def load(self, animation_directory: str): if not os.path.isdir(animation_directory): - raise Exception(f"Animation folder doesn't exists: { animation_directory }") + raise Exception(f"Animation folder doesn't exist: { animation_directory }") meta_filename = os.path.join(animation_directory, "meta.txt") if not os.path.isfile(meta_filename): - raise Exception(f"Animation meta file doesn't exists: { meta_filename }") + raise Exception(f"Animation meta file doesn't exist: { meta_filename }") self.logger.info(f"Loading meta from {meta_filename}") file = FlipperFormatFile() diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index f4d622bfe427..2c9c043d5a85 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -257,12 +257,12 @@ def send_file(self, filename_from: str, filename_to: str): self.read.until(self.CLI_PROMPT) ftell = file.tell() - percent = str(math.ceil(ftell / filesize * 100)) - total_chunks = str(math.ceil(filesize / buffer_size)) - current_chunk = str(math.ceil(ftell / buffer_size)) + percent = math.ceil(ftell / filesize * 100) + total_chunks = math.ceil(filesize / buffer_size) + current_chunk = math.ceil(ftell / buffer_size) approx_speed = ftell / (time.time() - start_time + 0.0001) sys.stdout.write( - f"\r{percent}%, chunk {current_chunk} of {total_chunks} @ {approx_speed/1024:.2f} kb/s" + f"\r<{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s" ) sys.stdout.flush() print() @@ -270,6 +270,7 @@ def send_file(self, filename_from: str, filename_to: str): def read_file(self, filename: str): """Receive file from Flipper, and get filedata (bytes)""" buffer_size = self.chunk_size + start_time = time.time() self.send_and_wait_eol( 'storage read_chunks "' + filename + '" ' + str(buffer_size) + "\r" ) @@ -290,10 +291,13 @@ def read_file(self, filename: str): filedata.extend(self.port.read(chunk_size)) read_size = read_size + chunk_size - percent = str(math.ceil(read_size / size * 100)) - total_chunks = str(math.ceil(size / buffer_size)) - current_chunk = str(math.ceil(read_size / buffer_size)) - sys.stdout.write(f"\r{percent}%, chunk {current_chunk} of {total_chunks}") + percent = math.ceil(read_size / size * 100) + total_chunks = math.ceil(size / buffer_size) + current_chunk = math.ceil(read_size / buffer_size) + approx_speed = read_size / (time.time() - start_time + 0.0001) + sys.stdout.write( + f"\r>{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s" + ) sys.stdout.flush() print() self.read.until(self.CLI_PROMPT) diff --git a/scripts/flipper/utils/cdc.py b/scripts/flipper/utils/cdc.py index 7c7351670291..9564088598e0 100644 --- a/scripts/flipper/utils/cdc.py +++ b/scripts/flipper/utils/cdc.py @@ -6,7 +6,7 @@ def resolve_port(logger, portname: str = "auto"): if portname != "auto": return portname # Try guessing - flippers = list(list_ports.grep("flip")) + flippers = list(list_ports.grep("flip_")) if len(flippers) == 1: flipper = flippers[0] logger.info(f"Using {flipper.serial_number} on {flipper.device}") diff --git a/scripts/fwsize.py b/scripts/fwsize.py old mode 100644 new mode 100755 diff --git a/scripts/get_env.py b/scripts/get_env.py old mode 100644 new mode 100755 diff --git a/scripts/lint.py b/scripts/lint.py index 8530209bec08..1fc327e545ad 100755 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -39,6 +39,9 @@ def _filter_lint_directories(dirnames: list[str]): # Skipping 3rd-party code - usually resides in subfolder "lib" if "lib" in dirnames: dirnames.remove("lib") + if "compiled" in dirnames: + dirnames.remove("compiled") + # Skipping hidden folders for dirname in dirnames.copy(): if dirname.startswith("."): diff --git a/scripts/map_mariadb_insert.py b/scripts/map_mariadb_insert.py new file mode 100755 index 000000000000..a4c9ed5c788d --- /dev/null +++ b/scripts/map_mariadb_insert.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 + +# Requiremets: +# mariadb==1.1.6 + +from datetime import datetime +import argparse +import mariadb +import sys +import os + + +def parseArgs(): + parser = argparse.ArgumentParser() + parser.add_argument("db_user", help="MariaDB user") + parser.add_argument("db_pass", help="MariaDB password") + parser.add_argument("db_host", help="MariaDB hostname") + parser.add_argument("db_port", type=int, help="MariaDB port") + parser.add_argument("db_name", help="MariaDB database") + parser.add_argument("report_file", help="Report file(.map.all)") + args = parser.parse_args() + return args + + +def mariadbConnect(args): + try: + conn = mariadb.connect( + user=args.db_user, + password=args.db_pass, + host=args.db_host, + port=args.db_port, + database=args.db_name, + ) + except mariadb.Error as e: + print(f"Error connecting to MariaDB: {e}") + sys.exit(1) + return conn + + +def parseEnv(): + outArr = [] + outArr.append(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + outArr.append(os.getenv("COMMIT_HASH", default=None)) + outArr.append(os.getenv("COMMIT_MSG", default=None)) + outArr.append(os.getenv("BRANCH_NAME", default=None)) + outArr.append(os.getenv("BSS_SIZE", default=None)) + outArr.append(os.getenv("TEXT_SIZE", default=None)) + outArr.append(os.getenv("RODATA_SIZE", default=None)) + outArr.append(os.getenv("DATA_SIZE", default=None)) + outArr.append(os.getenv("FREE_FLASH_SIZE", default=None)) + outArr.append(os.getenv("PULL_ID", default=None)) + outArr.append(os.getenv("PULL_NAME", default=None)) + return outArr + + +def createTables(cur, conn): + headerTable = "CREATE TABLE IF NOT EXISTS `header` ( \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `datetime` datetime NOT NULL, \ + `commit` varchar(40) NOT NULL, \ + `commit_msg` text NOT NULL, \ + `branch_name` text NOT NULL, \ + `bss_size` int(10) unsigned NOT NULL, \ + `text_size` int(10) unsigned NOT NULL, \ + `rodata_size` int(10) unsigned NOT NULL, \ + `data_size` int(10) unsigned NOT NULL, \ + `free_flash_size` int(10) unsigned NOT NULL, \ + `pullrequest_id` int(10) unsigned DEFAULT NULL, \ + `pullrequest_name` text DEFAULT NULL, \ + PRIMARY KEY (`id`), \ + KEY `header_id_index` (`id`) )" + dataTable = "CREATE TABLE IF NOT EXISTS `data` ( \ + `header_id` int(10) unsigned NOT NULL, \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `section` text NOT NULL, \ + `address` text NOT NULL, \ + `size` int(10) unsigned NOT NULL, \ + `name` text NOT NULL, \ + `lib` text NOT NULL, \ + `obj_name` text NOT NULL, \ + PRIMARY KEY (`id`), \ + KEY `data_id_index` (`id`), \ + KEY `data_header_id_index` (`header_id`), \ + CONSTRAINT `data_header_id_foreign` FOREIGN KEY (`header_id`) REFERENCES `header` (`id`) )" + cur.execute(headerTable) + cur.execute(dataTable) + conn.commit() + + +def insertHeader(data, cur, conn): + query = "INSERT INTO `header` ( \ + datetime, commit, commit_msg, branch_name, bss_size, text_size, \ + rodata_size, data_size, free_flash_size, pullrequest_id, pullrequest_name) \ + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + cur.execute(query, data) + conn.commit() + return cur.lastrowid + + +def parseFile(fileObj, headerID): + arr = [] + fileLines = fileObj.readlines() + for line in fileLines: + lineArr = [] + tempLineArr = line.split("\t") + lineArr.append(headerID) + lineArr.append(tempLineArr[0]) # section + lineArr.append(int(tempLineArr[2], 16)) # address hex + lineArr.append(int(tempLineArr[3])) # size + lineArr.append(tempLineArr[4]) # name + lineArr.append(tempLineArr[5]) # lib + lineArr.append(tempLineArr[6]) # obj_name + arr.append(tuple(lineArr)) + return arr + + +def insertData(data, cur, conn): + query = "INSERT INTO `data` ( \ + header_id, section, address, size, \ + name, lib, obj_name) \ + VALUES (?, ?, ?, ?, ? ,?, ?)" + cur.executemany(query, data) + conn.commit() + + +def main(): + args = parseArgs() + dbConn = mariadbConnect(args) + reportFile = open(args.report_file) + dbCurs = dbConn.cursor() + createTables(dbCurs, dbConn) + headerID = insertHeader(parseEnv(), dbCurs, dbConn) + insertData(parseFile(reportFile, headerID), dbCurs, dbConn) + reportFile.close() + dbCurs.close() + + +if __name__ == "__main__": + main() diff --git a/scripts/map_parser.py b/scripts/map_parser.py new file mode 100755 index 000000000000..1efc4fe82f3c --- /dev/null +++ b/scripts/map_parser.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python3 + +# Requiremets: +# cxxfilt==0.3.0 + +# Most part of this code written by Lars-Dominik Braun https://github.com/PromyLOPh/linkermapviz +# and distributes under MIT licence + +# Copyright (c) 2017 Lars-Dominik Braun +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import sys +import re +import os +from typing import TextIO +from cxxfilt import demangle + + +class Objectfile: + def __init__(self, section: str, offset: int, size: int, comment: str): + self.section = section.strip() + self.offset = offset + self.size = size + self.path = (None, None) + self.basepath = None + + if comment: + self.path = re.match(r"^(.+?)(?:\(([^\)]+)\))?$", comment).groups() + self.basepath = os.path.basename(self.path[0]) + + self.children = [] + + def __repr__(self) -> str: + return f"" + + +def update_children_size(children: list[list], subsection_size: int) -> list: + # set subsection size to an only child + if len(children) == 1: + children[0][1] = subsection_size + return children + + rest_size = subsection_size + + for index in range(1, len(children)): + if rest_size > 0: + # current size = current address - previous child address + child_size = children[index][0] - children[index - 1][0] + rest_size -= child_size + children[index - 1][1] = child_size + + # if there is rest size, set it to the last child element + if rest_size > 0: + children[-1][1] = rest_size + + return children + + +def parse_sections(file_name: str) -> list: + """ + Quick&Dirty parsing for GNU ld’s linker map output, needs LANG=C, because + some messages are localized. + """ + + sections = [] + with open(file_name, "r") as file: + # skip until memory map is found + found = False + + while True: + line = file.readline() + if not line: + break + if line.strip() == "Memory Configuration": + found = True + break + + if not found: + raise Exception(f"Memory configuration is not found in the{input_file}") + + # long section names result in a linebreak afterwards + sectionre = re.compile( + "(?P
.+?|.{14,}\n)[ ]+0x(?P[0-9a-f]+)[ ]+0x(?P[0-9a-f]+)(?:[ ]+(?P.+))?\n+", + re.I, + ) + subsectionre = re.compile( + "[ ]{16}0x(?P[0-9a-f]+)[ ]+(?P.+)\n+", re.I + ) + s = file.read() + pos = 0 + + while True: + m = sectionre.match(s, pos) + if not m: + # skip that line + try: + nextpos = s.index("\n", pos) + 1 + pos = nextpos + continue + except ValueError: + break + + pos = m.end() + section = m.group("section") + v = m.group("offset") + offset = int(v, 16) if v is not None else None + v = m.group("size") + size = int(v, 16) if v is not None else None + comment = m.group("comment") + + if section != "*default*" and size > 0: + of = Objectfile(section, offset, size, comment) + + if section.startswith(" "): + children = [] + sections[-1].children.append(of) + + while True: + m = subsectionre.match(s, pos) + if not m: + break + pos = m.end() + offset, function = m.groups() + offset = int(offset, 16) + if sections and sections[-1].children: + children.append([offset, 0, function]) + + if children: + children = update_children_size( + children=children, subsection_size=of.size + ) + + sections[-1].children[-1].children.extend(children) + + else: + sections.append(of) + + return sections + + +def get_subsection_name(section_name: str, subsection: Objectfile) -> str: + subsection_split_names = subsection.section.split(".") + if subsection.section.startswith("."): + subsection_split_names = subsection_split_names[1:] + + return ( + f".{subsection_split_names[1]}" + if len(subsection_split_names) > 2 + else section_name + ) + + +def write_subsection( + section_name: str, + subsection_name: str, + address: str, + size: int, + demangled_name: str, + module_name: str, + file_name: str, + mangled_name: str, + write_file_object: TextIO, +) -> None: + write_file_object.write( + f"{section_name}\t" + f"{subsection_name}\t" + f"{address}\t" + f"{size}\t" + f"{demangled_name}\t" + f"{module_name}\t" + f"{file_name}\t" + f"{mangled_name}\n" + ) + + +def save_subsection( + section_name: str, subsection: Objectfile, write_file_object: TextIO +) -> None: + subsection_name = get_subsection_name(section_name, subsection) + module_name = subsection.path[0] + file_name = subsection.path[1] + + if not file_name: + file_name, module_name = module_name, "" + + if not subsection.children: + address = f"{subsection.offset:x}" + size = subsection.size + mangled_name = ( + "" + if subsection.section == section_name + else subsection.section.split(".")[-1] + ) + demangled_name = demangle(mangled_name) if mangled_name else mangled_name + + write_subsection( + section_name=section_name, + subsection_name=subsection_name, + address=address, + size=size, + demangled_name=demangled_name, + module_name=module_name, + file_name=file_name, + mangled_name=mangled_name, + write_file_object=write_file_object, + ) + return + + for subsection_child in subsection.children: + address = f"{subsection_child[0]:x}" + size = subsection_child[1] + mangled_name = subsection_child[2] + demangled_name = demangle(mangled_name) + + write_subsection( + section_name=section_name, + subsection_name=subsection_name, + address=address, + size=size, + demangled_name=demangled_name, + module_name=module_name, + file_name=file_name, + mangled_name=mangled_name, + write_file_object=write_file_object, + ) + + +def save_section(section: Objectfile, write_file_object: TextIO) -> None: + section_name = section.section + for subsection in section.children: + save_subsection( + section_name=section_name, + subsection=subsection, + write_file_object=write_file_object, + ) + + +def save_parsed_data(parsed_data: list[Objectfile], output_file_name: str) -> None: + with open(output_file_name, "w") as write_file_object: + for section in parsed_data: + if section.children: + save_section(section=section, write_file_object=write_file_object) + + +if __name__ == "__main__": + if len(sys.argv) < 3: + raise Exception(f"Usage: {sys.argv[0]} ") + + input_file = sys.argv[1] + output_file = sys.argv[2] + + parsed_sections = parse_sections(input_file) + + if parsed_sections is None: + raise Exception(f"Memory configuration is not {input_file}") + + save_parsed_data(parsed_sections, output_file) diff --git a/scripts/runfap.py b/scripts/runfap.py old mode 100644 new mode 100755 index a240acf1212f..42141acff656 --- a/scripts/runfap.py +++ b/scripts/runfap.py @@ -63,7 +63,7 @@ def install(self): storage_ops.recursive_send(fap_dst_path, fap_local_path, False) fap_host_app = self.args.targets[0] - startup_command = f'"Applications" {fap_host_app}' + startup_command = f"{fap_host_app}" if self.args.host_app: startup_command = self.args.host_app diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py old mode 100644 new mode 100755 index d2d1d2f49c95..2cf43dce02a4 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -84,17 +84,6 @@ def copy_single_project(self, project: ProjectDir) -> None: if exists(sdk_folder := join(obj_directory, foldertype)): self.note_dist_component(foldertype, "dir", sdk_folder) - # TODO: remove this after everyone migrates to new uFBT - self.create_zip_stub("lib") - - def create_zip_stub(self, foldertype): - with zipfile.ZipFile( - self.get_dist_path(self.get_dist_file_name(foldertype, "zip")), - "w", - zipfile.ZIP_DEFLATED, - ) as _: - pass - def copy(self) -> int: self._dist_components: dict[str, str] = dict() self.projects: dict[str, ProjectDir] = dict( @@ -271,7 +260,15 @@ def bundle_update_package(self): self.note_dist_component( "update", "tgz", self.get_dist_path(bundle_tgz) ) - tar.add(bundle_dir, arcname=bundle_dir_name) + + # Strip uid and gid in case of overflow + def tar_filter(tarinfo): + tarinfo.uid = tarinfo.gid = 0 + tarinfo.mtime = 0 + tarinfo.uname = tarinfo.gname = "furippa" + return tarinfo + + tar.add(bundle_dir, arcname=bundle_dir_name, filter=tar_filter) return bundle_result diff --git a/scripts/selfupdate.py b/scripts/selfupdate.py old mode 100644 new mode 100755 diff --git a/scripts/serial_cli.py b/scripts/serial_cli.py index 2fa37d7512a6..8e35d57facdd 100644 --- a/scripts/serial_cli.py +++ b/scripts/serial_cli.py @@ -1,3 +1,4 @@ +import argparse import logging import os import subprocess @@ -8,8 +9,11 @@ def main(): logger = logging.getLogger() - if not (port := resolve_port(logger, "auto")): - logger.error("Is Flipper connected over USB and is it not in DFU mode?") + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", help="CDC Port", default="auto") + args = parser.parse_args() + if not (port := resolve_port(logger, args.port)): + logger.error("Is Flipper connected via USB and not in DFU mode?") return 1 subprocess.call( [ diff --git a/scripts/slideshow.py b/scripts/slideshow.py old mode 100644 new mode 100755 diff --git a/scripts/testing/await_flipper.py b/scripts/testing/await_flipper.py index 2b4c8b4c39bc..ea07d6be7ff3 100755 --- a/scripts/testing/await_flipper.py +++ b/scripts/testing/await_flipper.py @@ -8,6 +8,7 @@ def flp_serial_by_name(flp_name): if sys.platform == "darwin": # MacOS flp_serial = "/dev/cu.usbmodemflip_" + flp_name + "1" + logging.info(f"Darwin, looking for {flp_serial}") elif sys.platform == "linux": # Linux flp_serial = ( "/dev/serial/by-id/usb-Flipper_Devices_Inc._Flipper_" @@ -16,10 +17,12 @@ def flp_serial_by_name(flp_name): + flp_name + "-if00" ) + logging.info(f"linux, looking for {flp_serial}") if os.path.exists(flp_serial): return flp_serial else: + logging.info(f"Couldn't find {logging.info} on this attempt.") if os.path.exists(flp_name): return flp_name else: @@ -38,7 +41,7 @@ def main(): level=logging.INFO, datefmt="%Y-%m-%d %H:%M:%S", ) - logging.info("Waiting for Flipper to be ready...") + logging.info(f"Waiting for Flipper {flipper_name} to be ready...") while flipper == "" and elapsed < UPDATE_TIMEOUT: elapsed += 1 diff --git a/scripts/testing/units.py b/scripts/testing/units.py index 5083bcd4350b..fd8e29a73336 100755 --- a/scripts/testing/units.py +++ b/scripts/testing/units.py @@ -20,13 +20,13 @@ def main(): logging.error("Flipper not found!") sys.exit(1) - with serial.Serial(flp_serial, timeout=1) as flipper: + with serial.Serial(flp_serial, timeout=10) as flipper: logging.info(f"Found Flipper at {flp_serial}") flipper.baudrate = 230400 flipper.flushOutput() flipper.flushInput() - flipper.timeout = 180 + flipper.timeout = 300 flipper.read_until(b">: ").decode("utf-8") flipper.write(b"unit_tests\r") diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 9d45b7e9d7ec..51708b8c4882 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=21" +set "FLIPPER_TOOLCHAIN_VERSION=23" if ["%FBT_TOOLCHAIN_PATH%"] == [""] ( set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 143dce74b914..85d139040b1f 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -4,7 +4,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"23"}"; if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then FBT_TOOLCHAIN_PATH_WAS_SET=0; diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 4dd1fb5b9044..9a9e0938c1b8 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -75,7 +75,7 @@ from fbt.util import ( wrap_tempfile, path_as_posix, ) -from fbt.appmanifest import FlipperAppType +from fbt.appmanifest import FlipperAppType, FlipperApplication from fbt.sdk.cache import SdkCache # Base environment with all tools loaded from SDK @@ -98,6 +98,7 @@ env = core_env.Clone( "fbt_apps", "fbt_extapps", "fbt_assets", + "fbt_envhooks", ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}), ], FBT_FAP_DEBUG_ELF_ROOT=ufbt_build_dir, @@ -117,6 +118,8 @@ env = core_env.Clone( wrap_tempfile(env, "LINKCOM") wrap_tempfile(env, "ARCOM") +env.PreConfigureUfbtEnvionment() + # print(env.Dump()) # Dist env @@ -259,6 +262,7 @@ apps_artifacts = appenv["EXT_APPS"] apps_to_build_as_faps = [ FlipperAppType.PLUGIN, FlipperAppType.EXTERNAL, + FlipperAppType.MENUEXTERNAL, ] known_extapps = [ @@ -332,14 +336,16 @@ def ambiguous_app_call(**kw): if app_to_launch: appenv.AddAppLaunchTarget(app_to_launch, "launch") + appenv.AddAppBuildTarget(app_to_launch, "build") else: dist_env.PhonyTarget("launch", Action(ambiguous_app_call, None)) + dist_env.PhonyTarget("build", Action(ambiguous_app_call, None)) # cli handler appenv.PhonyTarget( "cli", - '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py"', + '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}', ) # Linter @@ -407,6 +413,12 @@ dist_env.Alias("vscode_dist", vscode_dist) # Creating app from base template dist_env.SetDefault(FBT_APPID=appenv.subst("$APPID") or "template") +if fbt_appid := dist_env.subst("$FBT_APPID"): + if not FlipperApplication.APP_ID_REGEX.match(fbt_appid): + raise UserError( + f"Invalid app id '{fbt_appid}'. App id must match {FlipperApplication.APP_ID_REGEX.pattern}" + ) + app_template_dir = project_template_dir.Dir("app_template") app_template_dist = [] for template_file in app_template_dir.glob("*"): @@ -460,7 +472,7 @@ if dolphin_src_dir.exists(): ) dist_env.PhonyTarget( "dolphin_ext", - '${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py send "${SOURCE}" /ext/dolphin', + '${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send "${SOURCE}" /ext/dolphin', source=ufbt_build_dir.Dir("dolphin"), ) else: @@ -474,3 +486,5 @@ dist_env.PhonyTarget( "env", "@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)", ) + +dist_env.PostConfigureUfbtEnvionment() diff --git a/scripts/ufbt/commandline.scons b/scripts/ufbt/commandline.scons index a9b91bbca970..349b4ef2524b 100644 --- a/scripts/ufbt/commandline.scons +++ b/scripts/ufbt/commandline.scons @@ -71,6 +71,11 @@ vars.AddVariables( validator=PathVariable.PathIsDir, default="", ), + ( + "FLIP_PORT", + "CDC Port of Flipper to use, if multiple are connected", + "auto", + ), ) Return("vars") diff --git a/scripts/ufbt/project_template/app_template/application.fam b/scripts/ufbt/project_template/app_template/application.fam index 37a4ce665522..a2d23ef46605 100644 --- a/scripts/ufbt/project_template/app_template/application.fam +++ b/scripts/ufbt/project_template/app_template/application.fam @@ -8,7 +8,7 @@ App( stack_size=2 * 1024, fap_category="Examples", # Optional values - # fap_version=(0, 1), # (major, minor) + # fap_version="0.1", fap_icon="@FBT_APPID@.png", # 10x10 1-bit PNG # fap_description="A simple app", # fap_author="J. Doe", diff --git a/scripts/update.py b/scripts/update.py index 0f3ee6ea8b9d..9f0d95d94eea 100755 --- a/scripts/update.py +++ b/scripts/update.py @@ -211,6 +211,9 @@ def _tar_filter(self, tarinfo: tarfile.TarInfo): f"Cannot package resource: name '{tarinfo.name}' too long" ) raise ValueError("Resource name too long") + tarinfo.gid = tarinfo.uid = 0 + tarinfo.mtime = 0 + tarinfo.uname = tarinfo.gname = "furippa" return tarinfo def package_resources(self, srcdir: str, dst_name: str): diff --git a/scripts/version.py b/scripts/version.py old mode 100644 new mode 100755 index 3d68b2e98d41..e68f7b41d7c6 --- a/scripts/version.py +++ b/scripts/version.py @@ -40,13 +40,39 @@ def get_version_info(self): except subprocess.CalledProcessError: version = "unknown" + if "SOURCE_DATE_EPOCH" in os.environ: + commit_date = datetime.utcfromtimestamp( + int(os.environ["SOURCE_DATE_EPOCH"]) + ) + else: + commit_date = datetime.strptime( + self._exec_git("log -1 --format=%cd").strip(), + "%a %b %d %H:%M:%S %Y %z", + ) + return { "GIT_COMMIT": commit, "GIT_BRANCH": branch, "VERSION": version, "BUILD_DIRTY": dirty and 1 or 0, + "GIT_ORIGIN": ",".join(self._get_git_origins()), + "GIT_COMMIT_DATE": commit_date, } + def _get_git_origins(self): + try: + remotes = self._exec_git("remote -v") + except subprocess.CalledProcessError: + return set() + origins = set() + for line in remotes.split("\n"): + if not line: + continue + _, destination = line.split("\t") + url, _ = destination.split(" ") + origins.add(url) + return origins + def _exec_git(self, args): cmd = ["git"] cmd.extend(args.split(" ")) @@ -74,24 +100,35 @@ def init(self): help="hardware target", required=True, ) + self.parser_generate.add_argument( + "-fw-origin", + dest="firmware_origin", + type=str, + help="firmware origin", + required=True, + ) self.parser_generate.add_argument("--dir", dest="sourcedir", required=True) self.parser_generate.set_defaults(func=self.generate) def generate(self): current_info = GitVersion(self.args.sourcedir).get_version_info() - if "SOURCE_DATE_EPOCH" in os.environ: - build_date = datetime.utcfromtimestamp(int(os.environ["SOURCE_DATE_EPOCH"])) - else: - build_date = date.today() + build_date = ( + date.today() + if current_info["BUILD_DIRTY"] + else current_info["GIT_COMMIT_DATE"] + ) current_info.update( { "BUILD_DATE": build_date.strftime("%d-%m-%Y"), "TARGET": self.args.target, + "FIRMWARE_ORIGIN": self.args.firmware_origin, } ) + del current_info["GIT_COMMIT_DATE"] + version_values = [] for key in current_info: val = current_info[key] diff --git a/scripts/wifi_board.py b/scripts/wifi_board.py new file mode 100755 index 000000000000..3f89ebdc6565 --- /dev/null +++ b/scripts/wifi_board.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 + +from flipper.app import App +from serial.tools.list_ports_common import ListPortInfo + +import logging +import os +import tempfile +import subprocess +import serial.tools.list_ports as list_ports +import json +import requests +import tarfile + + +class UpdateDownloader: + UPDATE_SERVER = "https://update.flipperzero.one" + UPDATE_PROJECT = "/blackmagic-firmware" + UPDATE_INDEX = UPDATE_SERVER + UPDATE_PROJECT + "/directory.json" + UPDATE_TYPE = "full_tgz" + + CHANNEL_ID_ALIAS = { + "dev": "development", + "rc": "release-candidate", + "r": "release", + "rel": "release", + } + + def __init__(self): + self.logger = logging.getLogger() + + def download(self, channel_id: str, dir: str) -> bool: + # Aliases + if channel_id in self.CHANNEL_ID_ALIAS: + channel_id = self.CHANNEL_ID_ALIAS[channel_id] + + # Make directory + if not os.path.exists(dir): + self.logger.info(f"Creating directory {dir}") + os.makedirs(dir) + + # Download json index + self.logger.info(f"Downloading {self.UPDATE_INDEX}") + response = requests.get(self.UPDATE_INDEX) + if response.status_code != 200: + self.logger.error(f"Failed to download {self.UPDATE_INDEX}") + return False + + # Parse json index + try: + index = json.loads(response.content) + except Exception as e: + self.logger.error(f"Failed to parse json index: {e}") + return False + + # Find channel + channel = None + for channel_candidate in index["channels"]: + if channel_candidate["id"] == channel_id: + channel = channel_candidate + break + + # Check if channel found + if channel is None: + self.logger.error( + f"Channel '{channel_id}' not found. Valid channels: {', '.join([c['id'] for c in index['channels']])}" + ) + return False + + self.logger.info(f"Using channel '{channel_id}'") + + # Get latest version + try: + version = channel["versions"][0] + except Exception as e: + self.logger.error(f"Failed to get version: {e}") + return False + + self.logger.info(f"Using version '{version['version']}'") + + # Get changelog + changelog = None + try: + changelog = version["changelog"] + except Exception as e: + self.logger.error(f"Failed to get changelog: {e}") + + # print changelog + if changelog is not None: + self.logger.info(f"Changelog:") + for line in changelog.split("\n"): + if line.strip() == "": + continue + self.logger.info(f" {line}") + + # Find file + file_url = None + for file_candidate in version["files"]: + if file_candidate["type"] == self.UPDATE_TYPE: + file_url = file_candidate["url"] + break + + if file_url is None: + self.logger.error(f"File not found") + return False + + # Make file path + file_name = file_url.split("/")[-1] + file_path = os.path.join(dir, file_name) + + # Download file + self.logger.info(f"Downloading {file_url} to {file_path}") + with open(file_path, "wb") as f: + response = requests.get(file_url) + f.write(response.content) + + # Unzip tgz + self.logger.info(f"Unzipping {file_path}") + with tarfile.open(file_path, "r") as tar: + tar.extractall(dir) + + return True + + +class Main(App): + def init(self): + self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") + self.parser.add_argument( + "-c", "--channel", help="Channel name", default="release" + ) + self.parser.set_defaults(func=self.update) + + # logging + self.logger = logging.getLogger() + + def find_wifi_board(self) -> bool: + # idk why, but python thinks that list_ports.grep returns tuple[str, str, str] + blackmagics: list[ListPortInfo] = list(list_ports.grep("blackmagic")) # type: ignore + daps: list[ListPortInfo] = list(list_ports.grep("CMSIS-DAP")) # type: ignore + + return len(blackmagics) > 0 or len(daps) > 0 + + def find_wifi_board_bootloader(self): + # idk why, but python thinks that list_ports.grep returns tuple[str, str, str] + ports: list[ListPortInfo] = list(list_ports.grep("ESP32-S2")) # type: ignore + + if len(ports) == 0: + # Blackmagic probe serial port not found, will be handled later + pass + elif len(ports) > 1: + raise Exception("More than one WiFi board found") + else: + port = ports[0] + if os.name == "nt": + port.device = f"\\\\.\\{port.device}" + return port.device + + def update(self): + try: + port = self.find_wifi_board_bootloader() + except Exception as e: + self.logger.error(f"{e}") + return 1 + + if self.args.port != "auto": + port = self.args.port + + available_ports = [p[0] for p in list(list_ports.comports())] + if port not in available_ports: + self.logger.error(f"Port {port} not found") + return 1 + + if port is None: + if self.find_wifi_board(): + self.logger.error("WiFi board found, but not in bootloader mode.") + self.logger.info("Please hold down BOOT button and press RESET button") + else: + self.logger.error("WiFi board not found") + self.logger.info( + "Please connect WiFi board to your computer, hold down BOOT button and press RESET button" + ) + return 1 + + # get temporary dir + with tempfile.TemporaryDirectory() as temp_dir: + downloader = UpdateDownloader() + + # download latest channel update + try: + if not downloader.download(self.args.channel, temp_dir): + self.logger.error(f"Cannot download update") + return 1 + except Exception as e: + self.logger.error(f"Cannot download update: {e}") + return 1 + + with open(os.path.join(temp_dir, "flash.command"), "r") as f: + flash_command = f.read() + + flash_command = flash_command.replace("\n", "").replace("\r", "") + flash_command = flash_command.replace("(PORT)", port) + + # We can't reset the board after flashing via usb + flash_command = flash_command.replace( + "--after hard_reset", "--after no_reset_stub" + ) + + args = flash_command.split(" ")[0:] + args = list(filter(None, args)) + + esptool_params = [] + esptool_params.extend(args) + + self.logger.info(f'Running command: "{" ".join(args)}" in "{temp_dir}"') + + process = subprocess.Popen( + esptool_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=temp_dir, + bufsize=1, + universal_newlines=True, + ) + + while process.poll() is None: + if process.stdout is not None: + for line in process.stdout: + self.logger.debug(f"{line.strip()}") + + if process.returncode != 0: + self.logger.error(f"Failed to flash WiFi board") + else: + self.logger.info("WiFi board flashed successfully") + self.logger.info("Press RESET button on WiFi board to start it") + + return process.returncode + + +if __name__ == "__main__": + Main()() diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 2e9486627388..776977cda74b 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -228,6 +228,7 @@ vars.AddVariables( ("applications/debug", False), ("applications/external", False), ("applications/examples", False), + ("applications/drivers", False), ("applications_user", False), ], ), @@ -236,6 +237,18 @@ vars.AddVariables( help="Don't open browser after generating error repots", default=False, ), + ( + "FIRMWARE_ORIGIN", + "Firmware origin. 'Official' if follows upstream's API structure, otherwise fork name. " + " This will also create a C define FW_ORIGIN_ so that " + " app can check what version it is being built for.", + "Official", + ), + ( + "FLIP_PORT", + "Full port name of Flipper to use, if multiple Flippers are connected", + "auto", + ), ) Return("vars") diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 6db0e538dfd4..5c6f18d6884e 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -67,6 +67,7 @@ class FlipperExtAppBuildArtifacts: apps_to_build_as_faps = [ FlipperAppType.PLUGIN, FlipperAppType.EXTERNAL, + FlipperAppType.MENUEXTERNAL, FlipperAppType.DEBUG, ] @@ -113,8 +114,20 @@ extapps.resources_dist = appenv.FapDist(appenv["RESOURCES_ROOT"], []) if appsrc := appenv.subst("$APPSRC"): - appenv.AddAppLaunchTarget(appsrc, "launch_app") + launch_target = appenv.AddAppLaunchTarget(appsrc, "launch") + Alias("launch_app", launch_target) + appenv.PhonyTarget( + "launch_app", + Action( + lambda **kw: warn( + WarningOnByDefault, + "The 'launch_app' target is deprecated. Use 'launch' instead.", + ), + None, + ), + ) + appenv.AddAppBuildTarget(appsrc, "build") # SDK management