Merge branch 'fz-dev' into dev

This commit is contained in:
MX 2022-11-10 21:38:03 +03:00
commit 930b369812
No known key found for this signature in database
GPG key ID: 6C4C311DFD4B4AB5
44 changed files with 655 additions and 112 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

@ -0,0 +1,2 @@
/html
/latex

View file

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

View file

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

View file

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

View file

@ -77,6 +77,12 @@ FIRMWARE_APPS = {
# Debug
# "debug_apps",
],
"unit_tests": [
"basic_services",
"updater_app",
"unit_tests",
"nfc",
],
"debug_pack": [
# Svc
"basic_services",

View file

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

View file

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

1 entry status name type params
2 Version + 7.32 7.41
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
1865 Function - mf_classic_get_sector_by_block uint8_t uint8_t
1866 Function - mf_classic_get_sector_trailer_block_num_by_sector uint8_t uint8_t
1867 Function - mf_classic_get_sector_trailer_by_sector MfClassicSectorTrailer* MfClassicData*, uint8_t
1868 Function - mf_classic_get_total_block_num uint16_t MfClassicType
1869 Function - mf_classic_get_total_sectors_num uint8_t MfClassicType
1870 Function - mf_classic_get_type_str const char* MfClassicType
1871 Function - mf_classic_is_allowed_access_data_block _Bool MfClassicData*, uint8_t, MfClassicKey, MfClassicAction

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 = []

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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