Merge branch 'release-candidate' into release
This commit is contained in:
		
						commit
						303c266eba
					
				
							
								
								
									
										9
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							| @ -18,7 +18,7 @@ | ||||
| /applications/main/gpio/ @skotopes @DrZlo13 @hedger @nminaylov | ||||
| /applications/main/ibutton/ @skotopes @DrZlo13 @hedger @gsurkov | ||||
| /applications/main/infrared/ @skotopes @DrZlo13 @hedger @gsurkov | ||||
| /applications/main/nfc/ @skotopes @DrZlo13 @hedger @gornekich | ||||
| /applications/main/nfc/ @skotopes @DrZlo13 @hedger @gornekich @Astrrra | ||||
| /applications/main/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm | ||||
| /applications/main/u2f/ @skotopes @DrZlo13 @hedger @nminaylov | ||||
| 
 | ||||
| @ -40,6 +40,8 @@ | ||||
| 
 | ||||
| /applications/system/storage_move_to_sd/ @skotopes @DrZlo13 @hedger @nminaylov | ||||
| 
 | ||||
| /applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @nminaylov @gornekich @Astrrra @gsurkov @Skorpionm | ||||
| 
 | ||||
| # Documentation | ||||
| /documentation/ @skotopes @DrZlo13 @hedger @drunkbatya | ||||
| /scripts/toolchain/ @skotopes @DrZlo13 @hedger @drunkbatya | ||||
| @ -54,6 +56,9 @@ | ||||
| /lib/mbedtls/ @skotopes @DrZlo13 @hedger @nminaylov | ||||
| /lib/micro-ecc/ @skotopes @DrZlo13 @hedger @nminaylov | ||||
| /lib/nanopb/ @skotopes @DrZlo13 @hedger @nminaylov | ||||
| /lib/nfc/ @skotopes @DrZlo13 @hedger @gornekich | ||||
| /lib/nfc/ @skotopes @DrZlo13 @hedger @gornekich @Astrrra | ||||
| /lib/one_wire/ @skotopes @DrZlo13 @hedger @gsurkov | ||||
| /lib/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm | ||||
| 
 | ||||
| # CI/CD | ||||
| /.github/workflows/ @skotopes @DrZlo13 @hedger @drunkbatya | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/amap_analyse.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/amap_analyse.yml
									
									
									
									
										vendored
									
									
								
							| @ -91,7 +91,7 @@ jobs: | ||||
|           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 | ||||
|           python3 -m pip install mariadb==1.1.4 | ||||
|           python3 scripts/amap_mariadb_insert.py \ | ||||
|             ${{ secrets.AMAP_MARIADB_USER }} \ | ||||
|             ${{ secrets.AMAP_MARIADB_PASSWORD }} \ | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/pvs_studio.yml
									
									
									
									
										vendored
									
									
								
							| @ -57,7 +57,7 @@ jobs: | ||||
| 
 | ||||
|       - name: 'Generate compile_comands.json' | ||||
|         run: | | ||||
|           FBT_TOOLCHAIN_PATH=/runner/_work ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking | ||||
|           FBT_TOOLCHAIN_PATH=/runner/_work ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons | ||||
| 
 | ||||
|       - name: 'Static code analysis' | ||||
|         run: | | ||||
|  | ||||
							
								
								
									
										16
									
								
								.github/workflows/unit_tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/unit_tests.yml
									
									
									
									
										vendored
									
									
								
							| @ -8,7 +8,7 @@ env: | ||||
|   DEFAULT_TARGET: f7 | ||||
| 
 | ||||
| jobs: | ||||
|   main: | ||||
|   run_units_on_test_bench: | ||||
|     runs-on: [self-hosted, FlipperZeroTest] | ||||
|     steps: | ||||
|       - name: Checkout code | ||||
| @ -22,31 +22,35 @@ jobs: | ||||
|         run: | | ||||
|           echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT | ||||
| 
 | ||||
|       - name: 'Compile unit tests firmware' | ||||
|         id: compile | ||||
|       - name: 'Flash unit tests firmware' | ||||
|         id: flashing | ||||
|         run: | | ||||
|           FBT_TOOLCHAIN_PATH=/opt ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 | ||||
| 
 | ||||
|       - name: 'Wait for flipper to finish updating' | ||||
|         id: connect | ||||
|         if: steps.compile.outcome == 'success' | ||||
|         if: steps.flashing.outcome == 'success' | ||||
|         run: | | ||||
|           python3 ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} | ||||
|           . scripts/toolchain/fbtenv.sh | ||||
|           ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} | ||||
| 
 | ||||
|       - name: 'Format flipper SD card' | ||||
|         id: format | ||||
|         if: steps.connect.outcome == 'success' | ||||
|         run: | | ||||
|           . scripts/toolchain/fbtenv.sh | ||||
|           ./scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext | ||||
| 
 | ||||
|       - name: 'Copy assets and unit tests data to flipper' | ||||
|         id: copy | ||||
|         if: steps.format.outcome == 'success' | ||||
|         run: | | ||||
|           . scripts/toolchain/fbtenv.sh | ||||
|           ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/resources /ext | ||||
|           ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/unit_tests /ext/unit_tests | ||||
| 
 | ||||
|       - name: 'Run units and validate results' | ||||
|         if: steps.copy.outcome == 'success' | ||||
|         run: | | ||||
|           python3 ./scripts/testing/units.py ${{steps.device.outputs.flipper}} | ||||
|           . scripts/toolchain/fbtenv.sh | ||||
|           ./scripts/testing/units.py ${{steps.device.outputs.flipper}} | ||||
|  | ||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -54,3 +54,5 @@ openocd.log | ||||
| # PVS Studio temporary files | ||||
| .PVS-Studio/ | ||||
| PVS-Studio.log | ||||
| 
 | ||||
| .gdbinit | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| //-V:BPTREE_DEF2:779,1086,557,773,512 | ||||
| //-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685 | ||||
| //-V:ALGO_DEF:1048,747,1044 | ||||
| //-V:TUPLE_DEF2:524,590,1001,760 | ||||
| 
 | ||||
| # Non-severe malloc/null pointer deref warnings | ||||
| //-V::522:2,3 | ||||
|  | ||||
							
								
								
									
										4
									
								
								.vscode/example/launch.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.vscode/example/launch.json
									
									
									
									
										vendored
									
									
								
							| @ -38,6 +38,7 @@ | ||||
|             "postAttachCommands": [ | ||||
|                 // "compare-sections", | ||||
|                 "source debug/flipperapps.py", | ||||
|                 "fap-set-debug-elf-root build/latest/.extapps", | ||||
|                 // "source debug/FreeRTOS/FreeRTOS.py", | ||||
|                 // "svd_load debug/STM32WB55_CM4.svd" | ||||
|             ] | ||||
| @ -59,6 +60,7 @@ | ||||
|                 "set confirm off", | ||||
|                 "set mem inaccessible-by-default off", | ||||
|                 "source debug/flipperapps.py", | ||||
|                 "fap-set-debug-elf-root build/latest/.extapps", | ||||
|                 // "compare-sections", | ||||
|             ] | ||||
|             // "showDevDebugOutput": "raw", | ||||
| @ -76,6 +78,7 @@ | ||||
|             "rtos": "FreeRTOS", | ||||
|             "postAttachCommands": [ | ||||
|                 "source debug/flipperapps.py", | ||||
|                 "fap-set-debug-elf-root build/latest/.extapps", | ||||
|             ] | ||||
|             // "showDevDebugOutput": "raw", | ||||
|         }, | ||||
| @ -95,6 +98,7 @@ | ||||
|             ], | ||||
|             "postAttachCommands": [ | ||||
|                 "source debug/flipperapps.py", | ||||
|                 "fap-set-debug-elf-root build/latest/.extapps", | ||||
|             ], | ||||
|             // "showDevDebugOutput": "raw", | ||||
|         }, | ||||
|  | ||||
							
								
								
									
										45
									
								
								SConstruct
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								SConstruct
									
									
									
									
									
								
							| @ -7,6 +7,7 @@ | ||||
| # construction of certain targets behind command-line options. | ||||
| 
 | ||||
| import os | ||||
| from fbt.util import path_as_posix | ||||
| 
 | ||||
| DefaultEnvironment(tools=[]) | ||||
| 
 | ||||
| @ -33,10 +34,6 @@ coreenv = SConscript( | ||||
| ) | ||||
| SConscript("site_scons/cc.scons", exports={"ENV": coreenv}) | ||||
| 
 | ||||
| # Store root dir in environment for certain tools | ||||
| coreenv["ROOT_DIR"] = Dir(".") | ||||
| 
 | ||||
| 
 | ||||
| # Create a separate "dist" environment and add construction envs to it | ||||
| distenv = coreenv.Clone( | ||||
|     tools=[ | ||||
| @ -47,6 +44,7 @@ distenv = coreenv.Clone( | ||||
|         "jflash", | ||||
|     ], | ||||
|     ENV=os.environ, | ||||
|     UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}", | ||||
| ) | ||||
| 
 | ||||
| firmware_env = distenv.AddFwProject( | ||||
| @ -144,21 +142,28 @@ distenv.Default(basic_dist) | ||||
| dist_dir = distenv.GetProjetDirName() | ||||
| fap_dist = [ | ||||
|     distenv.Install( | ||||
|         f"#/dist/{dist_dir}/apps/debug_elf", | ||||
|         firmware_env["FW_EXTAPPS"]["debug"].values(), | ||||
|         distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"), | ||||
|         list( | ||||
|             app_artifact.debug | ||||
|             for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() | ||||
|         ), | ||||
|     *( | ||||
|         distenv.Install(f"#/dist/{dist_dir}/apps/{dist_entry[0]}", dist_entry[1]) | ||||
|         for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values() | ||||
|     ), | ||||
|     distenv.Install( | ||||
|         f"#/dist/{dist_dir}/apps", | ||||
|         "#/assets/resources/apps", | ||||
|     ), | ||||
| ] | ||||
| Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values()) | ||||
| Depends( | ||||
|     fap_dist, | ||||
|     list( | ||||
|         app_artifact.validator | ||||
|         for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() | ||||
|     ), | ||||
| ) | ||||
| Alias("fap_dist", fap_dist) | ||||
| # distenv.Default(fap_dist) | ||||
| 
 | ||||
| distenv.Depends( | ||||
|     firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"]["resources_dist"] | ||||
| ) | ||||
| distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist) | ||||
| 
 | ||||
| 
 | ||||
| # Target for bundling core2 package for qFlipper | ||||
| @ -196,6 +201,7 @@ firmware_debug = distenv.PhonyTarget( | ||||
|     source=firmware_env["FW_ELF"], | ||||
|     GDBOPTS="${GDBOPTS_BASE}", | ||||
|     GDBREMOTE="${OPENOCD_GDB_PIPE}", | ||||
|     FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), | ||||
| ) | ||||
| distenv.Depends(firmware_debug, firmware_flash) | ||||
| 
 | ||||
| @ -205,6 +211,7 @@ distenv.PhonyTarget( | ||||
|     source=firmware_env["FW_ELF"], | ||||
|     GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", | ||||
|     GDBREMOTE="${BLACKMAGIC_ADDR}", | ||||
|     FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), | ||||
| ) | ||||
| 
 | ||||
| # Debug alien elf | ||||
| @ -213,7 +220,7 @@ distenv.PhonyTarget( | ||||
|     "${GDBPYCOM}", | ||||
|     GDBOPTS="${GDBOPTS_BASE}", | ||||
|     GDBREMOTE="${OPENOCD_GDB_PIPE}", | ||||
|     GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ', | ||||
|     GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ', | ||||
| ) | ||||
| 
 | ||||
| distenv.PhonyTarget( | ||||
| @ -233,14 +240,14 @@ distenv.PhonyTarget( | ||||
| # Linter | ||||
| distenv.PhonyTarget( | ||||
|     "lint", | ||||
|     "${PYTHON3} scripts/lint.py check ${LINT_SOURCES}", | ||||
|     LINT_SOURCES=firmware_env["LINT_SOURCES"], | ||||
|     "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}", | ||||
|     LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], | ||||
| ) | ||||
| 
 | ||||
| distenv.PhonyTarget( | ||||
|     "format", | ||||
|     "${PYTHON3} scripts/lint.py format ${LINT_SOURCES}", | ||||
|     LINT_SOURCES=firmware_env["LINT_SOURCES"], | ||||
|     "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}", | ||||
|     LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], | ||||
| ) | ||||
| 
 | ||||
| # PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests | ||||
| @ -280,7 +287,7 @@ distenv.PhonyTarget( | ||||
| ) | ||||
| 
 | ||||
| # Start Flipper CLI via PySerial's miniterm | ||||
| distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py") | ||||
| distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py") | ||||
| 
 | ||||
| 
 | ||||
| # Find blackmagic probe | ||||
|  | ||||
| @ -424,6 +424,7 @@ MU_TEST(infrared_test_decoder_mixed) { | ||||
|     infrared_test_run_decoder(InfraredProtocolRC5, 5); | ||||
|     infrared_test_run_decoder(InfraredProtocolSamsung32, 1); | ||||
|     infrared_test_run_decoder(InfraredProtocolSIRC, 3); | ||||
|     infrared_test_run_decoder(InfraredProtocolKaseikyo, 1); | ||||
| } | ||||
| 
 | ||||
| MU_TEST(infrared_test_decoder_nec) { | ||||
| @ -489,6 +490,15 @@ MU_TEST(infrared_test_encoder_rc6) { | ||||
|     infrared_test_run_encoder(InfraredProtocolRC6, 1); | ||||
| } | ||||
| 
 | ||||
| MU_TEST(infrared_test_decoder_kaseikyo) { | ||||
|     infrared_test_run_decoder(InfraredProtocolKaseikyo, 1); | ||||
|     infrared_test_run_decoder(InfraredProtocolKaseikyo, 2); | ||||
|     infrared_test_run_decoder(InfraredProtocolKaseikyo, 3); | ||||
|     infrared_test_run_decoder(InfraredProtocolKaseikyo, 4); | ||||
|     infrared_test_run_decoder(InfraredProtocolKaseikyo, 5); | ||||
|     infrared_test_run_decoder(InfraredProtocolKaseikyo, 6); | ||||
| } | ||||
| 
 | ||||
| MU_TEST(infrared_test_encoder_decoder_all) { | ||||
|     infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1); | ||||
|     infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1); | ||||
| @ -498,6 +508,7 @@ MU_TEST(infrared_test_encoder_decoder_all) { | ||||
|     infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1); | ||||
|     infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1); | ||||
|     infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1); | ||||
|     infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1); | ||||
| } | ||||
| 
 | ||||
| MU_TEST_SUITE(infrared_test) { | ||||
| @ -515,6 +526,7 @@ MU_TEST_SUITE(infrared_test) { | ||||
|     MU_RUN_TEST(infrared_test_decoder_nec); | ||||
|     MU_RUN_TEST(infrared_test_decoder_samsung32); | ||||
|     MU_RUN_TEST(infrared_test_decoder_necext1); | ||||
|     MU_RUN_TEST(infrared_test_decoder_kaseikyo); | ||||
|     MU_RUN_TEST(infrared_test_decoder_mixed); | ||||
|     MU_RUN_TEST(infrared_test_encoder_decoder_all); | ||||
| } | ||||
|  | ||||
| @ -5,6 +5,8 @@ | ||||
| #include <lib/nfc/protocols/nfca.h> | ||||
| #include <lib/nfc/helpers/mf_classic_dict.h> | ||||
| #include <lib/digital_signal/digital_signal.h> | ||||
| #include <lib/nfc/nfc_device.h> | ||||
| #include <applications/main/nfc/helpers/nfc_generators.h> | ||||
| 
 | ||||
| #include <lib/flipper_format/flipper_format_i.h> | ||||
| #include <lib/toolbox/stream/file_stream.h> | ||||
| @ -17,6 +19,7 @@ | ||||
| #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; | ||||
| @ -287,9 +290,203 @@ MU_TEST(mf_classic_dict_load_test) { | ||||
|     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] + 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"); | ||||
|     for(uint8_t i = uid_len; 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_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_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); | ||||
|  | ||||
| @ -133,7 +133,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { | ||||
|         case ArchiveBrowserEventFileMenuRename: | ||||
|             if(favorites) { | ||||
|                 browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); | ||||
|             } else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) { | ||||
|             } else if(selected->is_app == false) { | ||||
|                 archive_show_file_menu(browser, false); | ||||
|                 scene_manager_set_scene_state( | ||||
|                     archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); | ||||
|  | ||||
| @ -57,9 +57,11 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { | ||||
|             ArchiveFile_t* file = archive_get_current_file(archive->browser); | ||||
| 
 | ||||
|             FuriString* path_dst; | ||||
| 
 | ||||
|             path_dst = furi_string_alloc(); | ||||
|             path_extract_dirname(path_src, path_dst); | ||||
|             furi_string_cat_printf(path_dst, "/%s%s", archive->text_store, known_ext[file->type]); | ||||
|             furi_string_cat_printf( | ||||
|                 path_dst, "/%s%s", archive->text_store, archive->file_extension); | ||||
| 
 | ||||
|             storage_common_rename(fs_api, path_src, furi_string_get_cstr(path_dst)); | ||||
|             furi_record_close(RECORD_STORAGE); | ||||
|  | ||||
| @ -65,7 +65,6 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { | ||||
|     if(!archive_is_known_app(selected->type)) { | ||||
|         furi_string_set(menu[0], "---"); | ||||
|         furi_string_set(menu[1], "---"); | ||||
|         furi_string_set(menu[2], "---"); | ||||
|     } else { | ||||
|         if(model->tab_idx == ArchiveTabFavorites) { | ||||
|             furi_string_set(menu[2], "Move"); | ||||
|  | ||||
| @ -11,4 +11,5 @@ App( | ||||
|     stack_size=2 * 1024, | ||||
|     icon="A_BadUsb_14", | ||||
|     order=70, | ||||
|     fap_libs=["assets"], | ||||
| ) | ||||
|  | ||||
| @ -82,7 +82,7 @@ static const DuckyKey ducky_keys[] = { | ||||
|     {"PAGEUP", HID_KEYBOARD_PAGE_UP}, | ||||
|     {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, | ||||
|     {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, | ||||
|     {"SCROLLOCK", HID_KEYBOARD_SCROLL_LOCK}, | ||||
|     {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK}, | ||||
|     {"SPACE", HID_KEYBOARD_SPACEBAR}, | ||||
|     {"TAB", HID_KEYBOARD_TAB}, | ||||
|     {"MENU", HID_KEYBOARD_APPLICATION}, | ||||
| @ -237,12 +237,8 @@ static int32_t | ||||
|     const char* line_tmp = furi_string_get_cstr(line); | ||||
|     bool state = false; | ||||
| 
 | ||||
|     for(uint32_t i = 0; i < line_len; i++) { | ||||
|         if((line_tmp[i] != ' ') && (line_tmp[i] != '\t') && (line_tmp[i] != '\n')) { | ||||
|             line_tmp = &line_tmp[i]; | ||||
|             break; // Skip spaces and tabs
 | ||||
|         } | ||||
|         if(i == line_len - 1) return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
 | ||||
|     if(line_len == 0) { | ||||
|         return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
 | ||||
|     } | ||||
| 
 | ||||
|     FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); | ||||
| @ -338,10 +334,6 @@ static int32_t | ||||
|         furi_hal_hid_kb_release(key); | ||||
|         return (0); | ||||
|     } | ||||
|     if(error != NULL) { | ||||
|         strncpy(error, "Unknown error", error_len); | ||||
|     } | ||||
|     return SCRIPT_STATE_ERROR; | ||||
| } | ||||
| 
 | ||||
| static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) { | ||||
| @ -454,10 +446,12 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil | ||||
|                 bad_usb->st.line_cur++; | ||||
|                 bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1); | ||||
|                 bad_usb->buf_start = i + 1; | ||||
|                 furi_string_trim(bad_usb->line); | ||||
|                 delay_val = ducky_parse_line( | ||||
|                     bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error)); | ||||
| 
 | ||||
|                 if(delay_val < 0) { | ||||
|                 if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
 | ||||
|                     return 0; | ||||
|                 } else if(delay_val < 0) { | ||||
|                     bad_usb->st.error_line = bad_usb->st.line_cur; | ||||
|                     FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur); | ||||
|                     return SCRIPT_STATE_ERROR; | ||||
| @ -524,12 +518,16 @@ static int32_t bad_usb_worker(void* context) { | ||||
| 
 | ||||
|         } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected
 | ||||
|             uint32_t flags = furi_thread_flags_wait( | ||||
|                 WorkerEvtEnd | WorkerEvtConnect, FuriFlagWaitAny, FuriWaitForever); | ||||
|                 WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, | ||||
|                 FuriFlagWaitAny, | ||||
|                 FuriWaitForever); | ||||
|             furi_check((flags & FuriFlagError) == 0); | ||||
|             if(flags & WorkerEvtEnd) { | ||||
|                 break; | ||||
|             } else if(flags & WorkerEvtConnect) { | ||||
|                 worker_state = BadUsbStateIdle; // Ready to run
 | ||||
|             } else if(flags & WorkerEvtToggle) { | ||||
|                 worker_state = BadUsbStateWillRun; // Will run when USB is connected
 | ||||
|             } | ||||
|             bad_usb->st.state = worker_state; | ||||
| 
 | ||||
| @ -556,6 +554,31 @@ static int32_t bad_usb_worker(void* context) { | ||||
|             } | ||||
|             bad_usb->st.state = worker_state; | ||||
| 
 | ||||
|         } else if(worker_state == BadUsbStateWillRun) { // State: start on connection
 | ||||
|             uint32_t flags = furi_thread_flags_wait( | ||||
|                 WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, | ||||
|                 FuriFlagWaitAny, | ||||
|                 FuriWaitForever); | ||||
|             furi_check((flags & FuriFlagError) == 0); | ||||
|             if(flags & WorkerEvtEnd) { | ||||
|                 break; | ||||
|             } else if(flags & WorkerEvtConnect) { // Start executing script
 | ||||
|                 DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); | ||||
|                 delay_val = 0; | ||||
|                 bad_usb->buf_len = 0; | ||||
|                 bad_usb->st.line_cur = 0; | ||||
|                 bad_usb->defdelay = 0; | ||||
|                 bad_usb->repeat_cnt = 0; | ||||
|                 bad_usb->file_end = false; | ||||
|                 storage_file_seek(script_file, 0, true); | ||||
|                 // extra time for PC to recognize Flipper as keyboard
 | ||||
|                 furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); | ||||
|                 worker_state = BadUsbStateRunning; | ||||
|             } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution
 | ||||
|                 worker_state = BadUsbStateNotConnected; | ||||
|             } | ||||
|             bad_usb->st.state = worker_state; | ||||
| 
 | ||||
|         } else if(worker_state == BadUsbStateRunning) { // State: running
 | ||||
|             uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); | ||||
|             uint32_t flags = furi_thread_flags_wait( | ||||
| @ -627,7 +650,7 @@ static int32_t bad_usb_worker(void* context) { | ||||
| BadUsbScript* bad_usb_script_open(FuriString* file_path) { | ||||
|     furi_assert(file_path); | ||||
| 
 | ||||
|     BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); | ||||
|     BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); //-V773
 | ||||
|     bad_usb->file_path = furi_string_alloc(); | ||||
|     furi_string_set(bad_usb->file_path, file_path); | ||||
| 
 | ||||
|  | ||||
| @ -12,6 +12,7 @@ typedef enum { | ||||
|     BadUsbStateInit, | ||||
|     BadUsbStateNotConnected, | ||||
|     BadUsbStateIdle, | ||||
|     BadUsbStateWillRun, | ||||
|     BadUsbStateRunning, | ||||
|     BadUsbStateDelay, | ||||
|     BadUsbStateDone, | ||||
|  | ||||
| @ -29,10 +29,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { | ||||
| 
 | ||||
|     canvas_draw_icon(canvas, 22, 20, &I_UsbTree_48x22); | ||||
| 
 | ||||
|     if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) { | ||||
|     if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || | ||||
|        (model->state.state == BadUsbStateNotConnected)) { | ||||
|         elements_button_center(canvas, "Run"); | ||||
|     } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) { | ||||
|         elements_button_center(canvas, "Stop"); | ||||
|     } else if(model->state.state == BadUsbStateWillRun) { | ||||
|         elements_button_center(canvas, "Cancel"); | ||||
|     } | ||||
| 
 | ||||
|     if(model->state.state == BadUsbStateNotConnected) { | ||||
| @ -40,6 +43,11 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|         canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Connect"); | ||||
|         canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "to USB"); | ||||
|     } else if(model->state.state == BadUsbStateWillRun) { | ||||
|         canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|         canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Will run"); | ||||
|         canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "on connect"); | ||||
|     } else if(model->state.state == BadUsbStateFileError) { | ||||
|         canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|  | ||||
| @ -155,7 +155,7 @@ static bool fap_loader_select_app(FapLoader* loader) { | ||||
| } | ||||
| 
 | ||||
| static FapLoader* fap_loader_alloc(const char* path) { | ||||
|     FapLoader* loader = malloc(sizeof(FapLoader)); | ||||
|     FapLoader* loader = malloc(sizeof(FapLoader)); //-V773
 | ||||
|     loader->fap_path = furi_string_alloc_set(path); | ||||
|     loader->storage = furi_record_open(RECORD_STORAGE); | ||||
|     loader->dialogs = furi_record_open(RECORD_DIALOGS); | ||||
|  | ||||
| @ -8,4 +8,5 @@ App( | ||||
|     stack_size=1 * 1024, | ||||
|     icon="A_GPIO_14", | ||||
|     order=50, | ||||
|     fap_libs=["assets"], | ||||
| ) | ||||
|  | ||||
| @ -184,7 +184,7 @@ static int32_t usb_uart_worker(void* context) { | ||||
|     while(1) { | ||||
|         uint32_t events = | ||||
|             furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); | ||||
|         furi_check((events & FuriFlagError) == 0); | ||||
|         furi_check(!(events & FuriFlagError)); | ||||
|         if(events & WorkerEvtStop) break; | ||||
|         if(events & WorkerEvtRxDone) { | ||||
|             size_t len = furi_stream_buffer_receive( | ||||
| @ -288,7 +288,7 @@ static int32_t usb_uart_tx_thread(void* context) { | ||||
|     while(1) { | ||||
|         uint32_t events = | ||||
|             furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever); | ||||
|         furi_check((events & FuriFlagError) == 0); | ||||
|         furi_check(!(events & FuriFlagError)); | ||||
|         if(events & WorkerEvtTxStop) break; | ||||
|         if(events & WorkerEvtCdcRx) { | ||||
|             furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk); | ||||
|  | ||||
| @ -12,6 +12,7 @@ App( | ||||
|     icon="A_iButton_14", | ||||
|     stack_size=2 * 1024, | ||||
|     order=60, | ||||
|     fap_libs=["assets"], | ||||
| ) | ||||
| 
 | ||||
| App( | ||||
|  | ||||
| @ -39,10 +39,5 @@ void ibutton_scene_delete_success_on_exit(void* context) { | ||||
|     iButton* ibutton = context; | ||||
|     Popup* popup = ibutton->popup; | ||||
| 
 | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
| 
 | ||||
|     popup_disable_timeout(popup); | ||||
|     popup_set_context(popup, NULL); | ||||
|     popup_set_callback(popup, NULL); | ||||
|     popup_reset(popup); | ||||
| } | ||||
|  | ||||
| @ -39,10 +39,5 @@ void ibutton_scene_save_success_on_exit(void* context) { | ||||
|     iButton* ibutton = context; | ||||
|     Popup* popup = ibutton->popup; | ||||
| 
 | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
| 
 | ||||
|     popup_disable_timeout(popup); | ||||
|     popup_set_context(popup, NULL); | ||||
|     popup_set_callback(popup, NULL); | ||||
|     popup_reset(popup); | ||||
| } | ||||
|  | ||||
| @ -43,10 +43,5 @@ void ibutton_scene_write_success_on_exit(void* context) { | ||||
|     iButton* ibutton = context; | ||||
|     Popup* popup = ibutton->popup; | ||||
| 
 | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
| 
 | ||||
|     popup_disable_timeout(popup); | ||||
|     popup_set_context(popup, NULL); | ||||
|     popup_set_callback(popup, NULL); | ||||
|     popup_reset(popup); | ||||
| } | ||||
|  | ||||
| @ -12,6 +12,7 @@ App( | ||||
|     icon="A_Infrared_14", | ||||
|     stack_size=3 * 1024, | ||||
|     order=40, | ||||
|     fap_libs=["assets"], | ||||
| ) | ||||
| 
 | ||||
| App( | ||||
|  | ||||
| @ -14,6 +14,7 @@ App( | ||||
|     icon="A_125khz_14", | ||||
|     stack_size=2 * 1024, | ||||
|     order=20, | ||||
|     fap_libs=["assets"], | ||||
| ) | ||||
| 
 | ||||
| App( | ||||
|  | ||||
| @ -32,7 +32,7 @@ static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { | ||||
| } | ||||
| 
 | ||||
| static LfRfid* lfrfid_alloc() { | ||||
|     LfRfid* lfrfid = malloc(sizeof(LfRfid)); | ||||
|     LfRfid* lfrfid = malloc(sizeof(LfRfid)); //-V773
 | ||||
| 
 | ||||
|     lfrfid->storage = furi_record_open(RECORD_STORAGE); | ||||
|     lfrfid->dialogs = furi_record_open(RECORD_DIALOGS); | ||||
|  | ||||
| @ -314,7 +314,7 @@ static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) { | ||||
|     mful->version.storage_size = 0x15; | ||||
| } | ||||
| 
 | ||||
| static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { | ||||
| void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { | ||||
|     nfc_generate_common_start(data); | ||||
|     nfc_generate_mf_classic_common(data, uid_len, type); | ||||
| 
 | ||||
| @ -337,6 +337,9 @@ static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClas | ||||
|             } | ||||
|             mf_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) { | ||||
| @ -347,6 +350,8 @@ static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClas | ||||
|             } | ||||
|             mf_classic_set_block_read(mfc, i, &mfc->block[i]); | ||||
|         } | ||||
|         // Set SAK to 08
 | ||||
|         data->nfc_data.sak = 0x08; | ||||
|     } | ||||
| 
 | ||||
|     mfc->type = type; | ||||
|  | ||||
| @ -11,3 +11,5 @@ struct NfcGenerator { | ||||
| }; | ||||
| 
 | ||||
| extern const NfcGenerator* const nfc_generators[]; | ||||
| 
 | ||||
| void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type); | ||||
|  | ||||
| @ -116,7 +116,9 @@ void nfc_free(Nfc* nfc) { | ||||
|         // Stop worker
 | ||||
|         nfc_worker_stop(nfc->worker); | ||||
|         // Save data in shadow file
 | ||||
|         nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); | ||||
|         if(furi_string_size(nfc->dev->load_path)) { | ||||
|             nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); | ||||
|         } | ||||
|     } | ||||
|     if(nfc->rpc_ctx) { | ||||
|         rpc_system_app_send_exited(nfc->rpc_ctx); | ||||
| @ -218,6 +220,13 @@ void nfc_blink_stop(Nfc* nfc) { | ||||
|     notification_message(nfc->notifications, &sequence_blink_stop); | ||||
| } | ||||
| 
 | ||||
| bool nfc_save_file(Nfc* nfc) { | ||||
|     furi_string_printf( | ||||
|         nfc->dev->load_path, "%s/%s%s", NFC_APP_FOLDER, nfc->dev->dev_name, NFC_APP_EXTENSION); | ||||
|     bool file_saved = nfc_device_save(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); | ||||
|     return file_saved; | ||||
| } | ||||
| 
 | ||||
| void nfc_show_loading_popup(void* context, bool show) { | ||||
|     Nfc* nfc = context; | ||||
|     TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); | ||||
|  | ||||
| @ -114,4 +114,6 @@ void nfc_blink_detect_start(Nfc* nfc); | ||||
| 
 | ||||
| void nfc_blink_stop(Nfc* nfc); | ||||
| 
 | ||||
| bool nfc_save_file(Nfc* nfc); | ||||
| 
 | ||||
| void nfc_show_loading_popup(void* context, bool show); | ||||
|  | ||||
| @ -60,3 +60,4 @@ 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) | ||||
|  | ||||
| @ -29,9 +29,14 @@ bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { | ||||
|         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; | ||||
|     } | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| #include "../nfc_i.h" | ||||
| 
 | ||||
| enum SubmenuIndex { | ||||
|     SubmenuIndexReadCardType, | ||||
|     SubmenuIndexMfClassicKeys, | ||||
|     SubmenuIndexMfUltralightUnlock, | ||||
| }; | ||||
| @ -15,6 +16,12 @@ 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", | ||||
| @ -44,9 +51,15 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { | ||||
|             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; | ||||
|         } | ||||
|         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -5,6 +5,9 @@ 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); | ||||
|  | ||||
| @ -48,7 +48,10 @@ bool nfc_scene_mf_classic_emulate_on_event(void* context, SceneManagerEvent even | ||||
|            NFC_MF_CLASSIC_DATA_CHANGED) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_NOT_CHANGED); | ||||
|             nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); | ||||
|             // Save shadow file
 | ||||
|             if(furi_string_size(nfc->dev->load_path)) { | ||||
|                 nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); | ||||
|             } | ||||
|         } | ||||
|         consumed = false; | ||||
|     } | ||||
|  | ||||
| @ -24,7 +24,7 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) { | ||||
|     widget_add_button_element( | ||||
|         widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc); | ||||
| 
 | ||||
|     FuriString* temp_str; | ||||
|     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 { | ||||
|  | ||||
| @ -57,7 +57,7 @@ bool nfc_scene_mf_classic_update_on_event(void* context, SceneManagerEvent event | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcWorkerEventSuccess) { | ||||
|             nfc_worker_stop(nfc->worker); | ||||
|             if(nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name)) { | ||||
|             if(nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path))) { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdateSuccess); | ||||
|             } else { | ||||
|                 scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard); | ||||
|  | ||||
| @ -48,7 +48,10 @@ bool nfc_scene_mf_ultralight_emulate_on_event(void* context, SceneManagerEvent e | ||||
|            NFC_MF_UL_DATA_CHANGED) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_NOT_CHANGED); | ||||
|             nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); | ||||
|             // Save shadow file
 | ||||
|             if(furi_string_size(nfc->dev->load_path)) { | ||||
|                 nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); | ||||
|             } | ||||
|         } | ||||
|         consumed = false; | ||||
|     } | ||||
|  | ||||
| @ -31,7 +31,7 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { | ||||
|         nfc_scene_mf_ultralight_read_success_widget_callback, | ||||
|         nfc); | ||||
| 
 | ||||
|     FuriString* temp_str; | ||||
|     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 { | ||||
|  | ||||
							
								
								
									
										97
									
								
								applications/main/nfc/scenes/nfc_scene_read_card_type.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								applications/main/nfc/scenes/nfc_scene_read_card_type.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include "nfc_worker_i.h" | ||||
| 
 | ||||
| enum SubmenuIndex { | ||||
|     SubmenuIndexReadMifareClassic, | ||||
|     SubmenuIndexReadMifareDesfire, | ||||
|     SubmenuIndexReadMfUltralight, | ||||
|     SubmenuIndexReadEMV, | ||||
|     SubmenuIndexReadNFCA, | ||||
| }; | ||||
| 
 | ||||
| void nfc_scene_read_card_type_submenu_callback(void* context, uint32_t index) { | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(nfc->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_card_type_on_enter(void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     Submenu* submenu = nfc->submenu; | ||||
| 
 | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "Read Mifare Classic", | ||||
|         SubmenuIndexReadMifareClassic, | ||||
|         nfc_scene_read_card_type_submenu_callback, | ||||
|         nfc); | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "Read Mifare DESFire", | ||||
|         SubmenuIndexReadMifareDesfire, | ||||
|         nfc_scene_read_card_type_submenu_callback, | ||||
|         nfc); | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "Read NTAG/Ultralight", | ||||
|         SubmenuIndexReadMfUltralight, | ||||
|         nfc_scene_read_card_type_submenu_callback, | ||||
|         nfc); | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "Read EMV card", | ||||
|         SubmenuIndexReadEMV, | ||||
|         nfc_scene_read_card_type_submenu_callback, | ||||
|         nfc); | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "Read NFC-A data", | ||||
|         SubmenuIndexReadNFCA, | ||||
|         nfc_scene_read_card_type_submenu_callback, | ||||
|         nfc); | ||||
|     uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadCardType); | ||||
|     submenu_set_selected_item(submenu, state); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_read_card_type_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexReadMifareClassic) { | ||||
|             nfc->dev->dev_data.read_mode = NfcReadModeMfClassic; | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); | ||||
|             consumed = true; | ||||
|         } | ||||
|         if(event.event == SubmenuIndexReadMifareDesfire) { | ||||
|             nfc->dev->dev_data.read_mode = NfcReadModeMfDesfire; | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); | ||||
|             consumed = true; | ||||
|         } | ||||
|         if(event.event == SubmenuIndexReadMfUltralight) { | ||||
|             nfc->dev->dev_data.read_mode = NfcReadModeMfUltralight; | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); | ||||
|             consumed = true; | ||||
|         } | ||||
|         if(event.event == SubmenuIndexReadEMV) { | ||||
|             nfc->dev->dev_data.read_mode = NfcReadModeEMV; | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); | ||||
|             consumed = true; | ||||
|         } | ||||
|         if(event.event == SubmenuIndexReadNFCA) { | ||||
|             nfc->dev->dev_data.read_mode = NfcReadModeNFCA; | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); | ||||
|             consumed = true; | ||||
|         } | ||||
|         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, event.event); | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_scene_read_card_type_on_exit(void* context) { | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     submenu_reset(nfc->submenu); | ||||
| } | ||||
| @ -62,7 +62,7 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { | ||||
|                 nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; | ||||
|             } | ||||
|             strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1); | ||||
|             if(nfc_device_save(nfc->dev, nfc->text_store)) { | ||||
|             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
 | ||||
|  | ||||
| @ -31,7 +31,7 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { | ||||
|         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_device_save(nfc->dev, nfc->dev->dev_name)) { | ||||
|                 if(nfc_save_file(nfc)) { | ||||
|                     scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); | ||||
|                     consumed = true; | ||||
|                 } | ||||
| @ -41,6 +41,7 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include "nfc_worker_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| enum SubmenuIndex { | ||||
| @ -47,6 +48,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexRead) { | ||||
|             nfc->dev->dev_data.read_mode = NfcReadModeAuto; | ||||
|             scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); | ||||
|             DOLPHIN_DEED(DolphinDeedNfcRead); | ||||
|             consumed = true; | ||||
|  | ||||
| @ -0,0 +1,27 @@ | ||||
| #include "subghz_frequency_analyzer_log_item_array.h" | ||||
| 
 | ||||
| const char* | ||||
|     subghz_frequency_analyzer_log_get_order_name(SubGhzFrequencyAnalyzerLogOrderBy order_by) { | ||||
|     if(order_by == SubGhzFrequencyAnalyzerLogOrderBySeqAsc) { | ||||
|         return "Seq. A"; | ||||
|     } | ||||
|     if(order_by == SubGhzFrequencyAnalyzerLogOrderByCountDesc) { | ||||
|         return "Count D"; | ||||
|     } | ||||
|     if(order_by == SubGhzFrequencyAnalyzerLogOrderByCountAsc) { | ||||
|         return "Count A"; | ||||
|     } | ||||
|     if(order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIDesc) { | ||||
|         return "RSSI D"; | ||||
|     } | ||||
|     if(order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIAsc) { | ||||
|         return "RSSI A"; | ||||
|     } | ||||
|     if(order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc) { | ||||
|         return "Freq. D"; | ||||
|     } | ||||
|     if(order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { | ||||
|         return "Freq. A"; | ||||
|     } | ||||
|     return "Seq. D"; | ||||
| } | ||||
| @ -0,0 +1,73 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <m-tuple.h> | ||||
| #include <m-array.h> | ||||
| #include <m-algo.h> | ||||
| #include <m-funcobj.h> | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubGhzFrequencyAnalyzerLogOrderBySeqDesc, | ||||
|     SubGhzFrequencyAnalyzerLogOrderBySeqAsc, | ||||
|     SubGhzFrequencyAnalyzerLogOrderByCountDesc, | ||||
|     SubGhzFrequencyAnalyzerLogOrderByCountAsc, | ||||
|     SubGhzFrequencyAnalyzerLogOrderByRSSIDesc, | ||||
|     SubGhzFrequencyAnalyzerLogOrderByRSSIAsc, | ||||
|     SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc, | ||||
|     SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc, | ||||
| } SubGhzFrequencyAnalyzerLogOrderBy; | ||||
| 
 | ||||
| const char* | ||||
|     subghz_frequency_analyzer_log_get_order_name(SubGhzFrequencyAnalyzerLogOrderBy order_by); | ||||
| 
 | ||||
| TUPLE_DEF2( | ||||
|     SubGhzFrequencyAnalyzerLogItem, | ||||
|     (seq, uint8_t), | ||||
|     (frequency, uint32_t), | ||||
|     (count, uint8_t), | ||||
|     (rssi_max, uint8_t)) | ||||
| /* Register globaly the oplist */ | ||||
| #define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \ | ||||
|     TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_POD_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) | ||||
| 
 | ||||
| /* Define the array, register the oplist and define further algorithms on it */ | ||||
| ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) | ||||
| #define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_t() \ | ||||
|     ARRAY_OPLIST(SubGhzFrequencyAnalyzerLogItemArray, M_OPL_SubGhzFrequencyAnalyzerLogItem_t()) | ||||
| ALGO_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItemArray_t) | ||||
| 
 | ||||
| FUNC_OBJ_INS_DEF( | ||||
|     SubGhzFrequencyAnalyzerLogItemArray_compare_by /* name of the instance */, | ||||
|     SubGhzFrequencyAnalyzerLogItemArray_cmp_obj /* name of the interface */, | ||||
|     (a, | ||||
|      b) /* name of the input parameters of the function like object. The type are inherited from the interface. */ | ||||
|     , | ||||
|     { | ||||
|         /* code of the function object */ | ||||
|         if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { | ||||
|             return a->frequency < b->frequency ? -1 : a->frequency > b->frequency; | ||||
|         } | ||||
|         if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc) { | ||||
|             return a->frequency > b->frequency ? -1 : a->frequency < b->frequency; | ||||
|         } | ||||
|         if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIAsc) { | ||||
|             return a->rssi_max < b->rssi_max ? -1 : a->rssi_max > b->rssi_max; | ||||
|         } | ||||
|         if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIDesc) { | ||||
|             return a->rssi_max > b->rssi_max ? -1 : a->rssi_max < b->rssi_max; | ||||
|         } | ||||
|         if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByCountAsc) { | ||||
|             return a->count < b->count ? -1 : a->count > b->count; | ||||
|         } | ||||
|         if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByCountDesc) { | ||||
|             return a->count > b->count ? -1 : a->count < b->count; | ||||
|         } | ||||
|         if(self->order_by == SubGhzFrequencyAnalyzerLogOrderBySeqAsc) { | ||||
|             return a->seq < b->seq ? -1 : a->seq > b->seq; | ||||
|         } | ||||
| 
 | ||||
|         return a->seq > b->seq ? -1 : a->seq < b->seq; | ||||
|     }, | ||||
|     /* Additional fields stored in the function object */ | ||||
|     (order_by, SubGhzFrequencyAnalyzerLogOrderBy)) | ||||
| #define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_compare_by_t() \ | ||||
|     FUNC_OBJ_INS_OPLIST(SubGhzFrequencyAnalyzerLogItemArray_compare_by, M_DEFAULT_OPLIST) | ||||
| @ -44,14 +44,7 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event | ||||
| 
 | ||||
| void subghz_scene_delete_success_on_exit(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = subghz->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_set_callback(popup, NULL); | ||||
|     popup_set_context(popup, NULL); | ||||
|     popup_set_timeout(popup, 0); | ||||
|     popup_disable_timeout(popup); | ||||
| 
 | ||||
|     popup_reset(popup); | ||||
| } | ||||
|  | ||||
| @ -38,18 +38,34 @@ bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) { | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexDelete) { | ||||
|             if(subghz_file_available(subghz)) { | ||||
|                 scene_manager_set_scene_state( | ||||
|                     subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); | ||||
|                 scene_manager_set_scene_state( | ||||
|                     subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete); | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW); | ||||
|                 return true; | ||||
|             } else { | ||||
|                 if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                        subghz->scene_manager, SubGhzSceneStart)) { | ||||
|                     scene_manager_stop(subghz->scene_manager); | ||||
|                     view_dispatcher_stop(subghz->view_dispatcher); | ||||
|                 } | ||||
|             } | ||||
|         } else if(event.event == SubmenuIndexEdit) { | ||||
|             if(subghz_file_available(subghz)) { | ||||
|                 furi_string_reset(subghz->file_path_tmp); | ||||
|                 scene_manager_set_scene_state( | ||||
|                     subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit); | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||
|                 return true; | ||||
|             } else { | ||||
|                 if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                        subghz->scene_manager, SubGhzSceneStart)) { | ||||
|                     scene_manager_stop(subghz->scene_manager); | ||||
|                     view_dispatcher_stop(subghz->view_dispatcher); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
|  | ||||
| @ -198,6 +198,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             break; | ||||
| 
 | ||||
|         case SubGhzCustomEventViewReadRAWMore: | ||||
|             if(subghz_file_available(subghz)) { | ||||
|                 if(subghz_scene_read_raw_update_filename(subghz)) { | ||||
|                     scene_manager_set_scene_state( | ||||
|                         subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); | ||||
| @ -207,11 +208,18 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|                 } else { | ||||
|                     furi_crash("SubGhz: RAW file name update error."); | ||||
|                 } | ||||
|             } else { | ||||
|                 if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                        subghz->scene_manager, SubGhzSceneStart)) { | ||||
|                     scene_manager_stop(subghz->scene_manager); | ||||
|                     view_dispatcher_stop(subghz->view_dispatcher); | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
| 
 | ||||
|         case SubGhzCustomEventViewReadRAWSendStart: | ||||
| 
 | ||||
|             if(subghz_scene_read_raw_update_filename(subghz)) { | ||||
|             if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { | ||||
|                 //start send
 | ||||
|                 subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|                 if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||
| @ -238,6 +246,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|                         subghz->state_notifications = SubGhzNotificationStateTx; | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                        subghz->scene_manager, SubGhzSceneStart)) { | ||||
|                     scene_manager_stop(subghz->scene_manager); | ||||
|                     view_dispatcher_stop(subghz->view_dispatcher); | ||||
|                 } | ||||
|             } | ||||
|             consumed = true; | ||||
|             break; | ||||
| @ -314,11 +328,17 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|             break; | ||||
| 
 | ||||
|         case SubGhzCustomEventViewReadRAWSave: | ||||
|             if(subghz_scene_read_raw_update_filename(subghz)) { | ||||
|             if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { | ||||
|                 scene_manager_set_scene_state( | ||||
|                     subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW); | ||||
|                 subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; | ||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); | ||||
|             } else { | ||||
|                 if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                        subghz->scene_manager, SubGhzSceneStart)) { | ||||
|                     scene_manager_stop(subghz->scene_manager); | ||||
|                     view_dispatcher_stop(subghz->view_dispatcher); | ||||
|                 } | ||||
|             } | ||||
|             consumed = true; | ||||
|             break; | ||||
|  | ||||
| @ -155,9 +155,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) | ||||
|         } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { | ||||
|             //CC1101 Stop RX -> Save
 | ||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||
|             if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { | ||||
|             subghz->txrx->hopper_state = SubGhzHopperStateOFF; | ||||
|             } | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { | ||||
|                 subghz_rx_end(subghz); | ||||
|                 subghz_sleep(subghz); | ||||
|  | ||||
| @ -40,9 +40,8 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { | ||||
|             bool result = false; | ||||
|             if((subghz->txrx->txrx_state == SubGhzTxRxStateSleep) && | ||||
|                (state == SubGhzRpcStateLoaded)) { | ||||
|                 subghz_blink_start(subghz); | ||||
|                 result = subghz_tx_start(subghz, subghz->txrx->fff_data); | ||||
|                 result = true; | ||||
|                 if(result) subghz_blink_start(subghz); | ||||
|             } | ||||
|             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); | ||||
|         } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { | ||||
|  | ||||
| @ -44,14 +44,7 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) | ||||
| 
 | ||||
| void subghz_scene_save_success_on_exit(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = subghz->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_set_callback(popup, NULL); | ||||
|     popup_set_context(popup, NULL); | ||||
|     popup_set_timeout(popup, 0); | ||||
|     popup_disable_timeout(popup); | ||||
| 
 | ||||
|     popup_reset(popup); | ||||
| } | ||||
|  | ||||
| @ -36,16 +36,10 @@ bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event | ||||
| 
 | ||||
| void subghz_scene_show_error_sub_on_exit(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = subghz->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_set_callback(popup, NULL); | ||||
|     popup_set_context(popup, NULL); | ||||
|     popup_set_timeout(popup, 0); | ||||
|     popup_disable_timeout(popup); | ||||
| 
 | ||||
|     popup_reset(popup); | ||||
| 
 | ||||
|     furi_string_reset(subghz->error_str); | ||||
| 
 | ||||
|     notification_message(subghz->notifications, &sequence_reset_rgb); | ||||
|  | ||||
| @ -43,14 +43,7 @@ bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) | ||||
| 
 | ||||
| void subghz_scene_show_only_rx_on_exit(void* context) { | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     Popup* popup = subghz->popup; | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); | ||||
|     popup_set_icon(popup, 0, 0, NULL); | ||||
|     popup_set_callback(popup, NULL); | ||||
|     popup_set_context(popup, NULL); | ||||
|     popup_set_timeout(popup, 0); | ||||
|     popup_disable_timeout(popup); | ||||
| 
 | ||||
|     popup_reset(popup); | ||||
| } | ||||
|  | ||||
| @ -490,6 +490,23 @@ bool subghz_rename_file(SubGhz* subghz) { | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool subghz_file_available(SubGhz* subghz) { | ||||
|     furi_assert(subghz); | ||||
|     bool ret = true; | ||||
|     Storage* storage = furi_record_open(RECORD_STORAGE); | ||||
| 
 | ||||
|     FS_Error fs_result = | ||||
|         storage_common_stat(storage, furi_string_get_cstr(subghz->file_path), NULL); | ||||
| 
 | ||||
|     if(fs_result != FSE_OK) { | ||||
|         dialog_message_show_storage_error(subghz->dialogs, "File not available\n file/directory"); | ||||
|         ret = false; | ||||
|     } | ||||
| 
 | ||||
|     furi_record_close(RECORD_STORAGE); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool subghz_delete_file(SubGhz* subghz) { | ||||
|     furi_assert(subghz); | ||||
| 
 | ||||
| @ -513,12 +530,6 @@ bool subghz_path_is_file(FuriString* path) { | ||||
| } | ||||
| 
 | ||||
| uint32_t subghz_random_serial(void) { | ||||
|     static bool rand_generator_inited = false; | ||||
| 
 | ||||
|     if(!rand_generator_inited) { | ||||
|         srand(DWT->CYCCNT); | ||||
|         rand_generator_inited = true; | ||||
|     } | ||||
|     return (uint32_t)rand(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -124,6 +124,7 @@ bool subghz_save_protocol_to_file( | ||||
|     const char* dev_file_name); | ||||
| bool subghz_load_protocol_from_file(SubGhz* subghz); | ||||
| bool subghz_rename_file(SubGhz* subghz); | ||||
| bool subghz_file_available(SubGhz* subghz); | ||||
| bool subghz_delete_file(SubGhz* subghz); | ||||
| void subghz_file_name_clear(SubGhz* subghz); | ||||
| bool subghz_path_is_file(FuriString* path); | ||||
|  | ||||
| @ -5,30 +5,53 @@ | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| #include <input/input.h> | ||||
| #include <gui/elements.h> | ||||
| #include <notification/notification_messages.h> | ||||
| #include "../helpers/subghz_frequency_analyzer_worker.h" | ||||
| #include "../helpers/subghz_frequency_analyzer_log_item_array.h" | ||||
| 
 | ||||
| #include <assets_icons.h> | ||||
| 
 | ||||
| #define LOG_FREQUENCY_MAX_ITEMS 60 // uint8_t (limited by 'seq' of SubGhzFrequencyAnalyzerLogItem)
 | ||||
| #define RSSI_OFFSET 74 | ||||
| #define RSSI_MAX 53 // 127 - RSSI_OFFSET
 | ||||
| 
 | ||||
| #define SNPRINTF_FREQUENCY(buff, freq) \ | ||||
|     snprintf(buff, sizeof(buff), "%03ld.%03ld", freq / 1000000 % 1000, freq / 1000 % 1000); | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubGhzFrequencyAnalyzerStatusIDLE, | ||||
| } SubGhzFrequencyAnalyzerStatus; | ||||
| 
 | ||||
| typedef enum { | ||||
|     SubGhzFrequencyAnalyzerFragmentBottomTypeMain, | ||||
|     SubGhzFrequencyAnalyzerFragmentBottomTypeLog, | ||||
| } SubGhzFrequencyAnalyzerFragmentBottomType; | ||||
| 
 | ||||
| struct SubGhzFrequencyAnalyzer { | ||||
|     View* view; | ||||
|     SubGhzFrequencyAnalyzerWorker* worker; | ||||
|     SubGhzFrequencyAnalyzerCallback callback; | ||||
|     void* context; | ||||
|     bool locked; | ||||
|     uint32_t last_frequency; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint32_t frequency; | ||||
|     float rssi; | ||||
|     uint8_t rssi; | ||||
|     uint32_t history_frequency[3]; | ||||
|     bool signal; | ||||
|     SubGhzFrequencyAnalyzerLogItemArray_t log_frequency; | ||||
|     SubGhzFrequencyAnalyzerFragmentBottomType fragment_bottom_type; | ||||
|     SubGhzFrequencyAnalyzerLogOrderBy log_frequency_order_by; | ||||
|     uint8_t log_frequency_scroll_offset; | ||||
| } SubGhzFrequencyAnalyzerModel; | ||||
| 
 | ||||
| static inline uint8_t rssi_sanitize(float rssi) { | ||||
|     return (rssi * -1.0f) - RSSI_OFFSET; | ||||
| } | ||||
| 
 | ||||
| void subghz_frequency_analyzer_set_callback( | ||||
|     SubGhzFrequencyAnalyzer* subghz_frequency_analyzer, | ||||
|     SubGhzFrequencyAnalyzerCallback callback, | ||||
| @ -39,13 +62,11 @@ void subghz_frequency_analyzer_set_callback( | ||||
|     subghz_frequency_analyzer->context = context; | ||||
| } | ||||
| 
 | ||||
| void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi) { | ||||
|     uint8_t x = 20; | ||||
|     uint8_t y = 64; | ||||
| void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) { | ||||
|     uint8_t column_number = 0; | ||||
|     if(rssi) { | ||||
|         rssi = (rssi + 90) / 3; | ||||
|         for(size_t i = 1; i < (uint8_t)rssi; i++) { | ||||
|         rssi = rssi / 3; | ||||
|         for(uint8_t i = 1; i < rssi; i++) { | ||||
|             if(i > 20) break; | ||||
|             if(i % 4) { | ||||
|                 column_number++; | ||||
| @ -55,6 +76,54 @@ void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void subghz_frequency_analyzer_log_frequency_draw( | ||||
|     Canvas* canvas, | ||||
|     SubGhzFrequencyAnalyzerModel* model) { | ||||
|     char buffer[64]; | ||||
|     const uint8_t offset_x = 0; | ||||
|     const uint8_t offset_y = 43; | ||||
|     canvas_set_font(canvas, FontKeyboard); | ||||
| 
 | ||||
|     const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); | ||||
|     if(items_count == 0) { | ||||
|         canvas_draw_rframe(canvas, offset_x + 27u, offset_y - 3u, 73u, 16u, 5u); | ||||
|         canvas_draw_str_aligned( | ||||
|             canvas, offset_x + 64u, offset_y + 8u, AlignCenter, AlignBottom, "No records"); | ||||
|         return; | ||||
|     } else if(items_count > 3) { | ||||
|         elements_scrollbar_pos( | ||||
|             canvas, | ||||
|             offset_x + 127, | ||||
|             offset_y - 8, | ||||
|             29, | ||||
|             model->log_frequency_scroll_offset, | ||||
|             items_count - 2); | ||||
|     } | ||||
| 
 | ||||
|     SubGhzFrequencyAnalyzerLogItem_t* log_frequency_item; | ||||
|     for(uint8_t i = 0; i < 3; ++i) { | ||||
|         const uint8_t item_pos = model->log_frequency_scroll_offset + i; | ||||
|         if(item_pos >= items_count) { | ||||
|             break; | ||||
|         } | ||||
|         log_frequency_item = | ||||
|             SubGhzFrequencyAnalyzerLogItemArray_get(model->log_frequency, item_pos); | ||||
|         // Frequency
 | ||||
|         SNPRINTF_FREQUENCY(buffer, (*log_frequency_item)->frequency) | ||||
|         canvas_draw_str(canvas, offset_x, offset_y + i * 10, buffer); | ||||
| 
 | ||||
|         // Count
 | ||||
|         snprintf(buffer, sizeof(buffer), "%3d", (*log_frequency_item)->count); | ||||
|         canvas_draw_str(canvas, offset_x + 48, offset_y + i * 10, buffer); | ||||
| 
 | ||||
|         // Max RSSI
 | ||||
|         subghz_frequency_analyzer_draw_rssi( | ||||
|             canvas, (*log_frequency_item)->rssi_max, offset_x + 69, (offset_y + i * 10)); | ||||
|     } | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
| } | ||||
| 
 | ||||
| static void subghz_frequency_analyzer_history_frequency_draw( | ||||
|     Canvas* canvas, | ||||
|     SubGhzFrequencyAnalyzerModel* model) { | ||||
| @ -65,12 +134,7 @@ static void subghz_frequency_analyzer_history_frequency_draw( | ||||
|     canvas_set_font(canvas, FontKeyboard); | ||||
|     for(uint8_t i = 0; i < 3; i++) { | ||||
|         if(model->history_frequency[i]) { | ||||
|             snprintf( | ||||
|                 buffer, | ||||
|                 sizeof(buffer), | ||||
|                 "%03ld.%03ld", | ||||
|                 model->history_frequency[i] / 1000000 % 1000, | ||||
|                 model->history_frequency[i] / 1000 % 1000); | ||||
|             SNPRINTF_FREQUENCY(buffer, model->history_frequency[i]) | ||||
|             canvas_draw_str(canvas, x, y + i * 10, buffer); | ||||
|         } else { | ||||
|             canvas_draw_str(canvas, x, y + i * 10, "---.---"); | ||||
| @ -81,16 +145,32 @@ static void subghz_frequency_analyzer_history_frequency_draw( | ||||
| } | ||||
| 
 | ||||
| void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) { | ||||
|     furi_assert(canvas); | ||||
|     furi_assert(model); | ||||
|     char buffer[64]; | ||||
| 
 | ||||
|     canvas_set_color(canvas, ColorBlack); | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); | ||||
| 
 | ||||
|     if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { | ||||
|         const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); | ||||
|         const char* log_order_by_name = | ||||
|             subghz_frequency_analyzer_log_get_order_name(model->log_frequency_order_by); | ||||
|         if(items_count < LOG_FREQUENCY_MAX_ITEMS) { | ||||
|             snprintf(buffer, sizeof(buffer), "Frequency Analyzer [%s]", log_order_by_name); | ||||
|             canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignBottom, buffer); | ||||
|         } else { | ||||
|             snprintf(buffer, sizeof(buffer), "The log is full! [%s]", log_order_by_name); | ||||
|             canvas_draw_str(canvas, 2, 8, buffer); | ||||
|         } | ||||
|         subghz_frequency_analyzer_log_frequency_draw(canvas, model); | ||||
|     } else { | ||||
|         canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); | ||||
|         canvas_draw_str(canvas, 0, 64, "RSSI"); | ||||
|     subghz_frequency_analyzer_draw_rssi(canvas, model->rssi); | ||||
|         subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20u, 64u); | ||||
| 
 | ||||
|         subghz_frequency_analyzer_history_frequency_draw(canvas, model); | ||||
|     } | ||||
| 
 | ||||
|     // Frequency
 | ||||
|     canvas_set_font(canvas, FontBigNumbers); | ||||
| @ -103,23 +183,151 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel | ||||
|     if(model->signal) { | ||||
|         canvas_draw_box(canvas, 4, 12, 121, 22); | ||||
|         canvas_set_color(canvas, ColorWhite); | ||||
|     } else { | ||||
|     } | ||||
| 
 | ||||
|     canvas_draw_str(canvas, 8, 30, buffer); | ||||
|     canvas_draw_icon(canvas, 96, 19, &I_MHz_25x11); | ||||
| } | ||||
| 
 | ||||
| static void subghz_frequency_analyzer_log_frequency_sort(SubGhzFrequencyAnalyzerModel* model) { | ||||
|     furi_assert(model); | ||||
|     M_LET((cmp, model->log_frequency_order_by), SubGhzFrequencyAnalyzerLogItemArray_compare_by_t) | ||||
|     SubGhzFrequencyAnalyzerLogItemArray_sort_fo( | ||||
|         model->log_frequency, SubGhzFrequencyAnalyzerLogItemArray_compare_by_as_interface(cmp)); | ||||
| } | ||||
| 
 | ||||
| bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhzFrequencyAnalyzer* instance = context; | ||||
| 
 | ||||
|     if(event->key == InputKeyBack) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if((event->type == InputTypeShort) && | ||||
|        ((event->key == InputKeyLeft) || (event->key == InputKeyRight))) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             SubGhzFrequencyAnalyzerModel * model, | ||||
|             { | ||||
|                 if(event->key == InputKeyLeft) { | ||||
|                     if(model->fragment_bottom_type == 0) { | ||||
|                         model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeLog; | ||||
|                     } else { | ||||
|                         --model->fragment_bottom_type; | ||||
|                     } | ||||
|                 } else if(event->key == InputKeyRight) { | ||||
|                     if(model->fragment_bottom_type == | ||||
|                        SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { | ||||
|                         model->fragment_bottom_type = 0; | ||||
|                     } else { | ||||
|                         ++model->fragment_bottom_type; | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             true); | ||||
|     } else if((event->type == InputTypeShort) && (event->key == InputKeyOk)) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             SubGhzFrequencyAnalyzerModel * model, | ||||
|             { | ||||
|                 if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { | ||||
|                     ++model->log_frequency_order_by; | ||||
|                     if(model->log_frequency_order_by > | ||||
|                        SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { | ||||
|                         model->log_frequency_order_by = 0; | ||||
|                     } | ||||
|                     subghz_frequency_analyzer_log_frequency_sort(model); | ||||
|                 } | ||||
|             }, | ||||
|             true); | ||||
|     } else if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             SubGhzFrequencyAnalyzerModel * model, | ||||
|             { | ||||
|                 if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { | ||||
|                     if(event->key == InputKeyUp) { | ||||
|                         if(model->log_frequency_scroll_offset > 0) { | ||||
|                             --model->log_frequency_scroll_offset; | ||||
|                         } | ||||
|                     } else if(event->key == InputKeyDown) { | ||||
|                         const size_t items_count = | ||||
|                             SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); | ||||
|                         if((model->log_frequency_scroll_offset + 3u) < items_count) { | ||||
|                             ++model->log_frequency_scroll_offset; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             true); | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static void subghz_frequency_analyzer_log_frequency_search_it( | ||||
|     SubGhzFrequencyAnalyzerLogItemArray_it_t* itref, | ||||
|     SubGhzFrequencyAnalyzerLogItemArray_t* log_frequency, | ||||
|     uint32_t frequency) { | ||||
|     furi_assert(log_frequency); | ||||
| 
 | ||||
|     SubGhzFrequencyAnalyzerLogItemArray_it(*itref, *log_frequency); | ||||
|     SubGhzFrequencyAnalyzerLogItem_t* item; | ||||
|     while(!SubGhzFrequencyAnalyzerLogItemArray_end_p(*itref)) { | ||||
|         item = SubGhzFrequencyAnalyzerLogItemArray_ref(*itref); | ||||
|         if((*item)->frequency == frequency) { | ||||
|             break; | ||||
|         } | ||||
|         SubGhzFrequencyAnalyzerLogItemArray_next(*itref); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool subghz_frequency_analyzer_log_frequency_insert(SubGhzFrequencyAnalyzerModel* model) { | ||||
|     furi_assert(model); | ||||
|     const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); | ||||
|     if(items_count < LOG_FREQUENCY_MAX_ITEMS) { | ||||
|         SubGhzFrequencyAnalyzerLogItem_t* item = | ||||
|             SubGhzFrequencyAnalyzerLogItemArray_push_new(model->log_frequency); | ||||
|         if(item == NULL) { | ||||
|             return false; | ||||
|         } | ||||
|         (*item)->frequency = model->frequency; | ||||
|         (*item)->count = 1u; | ||||
|         (*item)->rssi_max = model->rssi; | ||||
|         (*item)->seq = items_count; | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static void subghz_frequency_analyzer_log_frequency_update( | ||||
|     SubGhzFrequencyAnalyzerModel* model, | ||||
|     bool need_insert) { | ||||
|     furi_assert(model); | ||||
|     if(!model->frequency) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     SubGhzFrequencyAnalyzerLogItemArray_it_t it; | ||||
|     subghz_frequency_analyzer_log_frequency_search_it( | ||||
|         &it, &model->log_frequency, model->frequency); | ||||
|     if(!SubGhzFrequencyAnalyzerLogItemArray_end_p(it)) { | ||||
|         SubGhzFrequencyAnalyzerLogItem_t* item = SubGhzFrequencyAnalyzerLogItemArray_ref(it); | ||||
|         if((*item)->rssi_max < model->rssi) { | ||||
|             (*item)->rssi_max = model->rssi; | ||||
|         } | ||||
| 
 | ||||
|         if(need_insert && (*item)->count < UINT8_MAX) { | ||||
|             ++(*item)->count; | ||||
|             subghz_frequency_analyzer_log_frequency_sort(model); | ||||
|         } | ||||
|     } else if(need_insert) { | ||||
|         if(subghz_frequency_analyzer_log_frequency_insert(model)) { | ||||
|             subghz_frequency_analyzer_log_frequency_sort(model); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void subghz_frequency_analyzer_pair_callback( | ||||
|     void* context, | ||||
|     uint32_t frequency, | ||||
| @ -130,6 +338,7 @@ void subghz_frequency_analyzer_pair_callback( | ||||
|         if(instance->callback) { | ||||
|             instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context); | ||||
|         } | ||||
|         instance->last_frequency = 0; | ||||
|         //update history
 | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
| @ -151,9 +360,14 @@ void subghz_frequency_analyzer_pair_callback( | ||||
|         instance->view, | ||||
|         SubGhzFrequencyAnalyzerModel * model, | ||||
|         { | ||||
|             model->rssi = rssi; | ||||
|             model->rssi = rssi_sanitize(rssi); | ||||
|             model->frequency = frequency; | ||||
|             model->signal = signal; | ||||
|             if(frequency) { | ||||
|                 subghz_frequency_analyzer_log_frequency_update( | ||||
|                     model, frequency != instance->last_frequency); | ||||
|                 instance->last_frequency = frequency; | ||||
|             } | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| @ -176,11 +390,14 @@ void subghz_frequency_analyzer_enter(void* context) { | ||||
|         instance->view, | ||||
|         SubGhzFrequencyAnalyzerModel * model, | ||||
|         { | ||||
|             model->rssi = 0; | ||||
|             model->rssi = 0u; | ||||
|             model->frequency = 0; | ||||
|             model->history_frequency[2] = 0; | ||||
|             model->history_frequency[1] = 0; | ||||
|             model->history_frequency[0] = 0; | ||||
|             model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; | ||||
|             model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; | ||||
|             model->log_frequency_scroll_offset = 0u; | ||||
|             model->history_frequency[0] = model->history_frequency[1] = | ||||
|                 model->history_frequency[2] = 0u; | ||||
|             SubGhzFrequencyAnalyzerLogItemArray_init(model->log_frequency); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| @ -196,13 +413,26 @@ void subghz_frequency_analyzer_exit(void* context) { | ||||
|     subghz_frequency_analyzer_worker_free(instance->worker); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, SubGhzFrequencyAnalyzerModel * model, { model->rssi = 0; }, true); | ||||
|         instance->view, | ||||
|         SubGhzFrequencyAnalyzerModel * model, | ||||
|         { | ||||
|             model->rssi = 0u; | ||||
|             model->frequency = 0; | ||||
|             model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; | ||||
|             model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; | ||||
|             model->log_frequency_scroll_offset = 0u; | ||||
|             model->history_frequency[0] = model->history_frequency[1] = | ||||
|                 model->history_frequency[2] = 0u; | ||||
|             SubGhzFrequencyAnalyzerLogItemArray_clear(model->log_frequency); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() { | ||||
|     SubGhzFrequencyAnalyzer* instance = malloc(sizeof(SubGhzFrequencyAnalyzer)); | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     instance->last_frequency = 0; | ||||
|     instance->view = view_alloc(); | ||||
|     view_allocate_model( | ||||
|         instance->view, ViewModelTypeLocking, sizeof(SubGhzFrequencyAnalyzerModel)); | ||||
|  | ||||
| @ -11,4 +11,5 @@ App( | ||||
|     stack_size=2 * 1024, | ||||
|     icon="A_U2F_14", | ||||
|     order=80, | ||||
|     fap_libs=["assets"], | ||||
| ) | ||||
|  | ||||
| @ -203,7 +203,7 @@ static int32_t u2f_hid_worker(void* context) { | ||||
|             WorkerEvtStop | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtRequest, | ||||
|             FuriFlagWaitAny, | ||||
|             FuriWaitForever); | ||||
|         furi_check((flags & FuriFlagError) == 0); | ||||
|         furi_check(!(flags & FuriFlagError)); | ||||
|         if(flags & WorkerEvtStop) break; | ||||
|         if(flags & WorkerEvtConnect) { | ||||
|             u2f_set_state(u2f_hid->u2f_instance, 1); | ||||
|  | ||||
| @ -13,6 +13,8 @@ | ||||
| #include "dap_config.h" | ||||
| #include "gui/dap_gui.h" | ||||
| #include "usb/dap_v2_usb.h" | ||||
| #include <dialogs/dialogs.h> | ||||
| #include "dap_link_icons.h" | ||||
| 
 | ||||
| /***************************************************************************/ | ||||
| /****************************** DAP COMMON *********************************/ | ||||
| @ -495,6 +497,24 @@ DapConfig* dap_app_get_config(DapApp* app) { | ||||
| int32_t dap_link_app(void* p) { | ||||
|     UNUSED(p); | ||||
| 
 | ||||
|     if(furi_hal_usb_is_locked()) { | ||||
|         DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); | ||||
|         DialogMessage* message = dialog_message_alloc(); | ||||
|         dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop); | ||||
|         dialog_message_set_text( | ||||
|             message, | ||||
|             "Disconnect from\nPC or phone to\nuse this function.", | ||||
|             3, | ||||
|             30, | ||||
|             AlignLeft, | ||||
|             AlignTop); | ||||
|         dialog_message_set_icon(message, &I_ActiveConnection_50x64, 78, 0); | ||||
|         dialog_message_show(dialogs, message); | ||||
|         dialog_message_free(message); | ||||
|         furi_record_close(RECORD_DIALOGS); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     // alloc app
 | ||||
|     DapApp* app = dap_app_alloc(); | ||||
|     app_handle = app; | ||||
|  | ||||
| @ -72,8 +72,8 @@ void dap_scene_config_on_enter(void* context) { | ||||
|     variable_item_set_current_value_index(item, config->uart_swap); | ||||
|     variable_item_set_current_value_text(item, uart_swap[config->uart_swap]); | ||||
| 
 | ||||
|     item = variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL); | ||||
|     item = variable_item_list_add(var_item_list, "About", 0, NULL, NULL); | ||||
|     variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL); | ||||
|     variable_item_list_add(var_item_list, "About", 0, NULL, NULL); | ||||
| 
 | ||||
|     variable_item_list_set_selected_item( | ||||
|         var_item_list, scene_manager_get_scene_state(app->scene_manager, DapSceneConfig)); | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/dap_link/icons/ActiveConnection_50x64.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/dap_link/icons/ActiveConnection_50x64.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										20
									
								
								applications/plugins/nfc_magic/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								applications/plugins/nfc_magic/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| App( | ||||
|     appid="nfc_magic", | ||||
|     name="Nfc Magic", | ||||
|     apptype=FlipperAppType.EXTERNAL, | ||||
|     entry_point="nfc_magic_app", | ||||
|     requires=[ | ||||
|         "storage", | ||||
|         "gui", | ||||
|     ], | ||||
|     stack_size=4 * 1024, | ||||
|     order=30, | ||||
|     fap_icon="../../../assets/icons/Archive/125_10px.png", | ||||
|     fap_category="Tools", | ||||
|     fap_private_libs=[ | ||||
|         Lib( | ||||
|             name="magic", | ||||
|         ), | ||||
|     ], | ||||
|     fap_icon_assets="assets", | ||||
| ) | ||||
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/nfc_magic/assets/DolphinCommon_56x48.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/nfc_magic/assets/DolphinCommon_56x48.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/nfc_magic/assets/DolphinNice_96x59.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/nfc_magic/assets/DolphinNice_96x59.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/nfc_magic/assets/Loading_24.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/nfc_magic/assets/Loading_24.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/nfc_magic/assets/NFC_manual_60x50.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/nfc_magic/assets/NFC_manual_60x50.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										214
									
								
								applications/plugins/nfc_magic/lib/magic/magic.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								applications/plugins/nfc_magic/lib/magic/magic.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,214 @@ | ||||
| #include "magic.h" | ||||
| 
 | ||||
| #include <furi_hal_nfc.h> | ||||
| 
 | ||||
| #define TAG "Magic" | ||||
| 
 | ||||
| #define MAGIC_CMD_WUPA (0x40) | ||||
| #define MAGIC_CMD_WIPE (0x41) | ||||
| #define MAGIC_CMD_READ (0x43) | ||||
| #define MAGIC_CMD_WRITE (0x43) | ||||
| 
 | ||||
| #define MAGIC_MIFARE_READ_CMD (0x30) | ||||
| #define MAGIC_MIFARE_WRITE_CMD (0xA0) | ||||
| 
 | ||||
| #define MAGIC_ACK (0x0A) | ||||
| 
 | ||||
| #define MAGIC_BUFFER_SIZE (32) | ||||
| 
 | ||||
| bool magic_wupa() { | ||||
|     bool magic_activated = false; | ||||
|     uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; | ||||
|     uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; | ||||
|     uint16_t rx_len = 0; | ||||
|     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( | ||||
|             tx_data, | ||||
|             7, | ||||
|             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(20)); | ||||
|         if(ret != FuriHalNfcReturnIncompleteByte) break; | ||||
|         if(rx_len != 4) break; | ||||
|         if(rx_data[0] != MAGIC_ACK) break; | ||||
|         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 write_cmd_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_WRITE; | ||||
|         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(20)); | ||||
|         if(ret != FuriHalNfcReturnIncompleteByte) break; | ||||
|         if(rx_len != 4) break; | ||||
|         if(rx_data[0] != MAGIC_ACK) break; | ||||
| 
 | ||||
|         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) { | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     bool read_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_MIFARE_READ_CMD; | ||||
|         tx_data[1] = block_num; | ||||
|         ret = furi_hal_nfc_ll_txrx_bits( | ||||
|             tx_data, | ||||
|             2 * 8, | ||||
|             rx_data, | ||||
|             sizeof(rx_data), | ||||
|             &rx_len, | ||||
|             FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON, | ||||
|             furi_hal_nfc_ll_ms2fc(20)); | ||||
| 
 | ||||
|         if(ret != FuriHalNfcReturnOk) break; | ||||
|         if(rx_len != 16 * 8) break; | ||||
|         memcpy(data->value, rx_data, sizeof(data->value)); | ||||
|         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) { | ||||
|     furi_assert(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 { | ||||
|         tx_data[0] = MAGIC_MIFARE_WRITE_CMD; | ||||
|         tx_data[1] = block_num; | ||||
|         ret = furi_hal_nfc_ll_txrx_bits( | ||||
|             tx_data, | ||||
|             2 * 8, | ||||
|             rx_data, | ||||
|             sizeof(rx_data), | ||||
|             &rx_len, | ||||
|             FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, | ||||
|             furi_hal_nfc_ll_ms2fc(20)); | ||||
|         if(ret != FuriHalNfcReturnIncompleteByte) break; | ||||
|         if(rx_len != 4) break; | ||||
|         if(rx_data[0] != MAGIC_ACK) break; | ||||
| 
 | ||||
|         memcpy(tx_data, data->value, sizeof(data->value)); | ||||
|         ret = furi_hal_nfc_ll_txrx_bits( | ||||
|             tx_data, | ||||
|             16 * 8, | ||||
|             rx_data, | ||||
|             sizeof(rx_data), | ||||
|             &rx_len, | ||||
|             FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, | ||||
|             furi_hal_nfc_ll_ms2fc(20)); | ||||
|         if(ret != FuriHalNfcReturnIncompleteByte) break; | ||||
|         if(rx_len != 4) break; | ||||
|         if(rx_data[0] != MAGIC_ACK) break; | ||||
| 
 | ||||
|         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(); | ||||
| } | ||||
							
								
								
									
										15
									
								
								applications/plugins/nfc_magic/lib/magic/magic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								applications/plugins/nfc_magic/lib/magic/magic.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <lib/nfc/protocols/mifare_classic.h> | ||||
| 
 | ||||
| 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(); | ||||
							
								
								
									
										169
									
								
								applications/plugins/nfc_magic/nfc_magic.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								applications/plugins/nfc_magic/nfc_magic.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,169 @@ | ||||
| #include "nfc_magic_i.h" | ||||
| 
 | ||||
| bool nfc_magic_custom_event_callback(void* context, uint32_t event) { | ||||
|     furi_assert(context); | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     return scene_manager_handle_custom_event(nfc_magic->scene_manager, event); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_back_event_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     return scene_manager_handle_back_event(nfc_magic->scene_manager); | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_tick_event_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     scene_manager_handle_tick_event(nfc_magic->scene_manager); | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_show_loading_popup(void* context, bool show) { | ||||
|     NfcMagic* nfc_magic = 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_magic->view_dispatcher, NfcMagicViewLoading); | ||||
|     } else { | ||||
|         // Restore default timer priority
 | ||||
|         vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| NfcMagic* nfc_magic_alloc() { | ||||
|     NfcMagic* nfc_magic = malloc(sizeof(NfcMagic)); | ||||
| 
 | ||||
|     nfc_magic->worker = nfc_magic_worker_alloc(); | ||||
|     nfc_magic->view_dispatcher = view_dispatcher_alloc(); | ||||
|     nfc_magic->scene_manager = scene_manager_alloc(&nfc_magic_scene_handlers, nfc_magic); | ||||
|     view_dispatcher_enable_queue(nfc_magic->view_dispatcher); | ||||
|     view_dispatcher_set_event_callback_context(nfc_magic->view_dispatcher, nfc_magic); | ||||
|     view_dispatcher_set_custom_event_callback( | ||||
|         nfc_magic->view_dispatcher, nfc_magic_custom_event_callback); | ||||
|     view_dispatcher_set_navigation_event_callback( | ||||
|         nfc_magic->view_dispatcher, nfc_magic_back_event_callback); | ||||
|     view_dispatcher_set_tick_event_callback( | ||||
|         nfc_magic->view_dispatcher, nfc_magic_tick_event_callback, 100); | ||||
| 
 | ||||
|     // Nfc device
 | ||||
|     nfc_magic->nfc_dev = nfc_device_alloc(); | ||||
| 
 | ||||
|     // Open GUI record
 | ||||
|     nfc_magic->gui = furi_record_open(RECORD_GUI); | ||||
|     view_dispatcher_attach_to_gui( | ||||
|         nfc_magic->view_dispatcher, nfc_magic->gui, ViewDispatcherTypeFullscreen); | ||||
| 
 | ||||
|     // Open Notification record
 | ||||
|     nfc_magic->notifications = furi_record_open(RECORD_NOTIFICATION); | ||||
| 
 | ||||
|     // Submenu
 | ||||
|     nfc_magic->submenu = submenu_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         nfc_magic->view_dispatcher, NfcMagicViewMenu, submenu_get_view(nfc_magic->submenu)); | ||||
| 
 | ||||
|     // Popup
 | ||||
|     nfc_magic->popup = popup_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         nfc_magic->view_dispatcher, NfcMagicViewPopup, popup_get_view(nfc_magic->popup)); | ||||
| 
 | ||||
|     // Loading
 | ||||
|     nfc_magic->loading = loading_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         nfc_magic->view_dispatcher, NfcMagicViewLoading, loading_get_view(nfc_magic->loading)); | ||||
| 
 | ||||
|     // Text Input
 | ||||
|     nfc_magic->text_input = text_input_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         nfc_magic->view_dispatcher, | ||||
|         NfcMagicViewTextInput, | ||||
|         text_input_get_view(nfc_magic->text_input)); | ||||
| 
 | ||||
|     // Custom Widget
 | ||||
|     nfc_magic->widget = widget_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         nfc_magic->view_dispatcher, NfcMagicViewWidget, widget_get_view(nfc_magic->widget)); | ||||
| 
 | ||||
|     return nfc_magic; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_free(NfcMagic* nfc_magic) { | ||||
|     furi_assert(nfc_magic); | ||||
| 
 | ||||
|     // Nfc device
 | ||||
|     nfc_device_free(nfc_magic->nfc_dev); | ||||
| 
 | ||||
|     // Submenu
 | ||||
|     view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewMenu); | ||||
|     submenu_free(nfc_magic->submenu); | ||||
| 
 | ||||
|     // Popup
 | ||||
|     view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); | ||||
|     popup_free(nfc_magic->popup); | ||||
| 
 | ||||
|     // Loading
 | ||||
|     view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewLoading); | ||||
|     loading_free(nfc_magic->loading); | ||||
| 
 | ||||
|     // TextInput
 | ||||
|     view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewTextInput); | ||||
|     text_input_free(nfc_magic->text_input); | ||||
| 
 | ||||
|     // Custom Widget
 | ||||
|     view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); | ||||
|     widget_free(nfc_magic->widget); | ||||
| 
 | ||||
|     // Worker
 | ||||
|     nfc_magic_worker_stop(nfc_magic->worker); | ||||
|     nfc_magic_worker_free(nfc_magic->worker); | ||||
| 
 | ||||
|     // View Dispatcher
 | ||||
|     view_dispatcher_free(nfc_magic->view_dispatcher); | ||||
| 
 | ||||
|     // Scene Manager
 | ||||
|     scene_manager_free(nfc_magic->scene_manager); | ||||
| 
 | ||||
|     // GUI
 | ||||
|     furi_record_close(RECORD_GUI); | ||||
|     nfc_magic->gui = NULL; | ||||
| 
 | ||||
|     // Notifications
 | ||||
|     furi_record_close(RECORD_NOTIFICATION); | ||||
|     nfc_magic->notifications = NULL; | ||||
| 
 | ||||
|     free(nfc_magic); | ||||
| } | ||||
| 
 | ||||
| static const NotificationSequence nfc_magic_sequence_blink_start_blue = { | ||||
|     &message_blink_start_10, | ||||
|     &message_blink_set_color_blue, | ||||
|     &message_do_not_reset, | ||||
|     NULL, | ||||
| }; | ||||
| 
 | ||||
| static const NotificationSequence nfc_magic_sequence_blink_stop = { | ||||
|     &message_blink_stop, | ||||
|     NULL, | ||||
| }; | ||||
| 
 | ||||
| void nfc_magic_blink_start(NfcMagic* nfc_magic) { | ||||
|     notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_blue); | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_blink_stop(NfcMagic* nfc_magic) { | ||||
|     notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_stop); | ||||
| } | ||||
| 
 | ||||
| int32_t nfc_magic_app(void* p) { | ||||
|     UNUSED(p); | ||||
|     NfcMagic* nfc_magic = nfc_magic_alloc(); | ||||
| 
 | ||||
|     scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneStart); | ||||
| 
 | ||||
|     view_dispatcher_run(nfc_magic->view_dispatcher); | ||||
| 
 | ||||
|     nfc_magic_free(nfc_magic); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										3
									
								
								applications/plugins/nfc_magic/nfc_magic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								applications/plugins/nfc_magic/nfc_magic.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| typedef struct NfcMagic NfcMagic; | ||||
							
								
								
									
										77
									
								
								applications/plugins/nfc_magic/nfc_magic_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								applications/plugins/nfc_magic/nfc_magic_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "nfc_magic.h" | ||||
| #include "nfc_magic_worker.h" | ||||
| 
 | ||||
| #include "lib/magic/magic.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <gui/gui.h> | ||||
| #include <gui/view_dispatcher.h> | ||||
| #include <gui/scene_manager.h> | ||||
| #include <notification/notification_messages.h> | ||||
| 
 | ||||
| #include <gui/modules/submenu.h> | ||||
| #include <gui/modules/popup.h> | ||||
| #include <gui/modules/loading.h> | ||||
| #include <gui/modules/text_input.h> | ||||
| #include <gui/modules/widget.h> | ||||
| 
 | ||||
| #include <input/input.h> | ||||
| 
 | ||||
| #include "scenes/nfc_magic_scene.h" | ||||
| 
 | ||||
| #include <storage/storage.h> | ||||
| #include <lib/toolbox/path.h> | ||||
| 
 | ||||
| #include <lib/nfc/nfc_device.h> | ||||
| #include "nfc_magic_icons.h" | ||||
| 
 | ||||
| enum NfcMagicCustomEvent { | ||||
|     // Reserve first 100 events for button types and indexes, starting from 0
 | ||||
|     NfcMagicCustomEventReserved = 100, | ||||
| 
 | ||||
|     NfcMagicCustomEventViewExit, | ||||
|     NfcMagicCustomEventWorkerExit, | ||||
|     NfcMagicCustomEventByteInputDone, | ||||
|     NfcMagicCustomEventTextInputDone, | ||||
| }; | ||||
| 
 | ||||
| struct NfcMagic { | ||||
|     NfcMagicWorker* worker; | ||||
|     ViewDispatcher* view_dispatcher; | ||||
|     Gui* gui; | ||||
|     NotificationApp* notifications; | ||||
|     SceneManager* scene_manager; | ||||
|     // NfcMagicDevice* dev;
 | ||||
|     NfcDevice* nfc_dev; | ||||
| 
 | ||||
|     FuriString* text_box_store; | ||||
| 
 | ||||
|     // Common Views
 | ||||
|     Submenu* submenu; | ||||
|     Popup* popup; | ||||
|     Loading* loading; | ||||
|     TextInput* text_input; | ||||
|     Widget* widget; | ||||
| }; | ||||
| 
 | ||||
| typedef enum { | ||||
|     NfcMagicViewMenu, | ||||
|     NfcMagicViewPopup, | ||||
|     NfcMagicViewLoading, | ||||
|     NfcMagicViewTextInput, | ||||
|     NfcMagicViewWidget, | ||||
| } NfcMagicView; | ||||
| 
 | ||||
| NfcMagic* nfc_magic_alloc(); | ||||
| 
 | ||||
| void nfc_magic_text_store_set(NfcMagic* nfc_magic, const char* text, ...); | ||||
| 
 | ||||
| void nfc_magic_text_store_clear(NfcMagic* nfc_magic); | ||||
| 
 | ||||
| void nfc_magic_blink_start(NfcMagic* nfc_magic); | ||||
| 
 | ||||
| void nfc_magic_blink_stop(NfcMagic* nfc_magic); | ||||
| 
 | ||||
| void nfc_magic_show_loading_popup(void* context, bool show); | ||||
							
								
								
									
										173
									
								
								applications/plugins/nfc_magic/nfc_magic_worker.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								applications/plugins/nfc_magic/nfc_magic_worker.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,173 @@ | ||||
| #include "nfc_magic_worker_i.h" | ||||
| 
 | ||||
| #include "lib/magic/magic.h" | ||||
| 
 | ||||
| #define TAG "NfcMagicWorker" | ||||
| 
 | ||||
| static void | ||||
|     nfc_magic_worker_change_state(NfcMagicWorker* nfc_magic_worker, NfcMagicWorkerState state) { | ||||
|     furi_assert(nfc_magic_worker); | ||||
| 
 | ||||
|     nfc_magic_worker->state = state; | ||||
| } | ||||
| 
 | ||||
| NfcMagicWorker* nfc_magic_worker_alloc() { | ||||
|     NfcMagicWorker* nfc_magic_worker = malloc(sizeof(NfcMagicWorker)); | ||||
| 
 | ||||
|     // Worker thread attributes
 | ||||
|     nfc_magic_worker->thread = furi_thread_alloc(); | ||||
|     furi_thread_set_name(nfc_magic_worker->thread, "NfcMagicWorker"); | ||||
|     furi_thread_set_stack_size(nfc_magic_worker->thread, 8192); | ||||
|     furi_thread_set_callback(nfc_magic_worker->thread, nfc_magic_worker_task); | ||||
|     furi_thread_set_context(nfc_magic_worker->thread, nfc_magic_worker); | ||||
| 
 | ||||
|     nfc_magic_worker->callback = NULL; | ||||
|     nfc_magic_worker->context = NULL; | ||||
| 
 | ||||
|     nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateReady); | ||||
| 
 | ||||
|     return nfc_magic_worker; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_worker_free(NfcMagicWorker* nfc_magic_worker) { | ||||
|     furi_assert(nfc_magic_worker); | ||||
| 
 | ||||
|     furi_thread_free(nfc_magic_worker->thread); | ||||
|     free(nfc_magic_worker); | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker) { | ||||
|     furi_assert(nfc_magic_worker); | ||||
| 
 | ||||
|     nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateStop); | ||||
|     furi_thread_join(nfc_magic_worker->thread); | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_worker_start( | ||||
|     NfcMagicWorker* nfc_magic_worker, | ||||
|     NfcMagicWorkerState state, | ||||
|     NfcDeviceData* dev_data, | ||||
|     NfcMagicWorkerCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(nfc_magic_worker); | ||||
|     furi_assert(dev_data); | ||||
| 
 | ||||
|     nfc_magic_worker->callback = callback; | ||||
|     nfc_magic_worker->context = context; | ||||
|     nfc_magic_worker->dev_data = dev_data; | ||||
|     nfc_magic_worker_change_state(nfc_magic_worker, state); | ||||
|     furi_thread_start(nfc_magic_worker->thread); | ||||
| } | ||||
| 
 | ||||
| int32_t nfc_magic_worker_task(void* context) { | ||||
|     NfcMagicWorker* nfc_magic_worker = context; | ||||
| 
 | ||||
|     if(nfc_magic_worker->state == NfcMagicWorkerStateCheck) { | ||||
|         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 == NfcMagicWorkerStateWipe) { | ||||
|         nfc_magic_worker_wipe(nfc_magic_worker); | ||||
|     } | ||||
| 
 | ||||
|     nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateReady); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { | ||||
|     bool card_found_notified = false; | ||||
|     FuriHalNfcDevData nfc_data = {}; | ||||
|     MfClassicData* src_data = &nfc_magic_worker->dev_data->mf_classic_data; | ||||
| 
 | ||||
|     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, "Not Magic card"); | ||||
|                 nfc_magic_worker->callback( | ||||
|                     NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); | ||||
|                 break; | ||||
|             } | ||||
|             if(!magic_data_access_cmd()) { | ||||
|                 FURI_LOG_E(TAG, "Not 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); | ||||
|                     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; | ||||
|             } | ||||
|         } | ||||
|         furi_delay_ms(300); | ||||
|     } | ||||
|     magic_deactivate(); | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) { | ||||
|     bool card_found_notified = false; | ||||
| 
 | ||||
|     while(nfc_magic_worker->state == NfcMagicWorkerStateCheck) { | ||||
|         if(magic_wupa()) { | ||||
|             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); | ||||
|             break; | ||||
|         } else { | ||||
|             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_wipe(NfcMagicWorker* nfc_magic_worker) { | ||||
|     MfClassicBlock block; | ||||
|     memset(&block, 0, sizeof(MfClassicBlock)); | ||||
|     block.value[0] = 0x01; | ||||
|     block.value[1] = 0x02; | ||||
|     block.value[2] = 0x03; | ||||
|     block.value[3] = 0x04; | ||||
|     block.value[4] = 0x04; | ||||
|     block.value[5] = 0x08; | ||||
|     block.value[6] = 0x04; | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
|     magic_deactivate(); | ||||
| } | ||||
							
								
								
									
										38
									
								
								applications/plugins/nfc_magic/nfc_magic_worker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								applications/plugins/nfc_magic/nfc_magic_worker.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <lib/nfc/nfc_device.h> | ||||
| 
 | ||||
| typedef struct NfcMagicWorker NfcMagicWorker; | ||||
| 
 | ||||
| typedef enum { | ||||
|     NfcMagicWorkerStateReady, | ||||
| 
 | ||||
|     NfcMagicWorkerStateCheck, | ||||
|     NfcMagicWorkerStateWrite, | ||||
|     NfcMagicWorkerStateWipe, | ||||
| 
 | ||||
|     NfcMagicWorkerStateStop, | ||||
| } NfcMagicWorkerState; | ||||
| 
 | ||||
| typedef enum { | ||||
|     NfcMagicWorkerEventSuccess, | ||||
|     NfcMagicWorkerEventFail, | ||||
|     NfcMagicWorkerEventCardDetected, | ||||
|     NfcMagicWorkerEventNoCardDetected, | ||||
|     NfcMagicWorkerEventWrongCard, | ||||
| } NfcMagicWorkerEvent; | ||||
| 
 | ||||
| typedef bool (*NfcMagicWorkerCallback)(NfcMagicWorkerEvent event, void* context); | ||||
| 
 | ||||
| NfcMagicWorker* nfc_magic_worker_alloc(); | ||||
| 
 | ||||
| void nfc_magic_worker_free(NfcMagicWorker* nfc_magic_worker); | ||||
| 
 | ||||
| void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker); | ||||
| 
 | ||||
| void nfc_magic_worker_start( | ||||
|     NfcMagicWorker* nfc_magic_worker, | ||||
|     NfcMagicWorkerState state, | ||||
|     NfcDeviceData* dev_data, | ||||
|     NfcMagicWorkerCallback callback, | ||||
|     void* context); | ||||
							
								
								
									
										24
									
								
								applications/plugins/nfc_magic/nfc_magic_worker_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								applications/plugins/nfc_magic/nfc_magic_worker_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #include "nfc_magic_worker.h" | ||||
| 
 | ||||
| struct NfcMagicWorker { | ||||
|     FuriThread* thread; | ||||
| 
 | ||||
|     NfcDeviceData* dev_data; | ||||
| 
 | ||||
|     NfcMagicWorkerCallback callback; | ||||
|     void* context; | ||||
| 
 | ||||
|     NfcMagicWorkerState state; | ||||
| }; | ||||
| 
 | ||||
| int32_t nfc_magic_worker_task(void* context); | ||||
| 
 | ||||
| void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker); | ||||
| 
 | ||||
| void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker); | ||||
| 
 | ||||
| void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker); | ||||
							
								
								
									
										30
									
								
								applications/plugins/nfc_magic/scenes/nfc_magic_scene.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								applications/plugins/nfc_magic/scenes/nfc_magic_scene.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| #include "nfc_magic_scene.h" | ||||
| 
 | ||||
| // Generate scene on_enter handlers array
 | ||||
| #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, | ||||
| void (*const nfc_magic_on_enter_handlers[])(void*) = { | ||||
| #include "nfc_magic_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_magic_on_event_handlers[])(void* context, SceneManagerEvent event) = { | ||||
| #include "nfc_magic_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_magic_on_exit_handlers[])(void* context) = { | ||||
| #include "nfc_magic_scene_config.h" | ||||
| }; | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| // Initialize scene handlers configuration structure
 | ||||
| const SceneManagerHandlers nfc_magic_scene_handlers = { | ||||
|     .on_enter_handlers = nfc_magic_on_enter_handlers, | ||||
|     .on_event_handlers = nfc_magic_on_event_handlers, | ||||
|     .on_exit_handlers = nfc_magic_on_exit_handlers, | ||||
|     .scene_num = NfcMagicSceneNum, | ||||
| }; | ||||
							
								
								
									
										29
									
								
								applications/plugins/nfc_magic/scenes/nfc_magic_scene.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								applications/plugins/nfc_magic/scenes/nfc_magic_scene.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/scene_manager.h> | ||||
| 
 | ||||
| // Generate scene id and total number
 | ||||
| #define ADD_SCENE(prefix, name, id) NfcMagicScene##id, | ||||
| typedef enum { | ||||
| #include "nfc_magic_scene_config.h" | ||||
|     NfcMagicSceneNum, | ||||
| } NfcMagicScene; | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| extern const SceneManagerHandlers nfc_magic_scene_handlers; | ||||
| 
 | ||||
| // Generate scene on_enter handlers declaration
 | ||||
| #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); | ||||
| #include "nfc_magic_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_magic_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_magic_scene_config.h" | ||||
| #undef ADD_SCENE | ||||
| @ -0,0 +1,87 @@ | ||||
| #include "../nfc_magic_i.h" | ||||
| 
 | ||||
| enum { | ||||
|     NfcMagicSceneCheckStateCardSearch, | ||||
|     NfcMagicSceneCheckStateCardFound, | ||||
| }; | ||||
| 
 | ||||
| bool nfc_magic_check_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_check_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, NfcMagicSceneCheck); | ||||
| 
 | ||||
|     if(state == NfcMagicSceneCheckStateCardSearch) { | ||||
|         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); | ||||
|     } else { | ||||
|         popup_set_icon(popup, 12, 23, &I_Loading_24); | ||||
|         popup_set_header(popup, "Checking\nDon't move...", 52, 32, AlignLeft, AlignCenter); | ||||
|     } | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_check_on_enter(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     scene_manager_set_scene_state( | ||||
|         nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch); | ||||
|     nfc_magic_scene_check_setup_view(nfc_magic); | ||||
| 
 | ||||
|     // Setup and start worker
 | ||||
|     nfc_magic_worker_start( | ||||
|         nfc_magic->worker, | ||||
|         NfcMagicWorkerStateCheck, | ||||
|         &nfc_magic->nfc_dev->dev_data, | ||||
|         nfc_magic_check_worker_callback, | ||||
|         nfc_magic); | ||||
|     nfc_magic_blink_start(nfc_magic); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_check_on_event(void* context, SceneManagerEvent event) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcMagicWorkerEventSuccess) { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneMagicInfo); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcMagicWorkerEventWrongCard) { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNotMagic); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcMagicWorkerEventCardDetected) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardFound); | ||||
|             nfc_magic_scene_check_setup_view(nfc_magic); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcMagicWorkerEventNoCardDetected) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch); | ||||
|             nfc_magic_scene_check_setup_view(nfc_magic); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_check_on_exit(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     nfc_magic_worker_stop(nfc_magic->worker); | ||||
|     scene_manager_set_scene_state( | ||||
|         nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch); | ||||
|     // Clear view
 | ||||
|     popup_reset(nfc_magic->popup); | ||||
| 
 | ||||
|     nfc_magic_blink_stop(nfc_magic); | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| ADD_SCENE(nfc_magic, start, Start) | ||||
| ADD_SCENE(nfc_magic, file_select, FileSelect) | ||||
| ADD_SCENE(nfc_magic, write_confirm, WriteConfirm) | ||||
| ADD_SCENE(nfc_magic, wrong_card, WrongCard) | ||||
| ADD_SCENE(nfc_magic, write, Write) | ||||
| ADD_SCENE(nfc_magic, write_fail, WriteFail) | ||||
| 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, wipe, Wipe) | ||||
| ADD_SCENE(nfc_magic, wipe_fail, WipeFail) | ||||
| @ -0,0 +1,34 @@ | ||||
| #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); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
|     if(nfc_file_select(nfc_magic->nfc_dev)) { | ||||
|         if(nfc_magic_scene_file_select_is_file_suitable(nfc_magic->nfc_dev)) { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWriteConfirm); | ||||
|         } else { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWrongCard); | ||||
|         } | ||||
|     } else { | ||||
|         scene_manager_previous_scene(nfc_magic->scene_manager); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_file_select_on_event(void* context, SceneManagerEvent event) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(event); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| @ -0,0 +1,45 @@ | ||||
| #include "../nfc_magic_i.h" | ||||
| 
 | ||||
| void nfc_magic_scene_magic_info_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_magic_info_on_enter(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     Widget* widget = nfc_magic->widget; | ||||
| 
 | ||||
|     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_button_element( | ||||
|         widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_magic_info_widget_callback, nfc_magic); | ||||
| 
 | ||||
|     // Setup and start worker
 | ||||
|     view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_magic_info_on_event(void* context, SceneManagerEvent event) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == GuiButtonTypeLeft) { | ||||
|             consumed = scene_manager_previous_scene(nfc_magic->scene_manager); | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_magic_info_on_exit(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     widget_reset(nfc_magic->widget); | ||||
| } | ||||
| @ -0,0 +1,44 @@ | ||||
| #include "../nfc_magic_i.h" | ||||
| 
 | ||||
| void nfc_magic_scene_not_magic_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_not_magic_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, 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_add_button_element( | ||||
|         widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_not_magic_widget_callback, nfc_magic); | ||||
| 
 | ||||
|     // Setup and start worker
 | ||||
|     view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_not_magic_on_event(void* context, SceneManagerEvent event) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == GuiButtonTypeLeft) { | ||||
|             consumed = scene_manager_previous_scene(nfc_magic->scene_manager); | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_not_magic_on_exit(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     widget_reset(nfc_magic->widget); | ||||
| } | ||||
| @ -0,0 +1,61 @@ | ||||
| #include "../nfc_magic_i.h" | ||||
| enum SubmenuIndex { | ||||
|     SubmenuIndexCheck, | ||||
|     SubmenuIndexWriteGen1A, | ||||
|     SubmenuIndexWipe, | ||||
| }; | ||||
| 
 | ||||
| void nfc_magic_scene_start_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_start_on_enter(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     Submenu* submenu = nfc_magic->submenu; | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "Check Magic Tag", | ||||
|         SubmenuIndexCheck, | ||||
|         nfc_magic_scene_start_submenu_callback, | ||||
|         nfc_magic); | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "Write Gen1A", | ||||
|         SubmenuIndexWriteGen1A, | ||||
|         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)); | ||||
|     view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexCheck) { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexWriteGen1A) { | ||||
|             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, NfcMagicSceneStart, event.event); | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_start_on_exit(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     submenu_reset(nfc_magic->submenu); | ||||
| } | ||||
| @ -0,0 +1,42 @@ | ||||
| #include "../nfc_magic_i.h" | ||||
| 
 | ||||
| void nfc_magic_scene_success_popup_callback(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, NfcMagicCustomEventViewExit); | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_success_on_enter(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     notification_message(nfc_magic->notifications, &sequence_success); | ||||
| 
 | ||||
|     Popup* popup = nfc_magic->popup; | ||||
|     popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); | ||||
|     popup_set_header(popup, "Success!", 10, 20, AlignLeft, AlignBottom); | ||||
|     popup_set_timeout(popup, 1500); | ||||
|     popup_set_context(popup, nfc_magic); | ||||
|     popup_set_callback(popup, nfc_magic_scene_success_popup_callback); | ||||
|     popup_enable_timeout(popup); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcMagicCustomEventViewExit) { | ||||
|             consumed = scene_manager_search_and_switch_to_previous_scene( | ||||
|                 nfc_magic->scene_manager, NfcMagicSceneStart); | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_success_on_exit(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     // Clear view
 | ||||
|     popup_reset(nfc_magic->popup); | ||||
| } | ||||
							
								
								
									
										90
									
								
								applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,90 @@ | ||||
| #include "../nfc_magic_i.h" | ||||
| 
 | ||||
| enum { | ||||
|     NfcMagicSceneWipeStateCardSearch, | ||||
|     NfcMagicSceneWipeStateCardFound, | ||||
| }; | ||||
| 
 | ||||
| bool nfc_magic_wipe_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_wipe_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, NfcMagicSceneWipe); | ||||
| 
 | ||||
|     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); | ||||
|     } else { | ||||
|         popup_set_icon(popup, 12, 23, &I_Loading_24); | ||||
|         popup_set_header(popup, "Wiping\nDon't move...", 52, 32, AlignLeft, AlignCenter); | ||||
|     } | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_wipe_on_enter(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     scene_manager_set_scene_state( | ||||
|         nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardSearch); | ||||
|     nfc_magic_scene_wipe_setup_view(nfc_magic); | ||||
| 
 | ||||
|     // Setup and start worker
 | ||||
|     nfc_magic_worker_start( | ||||
|         nfc_magic->worker, | ||||
|         NfcMagicWorkerStateWipe, | ||||
|         &nfc_magic->nfc_dev->dev_data, | ||||
|         nfc_magic_wipe_worker_callback, | ||||
|         nfc_magic); | ||||
|     nfc_magic_blink_start(nfc_magic); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_wipe_on_event(void* context, SceneManagerEvent event) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcMagicWorkerEventSuccess) { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneSuccess); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcMagicWorkerEventFail) { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipeFail); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcMagicWorkerEventWrongCard) { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNotMagic); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcMagicWorkerEventCardDetected) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardFound); | ||||
|             nfc_magic_scene_wipe_setup_view(nfc_magic); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcMagicWorkerEventNoCardDetected) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardSearch); | ||||
|             nfc_magic_scene_wipe_setup_view(nfc_magic); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_wipe_on_exit(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     nfc_magic_worker_stop(nfc_magic->worker); | ||||
|     scene_manager_set_scene_state( | ||||
|         nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardSearch); | ||||
|     // Clear view
 | ||||
|     popup_reset(nfc_magic->popup); | ||||
| 
 | ||||
|     nfc_magic_blink_stop(nfc_magic); | ||||
| } | ||||
| @ -0,0 +1,41 @@ | ||||
| #include "../nfc_magic_i.h" | ||||
| 
 | ||||
| void nfc_magic_scene_wipe_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_wipe_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, 73, 17, &I_DolphinCommon_56x48); | ||||
|     widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Wipe failed"); | ||||
|     widget_add_button_element( | ||||
|         widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wipe_fail_widget_callback, nfc_magic); | ||||
| 
 | ||||
|     // Setup and start worker
 | ||||
|     view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_wipe_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_previous_scene(nfc_magic->scene_manager); | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_wipe_fail_on_exit(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     widget_reset(nfc_magic->widget); | ||||
| } | ||||
| @ -0,0 +1,90 @@ | ||||
| #include "../nfc_magic_i.h" | ||||
| 
 | ||||
| enum { | ||||
|     NfcMagicSceneWriteStateCardSearch, | ||||
|     NfcMagicSceneWriteStateCardFound, | ||||
| }; | ||||
| 
 | ||||
| bool nfc_magic_write_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_write_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, NfcMagicSceneWrite); | ||||
| 
 | ||||
|     if(state == NfcMagicSceneWriteStateCardSearch) { | ||||
|         popup_set_text( | ||||
|             nfc_magic->popup, "Apply card to\nthe 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_write_on_enter(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     scene_manager_set_scene_state( | ||||
|         nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardSearch); | ||||
|     nfc_magic_scene_write_setup_view(nfc_magic); | ||||
| 
 | ||||
|     // Setup and start worker
 | ||||
|     nfc_magic_worker_start( | ||||
|         nfc_magic->worker, | ||||
|         NfcMagicWorkerStateWrite, | ||||
|         &nfc_magic->nfc_dev->dev_data, | ||||
|         nfc_magic_write_worker_callback, | ||||
|         nfc_magic); | ||||
|     nfc_magic_blink_start(nfc_magic); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_write_on_event(void* context, SceneManagerEvent event) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcMagicWorkerEventSuccess) { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneSuccess); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcMagicWorkerEventFail) { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWriteFail); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcMagicWorkerEventWrongCard) { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNotMagic); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcMagicWorkerEventCardDetected) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardFound); | ||||
|             nfc_magic_scene_write_setup_view(nfc_magic); | ||||
|             consumed = true; | ||||
|         } else if(event.event == NfcMagicWorkerEventNoCardDetected) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardSearch); | ||||
|             nfc_magic_scene_write_setup_view(nfc_magic); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_write_on_exit(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     nfc_magic_worker_stop(nfc_magic->worker); | ||||
|     scene_manager_set_scene_state( | ||||
|         nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardSearch); | ||||
|     // Clear view
 | ||||
|     popup_reset(nfc_magic->popup); | ||||
| 
 | ||||
|     nfc_magic_blink_stop(nfc_magic); | ||||
| } | ||||
| @ -0,0 +1,64 @@ | ||||
| #include "../nfc_magic_i.h" | ||||
| 
 | ||||
| void nfc_magic_scene_write_confirm_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_write_confirm_on_enter(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     Widget* widget = nfc_magic->widget; | ||||
| 
 | ||||
|     widget_add_string_element(widget, 3, 0, AlignLeft, AlignTop, FontPrimary, "Risky operation"); | ||||
|     widget_add_text_box_element( | ||||
|         widget, | ||||
|         0, | ||||
|         13, | ||||
|         128, | ||||
|         54, | ||||
|         AlignLeft, | ||||
|         AlignTop, | ||||
|         "Writing to this card will change manufacturer block. On some cards it may not be rewritten", | ||||
|         false); | ||||
|     widget_add_button_element( | ||||
|         widget, | ||||
|         GuiButtonTypeCenter, | ||||
|         "Continue", | ||||
|         nfc_magic_scene_write_confirm_widget_callback, | ||||
|         nfc_magic); | ||||
|     widget_add_button_element( | ||||
|         widget, | ||||
|         GuiButtonTypeLeft, | ||||
|         "Back", | ||||
|         nfc_magic_scene_write_confirm_widget_callback, | ||||
|         nfc_magic); | ||||
| 
 | ||||
|     // Setup and start worker
 | ||||
|     view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_write_confirm_on_event(void* context, SceneManagerEvent event) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == GuiButtonTypeLeft) { | ||||
|             consumed = scene_manager_previous_scene(nfc_magic->scene_manager); | ||||
|         } else if(event.event == GuiButtonTypeCenter) { | ||||
|             scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWrite); | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_write_confirm_on_exit(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     widget_reset(nfc_magic->widget); | ||||
| } | ||||
| @ -0,0 +1,58 @@ | ||||
| #include "../nfc_magic_i.h" | ||||
| 
 | ||||
| void nfc_magic_scene_write_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_write_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, "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_magic_scene_write_fail_widget_callback, nfc_magic); | ||||
| 
 | ||||
|     // Setup and start worker
 | ||||
|     view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_write_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_write_fail_on_exit(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     widget_reset(nfc_magic->widget); | ||||
| } | ||||
| @ -0,0 +1,53 @@ | ||||
| #include "../nfc_magic_i.h" | ||||
| 
 | ||||
| void nfc_magic_scene_wrong_card_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_wrong_card_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, 73, 17, &I_DolphinCommon_56x48); | ||||
|     widget_add_string_element( | ||||
|         widget, 1, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); | ||||
|     widget_add_string_multiline_element( | ||||
|         widget, | ||||
|         1, | ||||
|         17, | ||||
|         AlignLeft, | ||||
|         AlignTop, | ||||
|         FontSecondary, | ||||
|         "Writing is supported\nonly for 4 bytes UID\nMifare CLassic 1k"); | ||||
|     widget_add_button_element( | ||||
|         widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wrong_card_widget_callback, nfc_magic); | ||||
| 
 | ||||
|     // Setup and start worker
 | ||||
|     view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool nfc_magic_scene_wrong_card_on_event(void* context, SceneManagerEvent event) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == GuiButtonTypeLeft) { | ||||
|             consumed = scene_manager_previous_scene(nfc_magic->scene_manager); | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void nfc_magic_scene_wrong_card_on_exit(void* context) { | ||||
|     NfcMagic* nfc_magic = context; | ||||
| 
 | ||||
|     widget_reset(nfc_magic->widget); | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/picopass/125_10px.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/picopass/125_10px.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 308 B | 
| @ -9,7 +9,7 @@ App( | ||||
|     ], | ||||
|     stack_size=4 * 1024, | ||||
|     order=30, | ||||
|     fap_icon="../../../assets/icons/Archive/125_10px.png", | ||||
|     fap_icon="125_10px.png", | ||||
|     fap_category="Tools", | ||||
|     fap_libs=["mbedtls"], | ||||
|     fap_private_libs=[ | ||||
|  | ||||
| @ -9,8 +9,6 @@ | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <st25r3916.h> | ||||
| #include <rfal_analogConfig.h> | ||||
| #include <rfal_rf.h> | ||||
| 
 | ||||
| #include <platform.h> | ||||
|  | ||||
| @ -1,5 +1,4 @@ | ||||
| #include "rfal_picopass.h" | ||||
| #include "utils.h" | ||||
| 
 | ||||
| #define RFAL_PICOPASS_TXRX_FLAGS                                                    \ | ||||
|     (FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | \ | ||||
| @ -97,7 +96,7 @@ FuriHalNfcReturn rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* s | ||||
| 
 | ||||
|     rfalPicoPassSelectReq selReq; | ||||
|     selReq.CMD = RFAL_PICOPASS_CMD_SELECT; | ||||
|     ST_MEMCPY(selReq.CSN, csn, RFAL_PICOPASS_UID_LEN); | ||||
|     memcpy(selReq.CSN, csn, RFAL_PICOPASS_UID_LEN); | ||||
|     uint16_t recvLen = 0; | ||||
|     uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; | ||||
|     uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); | ||||
| @ -146,8 +145,8 @@ FuriHalNfcReturn rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chk | ||||
|     FuriHalNfcReturn ret; | ||||
|     rfalPicoPassCheckReq chkReq; | ||||
|     chkReq.CMD = RFAL_PICOPASS_CMD_CHECK; | ||||
|     ST_MEMCPY(chkReq.mac, mac, 4); | ||||
|     ST_MEMSET(chkReq.null, 0, 4); | ||||
|     memcpy(chkReq.mac, mac, 4); | ||||
|     memset(chkReq.null, 0, 4); | ||||
|     uint16_t recvLen = 0; | ||||
|     uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; | ||||
|     uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); | ||||
|  | ||||
| @ -318,7 +318,6 @@ static void | ||||
| 
 | ||||
| int32_t snake_game_app(void* p) { | ||||
|     UNUSED(p); | ||||
|     srand(DWT->CYCCNT); | ||||
| 
 | ||||
|     FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent)); | ||||
| 
 | ||||
| @ -380,6 +379,8 @@ int32_t snake_game_app(void* p) { | ||||
|                     case InputKeyBack: | ||||
|                         processing = false; | ||||
|                         break; | ||||
|                     default: | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } else if(event.type == EventTypeTick) { | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| #define WS_VERSION_APP "0.3.1" | ||||
| #define WS_VERSION_APP "0.4" | ||||
| #define WS_DEVELOPED "SkorP" | ||||
| #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
 | ||||
| 
 | ||||
|  | ||||
| @ -4,7 +4,7 @@ | ||||
| 
 | ||||
| /*
 | ||||
|  * Help | ||||
|  * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c
 | ||||
|  * https://github.com/merbanan/rtl_433/blob/master/src/devices/acurite.c
 | ||||
|  *  | ||||
|  * Acurite 592TXR Temperature Humidity sensor decoder | ||||
|  * Message Type 0x04, 7 bytes | ||||
| @ -293,7 +293,7 @@ void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* ou | ||||
|         "%s %dbit\r\n" | ||||
|         "Key:0x%lX%08lX\r\n" | ||||
|         "Sn:0x%lX Ch:%d  Bat:%d\r\n" | ||||
|         "Temp:%d.%d C Hum:%d%%", | ||||
|         "Temp:%3.1f C Hum:%d%%", | ||||
|         instance->generic.protocol_name, | ||||
|         instance->generic.data_count_bit, | ||||
|         (uint32_t)(instance->generic.data >> 32), | ||||
| @ -301,7 +301,6 @@ void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* ou | ||||
|         instance->generic.id, | ||||
|         instance->generic.channel, | ||||
|         instance->generic.battery_low, | ||||
|         (int16_t)instance->generic.temp, | ||||
|         abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), | ||||
|         (double)instance->generic.temp, | ||||
|         instance->generic.humidity); | ||||
| } | ||||
|  | ||||
| @ -234,7 +234,7 @@ void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* out | ||||
|         "%s %dbit\r\n" | ||||
|         "Key:0x%lX%08lX\r\n" | ||||
|         "Sn:0x%lX Ch:%d  Bat:%d\r\n" | ||||
|         "Temp:%d.%d C Hum:%d%%", | ||||
|         "Temp:%3.1f C Hum:%d%%", | ||||
|         instance->generic.protocol_name, | ||||
|         instance->generic.data_count_bit, | ||||
|         (uint32_t)(instance->generic.data >> 32), | ||||
| @ -242,7 +242,6 @@ void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* out | ||||
|         instance->generic.id, | ||||
|         instance->generic.channel, | ||||
|         instance->generic.battery_low, | ||||
|         (int16_t)instance->generic.temp, | ||||
|         abs(((int16_t)(instance->generic.temp * 10) - (((int16_t)instance->generic.temp) * 10))), | ||||
|         (double)instance->generic.temp, | ||||
|         instance->generic.humidity); | ||||
| } | ||||
|  | ||||
							
								
								
									
										247
									
								
								applications/plugins/weather_station/protocols/acurite_609txc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								applications/plugins/weather_station/protocols/acurite_609txc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,247 @@ | ||||
| #include "acurite_609txc.h" | ||||
| 
 | ||||
| #define TAG "WSProtocolAcurite_609TXC" | ||||
| 
 | ||||
| /*
 | ||||
|  * Help | ||||
|  * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c#L216
 | ||||
|  * | ||||
|  *     0000 1111 | 0011 0000 | 0101 1100 | 0000 0000 | 1110 0111 | ||||
|  *     iiii iiii | buuu tttt | tttt tttt | hhhh hhhh | cccc cccc | ||||
|  * - i: identification; changes on battery switch | ||||
|  * - c: checksum (sum of previous by bytes) | ||||
|  * - u: unknown | ||||
|  * - b: battery low; flag to indicate low battery voltage | ||||
|  * - t: temperature; in °C * 10, 12 bit with complement | ||||
|  * - h: humidity | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| static const SubGhzBlockConst ws_protocol_acurite_609txc_const = { | ||||
|     .te_short = 500, | ||||
|     .te_long = 1000, | ||||
|     .te_delta = 150, | ||||
|     .min_count_bit_for_found = 40, | ||||
| }; | ||||
| 
 | ||||
| struct WSProtocolDecoderAcurite_609TXC { | ||||
|     SubGhzProtocolDecoderBase base; | ||||
| 
 | ||||
|     SubGhzBlockDecoder decoder; | ||||
|     WSBlockGeneric generic; | ||||
| }; | ||||
| 
 | ||||
| struct WSProtocolEncoderAcurite_609TXC { | ||||
|     SubGhzProtocolEncoderBase base; | ||||
| 
 | ||||
|     SubGhzProtocolBlockEncoder encoder; | ||||
|     WSBlockGeneric generic; | ||||
| }; | ||||
| 
 | ||||
| typedef enum { | ||||
|     Acurite_609TXCDecoderStepReset = 0, | ||||
|     Acurite_609TXCDecoderStepSaveDuration, | ||||
|     Acurite_609TXCDecoderStepCheckDuration, | ||||
| } Acurite_609TXCDecoderStep; | ||||
| 
 | ||||
| const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder = { | ||||
|     .alloc = ws_protocol_decoder_acurite_609txc_alloc, | ||||
|     .free = ws_protocol_decoder_acurite_609txc_free, | ||||
| 
 | ||||
|     .feed = ws_protocol_decoder_acurite_609txc_feed, | ||||
|     .reset = ws_protocol_decoder_acurite_609txc_reset, | ||||
| 
 | ||||
|     .get_hash_data = ws_protocol_decoder_acurite_609txc_get_hash_data, | ||||
|     .serialize = ws_protocol_decoder_acurite_609txc_serialize, | ||||
|     .deserialize = ws_protocol_decoder_acurite_609txc_deserialize, | ||||
|     .get_string = ws_protocol_decoder_acurite_609txc_get_string, | ||||
| }; | ||||
| 
 | ||||
| const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder = { | ||||
|     .alloc = NULL, | ||||
|     .free = NULL, | ||||
| 
 | ||||
|     .deserialize = NULL, | ||||
|     .stop = NULL, | ||||
|     .yield = NULL, | ||||
| }; | ||||
| 
 | ||||
| const SubGhzProtocol ws_protocol_acurite_609txc = { | ||||
|     .name = WS_PROTOCOL_ACURITE_609TXC_NAME, | ||||
|     .type = SubGhzProtocolWeatherStation, | ||||
|     .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | | ||||
|             SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, | ||||
| 
 | ||||
|     .decoder = &ws_protocol_acurite_609txc_decoder, | ||||
|     .encoder = &ws_protocol_acurite_609txc_encoder, | ||||
| }; | ||||
| 
 | ||||
| void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment) { | ||||
|     UNUSED(environment); | ||||
|     WSProtocolDecoderAcurite_609TXC* instance = malloc(sizeof(WSProtocolDecoderAcurite_609TXC)); | ||||
|     instance->base.protocol = &ws_protocol_acurite_609txc; | ||||
|     instance->generic.protocol_name = instance->base.protocol->name; | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void ws_protocol_decoder_acurite_609txc_free(void* context) { | ||||
|     furi_assert(context); | ||||
|     WSProtocolDecoderAcurite_609TXC* instance = context; | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void ws_protocol_decoder_acurite_609txc_reset(void* context) { | ||||
|     furi_assert(context); | ||||
|     WSProtocolDecoderAcurite_609TXC* instance = context; | ||||
|     instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; | ||||
| } | ||||
| 
 | ||||
| static bool ws_protocol_acurite_609txc_check(WSProtocolDecoderAcurite_609TXC* instance) { | ||||
|     if(!instance->decoder.decode_data) return false; | ||||
|     uint8_t crc = (uint8_t)(instance->decoder.decode_data >> 32) + | ||||
|                   (uint8_t)(instance->decoder.decode_data >> 24) + | ||||
|                   (uint8_t)(instance->decoder.decode_data >> 16) + | ||||
|                   (uint8_t)(instance->decoder.decode_data >> 8); | ||||
|     return (crc == (instance->decoder.decode_data & 0xFF)); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Analysis of received data | ||||
|  * @param instance Pointer to a WSBlockGeneric* instance | ||||
|  */ | ||||
| static void ws_protocol_acurite_609txc_remote_controller(WSBlockGeneric* instance) { | ||||
|     instance->id = (instance->data >> 32) & 0xFF; | ||||
|     instance->battery_low = (instance->data >> 31) & 1; | ||||
| 
 | ||||
|     instance->channel = WS_NO_CHANNEL; | ||||
| 
 | ||||
|     // Temperature in Celsius is encoded as a 12 bit integer value
 | ||||
|     // multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2)
 | ||||
|     // negative values are recovered by sign extend from int16_t.
 | ||||
|     int16_t temp_raw = | ||||
|         (int16_t)(((instance->data >> 12) & 0xf000) | ((instance->data >> 16) << 4)); | ||||
|     instance->temp = (temp_raw >> 4) * 0.1f; | ||||
|     instance->humidity = (instance->data >> 8) & 0xff; | ||||
|     instance->btn = WS_NO_BTN; | ||||
| } | ||||
| 
 | ||||
| void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration) { | ||||
|     furi_assert(context); | ||||
|     WSProtocolDecoderAcurite_609TXC* instance = context; | ||||
| 
 | ||||
|     switch(instance->decoder.parser_step) { | ||||
|     case Acurite_609TXCDecoderStepReset: | ||||
|         if((!level) && (DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short * 17) < | ||||
|                         ws_protocol_acurite_609txc_const.te_delta * 8)) { | ||||
|             //Found syncPrefix
 | ||||
|             instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; | ||||
|             instance->decoder.decode_data = 0; | ||||
|             instance->decoder.decode_count_bit = 0; | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case Acurite_609TXCDecoderStepSaveDuration: | ||||
|         if(level) { | ||||
|             instance->decoder.te_last = duration; | ||||
|             instance->decoder.parser_step = Acurite_609TXCDecoderStepCheckDuration; | ||||
|         } else { | ||||
|             instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case Acurite_609TXCDecoderStepCheckDuration: | ||||
|         if(!level) { | ||||
|             if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_609txc_const.te_short) < | ||||
|                ws_protocol_acurite_609txc_const.te_delta) { | ||||
|                 if((DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short) < | ||||
|                     ws_protocol_acurite_609txc_const.te_delta) || | ||||
|                    (duration > ws_protocol_acurite_609txc_const.te_long * 3)) { | ||||
|                     //Found syncPostfix
 | ||||
|                     instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; | ||||
|                     if((instance->decoder.decode_count_bit == | ||||
|                         ws_protocol_acurite_609txc_const.min_count_bit_for_found) && | ||||
|                        ws_protocol_acurite_609txc_check(instance)) { | ||||
|                         instance->generic.data = instance->decoder.decode_data; | ||||
|                         instance->generic.data_count_bit = instance->decoder.decode_count_bit; | ||||
|                         ws_protocol_acurite_609txc_remote_controller(&instance->generic); | ||||
|                         if(instance->base.callback) | ||||
|                             instance->base.callback(&instance->base, instance->base.context); | ||||
|                     } | ||||
|                     instance->decoder.decode_data = 0; | ||||
|                     instance->decoder.decode_count_bit = 0; | ||||
|                 } else if( | ||||
|                     DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_long) < | ||||
|                     ws_protocol_acurite_609txc_const.te_delta * 2) { | ||||
|                     subghz_protocol_blocks_add_bit(&instance->decoder, 0); | ||||
|                     instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; | ||||
|                 } else if( | ||||
|                     DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_long * 2) < | ||||
|                     ws_protocol_acurite_609txc_const.te_delta * 4) { | ||||
|                     subghz_protocol_blocks_add_bit(&instance->decoder, 1); | ||||
|                     instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; | ||||
|                 } else { | ||||
|                     instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; | ||||
|                 } | ||||
|             } else { | ||||
|                 instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; | ||||
|             } | ||||
|         } else { | ||||
|             instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context) { | ||||
|     furi_assert(context); | ||||
|     WSProtocolDecoderAcurite_609TXC* instance = context; | ||||
|     return subghz_protocol_blocks_get_hash_data( | ||||
|         &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); | ||||
| } | ||||
| 
 | ||||
| bool ws_protocol_decoder_acurite_609txc_serialize( | ||||
|     void* context, | ||||
|     FlipperFormat* flipper_format, | ||||
|     SubGhzRadioPreset* preset) { | ||||
|     furi_assert(context); | ||||
|     WSProtocolDecoderAcurite_609TXC* instance = context; | ||||
|     return ws_block_generic_serialize(&instance->generic, flipper_format, preset); | ||||
| } | ||||
| 
 | ||||
| bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format) { | ||||
|     furi_assert(context); | ||||
|     WSProtocolDecoderAcurite_609TXC* instance = context; | ||||
|     bool ret = false; | ||||
|     do { | ||||
|         if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { | ||||
|             break; | ||||
|         } | ||||
|         if(instance->generic.data_count_bit != | ||||
|            ws_protocol_acurite_609txc_const.min_count_bit_for_found) { | ||||
|             FURI_LOG_E(TAG, "Wrong number of bits in key"); | ||||
|             break; | ||||
|         } | ||||
|         ret = true; | ||||
|     } while(false); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output) { | ||||
|     furi_assert(context); | ||||
|     WSProtocolDecoderAcurite_609TXC* instance = context; | ||||
|     furi_string_printf( | ||||
|         output, | ||||
|         "%s %dbit\r\n" | ||||
|         "Key:0x%lX%08lX\r\n" | ||||
|         "Sn:0x%lX Ch:%d  Bat:%d\r\n" | ||||
|         "Temp:%3.1f C Hum:%d%%", | ||||
|         instance->generic.protocol_name, | ||||
|         instance->generic.data_count_bit, | ||||
|         (uint32_t)(instance->generic.data >> 40), | ||||
|         (uint32_t)(instance->generic.data), | ||||
|         instance->generic.id, | ||||
|         instance->generic.channel, | ||||
|         instance->generic.battery_low, | ||||
|         (double)instance->generic.temp, | ||||
|         instance->generic.humidity); | ||||
| } | ||||
| @ -0,0 +1,79 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <lib/subghz/protocols/base.h> | ||||
| 
 | ||||
| #include <lib/subghz/blocks/const.h> | ||||
| #include <lib/subghz/blocks/decoder.h> | ||||
| #include <lib/subghz/blocks/encoder.h> | ||||
| #include "ws_generic.h" | ||||
| #include <lib/subghz/blocks/math.h> | ||||
| 
 | ||||
| #define WS_PROTOCOL_ACURITE_609TXC_NAME "Acurite-609TXC" | ||||
| 
 | ||||
| typedef struct WSProtocolDecoderAcurite_609TXC WSProtocolDecoderAcurite_609TXC; | ||||
| typedef struct WSProtocolEncoderAcurite_609TXC WSProtocolEncoderAcurite_609TXC; | ||||
| 
 | ||||
| extern const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder; | ||||
| extern const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder; | ||||
| extern const SubGhzProtocol ws_protocol_acurite_609txc; | ||||
| 
 | ||||
| /**
 | ||||
|  * Allocate WSProtocolDecoderAcurite_609TXC. | ||||
|  * @param environment Pointer to a SubGhzEnvironment instance | ||||
|  * @return WSProtocolDecoderAcurite_609TXC* pointer to a WSProtocolDecoderAcurite_609TXC instance | ||||
|  */ | ||||
| void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment); | ||||
| 
 | ||||
| /**
 | ||||
|  * Free WSProtocolDecoderAcurite_609TXC. | ||||
|  * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance | ||||
|  */ | ||||
| void ws_protocol_decoder_acurite_609txc_free(void* context); | ||||
| 
 | ||||
| /**
 | ||||
|  * Reset decoder WSProtocolDecoderAcurite_609TXC. | ||||
|  * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance | ||||
|  */ | ||||
| void ws_protocol_decoder_acurite_609txc_reset(void* context); | ||||
| 
 | ||||
| /**
 | ||||
|  * Parse a raw sequence of levels and durations received from the air. | ||||
|  * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance | ||||
|  * @param level Signal level true-high false-low | ||||
|  * @param duration Duration of this level in, us | ||||
|  */ | ||||
| void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration); | ||||
| 
 | ||||
| /**
 | ||||
|  * Getting the hash sum of the last randomly received parcel. | ||||
|  * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance | ||||
|  * @return hash Hash sum | ||||
|  */ | ||||
| uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context); | ||||
| 
 | ||||
| /**
 | ||||
|  * Serialize data WSProtocolDecoderAcurite_609TXC. | ||||
|  * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance | ||||
|  * @param flipper_format Pointer to a FlipperFormat instance | ||||
|  * @param preset The modulation on which the signal was received, SubGhzRadioPreset | ||||
|  * @return true On success | ||||
|  */ | ||||
| bool ws_protocol_decoder_acurite_609txc_serialize( | ||||
|     void* context, | ||||
|     FlipperFormat* flipper_format, | ||||
|     SubGhzRadioPreset* preset); | ||||
| 
 | ||||
| /**
 | ||||
|  * Deserialize data WSProtocolDecoderAcurite_609TXC. | ||||
|  * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance | ||||
|  * @param flipper_format Pointer to a FlipperFormat instance | ||||
|  * @return true On success | ||||
|  */ | ||||
| bool ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format); | ||||
| 
 | ||||
| /**
 | ||||
|  * Getting a textual representation of the received data. | ||||
|  * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance | ||||
|  * @param output Resulting text | ||||
|  */ | ||||
| void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output); | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandr Kutuzov
						Aleksandr Kutuzov