mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-25 22:10:21 +00:00
Merge branch 'fz-dev' into dev
This commit is contained in:
commit
930b369812
44 changed files with 655 additions and 112 deletions
|
@ -7,6 +7,7 @@
|
|||
# construction of certain targets behind command-line options.
|
||||
|
||||
import os
|
||||
from fbt.util import path_as_posix
|
||||
|
||||
DefaultEnvironment(tools=[])
|
||||
|
||||
|
@ -200,9 +201,7 @@ firmware_debug = distenv.PhonyTarget(
|
|||
source=firmware_env["FW_ELF"],
|
||||
GDBOPTS="${GDBOPTS_BASE}",
|
||||
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||
FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT").replace(
|
||||
"\\", "/"
|
||||
),
|
||||
FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
|
||||
)
|
||||
distenv.Depends(firmware_debug, firmware_flash)
|
||||
|
||||
|
@ -212,9 +211,7 @@ distenv.PhonyTarget(
|
|||
source=firmware_env["FW_ELF"],
|
||||
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
||||
FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT").replace(
|
||||
"\\", "/"
|
||||
),
|
||||
FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
|
||||
)
|
||||
|
||||
# Debug alien elf
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,8 +29,13 @@ 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) {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
nfc->scene_manager, NfcSceneStart);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
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;
|
||||
|
|
|
@ -46,23 +46,47 @@ bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) {
|
|||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexDelete) {
|
||||
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;
|
||||
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) {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else if(event.event == SubmenuIndexDecode) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDecode);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDecodeRAW);
|
||||
return true;
|
||||
if(subghz_file_available(subghz)) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDecode);
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDecodeRAW);
|
||||
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;
|
||||
|
|
|
@ -210,20 +210,28 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
|
|||
break;
|
||||
|
||||
case SubGhzCustomEventViewReadRAWMore:
|
||||
if(subghz_scene_read_raw_update_filename(subghz)) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet);
|
||||
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW);
|
||||
consumed = true;
|
||||
if(subghz_file_available(subghz)) {
|
||||
if(subghz_scene_read_raw_update_filename(subghz)) {
|
||||
scene_manager_set_scene_state(
|
||||
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet);
|
||||
subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad;
|
||||
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW);
|
||||
consumed = true;
|
||||
} else {
|
||||
furi_crash("SubGhz: RAW file name update error.");
|
||||
}
|
||||
} else {
|
||||
furi_crash("SubGhz: RAW file name update error.");
|
||||
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) {
|
||||
|
@ -250,6 +258,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;
|
||||
|
@ -326,11 +340,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;
|
||||
|
|
|
@ -497,6 +497,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);
|
||||
|
||||
|
|
|
@ -153,6 +153,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);
|
||||
|
|
|
@ -210,5 +210,5 @@ bool magic_wipe() {
|
|||
|
||||
void magic_deactivate() {
|
||||
furi_hal_nfc_ll_txrx_off();
|
||||
furi_hal_nfc_start_sleep();
|
||||
furi_hal_nfc_sleep();
|
||||
}
|
||||
|
|
|
@ -167,7 +167,6 @@ void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) {
|
|||
if(!magic_data_access_cmd()) continue;
|
||||
if(!magic_write_blk(0, &block)) continue;
|
||||
nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context);
|
||||
magic_deactivate();
|
||||
break;
|
||||
}
|
||||
magic_deactivate();
|
||||
|
|
|
@ -372,7 +372,7 @@ RpcSession* rpc_session_open(Rpc* rpc) {
|
|||
|
||||
session->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(session->thread, "RpcSessionWorker");
|
||||
furi_thread_set_stack_size(session->thread, 2048);
|
||||
furi_thread_set_stack_size(session->thread, 3072);
|
||||
furi_thread_set_context(session->thread, session);
|
||||
furi_thread_set_callback(session->thread, rpc_session_worker);
|
||||
|
||||
|
|
2
documentation/.gitignore
vendored
Normal file
2
documentation/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/html
|
||||
/latex
|
|
@ -40,6 +40,7 @@ Only 2 parameters are mandatory: ***appid*** and ***apptype***, others are optio
|
|||
* **icon**: Animated icon name from built-in assets to be used when building app as a part of firmware.
|
||||
* **order**: Order of an application within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. *Used for ordering startup hooks and menu entries.*
|
||||
* **sdk_headers**: List of C header files from this app's code to include in API definitions for external applications.
|
||||
* **targets**: list of strings, target names, which this application is compatible with. If not specified, application is built for all targets. Default value is `["all"]`.
|
||||
|
||||
|
||||
#### Parameters for external applications
|
||||
|
|
|
@ -872,12 +872,9 @@ WARN_LOGFILE =
|
|||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = applications \
|
||||
core \
|
||||
lib/infrared \
|
||||
lib/subghz \
|
||||
lib/toolbox \
|
||||
lib/onewire \
|
||||
firmware/targets/furi_hal_include
|
||||
lib \
|
||||
firmware \
|
||||
furi
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
@ -930,7 +927,18 @@ RECURSIVE = YES
|
|||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE =
|
||||
EXCLUDE = \
|
||||
lib/mlib \
|
||||
lib/STM32CubeWB \
|
||||
lib/littlefs \
|
||||
lib/nanopb \
|
||||
assets/protobuf \
|
||||
lib/libusb_stm32 \
|
||||
lib/FreeRTOS-Kernel \
|
||||
lib/microtar \
|
||||
lib/mbedtls \
|
||||
lib/cxxheaderparser \
|
||||
applications/plugins/dap_link/lib/free-dap
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Key Combos
|
||||
|
||||
There are times when your flipper feels blue and don't respond to your commands.
|
||||
In that case you may find this guide useful.
|
||||
There are times when your flipper feels blue and doesn't respond to your commands.
|
||||
In that case, you may find this guide useful.
|
||||
|
||||
|
||||
## Basic Combos
|
||||
|
@ -9,7 +9,7 @@ In that case you may find this guide useful.
|
|||
|
||||
### Hardware Reset
|
||||
|
||||
- Press `LEFT` and `BACK` and hold for couple seconds
|
||||
- Press `LEFT` and `BACK` and hold for a couple of seconds
|
||||
- Release `LEFT` and `BACK`
|
||||
|
||||
This combo performs hardware reset by pulling MCU reset line down.
|
||||
|
@ -29,7 +29,7 @@ There is 1 case when it's not working:
|
|||
- If you have not disconnected USB, then disconnect USB and repeat previous step
|
||||
- Release `BACK` key
|
||||
|
||||
This combo performs reset by switching SYS power line off and then on.
|
||||
This combo performs a reset by switching SYS power line off and then on.
|
||||
Main components involved: Keys -> DD6(bq25896, charger)
|
||||
|
||||
There is 1 case when it's not working:
|
||||
|
@ -60,13 +60,13 @@ There is 1 case when it's not working:
|
|||
|
||||
### Hardware Reset + Software DFU
|
||||
|
||||
- Press `LEFT` and `BACK` and hold for couple seconds
|
||||
- Press `LEFT` and `BACK` and hold for a couple of seconds
|
||||
- Release `BACK`
|
||||
- Device will enter DFU with indication (Blue LED + DFU Screen)
|
||||
- Release `LEFT`
|
||||
|
||||
This combo performs hardware reset by pulling MCU reset line down.
|
||||
Then `LEFT` key indicates to boot-loader that DFU mode requested.
|
||||
Then `LEFT` key indicates to boot-loader that DFU mode is requested.
|
||||
|
||||
There are 2 cases when it's not working:
|
||||
|
||||
|
@ -76,7 +76,7 @@ There are 2 cases when it's not working:
|
|||
|
||||
### Hardware Reset + Hardware DFU
|
||||
|
||||
- Press `LEFT` and `BACK` and `OK` and hold for couple seconds
|
||||
- Press `LEFT` and `BACK` and `OK` and hold for a couple of seconds
|
||||
- Release `BACK` and `LEFT`
|
||||
- Device will enter DFU without indication
|
||||
|
||||
|
@ -127,8 +127,8 @@ There are 2 cases when it's not working:
|
|||
|
||||
If none of the described methods were useful:
|
||||
|
||||
- Ensure battery charged
|
||||
- Disconnect battery and connect again (Requires disassembly)
|
||||
- Try to Flash device with ST-Link or other programmer that support SWD
|
||||
- Ensure the battery charged
|
||||
- Disconnect the battery and connect again (Requires disassembly)
|
||||
- Try to Flash device with ST-Link or other programmer that supports SWD
|
||||
|
||||
If you still here and your device is not working: it's not software issue.
|
||||
If you still here and your device is not working: it's not a software issue.
|
||||
|
|
|
@ -77,6 +77,12 @@ FIRMWARE_APPS = {
|
|||
# Debug
|
||||
# "debug_apps",
|
||||
],
|
||||
"unit_tests": [
|
||||
"basic_services",
|
||||
"updater_app",
|
||||
"unit_tests",
|
||||
"nfc",
|
||||
],
|
||||
"debug_pack": [
|
||||
# Svc
|
||||
"basic_services",
|
||||
|
|
|
@ -40,11 +40,11 @@ env = ENV.Clone(
|
|||
FW_LIB_OPTS={
|
||||
"Default": {
|
||||
"CCFLAGS": [
|
||||
"-Os",
|
||||
"-Og" if ENV["LIB_DEBUG"] else "-Os",
|
||||
],
|
||||
"CPPDEFINES": [
|
||||
"NDEBUG",
|
||||
"FURI_NDEBUG",
|
||||
"FURI_DEBUG" if ENV["LIB_DEBUG"] else "FURI_NDEBUG",
|
||||
],
|
||||
# You can add other entries named after libraries
|
||||
# If they are present, they have precedence over Default
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,7.32,,
|
||||
Version,+,7.41,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
|
@ -1865,6 +1865,7 @@ Function,-,mf_classic_get_read_sectors_and_keys,void,"MfClassicData*, uint8_t*,
|
|||
Function,-,mf_classic_get_sector_by_block,uint8_t,uint8_t
|
||||
Function,-,mf_classic_get_sector_trailer_block_num_by_sector,uint8_t,uint8_t
|
||||
Function,-,mf_classic_get_sector_trailer_by_sector,MfClassicSectorTrailer*,"MfClassicData*, uint8_t"
|
||||
Function,-,mf_classic_get_total_block_num,uint16_t,MfClassicType
|
||||
Function,-,mf_classic_get_total_sectors_num,uint8_t,MfClassicType
|
||||
Function,-,mf_classic_get_type_str,const char*,MfClassicType
|
||||
Function,-,mf_classic_is_allowed_access_data_block,_Bool,"MfClassicData*, uint8_t, MfClassicKey, MfClassicAction"
|
||||
|
|
|
|
@ -1034,12 +1034,7 @@ static void nfc_device_get_shadow_path(FuriString* orig_path, FuriString* shadow
|
|||
furi_string_cat_printf(shadow_path, "%s", NFC_APP_SHADOW_EXTENSION);
|
||||
}
|
||||
|
||||
static bool nfc_device_save_file(
|
||||
NfcDevice* dev,
|
||||
const char* dev_name,
|
||||
const char* folder,
|
||||
const char* extension,
|
||||
bool use_load_path) {
|
||||
bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
|
||||
furi_assert(dev);
|
||||
|
||||
bool saved = false;
|
||||
|
@ -1049,19 +1044,10 @@ static bool nfc_device_save_file(
|
|||
temp_str = furi_string_alloc();
|
||||
|
||||
do {
|
||||
if(use_load_path && !furi_string_empty(dev->load_path)) {
|
||||
// Get directory name
|
||||
path_extract_dirname(furi_string_get_cstr(dev->load_path), temp_str);
|
||||
// Create nfc directory if necessary
|
||||
if(!storage_simply_mkdir(dev->storage, furi_string_get_cstr(temp_str))) break;
|
||||
// Make path to file to save
|
||||
furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension);
|
||||
} else {
|
||||
// Create nfc directory if necessary
|
||||
if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break;
|
||||
// First remove nfc device file if it was saved
|
||||
furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension);
|
||||
}
|
||||
// Create nfc directory if necessary
|
||||
if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break;
|
||||
// First remove nfc device file if it was saved
|
||||
furi_string_printf(temp_str, "%s", dev_name);
|
||||
// Open file
|
||||
if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
|
||||
// Write header
|
||||
|
@ -1102,13 +1088,19 @@ static bool nfc_device_save_file(
|
|||
return saved;
|
||||
}
|
||||
|
||||
bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
|
||||
return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_EXTENSION, true);
|
||||
}
|
||||
|
||||
bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) {
|
||||
bool nfc_device_save_shadow(NfcDevice* dev, const char* path) {
|
||||
dev->shadow_file_exist = true;
|
||||
return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION, true);
|
||||
// Replace extension from .nfc to .shd if necessary
|
||||
FuriString* orig_path = furi_string_alloc();
|
||||
furi_string_set_str(orig_path, path);
|
||||
FuriString* shadow_path = furi_string_alloc();
|
||||
nfc_device_get_shadow_path(orig_path, shadow_path);
|
||||
|
||||
bool file_saved = nfc_device_save(dev, furi_string_get_cstr(shadow_path));
|
||||
furi_string_free(orig_path);
|
||||
furi_string_free(shadow_path);
|
||||
|
||||
return file_saved;
|
||||
}
|
||||
|
||||
static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dialog) {
|
||||
|
@ -1225,7 +1217,7 @@ bool nfc_file_select(NfcDevice* dev) {
|
|||
};
|
||||
|
||||
bool res =
|
||||
dialog_file_browser_show(dev->dialogs, dev->load_path, nfc_app_folder, &browser_options);
|
||||
dialog_file_browser_show(dev->dialogs, dev->load_path, dev->load_path, &browser_options);
|
||||
|
||||
furi_string_free(nfc_app_folder);
|
||||
if(res) {
|
||||
|
|
|
@ -51,9 +51,19 @@ typedef struct {
|
|||
MfClassicDict* dict;
|
||||
} NfcMfClassicDictAttackData;
|
||||
|
||||
typedef enum {
|
||||
NfcReadModeAuto,
|
||||
NfcReadModeMfClassic,
|
||||
NfcReadModeMfUltralight,
|
||||
NfcReadModeMfDesfire,
|
||||
NfcReadModeEMV,
|
||||
NfcReadModeNFCA,
|
||||
} NfcReadMode;
|
||||
|
||||
typedef struct {
|
||||
FuriHalNfcDevData nfc_data;
|
||||
NfcProtocol protocol;
|
||||
NfcReadMode read_mode;
|
||||
union {
|
||||
NfcReaderRequestData reader_data;
|
||||
NfcMfClassicDictAttackData mf_classic_dict_attack_data;
|
||||
|
|
|
@ -90,7 +90,11 @@ int32_t nfc_worker_task(void* context) {
|
|||
furi_hal_nfc_exit_sleep();
|
||||
|
||||
if(nfc_worker->state == NfcWorkerStateRead) {
|
||||
nfc_worker_read(nfc_worker);
|
||||
if(nfc_worker->dev_data->read_mode == NfcReadModeAuto) {
|
||||
nfc_worker_read(nfc_worker);
|
||||
} else {
|
||||
nfc_worker_read_type(nfc_worker);
|
||||
}
|
||||
} else if(nfc_worker->state == NfcWorkerStateUidEmulate) {
|
||||
nfc_worker_emulate_uid(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
|
||||
|
@ -394,6 +398,81 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
|
|||
}
|
||||
}
|
||||
|
||||
void nfc_worker_read_type(NfcWorker* nfc_worker) {
|
||||
furi_assert(nfc_worker);
|
||||
furi_assert(nfc_worker->callback);
|
||||
|
||||
NfcReadMode read_mode = nfc_worker->dev_data->read_mode;
|
||||
nfc_device_data_clear(nfc_worker->dev_data);
|
||||
NfcDeviceData* dev_data = nfc_worker->dev_data;
|
||||
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
||||
FuriHalNfcTxRxContext tx_rx = {};
|
||||
NfcWorkerEvent event = 0;
|
||||
bool card_not_detected_notified = false;
|
||||
|
||||
while(nfc_worker->state == NfcWorkerStateRead) {
|
||||
if(furi_hal_nfc_detect(nfc_data, 300)) {
|
||||
FURI_LOG_D(TAG, "Card detected");
|
||||
furi_hal_nfc_sleep();
|
||||
// Process first found device
|
||||
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
||||
card_not_detected_notified = false;
|
||||
if(nfc_data->type == FuriHalNfcTypeA) {
|
||||
if(read_mode == NfcReadModeMfClassic) {
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic;
|
||||
nfc_worker->dev_data->mf_classic_data.type = mf_classic_get_classic_type(
|
||||
nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
|
||||
if(nfc_worker_read_mf_classic(nfc_worker, &tx_rx)) {
|
||||
FURI_LOG_D(TAG, "Card read");
|
||||
dev_data->protocol = NfcDeviceProtocolMifareClassic;
|
||||
event = NfcWorkerEventReadMfClassicDone;
|
||||
break;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Card read failed");
|
||||
dev_data->protocol = NfcDeviceProtocolMifareClassic;
|
||||
event = NfcWorkerEventReadMfClassicDictAttackRequired;
|
||||
break;
|
||||
}
|
||||
} else if(read_mode == NfcReadModeMfUltralight) {
|
||||
FURI_LOG_I(TAG, "Mifare Ultralight / NTAG");
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareUl;
|
||||
if(nfc_worker_read_mf_ultralight(nfc_worker, &tx_rx)) {
|
||||
event = NfcWorkerEventReadMfUltralight;
|
||||
break;
|
||||
}
|
||||
} else if(read_mode == NfcReadModeMfDesfire) {
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareDesfire;
|
||||
if(nfc_worker_read_mf_desfire(nfc_worker, &tx_rx)) {
|
||||
event = NfcWorkerEventReadMfDesfire;
|
||||
break;
|
||||
}
|
||||
} else if(read_mode == NfcReadModeEMV) {
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV;
|
||||
if(nfc_worker_read_bank_card(nfc_worker, &tx_rx)) {
|
||||
event = NfcWorkerEventReadBankCard;
|
||||
break;
|
||||
}
|
||||
} else if(read_mode == NfcReadModeNFCA) {
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown;
|
||||
event = NfcWorkerEventReadUidNfcA;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(!card_not_detected_notified) {
|
||||
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
|
||||
card_not_detected_notified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
furi_hal_nfc_sleep();
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
// Notify caller and exit
|
||||
if(event > NfcWorkerEventReserved) {
|
||||
nfc_worker->callback(event, nfc_worker->context);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
|
||||
FuriHalNfcTxRxContext tx_rx = {};
|
||||
FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data;
|
||||
|
|
|
@ -35,6 +35,8 @@ int32_t nfc_worker_task(void* context);
|
|||
|
||||
void nfc_worker_read(NfcWorker* nfc_worker);
|
||||
|
||||
void nfc_worker_read_type(NfcWorker* nfc_worker);
|
||||
|
||||
void nfc_worker_emulate_uid(NfcWorker* nfc_worker);
|
||||
|
||||
void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker);
|
||||
|
|
|
@ -82,7 +82,7 @@ uint8_t mf_classic_get_total_sectors_num(MfClassicType type) {
|
|||
}
|
||||
}
|
||||
|
||||
static uint16_t mf_classic_get_total_block_num(MfClassicType type) {
|
||||
uint16_t mf_classic_get_total_block_num(MfClassicType type) {
|
||||
if(type == MfClassicType1k) {
|
||||
return 64;
|
||||
} else if(type == MfClassicType4k) {
|
||||
|
|
|
@ -98,6 +98,8 @@ MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t S
|
|||
|
||||
uint8_t mf_classic_get_total_sectors_num(MfClassicType type);
|
||||
|
||||
uint16_t mf_classic_get_total_block_num(MfClassicType type);
|
||||
|
||||
uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector);
|
||||
|
||||
bool mf_classic_is_sector_trailer(uint8_t block);
|
||||
|
|
|
@ -52,6 +52,8 @@ class FlipperApplication:
|
|||
icon: Optional[str] = None
|
||||
order: int = 0
|
||||
sdk_headers: List[str] = field(default_factory=list)
|
||||
targets: List[str] = field(default_factory=lambda: ["all"])
|
||||
|
||||
# .fap-specific
|
||||
sources: List[str] = field(default_factory=lambda: ["*.c*"])
|
||||
fap_version: Tuple[int] = field(default_factory=lambda: (0, 1))
|
||||
|
@ -135,8 +137,8 @@ class AppManager:
|
|||
raise FlipperManifestException(f"Duplicate app declaration: {app.appid}")
|
||||
self.known_apps[app.appid] = app
|
||||
|
||||
def filter_apps(self, applist: List[str]):
|
||||
return AppBuildset(self, applist)
|
||||
def filter_apps(self, applist: List[str], hw_target: str):
|
||||
return AppBuildset(self, applist, hw_target)
|
||||
|
||||
|
||||
class AppBuilderException(Exception):
|
||||
|
@ -155,11 +157,13 @@ class AppBuildset:
|
|||
FlipperAppType.STARTUP,
|
||||
)
|
||||
|
||||
def __init__(self, appmgr: AppManager, appnames: List[str]):
|
||||
def __init__(self, appmgr: AppManager, appnames: List[str], hw_target: str):
|
||||
self.appmgr = appmgr
|
||||
self.appnames = set(appnames)
|
||||
self.hw_target = hw_target
|
||||
self._orig_appnames = appnames
|
||||
self._process_deps()
|
||||
self._filter_by_target()
|
||||
self._check_conflicts()
|
||||
self._check_unsatisfied() # unneeded?
|
||||
self.apps = sorted(
|
||||
|
@ -170,6 +174,16 @@ class AppBuildset:
|
|||
def _is_missing_dep(self, dep_name: str):
|
||||
return dep_name not in self.appnames
|
||||
|
||||
def _filter_by_target(self):
|
||||
for appname in self.appnames.copy():
|
||||
app = self.appmgr.get(appname)
|
||||
# if app.apptype not in self.BUILTIN_APP_TYPES:
|
||||
if not any(map(lambda t: t in app.targets, ["all", self.hw_target])):
|
||||
print(
|
||||
f"Removing {appname} due to target mismatch (building for {self.hw_target}, app supports {app.targets}"
|
||||
)
|
||||
self.appnames.remove(appname)
|
||||
|
||||
def _process_deps(self):
|
||||
while True:
|
||||
provided = []
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import SCons
|
||||
from SCons.Subst import quote_spaces
|
||||
from SCons.Errors import StopError
|
||||
from SCons.Node.FS import _my_normcase
|
||||
|
||||
import re
|
||||
import os
|
||||
|
@ -58,3 +57,9 @@ def extract_abs_dir_path(node):
|
|||
raise StopError(f"Can't find absolute path for {node.name}")
|
||||
|
||||
return abs_dir_node.abspath
|
||||
|
||||
|
||||
def path_as_posix(path):
|
||||
if SCons.Platform.platform_default() == "win32":
|
||||
return path.replace(os.path.sep, os.path.altsep)
|
||||
return path
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from SCons.Builder import Builder
|
||||
from SCons.Action import Action
|
||||
from SCons.Warnings import warn, WarningOnByDefault
|
||||
import SCons
|
||||
from ansi.color import fg
|
||||
|
||||
from fbt.appmanifest import (
|
||||
|
@ -33,14 +32,12 @@ def LoadAppManifest(env, entry):
|
|||
|
||||
|
||||
def PrepareApplicationsBuild(env):
|
||||
appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps(env["APPS"])
|
||||
appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps(
|
||||
env["APPS"], env.subst("f${TARGET_HW}")
|
||||
)
|
||||
env.Append(
|
||||
SDK_HEADERS=appbuild.get_sdk_headers(),
|
||||
)
|
||||
env["APPBUILD_DUMP"] = env.Action(
|
||||
DumpApplicationConfig,
|
||||
"\tINFO\t",
|
||||
)
|
||||
|
||||
|
||||
def DumpApplicationConfig(target, source, env):
|
||||
|
@ -68,6 +65,10 @@ def generate(env):
|
|||
env.AddMethod(PrepareApplicationsBuild)
|
||||
env.SetDefault(
|
||||
APPMGR=AppManager(),
|
||||
APPBUILD_DUMP=env.Action(
|
||||
DumpApplicationConfig,
|
||||
"\tINFO\t",
|
||||
),
|
||||
)
|
||||
|
||||
env.Append(
|
||||
|
|
|
@ -41,12 +41,12 @@ def generate(env, **kw):
|
|||
"|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
|
||||
],
|
||||
GDBOPTS_BASE=[
|
||||
"-ex",
|
||||
"set pagination off",
|
||||
"-ex",
|
||||
"target extended-remote ${GDBREMOTE}",
|
||||
"-ex",
|
||||
"set confirm off",
|
||||
"-ex",
|
||||
"set pagination off",
|
||||
],
|
||||
GDBOPTS_BLACKMAGIC=[
|
||||
"-ex",
|
||||
|
|
|
@ -14,6 +14,7 @@ import json
|
|||
|
||||
from fbt.sdk.collector import SdkCollector
|
||||
from fbt.sdk.cache import SdkCache
|
||||
from fbt.util import path_as_posix
|
||||
|
||||
|
||||
def ProcessSdkDepends(env, filename):
|
||||
|
@ -52,6 +53,8 @@ def prebuild_sdk_create_origin_file(target, source, env):
|
|||
|
||||
|
||||
class SdkMeta:
|
||||
MAP_FILE_SUBST = "SDK_MAP_FILE_SUBST"
|
||||
|
||||
def __init__(self, env, tree_builder: "SdkTreeBuilder"):
|
||||
self.env = env
|
||||
self.treebuilder = tree_builder
|
||||
|
@ -67,6 +70,7 @@ class SdkMeta:
|
|||
"linker_libs": self.env.subst("${LIBS}"),
|
||||
"app_ep_subst": self.env.subst("${APP_ENTRY}"),
|
||||
"sdk_path_subst": self.env.subst("${SDK_DIR_SUBST}"),
|
||||
"map_file_subst": self.MAP_FILE_SUBST,
|
||||
"hardware": self.env.subst("${TARGET_HW}"),
|
||||
}
|
||||
with open(json_manifest_path, "wt") as f:
|
||||
|
@ -75,9 +79,9 @@ class SdkMeta:
|
|||
def _wrap_scons_vars(self, vars: str):
|
||||
expanded_vars = self.env.subst(
|
||||
vars,
|
||||
target=Entry("dummy"),
|
||||
target=Entry(self.MAP_FILE_SUBST),
|
||||
)
|
||||
return expanded_vars.replace("\\", "/")
|
||||
return path_as_posix(expanded_vars)
|
||||
|
||||
|
||||
class SdkTreeBuilder:
|
||||
|
@ -142,13 +146,15 @@ class SdkTreeBuilder:
|
|||
meta.save_to(self.target[0].path)
|
||||
|
||||
def build_sdk_file_path(self, orig_path: str) -> str:
|
||||
return posixpath.normpath(
|
||||
posixpath.join(
|
||||
self.SDK_DIR_SUBST,
|
||||
self.target_sdk_dir_name,
|
||||
orig_path,
|
||||
return path_as_posix(
|
||||
posixpath.normpath(
|
||||
posixpath.join(
|
||||
self.SDK_DIR_SUBST,
|
||||
self.target_sdk_dir_name,
|
||||
orig_path,
|
||||
)
|
||||
)
|
||||
).replace("\\", "/")
|
||||
)
|
||||
|
||||
def emitter(self, target, source, env):
|
||||
target_folder = target[0]
|
||||
|
|
|
@ -63,6 +63,11 @@ vars.AddVariables(
|
|||
help="Enable debug build",
|
||||
default=True,
|
||||
),
|
||||
BoolVariable(
|
||||
"LIB_DEBUG",
|
||||
help="Enable debug build for libraries",
|
||||
default=False,
|
||||
),
|
||||
BoolVariable(
|
||||
"COMPACT",
|
||||
help="Optimize for size",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from dataclasses import dataclass, field
|
||||
from SCons.Errors import UserError
|
||||
from SCons.Node import NodeList
|
||||
from SCons.Warnings import warn, WarningOnByDefault
|
||||
|
||||
|
||||
Import("ENV")
|
||||
|
@ -80,6 +80,14 @@ if extra_app_list := GetOption("extra_ext_apps"):
|
|||
known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(",")))
|
||||
|
||||
for app in known_extapps:
|
||||
if not any(map(lambda t: t in app.targets, ["all", appenv.subst("f${TARGET_HW}")])):
|
||||
warn(
|
||||
WarningOnByDefault,
|
||||
f"Can't build '{app.name}' (id '{app.appid}'): target mismatch"
|
||||
f" (building for {appenv.subst('f${TARGET_HW}')}, app supports {app.targets}",
|
||||
)
|
||||
continue
|
||||
|
||||
appenv.BuildAppElf(app)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue