From 9ddff2a259ba0eba37cdde5ef3f30dc1277c3b7e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 20 May 2023 01:20:34 +0100 Subject: [PATCH] Merge branch 'dev' of https://github.com/DarkFlippers/unleashed-firmware into xfw-dev --- .vscode/example/tasks.json | 16 +- .../external/subghz_remote/application.fam | 5 +- .../helpers/subrem_custom_event.h | 18 + .../subghz_remote/helpers/subrem_types.h | 32 + .../subghz_remote/scenes/subrem_scene.c | 30 + .../subghz_remote/scenes/subrem_scene.h | 29 + .../scenes/subrem_scene_config.h | 3 + .../scenes/subrem_scene_openmapfile.c | 41 + .../scenes/subrem_scene_remote.c | 130 ++ .../subghz_remote/scenes/subrem_scene_start.c | 74 ++ .../subghz_remote/subghz_remote_app.c | 1156 ++--------------- .../subghz_remote/subghz_remote_app_i.c | 448 +++++++ .../subghz_remote/subghz_remote_app_i.h | 84 ++ .../external/subghz_remote/views/remote.c | 302 +++++ .../external/subghz_remote/views/remote.h | 38 + .../scenes/wifi_marauder_scene_start.c | 17 +- .../wifi_marauder_app_i.h | 2 +- applications/main/lfrfid/lfrfid.c | 15 +- applications/main/nfc/nfc.c | 1 + .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 4 +- applications/main/subghz/application.fam | 13 +- .../main/subghz/helpers/subghz_custom_event.h | 2 + .../helpers/subghz_txrx_create_protocol_key.c | 28 + .../helpers/subghz_txrx_create_protocol_key.h | 7 + .../scenes/subghz_scene_radio_settings.c | 84 +- .../subghz/scenes/subghz_scene_receiver.c | 8 + .../subghz/scenes/subghz_scene_set_type.c | 20 + .../main/subghz/subghz_extended_freq.c | 19 + applications/main/subghz/views/receiver.c | 124 +- applications/main/subghz/views/receiver.h | 4 + applications/main/xtreme_app/xtreme_app.c | 20 +- .../badkb/assets/layouts/fr-FR-mac.kl | Bin 0 -> 256 bytes assets/resources/infrared/assets/ac.ir | 52 +- assets/resources/infrared/assets/audio.ir | 2 +- assets/resources/infrared/assets/fans.ir | 34 +- .../resources/infrared/assets/projectors.ir | 2 +- assets/resources/infrared/assets/tv.ir | 78 +- firmware/targets/f7/api_symbols.csv | 3 +- .../targets/f7/furi_hal/furi_hal_subghz.c | 46 +- .../targets/f7/furi_hal/furi_hal_subghz.h | 17 +- .../targets/f7/furi_hal/furi_hal_subghz_i.h | 3 + lib/nfc/nfc_device.c | 17 +- lib/nfc/protocols/mifare_classic.c | 5 +- lib/nfc/protocols/mifare_common.c | 3 +- lib/nfc/protocols/nfcv.c | 296 +++-- lib/nfc/protocols/nfcv.h | 124 +- lib/nfc/protocols/slix.c | 26 +- lib/nfc/protocols/slix.h | 28 +- lib/subghz/protocols/came_atomo.c | 162 ++- lib/subghz/protocols/came_atomo.h | 16 + lib/subghz/protocols/dooya.c | 4 +- 51 files changed, 2272 insertions(+), 1420 deletions(-) create mode 100644 applications/external/subghz_remote/helpers/subrem_custom_event.h create mode 100644 applications/external/subghz_remote/helpers/subrem_types.h create mode 100644 applications/external/subghz_remote/scenes/subrem_scene.c create mode 100644 applications/external/subghz_remote/scenes/subrem_scene.h create mode 100644 applications/external/subghz_remote/scenes/subrem_scene_config.h create mode 100644 applications/external/subghz_remote/scenes/subrem_scene_openmapfile.c create mode 100644 applications/external/subghz_remote/scenes/subrem_scene_remote.c create mode 100644 applications/external/subghz_remote/scenes/subrem_scene_start.c create mode 100644 applications/external/subghz_remote/subghz_remote_app_i.c create mode 100644 applications/external/subghz_remote/subghz_remote_app_i.h create mode 100644 applications/external/subghz_remote/views/remote.c create mode 100644 applications/external/subghz_remote/views/remote.h create mode 100644 applications/main/subghz/subghz_extended_freq.c create mode 100644 assets/resources/badkb/assets/layouts/fr-FR-mac.kl create mode 100644 firmware/targets/f7/furi_hal/furi_hal_subghz_i.h diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json index f2522668d..107808ef3 100644 --- a/.vscode/example/tasks.json +++ b/.vscode/example/tasks.json @@ -13,7 +13,7 @@ "label": "[Debug] Build", "group": "build", "type": "shell", - "command": "./fbt" + "command": "./fbt FIRMWARE_APP_SET=debug_pack" }, { "label": "[Release] Flash (ST-Link)", @@ -25,7 +25,7 @@ "label": "[Debug] Flash (ST-Link)", "group": "build", "type": "shell", - "command": "./fbt FORCE=1 flash" + "command": "./fbt FIRMWARE_APP_SET=debug_pack FORCE=1 flash" }, { "label": "[Release] Flash (blackmagic)", @@ -37,7 +37,7 @@ "label": "[Debug] Flash (blackmagic)", "group": "build", "type": "shell", - "command": "./fbt FORCE=1 flash_blackmagic" + "command": "./fbt FIRMWARE_APP_SET=debug_pack FORCE=1 flash_blackmagic" }, { "label": "[Release] Flash (JLink)", @@ -49,7 +49,7 @@ "label": "[Debug] Flash (JLink)", "group": "build", "type": "shell", - "command": "./fbt FORCE=1 jflash" + "command": "./fbt FIRMWARE_APP_SET=debug_pack FORCE=1 jflash" }, { "label": "[Release] Build update bundle", @@ -61,7 +61,7 @@ "label": "[Debug] Build update bundle", "group": "build", "type": "shell", - "command": "./fbt updater_package" + "command": "./fbt FIRMWARE_APP_SET=debug_pack updater_package" }, { "label": "[Release] Build updater", @@ -73,13 +73,13 @@ "label": "[Debug] Build updater", "group": "build", "type": "shell", - "command": "./fbt updater_all" + "command": "./fbt FIRMWARE_APP_SET=debug_pack updater_all" }, { "label": "[Debug] Flash (USB, w/o resources)", "group": "build", "type": "shell", - "command": "./fbt FORCE=1 flash_usb" + "command": "./fbt FIRMWARE_APP_SET=debug_pack FORCE=1 flash_usb" }, { "label": "[Release] Flash (USB, w/o resources)", @@ -97,7 +97,7 @@ "label": "[Debug] Flash (USB, with resources)", "group": "build", "type": "shell", - "command": "./fbt FORCE=1 flash_usb_full" + "command": "./fbt FIRMWARE_APP_SET=debug_pack FORCE=1 flash_usb_full" }, { "label": "[Release] Flash (USB, with resources)", diff --git a/applications/external/subghz_remote/application.fam b/applications/external/subghz_remote/application.fam index e70e4050a..0523e911c 100644 --- a/applications/external/subghz_remote/application.fam +++ b/applications/external/subghz_remote/application.fam @@ -3,7 +3,10 @@ App( name="Sub-GHz Remote", apptype=FlipperAppType.EXTERNAL, entry_point="subghz_remote_app", - cdefines=["APP_SUBGHZREMOTE"], + cdefines=[ + "APP_SUBGHZREMOTE", + "SUBREM_LIGHT", + ], requires=[ "gui", "dialogs", diff --git a/applications/external/subghz_remote/helpers/subrem_custom_event.h b/applications/external/subghz_remote/helpers/subrem_custom_event.h new file mode 100644 index 000000000..46ab8ad54 --- /dev/null +++ b/applications/external/subghz_remote/helpers/subrem_custom_event.h @@ -0,0 +1,18 @@ +#pragma once + +typedef enum { + //SubmenuIndex + SubmenuIndexSubRemOpenMapFile, + SubmenuIndexSubRemRemoteView, + SubmenuIndexSubRemAbout, + + //SubRemCustomEvent + SubRemCustomEventViewRemoteStartUP = 100, + SubRemCustomEventViewRemoteStartDOWN, + SubRemCustomEventViewRemoteStartLEFT, + SubRemCustomEventViewRemoteStartRIGHT, + SubRemCustomEventViewRemoteStartOK, + SubRemCustomEventViewRemoteBack, + SubRemCustomEventViewRemoteStop, + SubRemCustomEventViewRemoteForcedStop, +} SubRemCustomEvent; \ No newline at end of file diff --git a/applications/external/subghz_remote/helpers/subrem_types.h b/applications/external/subghz_remote/helpers/subrem_types.h new file mode 100644 index 000000000..1b99aac6d --- /dev/null +++ b/applications/external/subghz_remote/helpers/subrem_types.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +// TODO: File version/type logic +// #define SUBREM_APP_APP_FILE_VERSION 1 +// #define SUBREM_APP_APP_FILE_TYPE "Flipper SubRem Map file" +#define SUBREM_APP_EXTENSION ".txt" + +typedef enum { + SubRemSubKeyNameUp = (0U), + SubRemSubKeyNameDown, + SubRemSubKeyNameLeft, + SubRemSubKeyNameRight, + SubRemSubKeyNameOk, + SubRemSubKeyNameMaxCount, +} SubRemSubKeyName; + +typedef enum { + SubRemViewSubmenu, + SubRemViewWidget, + SubRemViewPopup, + SubRemViewTextInput, + SubRemViewIDRemote, +} SubRemViewID; + +typedef enum { + SubRemLoadMapStateBack = 0, + SubRemLoadMapStateError, + SubRemLoadMapStateOK, +} SubRemLoadMapState; \ No newline at end of file diff --git a/applications/external/subghz_remote/scenes/subrem_scene.c b/applications/external/subghz_remote/scenes/subrem_scene.c new file mode 100644 index 000000000..c45285b96 --- /dev/null +++ b/applications/external/subghz_remote/scenes/subrem_scene.c @@ -0,0 +1,30 @@ +#include "../subghz_remote_app_i.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const subrem_scene_on_enter_handlers[])(void*) = { +#include "subrem_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const subrem_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "subrem_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const subrem_scene_on_exit_handlers[])(void* context) = { +#include "subrem_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers subrem_scene_handlers = { + .on_enter_handlers = subrem_scene_on_enter_handlers, + .on_event_handlers = subrem_scene_on_event_handlers, + .on_exit_handlers = subrem_scene_on_exit_handlers, + .scene_num = SubRemSceneNum, +}; diff --git a/applications/external/subghz_remote/scenes/subrem_scene.h b/applications/external/subghz_remote/scenes/subrem_scene.h new file mode 100644 index 000000000..5c01f8ca5 --- /dev/null +++ b/applications/external/subghz_remote/scenes/subrem_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) SubRemScene##id, +typedef enum { +#include "subrem_scene_config.h" + SubRemSceneNum, +} SubRemScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers subrem_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "subrem_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "subrem_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "subrem_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/subghz_remote/scenes/subrem_scene_config.h b/applications/external/subghz_remote/scenes/subrem_scene_config.h new file mode 100644 index 000000000..93d4de642 --- /dev/null +++ b/applications/external/subghz_remote/scenes/subrem_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(subrem, start, Start) +ADD_SCENE(subrem, openmapfile, OpenMapFile) +ADD_SCENE(subrem, remote, Remote) \ No newline at end of file diff --git a/applications/external/subghz_remote/scenes/subrem_scene_openmapfile.c b/applications/external/subghz_remote/scenes/subrem_scene_openmapfile.c new file mode 100644 index 000000000..3391845e1 --- /dev/null +++ b/applications/external/subghz_remote/scenes/subrem_scene_openmapfile.c @@ -0,0 +1,41 @@ +#include "../subghz_remote_app_i.h" + +void subrem_scene_openmapfile_on_enter(void* context) { + SubGhzRemoteApp* app = context; + SubRemLoadMapState load_state = subrem_load_from_file(app); + + if(load_state == SubRemLoadMapStateError) { +#ifdef SUBREM_LIGHT + dialog_message_show_storage_error(app->dialogs, "Can't load\nMap file"); +#else + DialogMessage* message = dialog_message_alloc(); + + dialog_message_set_header(message, "Map File Error", 64, 8, AlignCenter, AlignCenter); + dialog_message_set_text(message, "Can't load\nMap file", 64, 32, AlignCenter, AlignCenter); + dialog_message_set_buttons(message, "Back", NULL, NULL); + dialog_message_show(app->dialogs, message); + + dialog_message_free(message); +#endif + } + if(load_state == SubRemLoadMapStateOK) { + scene_manager_next_scene(app->scene_manager, SubRemSceneRemote); + } else { + // TODO: Map Preset Reset + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SubRemSceneStart)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + } +} + +bool subrem_scene_openmapfile_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void subrem_scene_openmapfile_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/external/subghz_remote/scenes/subrem_scene_remote.c b/applications/external/subghz_remote/scenes/subrem_scene_remote.c new file mode 100644 index 000000000..c24583233 --- /dev/null +++ b/applications/external/subghz_remote/scenes/subrem_scene_remote.c @@ -0,0 +1,130 @@ +#include "../subghz_remote_app_i.h" +#include "../views/remote.h" + +#include + +#define TAG "SubRemScenRemote" + +void subrem_scene_remote_callback(SubRemCustomEvent event, void* context) { + furi_assert(context); + SubGhzRemoteApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void subrem_scene_remote_raw_callback_end_tx(void* context) { + furi_assert(context); + SubGhzRemoteApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SubRemCustomEventViewRemoteForcedStop); +} + +static uint8_t subrem_scene_remote_event_to_index(SubRemCustomEvent event_id) { + uint8_t ret = 0; + + if(event_id == SubRemCustomEventViewRemoteStartUP) { + ret = SubRemSubKeyNameUp; + } else if(event_id == SubRemCustomEventViewRemoteStartDOWN) { + ret = SubRemSubKeyNameDown; + } else if(event_id == SubRemCustomEventViewRemoteStartLEFT) { + ret = SubRemSubKeyNameLeft; + } else if(event_id == SubRemCustomEventViewRemoteStartRIGHT) { + ret = SubRemSubKeyNameRight; + } else if(event_id == SubRemCustomEventViewRemoteStartOK) { + ret = SubRemSubKeyNameOk; + } + + return ret; +} + +static bool subrem_scene_remote_update_data_show(void* context) { + SubGhzRemoteApp* app = context; + bool ret = false; + + subrem_view_remote_add_data_to_show( + app->subrem_remote_view, + furi_string_get_cstr(app->subs_preset[0]->label), + furi_string_get_cstr(app->subs_preset[1]->label), + furi_string_get_cstr(app->subs_preset[2]->label), + furi_string_get_cstr(app->subs_preset[3]->label), + furi_string_get_cstr(app->subs_preset[4]->label)); + + return ret; +} + +void subrem_scene_remote_on_enter(void* context) { + SubGhzRemoteApp* app = context; + + subrem_scene_remote_update_data_show(app); + + subrem_view_remote_set_callback(app->subrem_remote_view, subrem_scene_remote_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDRemote); +} + +bool subrem_scene_remote_on_event(void* context, SceneManagerEvent event) { + SubGhzRemoteApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubRemCustomEventViewRemoteBack) { + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SubRemSceneOpenMapFile)) { + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SubRemSceneStart)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + } + return true; + } else if( + event.event == SubRemCustomEventViewRemoteStartUP || + event.event == SubRemCustomEventViewRemoteStartDOWN || + event.event == SubRemCustomEventViewRemoteStartLEFT || + event.event == SubRemCustomEventViewRemoteStartRIGHT || + event.event == SubRemCustomEventViewRemoteStartOK) { + // Start sending sub + subrem_tx_stop_sub(app, true); + app->chusen_sub = subrem_scene_remote_event_to_index(event.event); + subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateLoading); + if(subrem_tx_start_sub( + app, + app->subs_preset[app->chusen_sub], + subrem_scene_remote_raw_callback_end_tx)) { + subrem_view_remote_set_presed_btn(app->subrem_remote_view, app->chusen_sub); + subrem_view_remote_set_state( + app->subrem_remote_view, SubRemViewRemoteStateSending); + notification_message(app->notifications, &sequence_blink_start_magenta); + } else { + subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle); + notification_message(app->notifications, &sequence_blink_stop); + } + return true; + } else if(event.event == SubRemCustomEventViewRemoteForcedStop) { + subrem_tx_stop_sub(app, true); + subrem_view_remote_set_presed_btn(app->subrem_remote_view, 0); + subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle); + + notification_message(app->notifications, &sequence_blink_stop); + return true; + } else if(event.event == SubRemCustomEventViewRemoteStop) { + if(subrem_tx_stop_sub(app, false)) { + subrem_view_remote_set_presed_btn(app->subrem_remote_view, 0); + subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle); + + notification_message(app->notifications, &sequence_blink_stop); + } + return true; + } + } + // } else if(event.type == SceneManagerEventTypeTick) { + // } + return false; +} + +void subrem_scene_remote_on_exit(void* context) { + SubGhzRemoteApp* app = context; + + subrem_tx_stop_sub(app, true); + + subrem_view_remote_set_presed_btn(app->subrem_remote_view, 0); + subrem_view_remote_set_state(app->subrem_remote_view, SubRemViewRemoteStateIdle); + + notification_message(app->notifications, &sequence_blink_stop); +} diff --git a/applications/external/subghz_remote/scenes/subrem_scene_start.c b/applications/external/subghz_remote/scenes/subrem_scene_start.c new file mode 100644 index 000000000..962eda54c --- /dev/null +++ b/applications/external/subghz_remote/scenes/subrem_scene_start.c @@ -0,0 +1,74 @@ +#include "../subghz_remote_app_i.h" +#include "../helpers/subrem_custom_event.h" + +void subrem_scene_start_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + SubGhzRemoteApp* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void subrem_scene_start_on_enter(void* context) { + furi_assert(context); + + SubGhzRemoteApp* app = context; + Submenu* submenu = app->submenu; + submenu_add_item( + submenu, + "Open Map File", + SubmenuIndexSubRemOpenMapFile, + subrem_scene_start_submenu_callback, + app); +#if FURI_DEBUG + submenu_add_item( + submenu, + "Remote_Debug", + SubmenuIndexSubRemRemoteView, + subrem_scene_start_submenu_callback, + app); +#endif + // submenu_add_item( + // submenu, + // "About", + // SubmenuIndexSubGhzRemoteAbout, + // subrem_scene_start_submenu_callback, + // app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, SubRemSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewSubmenu); +} + +bool subrem_scene_start_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + + SubGhzRemoteApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSubRemOpenMapFile) { + scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile); + consumed = true; + } + // } else if(event.event == SubmenuIndexSubRemAbout) { + // scene_manager_next_scene(app->scene_manager, SubRemSceneAbout); + // consumed = true; + // } +#if FURI_DEBUG + else if(event.event == SubmenuIndexSubRemRemoteView) { + scene_manager_next_scene(app->scene_manager, SubRemSceneRemote); + consumed = true; + } +#endif + } + + return consumed; +} + +void subrem_scene_start_on_exit(void* context) { + furi_assert(context); + + SubGhzRemoteApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/subghz_remote/subghz_remote_app.c b/applications/external/subghz_remote/subghz_remote_app.c index 15bc5d220..35cced248 100644 --- a/applications/external/subghz_remote/subghz_remote_app.c +++ b/applications/external/subghz_remote/subghz_remote_app.c @@ -1,767 +1,28 @@ -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include +#include "subghz_remote_app_i.h" #include -#include +#include -#define SUBREMOTEMAP_FOLDER "/ext/subghz/remote" -#define SUBREMOTEMAP_EXTENSION ".txt" - -#define TAG "SubGHzRemote" - -typedef struct { - uint32_t frequency; - FuriString* name; - - FuriString* protocol; - uint32_t repeat; - - uint8_t* data; - size_t data_size; - - SubGhzProtocolDecoderBase* decoder; -} SubRemotePreset; - -typedef struct { - FuriMutex* model_mutex; - - FuriMessageQueue* input_queue; - - ViewPort* view_port; - Gui* gui; - - SubGhzSetting* setting; - SubGhzEnvironment* environment; - SubGhzReceiver* subghz_receiver; - NotificationApp* notification; - SubRemotePreset* txpreset; - - FuriString* up_file; - FuriString* down_file; - FuriString* left_file; - FuriString* right_file; - FuriString* ok_file; - - FuriString* file_path; - - char* up_label; - char* down_label; - char* left_label; - char* right_label; - char* ok_label; - - int up_enabled; - int down_enabled; - int left_enabled; - int right_enabled; - int ok_enabled; - - char* send_status; - int send_status_c; - int processing; - - SubGhzTransmitter* tx_transmitter; - FlipperFormat* tx_fff_data; - const char* tx_file_path; - int button; - - int file_result; - bool tx_not_allowed; - - FuriString* signal; -} SubGHzRemote; - -SubRemotePreset* subghz_remote_preset_alloc(void) { - SubRemotePreset* preset = malloc(sizeof(SubRemotePreset)); - preset->name = furi_string_alloc(); - preset->protocol = furi_string_alloc(); - preset->repeat = 200; - return preset; +static bool subghz_remote_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + SubGhzRemoteApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); } -void subghz_remote_preset_free(SubRemotePreset* preset) { - furi_string_free(preset->name); - furi_string_free(preset->protocol); - free(preset); +static bool subghz_remote_app_back_event_callback(void* context) { + furi_assert(context); + SubGhzRemoteApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); } -static char* char_to_str(char* str, int i) { - char* converted = malloc(sizeof(char) * i + 1); - memcpy(converted, str, i); - - converted[i] = '\0'; - - return converted; +static void subghz_remote_app_tick_event_callback(void* context) { + furi_assert(context); + SubGhzRemoteApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); } -//get filename without path -static char* extract_filename(const char* name, int len) { - FuriString* tmp; - tmp = furi_string_alloc(); - - //remove path - path_extract_filename_no_ext(name, tmp); - - return char_to_str((char*)furi_string_get_cstr(tmp), len); -} - -static void cfg_read_file_path( - FlipperFormat* fff_file, - FuriString* text_file_path, - char** text_file_label, - const char* read_key, - int* is_enabled) { - if(!flipper_format_read_string(fff_file, read_key, text_file_path)) { - FURI_LOG_W(TAG, "Could not read %s string", read_key); - *text_file_label = "N/A"; - *is_enabled = 0; - } else { - *text_file_label = extract_filename(furi_string_get_cstr(text_file_path), 16); - //FURI_LOG_D(TAG, "%s file: %s", read_key, furi_string_get_cstr(text_file_path)); - *is_enabled = 1; - } - flipper_format_rewind(fff_file); -} - -static void cfg_read_file_label( - FlipperFormat* fff_file, - char** text_file_label, - const char* read_key, - bool is_enabled) { - FuriString* temp_label = furi_string_alloc(); - - if(!flipper_format_read_string(fff_file, read_key, temp_label)) { - FURI_LOG_W(TAG, "Could not read %s string", read_key); - } else { - if(is_enabled == 1) { - *text_file_label = char_to_str((char*)furi_string_get_cstr(temp_label), 16); - } - //FURI_LOG_D(TAG, "%s label: %s", read_key, *text_file_label); - } - flipper_format_rewind(fff_file); - furi_string_free(temp_label); -} - -/* - * check that map file exists - * assign variables to values within map file - * set missing filenames to N/A - * set filename as label if label definitions are missing - * set error flag if all buttons are N/A - * set error flag if missing map file - */ - -void subghz_remote_cfg_set_check(SubGHzRemote* app, FuriString* file_name) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - - app->file_result = 1; - - app->up_enabled = 0; - app->down_enabled = 0; - app->left_enabled = 0; - app->right_enabled = 0; - app->ok_enabled = 0; - - //check that map file exists - if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { - FURI_LOG_E(TAG, "Could not open MAP file %s", furi_string_get_cstr(file_name)); - } else { - //Filename Assignment/Check Start - - //assign variables to values within map file - //set missing filenames to N/A - cfg_read_file_path(fff_data_file, app->up_file, &app->up_label, "UP", &app->up_enabled); - - cfg_read_file_path( - fff_data_file, app->down_file, &app->down_label, "DOWN", &app->down_enabled); - - cfg_read_file_path( - fff_data_file, app->left_file, &app->left_label, "LEFT", &app->left_enabled); - - cfg_read_file_path( - fff_data_file, app->right_file, &app->right_label, "RIGHT", &app->right_enabled); - - cfg_read_file_path(fff_data_file, app->ok_file, &app->ok_label, "OK", &app->ok_enabled); - - //File definitions are done. - //File checks will follow after label assignment in order to close the universal_rf_map file without the need to reopen it again. - - //Label Assignment/Check Start - - cfg_read_file_label(fff_data_file, &app->up_label, "ULABEL", app->up_enabled); - cfg_read_file_label(fff_data_file, &app->down_label, "DLABEL", app->down_enabled); - cfg_read_file_label(fff_data_file, &app->left_label, "LLABEL", app->left_enabled); - cfg_read_file_label(fff_data_file, &app->right_label, "RLABEL", app->right_enabled); - cfg_read_file_label(fff_data_file, &app->ok_label, "OKLABEL", app->ok_enabled); - } - - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - - //File Existence Check - //Check each file definition if not already set to "N/A" - - //determine if files exist. - //determine whether or not to continue to launch app with missing variables - //if 5 files are missing, throw error - - //if button is still enabled, check that file exists - if(app->up_enabled == 1) { - furi_string_set(file_name, app->up_file); - fff_data_file = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { - FURI_LOG_W(TAG, "Could not open UP file %s", furi_string_get_cstr(file_name)); - - //disable button, and set label to "N/A" - app->up_enabled = 0; - app->up_label = "N/A"; - } - - //close the file - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - } - - if(app->down_enabled == 1) { - furi_string_set(file_name, app->down_file); - fff_data_file = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { - FURI_LOG_W(TAG, "Could not open DOWN file %s", furi_string_get_cstr(file_name)); - - app->down_enabled = 0; - app->down_label = "N/A"; - } - - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - } - - if(app->left_enabled == 1) { - furi_string_set(file_name, app->left_file); - fff_data_file = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { - FURI_LOG_W(TAG, "Could not open LEFT file %s", furi_string_get_cstr(file_name)); - - app->left_enabled = 0; - app->left_label = "N/A"; - } - - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - } - - if(app->right_enabled == 1) { - furi_string_set(file_name, app->right_file); - fff_data_file = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { - FURI_LOG_W(TAG, "Could not open RIGHT file %s", furi_string_get_cstr(file_name)); - - app->right_enabled = 0; - app->right_label = "N/A"; - } - - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - } - - if(app->ok_enabled == 1) { - furi_string_set(file_name, app->ok_file); - fff_data_file = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { - FURI_LOG_W(TAG, "Could not open OK file %s", furi_string_get_cstr(file_name)); - - app->ok_enabled = 0; - app->ok_label = "N/A"; - } - - flipper_format_file_close(fff_data_file); - flipper_format_free(fff_data_file); - } - - furi_record_close(RECORD_STORAGE); - - if(app->up_enabled == 0 && app->down_enabled == 0 && app->left_enabled == 0 && - app->right_enabled == 0 && app->ok_enabled == 0) { - app->file_result = 1; - } else { - app->file_result = 2; - } -} - -static void subghz_remote_end_send(SubGHzRemote* app) { - app->processing = 0; -} - -bool subghz_remote_set_preset(SubRemotePreset* p, const char* preset) { - if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { - furi_string_set(p->name, "AM270"); - } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { - furi_string_set(p->name, "AM650"); - } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { - furi_string_set(p->name, "FM238"); - } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { - furi_string_set(p->name, "FM476"); - } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { - FURI_LOG_E(TAG, "Custom preset unsupported now"); - return false; - // furi_string_set(p->name, "CUSTOM"); - } else { - FURI_LOG_E(TAG, "Unsupported preset"); - return false; - } - return true; -} - -bool subghz_remote_key_load( - SubRemotePreset* preset, - FlipperFormat* fff_file, - FlipperFormat* fff_data, - SubGhzSetting* setting, - SubGhzReceiver* receiver, - const char* path) { - // - if(!flipper_format_rewind(fff_file)) { - FURI_LOG_E(TAG, "Rewind error"); - return false; - } - - FuriString* temp_str; - temp_str = furi_string_alloc(); - - bool res = false; - - subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK); - keeloq_reset_original_btn(); - subghz_custom_btns_reset(); - - do { - // load frequency from file - if(!flipper_format_read_uint32(fff_file, "Frequency", &preset->frequency, 1)) { - FURI_LOG_W(TAG, "Cannot read frequency. Defaulting to 433.92 MHz"); - preset->frequency = 433920000; - } - - // load preset from file - if(!flipper_format_read_string(fff_file, "Preset", temp_str)) { - FURI_LOG_W(TAG, "Could not read Preset. Defaulting to Ook650Async"); - furi_string_set(temp_str, "FuriHalSubGhzPresetOok650Async"); - } - if(!subghz_remote_set_preset(preset, furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Could not set preset"); - break; - } - if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { - // TODO: check if preset is custom - FURI_LOG_E(TAG, "Could not use custom preset"); - break; - } - size_t preset_index = - subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(preset->name)); - preset->data = subghz_setting_get_preset_data(setting, preset_index); - preset->data_size = subghz_setting_get_preset_data_size(setting, preset_index); - - // load protocol from file - if(!flipper_format_read_string(fff_file, "Protocol", preset->protocol)) { - FURI_LOG_E(TAG, "Could not read Protocol."); - break; - } - if(!flipper_format_rewind(fff_data)) { - FURI_LOG_E(TAG, "Rewind error"); - return false; - } - - if(!furi_string_cmp_str(preset->protocol, "RAW")) { - subghz_protocol_raw_gen_fff_data(fff_data, path); - // repeat - if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &preset->repeat, 1)) { - FURI_LOG_E(TAG, "Unable to insert or update Repeat"); - break; - } - - } else { - stream_copy_full( - flipper_format_get_raw_stream(fff_file), flipper_format_get_raw_stream(fff_data)); - // repeat - if(!flipper_format_insert_or_update_uint32(fff_data, "Repeat", &preset->repeat, 1)) { - FURI_LOG_E(TAG, "Unable to insert or update Repeat"); - break; - } - } - - if(!flipper_format_rewind(fff_file)) { - FURI_LOG_E(TAG, "Rewind error"); - return false; - } - - preset->decoder = subghz_receiver_search_decoder_base_by_name( - receiver, furi_string_get_cstr(preset->protocol)); - if(preset->decoder) { - SubGhzProtocolStatus status = - subghz_protocol_decoder_base_deserialize(preset->decoder, fff_data); - if(status != SubGhzProtocolStatusOk) { - FURI_LOG_E(TAG, "Protocol deserialize failed, status = %d", status); - break; - } - } else { - FURI_LOG_E(TAG, "Protocol %s not found", furi_string_get_cstr(temp_str)); - } - - res = true; - } while(0); - - furi_string_free(temp_str); - - return res; -} - -// method modified from subghz_i.c -// https://github.com/flipperdevices/flipperzero-firmware/blob/b0daa601ad5b87427a45f9089c8b403a01f72c2a/applications/subghz/subghz_i.c#L417-L456 -bool subghz_remote_save_protocol_to_file(FlipperFormat* fff_file, const char* dev_file_name) { - furi_assert(fff_file); - furi_assert(dev_file_name); - - Storage* storage = furi_record_open(RECORD_STORAGE); - Stream* flipper_format_stream = flipper_format_get_raw_stream(fff_file); - - bool saved = false; - FuriString* file_dir; - file_dir = furi_string_alloc(); - - path_extract_dirname(dev_file_name, file_dir); - do { - if(!storage_simply_mkdir(storage, furi_string_get_cstr(file_dir))) { - FURI_LOG_E(TAG, "(save) Cannot mkdir"); - break; - } - - if(!storage_simply_remove(storage, dev_file_name)) { - FURI_LOG_E(TAG, "(save) Cannot remove"); - break; - } - - stream_seek(flipper_format_stream, 0, StreamOffsetFromStart); - stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS); - - saved = true; - //FURI_LOG_D(TAG, "(save) OK Save"); - } while(0); - furi_string_free(file_dir); - furi_record_close(RECORD_STORAGE); - return saved; -} - -void subghz_remote_tx_stop(SubGHzRemote* app) { - if(app->processing == 0) { - return; - } - - if(!furi_string_cmp_str(app->txpreset->protocol, "RAW")) { - while(!furi_hal_subghz_is_async_tx_complete()) { - furi_delay_ms(15); - } - } - - //Stop TX - furi_hal_subghz_stop_async_tx(); - //FURI_LOG_I(TAG, "TX Done!"); - subghz_transmitter_stop(app->tx_transmitter); - - //FURI_LOG_D(TAG, "Checking if protocol is dynamic"); - const SubGhzProtocolRegistry* protocol_registry_items = - subghz_environment_get_protocol_registry(app->environment); - const SubGhzProtocol* proto = subghz_protocol_registry_get_by_name( - protocol_registry_items, furi_string_get_cstr(app->txpreset->protocol)); - //FURI_LOG_D(TAG, "Protocol-TYPE %d", proto->type); - - if(proto && proto->type == SubGhzProtocolTypeDynamic) { - //FURI_LOG_D(TAG, "Protocol is dynamic. Saving key"); - // Remove repeat if it was present - flipper_format_delete_key(app->tx_fff_data, "Repeat"); - - subghz_remote_save_protocol_to_file(app->tx_fff_data, app->tx_file_path); - - keeloq_reset_mfname(); - keeloq_reset_kl_type(); - keeloq_reset_original_btn(); - subghz_custom_btns_reset(); - star_line_reset_mfname(); - star_line_reset_kl_type(); - } - - subghz_transmitter_free(app->tx_transmitter); - furi_hal_subghz_idle(); - - notification_message(app->notification, &sequence_blink_stop); - - subghz_remote_preset_free(app->txpreset); - flipper_format_free(app->tx_fff_data); - subghz_remote_end_send(app); -} - -static bool subghz_remote_send_sub(SubGHzRemote* app, FlipperFormat* fff_data) { - // - bool res = false; - do { - if(!furi_hal_subghz_is_tx_allowed(app->txpreset->frequency)) { - FURI_LOG_E( - TAG, - "In your settings, only reception on this frequency (%lu) is allowed,\r\n" - "the actual operation of the subghz remote app is not possible\r\n ", - app->txpreset->frequency); - app->tx_not_allowed = true; - subghz_remote_end_send(app); - break; - } else { - app->tx_not_allowed = false; - } - - app->tx_transmitter = subghz_transmitter_alloc_init( - app->environment, furi_string_get_cstr(app->txpreset->protocol)); - if(!app->tx_transmitter) { - break; - } - - if(subghz_transmitter_deserialize(app->tx_transmitter, fff_data) != - SubGhzProtocolStatusOk) { - FURI_LOG_E(TAG, "Deserialize error!"); - break; - } - - furi_hal_subghz_reset(); - furi_hal_subghz_idle(); - furi_hal_subghz_load_custom_preset(app->txpreset->data); - furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); - - furi_hal_subghz_idle(); - - furi_hal_subghz_set_frequency_and_path(app->txpreset->frequency); - furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); - furi_hal_gpio_init( - furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - - if(!furi_hal_subghz_tx()) { - FURI_LOG_E(TAG, "Sending not allowed"); - break; - } - - //FURI_LOG_I(TAG, "Sending..."); - notification_message(app->notification, &sequence_blink_start_magenta); - - furi_hal_subghz_start_async_tx(subghz_transmitter_yield, app->tx_transmitter); - - res = true; - } while(0); - - return res; -} - -static void subghz_remote_send_signal(SubGHzRemote* app, Storage* storage, const char* path) { - //FURI_LOG_D(TAG, "Sending: %s", path); - - app->tx_file_path = path; - - app->tx_fff_data = flipper_format_string_alloc(); - - app->txpreset = subghz_remote_preset_alloc(); - - // load settings/stream from .sub file - FlipperFormat* fff_file = flipper_format_file_alloc(storage); - bool open_ok = false; - do { - if(!flipper_format_file_open_existing(fff_file, path)) { - FURI_LOG_E(TAG, "Could not open file %s", path); - break; - } - if(!subghz_remote_key_load( - app->txpreset, - fff_file, - app->tx_fff_data, - app->setting, - app->subghz_receiver, - path)) { - FURI_LOG_E(TAG, "Could not load key"); - break; - } - open_ok = true; - } while(0); - flipper_format_free(fff_file); - if(!open_ok) { - FURI_LOG_E(TAG, "Could not load file!"); - return; - } - - subghz_remote_send_sub(app, app->tx_fff_data); -} - -static void subghz_remote_process_signal(SubGHzRemote* app, FuriString* signal) { - view_port_update(app->view_port); - - //FURI_LOG_D(TAG, "signal = %s", furi_string_get_cstr(signal)); - - if(strlen(furi_string_get_cstr(signal)) > 12) { - Storage* storage = furi_record_open(RECORD_STORAGE); - subghz_remote_send_signal(app, storage, furi_string_get_cstr(signal)); - furi_record_close(RECORD_STORAGE); - } else if(strlen(furi_string_get_cstr(signal)) < 10) { - subghz_remote_end_send(app); - } -} - -static void render_callback(Canvas* canvas, void* ctx) { - SubGHzRemote* app = ctx; - furi_check(furi_mutex_acquire(app->model_mutex, FuriWaitForever) == FuriStatusOk); - - //setup different canvas settings - if(app->file_result == 1) { - //if map has no valid filenames defined - canvas_clear(canvas); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 62, 5, AlignCenter, AlignTop, "Config is incorrect."); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignTop, "Please configure map."); - canvas_draw_str_aligned(canvas, 62, 60, AlignCenter, AlignBottom, "Press Back to Exit."); - } else if(app->file_result == 3) { - //if map has no valid filenames defined - canvas_clear(canvas); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 62, 5, AlignCenter, AlignTop, "Checking config."); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignTop, "If app is stuck..."); - canvas_draw_str_aligned(canvas, 62, 60, AlignCenter, AlignBottom, "Press Back to Exit."); - } else if(app->tx_not_allowed) { - canvas_clear(canvas); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 62, 5, AlignCenter, AlignTop, "Transmission is blocked."); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 62, 15, AlignCenter, AlignTop, "Frequency is outside of"); - canvas_draw_str_aligned(canvas, 62, 25, AlignCenter, AlignTop, "default range."); - canvas_draw_str_aligned(canvas, 62, 35, AlignCenter, AlignTop, "Check docs."); - canvas_draw_str_aligned(canvas, 62, 60, AlignCenter, AlignBottom, "Press Back to Exit."); - } else { - //map found, draw all the things - canvas_clear(canvas); - - //canvas_set_font(canvas, FontPrimary); - //canvas_draw_str(canvas, 0, 10, "U: "); - //canvas_draw_str(canvas, 0, 20, "L: "); - //canvas_draw_str(canvas, 0, 30, "R: "); - //canvas_draw_str(canvas, 0, 40, "D: "); - //canvas_draw_str(canvas, 0, 50, "Ok: "); - - //PNGs are located in assets/icons/SubGHzRemote before compilation - - //Icons for Labels - //canvas_draw_icon(canvas, 0, 0, &I_SubGHzRemote_LeftAlignedButtons_9x64); - canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4); - canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4); - canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7); - canvas_draw_icon(canvas, 2, 33, &I_ButtonRight_4x7); - canvas_draw_icon(canvas, 0, 42, &I_Ok_btn_9x9); - canvas_draw_icon(canvas, 0, 53, &I_back_10px); - - //Labels - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 10, 10, app->up_label); - canvas_draw_str(canvas, 10, 20, app->down_label); - canvas_draw_str(canvas, 10, 30, app->left_label); - canvas_draw_str(canvas, 10, 40, app->right_label); - canvas_draw_str(canvas, 10, 50, app->ok_label); - - canvas_draw_str_aligned(canvas, 11, 62, AlignLeft, AlignBottom, "Press=Exit."); - - //Status text and indicator - canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, app->send_status); - - switch(app->send_status_c) { - case 0: - canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); - break; - case 1: - canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); - canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_up_7x9); - break; - case 2: - canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); - canvas_draw_icon_ex(canvas, 116, 17, &I_Pin_arrow_up_7x9, IconRotation180); - break; - case 3: - canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); - canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation90); - break; - case 4: - canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); - canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation270); - break; - case 5: - canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); - canvas_draw_icon(canvas, 116, 18, &I_Pin_star_7x7); - break; - } - - //Repeat indicator - //canvas_draw_str_aligned(canvas, 125, 40, AlignRight, AlignBottom, "Repeat:"); - //canvas_draw_icon(canvas, 115, 39, &I_SubGHzRemote_Repeat_12x14); - //canvas_draw_str_aligned(canvas, 125, 62, AlignRight, AlignBottom, int_to_char(app->repeat)); - } - - furi_mutex_release(app->model_mutex); -} - -static void input_callback(InputEvent* input_event, void* ctx) { - SubGHzRemote* app = ctx; - furi_message_queue_put(app->input_queue, input_event, 0); -} - -void subghz_remote_subghz_alloc(SubGHzRemote* app) { - // load subghz presets - app->setting = subghz_setting_alloc(); - subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user")); - - // load mfcodes - app->environment = subghz_environment_alloc(); - subghz_environment_load_keystore(app->environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); - subghz_environment_load_keystore( - app->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); - subghz_environment_set_came_atomo_rainbow_table_file_name( - app->environment, EXT_PATH("subghz/assets/came_atomo")); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - app->environment, EXT_PATH("subghz/assets/nice_flor_s")); - subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - app->environment, EXT_PATH("subghz/assets/alutech_at_4n")); - subghz_environment_set_protocol_registry(app->environment, (void*)&subghz_protocol_registry); - - app->subghz_receiver = subghz_receiver_alloc_init(app->environment); -} - -SubGHzRemote* subghz_remote_alloc(void) { - SubGHzRemote* app = malloc(sizeof(SubGHzRemote)); +SubGhzRemoteApp* subghz_remote_app_alloc() { + SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp)); // Enable power for External CC1101 if it is connected furi_hal_subghz_enable_ext_power(); @@ -774,24 +35,81 @@ SubGHzRemote* subghz_remote_alloc(void) { furi_hal_power_suppress_charge_enter(); - app->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + app->file_path = furi_string_alloc(); + furi_string_set(app->file_path, SUBREM_APP_FOLDER); - app->input_queue = furi_message_queue_alloc(32, sizeof(InputEvent)); - - app->view_port = view_port_alloc(); - view_port_draw_callback_set(app->view_port, render_callback, app); - view_port_input_callback_set(app->view_port, input_callback, app); - - // Open GUI and register view_port + // GUI app->gui = furi_record_open(RECORD_GUI); - gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); - app->notification = furi_record_open(RECORD_NOTIFICATION); + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&subrem_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, subghz_remote_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, subghz_remote_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, subghz_remote_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // SubMenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, SubRemViewSubmenu, submenu_get_view(app->submenu)); + + //Dialog + app->dialogs = furi_record_open(RECORD_DIALOGS); + + // Remote view + app->subrem_remote_view = subrem_view_remote_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + SubRemViewIDRemote, + subrem_view_remote_get_view(app->subrem_remote_view)); + + for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) { + app->subs_preset[i] = subrem_sub_file_preset_alloc(); + } + + app->setting = subghz_setting_alloc(); + subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user")); + + app->environment = subghz_environment_alloc(); + + subghz_environment_load_keystore(app->environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); + subghz_environment_load_keystore( + app->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); + subghz_environment_set_came_atomo_rainbow_table_file_name( + app->environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + app->environment, EXT_PATH("subghz/assets/alutech_at_4n")); + subghz_environment_set_nice_flor_s_rainbow_table_file_name( + app->environment, EXT_PATH("subghz/assets/nice_flor_s")); + subghz_environment_set_protocol_registry(app->environment, (void*)&subghz_protocol_registry); + + app->receiver = subghz_receiver_alloc_init(app->environment); + + app->tx_running = false; + +#ifdef SUBREM_LIGHT + scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile); +#else + scene_manager_next_scene(app->scene_manager, SubRemSceneStart); +#endif return app; } -void subghz_remote_free(SubGHzRemote* app, bool with_subghz) { +void subghz_remote_app_free(SubGhzRemoteApp* app) { + furi_assert(app); + furi_hal_power_suppress_charge_exit(); // Disable power for External CC1101 if it was enabled and module is connected @@ -799,33 +117,34 @@ void subghz_remote_free(SubGHzRemote* app, bool with_subghz) { // Reinit SPI handles for internal radio / nfc furi_hal_subghz_init_radio_type(SubGhzRadioInternal); - furi_string_free(app->up_file); - furi_string_free(app->down_file); - furi_string_free(app->left_file); - furi_string_free(app->right_file); - furi_string_free(app->ok_file); + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, SubRemViewSubmenu); + submenu_free(app->submenu); - furi_string_free(app->file_path); - furi_string_free(app->signal); + //Dialog + furi_record_close(RECORD_DIALOGS); - gui_remove_view_port(app->gui, app->view_port); - furi_record_close(RECORD_GUI); - view_port_free(app->view_port); - app->gui = NULL; + // Remote view + view_dispatcher_remove_view(app->view_dispatcher, SubRemViewIDRemote); + subrem_view_remote_free(app->subrem_remote_view); - furi_message_queue_free(app->input_queue); + subghz_receiver_free(app->receiver); + subghz_environment_free(app->environment); + subghz_setting_free(app->setting); - furi_mutex_free(app->model_mutex); - - if(with_subghz) { - furi_hal_subghz_sleep(); - subghz_setting_free(app->setting); - subghz_receiver_free(app->subghz_receiver); - subghz_environment_free(app->environment); + for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) { + subrem_sub_file_preset_free(app->subs_preset[i]); } + // Notifications furi_record_close(RECORD_NOTIFICATION); - app->notification = NULL; + app->notifications = NULL; + + // Close records + furi_record_close(RECORD_GUI); + + // Path strings + furi_string_free(app->file_path); free(app); } @@ -833,270 +152,13 @@ void subghz_remote_free(SubGHzRemote* app, bool with_subghz) { int32_t subghz_remote_app(void* p) { UNUSED(p); DOLPHIN_DEED(DolphinDeedPluginStart); - SubGHzRemote* app = subghz_remote_alloc(); + SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc(); - app->file_path = furi_string_alloc(); - app->signal = furi_string_alloc(); + furi_string_set(subghz_remote_app->file_path, SUBREM_APP_FOLDER); - //setup variables before population - app->up_file = furi_string_alloc(); - app->down_file = furi_string_alloc(); - app->left_file = furi_string_alloc(); - app->right_file = furi_string_alloc(); - app->ok_file = furi_string_alloc(); + view_dispatcher_run(subghz_remote_app->view_dispatcher); - app->file_result = 3; - - Storage* storage = furi_record_open(RECORD_STORAGE); - - if(!storage_simply_mkdir(storage, SUBREMOTEMAP_FOLDER)) { - FURI_LOG_E(TAG, "Could not create folder %s", SUBREMOTEMAP_FOLDER); - } - furi_record_close(RECORD_STORAGE); - - furi_string_set(app->file_path, SUBREMOTEMAP_FOLDER); - - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, SUBREMOTEMAP_EXTENSION, &I_sub1_10px); - browser_options.base_path = SUBREMOTEMAP_FOLDER; - - bool res = dialog_file_browser_show(dialogs, app->file_path, app->file_path, &browser_options); - - furi_record_close(RECORD_DIALOGS); - if(!res) { - FURI_LOG_E(TAG, "No file selected"); - subghz_remote_free(app, false); - return 255; - } else { - //check map and population variables - subghz_remote_cfg_set_check(app, app->file_path); - } - - // init subghz stuff - subghz_remote_subghz_alloc(app); - - bool exit_loop = false; - - if(app->file_result == 2) { - //FURI_LOG_D( - //TAG, - //"U: %s - D: %s - L: %s - R: %s - O: %s ", - //furi_string_get_cstr(app->up_file), - //furi_string_get_cstr(app->down_file), - //furi_string_get_cstr(app->left_file), - //furi_string_get_cstr(app->right_file), - //furi_string_get_cstr(app->ok_file)); - - //variables to control multiple button presses and status updates - app->send_status = "Idle"; - app->send_status_c = 0; - app->processing = 0; - //app->repeat = 1; - app->button = 0; - - //refresh screen to update variables before processing main screen or error screens - furi_mutex_release(app->model_mutex); - view_port_update(app->view_port); - - //input detect loop start - InputEvent input; - while(1) { - furi_check( - furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk); - //FURI_LOG_D( - //TAG, - //"key: %s type: %s", - //input_get_key_name(input.key), - //input_get_type_name(input.type)); - - switch(input.key) { - case InputKeyUp: - if(input.type == InputTypePress) { - if(app->up_enabled) { - if(app->processing == 0) { - furi_string_reset(app->signal); - furi_string_set(app->signal, app->up_file); - app->button = 1; - app->processing = 1; - } - } - } - if(input.type == InputTypeRelease) { - if(app->up_enabled) { - subghz_remote_tx_stop(app); - } - } - break; - - case InputKeyDown: - if(input.type == InputTypePress) { - if(app->down_enabled) { - if(app->processing == 0) { - furi_string_reset(app->signal); - furi_string_set(app->signal, app->down_file); - app->button = 2; - app->processing = 1; - } - } - } - if(input.type == InputTypeRelease) { - if(app->down_enabled) { - subghz_remote_tx_stop(app); - } - } - break; - - case InputKeyRight: - if(input.type == InputTypePress) { - if(app->right_enabled) { - if(app->processing == 0) { - furi_string_reset(app->signal); - furi_string_set(app->signal, app->right_file); - app->button = 3; - app->processing = 1; - } - } - } - if(input.type == InputTypeRelease) { - if(app->right_enabled) { - subghz_remote_tx_stop(app); - } - } - break; - - case InputKeyLeft: - if(input.type == InputTypePress) { - if(app->left_enabled) { - if(app->processing == 0) { - furi_string_reset(app->signal); - furi_string_set(app->signal, app->left_file); - app->button = 4; - app->processing = 1; - } - } - } - if(input.type == InputTypeRelease) { - if(app->left_enabled) { - subghz_remote_tx_stop(app); - } - } - break; - - case InputKeyOk: - if(input.type == InputTypePress) { - if(app->ok_enabled) { - if(app->processing == 0) { - furi_string_reset(app->signal); - furi_string_set(app->signal, app->ok_file); - app->button = 5; - app->processing = 1; - } - } - } - if(input.type == InputTypeRelease) { - if(app->ok_enabled) { - subghz_remote_tx_stop(app); - } - } - break; - - case InputKeyBack: - subghz_remote_tx_stop(app); - exit_loop = true; - break; - default: - break; - } - - if(app->processing == 0) { - //FURI_LOG_D(TAG, "processing 0"); - app->send_status = "Idle"; - app->send_status_c = 0; - app->button = 0; - } else if(app->processing == 1) { - //FURI_LOG_D(TAG, "processing 1"); - - app->send_status = "Send"; - - switch(app->button) { - case 1: - app->send_status_c = 1; - break; - case 2: - app->send_status_c = 2; - break; - case 3: - app->send_status_c = 3; - break; - case 4: - app->send_status_c = 4; - break; - case 5: - app->send_status_c = 5; - break; - } - - app->processing = 2; - - subghz_remote_process_signal(app, app->signal); - } - - if(exit_loop == true) { - furi_mutex_release(app->model_mutex); - break; - } - - furi_mutex_release(app->model_mutex); - view_port_update(app->view_port); - } - } else if(app->file_result == 1 || app->file_result == 3) { - //refresh screen to update variables before processing main screen or error screens - view_port_update(app->view_port); - - InputEvent input; - while(1) { - furi_check( - furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk); - //FURI_LOG_D( - //TAG, - //"key: %s type: %s", - //input_get_key_name(input.key), - //input_get_type_name(input.type)); - - switch(input.key) { - case InputKeyRight: - break; - case InputKeyLeft: - break; - case InputKeyUp: - break; - case InputKeyDown: - break; - case InputKeyOk: - break; - case InputKeyBack: - exit_loop = true; - break; - default: - break; - } - - if(exit_loop == true) { - furi_mutex_release(app->model_mutex); - break; - } - - furi_mutex_release(app->model_mutex); - view_port_update(app->view_port); - } - } else { - furi_mutex_release(app->model_mutex); - } - - // remove & free all stuff created by app - subghz_remote_free(app, true); + subghz_remote_app_free(subghz_remote_app); return 0; } diff --git a/applications/external/subghz_remote/subghz_remote_app_i.c b/applications/external/subghz_remote/subghz_remote_app_i.c new file mode 100644 index 000000000..9b0f77d17 --- /dev/null +++ b/applications/external/subghz_remote/subghz_remote_app_i.c @@ -0,0 +1,448 @@ +#include "subghz_remote_app_i.h" +#include +#include + +#include + +// #include +// #include + +#include + +#define TAG "SubGhzRemote" + +static const char* map_file_labels[SubRemSubKeyNameMaxCount][2] = { + [SubRemSubKeyNameUp] = {"UP", "ULABEL"}, + [SubRemSubKeyNameDown] = {"DOWN", "DLABEL"}, + [SubRemSubKeyNameLeft] = {"LEFT", "LLABEL"}, + [SubRemSubKeyNameRight] = {"RIGHT", "RLABEL"}, + [SubRemSubKeyNameOk] = {"OK", "OKLABEL"}, +}; + +static bool + subrem_set_preset_data(SubGhzSetting* setting, FreqPreset* freq_preset, const char* preset) { + const char* preset_name = ""; + if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { + preset_name = "AM270"; + } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { + preset_name = "AM650"; + } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { + preset_name = "FM238"; + } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { + preset_name = "FM476"; + } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { + // preset_name = "CUSTOM"; + return false; + } else { + FURI_LOG_E(TAG, "Unknown preset"); + return false; + } + size_t preset_index = subghz_setting_get_inx_preset_by_name(setting, preset_name); + freq_preset->data = subghz_setting_get_preset_data(setting, preset_index); + return true; +} + +SubRemSubFilePreset* subrem_sub_file_preset_alloc() { + SubRemSubFilePreset* sub_preset = malloc(sizeof(SubRemSubFilePreset)); + + sub_preset->fff_data = flipper_format_string_alloc(); + sub_preset->file_path = furi_string_alloc(); + sub_preset->protocaol_name = furi_string_alloc(); + sub_preset->label = furi_string_alloc_set_str("N/A"); + + sub_preset->type = SubGhzProtocolTypeUnknown; + + return sub_preset; +} + +void subrem_sub_file_preset_free(SubRemSubFilePreset* sub_preset) { + furi_assert(sub_preset); + + furi_string_free(sub_preset->label); + furi_string_free(sub_preset->protocaol_name); + furi_string_free(sub_preset->file_path); + flipper_format_free(sub_preset->fff_data); + + free(sub_preset); +} + +static void subrem_sub_file_preset_reset(SubRemSubFilePreset* sub_preset) { + furi_assert(sub_preset); + + furi_string_set_str(sub_preset->label, "N/A"); + furi_string_reset(sub_preset->protocaol_name); + furi_string_reset(sub_preset->file_path); + + Stream* fff_data_stream = flipper_format_get_raw_stream(sub_preset->fff_data); + stream_clean(fff_data_stream); + + sub_preset->type = SubGhzProtocolTypeUnknown; +} + +void subrem_map_preset_reset(SubGhzRemoteApp* app) { + furi_assert(app); + + for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) { + subrem_sub_file_preset_reset(app->subs_preset[i]); + } +} + +static bool subrem_map_preset_load(SubGhzRemoteApp* app, FlipperFormat* fff_data_file) { + furi_assert(app); + bool ret = false; + SubRemSubFilePreset* sub_preset; + for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) { + sub_preset = app->subs_preset[i]; + if(!flipper_format_read_string( + fff_data_file, map_file_labels[i][0], sub_preset->file_path)) { +#if FURI_DEBUG + FURI_LOG_W(TAG, "No file patch for %s", map_file_labels[i][0]); +#endif + sub_preset->type = SubGhzProtocolTypeUnknown; + } else if(!flipper_format_rewind(fff_data_file)) { + // Rewind error + } else if(!flipper_format_read_string( + fff_data_file, map_file_labels[i][1], sub_preset->label)) { +#if FURI_DEBUG + FURI_LOG_W(TAG, "No Label for %s", map_file_labels[i][0]); +#endif + path_extract_filename(sub_preset->file_path, sub_preset->label, true); + } else { + FURI_LOG_I( + TAG, + "%-5s: %s %s", + map_file_labels[i][0], + furi_string_get_cstr(sub_preset->label), + furi_string_get_cstr(sub_preset->file_path)); + ret = true; + } + flipper_format_rewind(fff_data_file); + } + return ret; +} + +bool subrem_save_protocol_to_file(FlipperFormat* flipper_format, const char* dev_file_name) { + furi_assert(flipper_format); + furi_assert(dev_file_name); + + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format); + + bool saved = false; + FuriString* file_dir = furi_string_alloc(); + + path_extract_dirname(dev_file_name, file_dir); + do { + //removing additional fields + flipper_format_delete_key(flipper_format, "Repeat"); + //flipper_format_delete_key(flipper_format, "Manufacture"); + + if(!storage_simply_remove(storage, dev_file_name)) { + break; + } + //ToDo check Write + stream_seek(flipper_format_stream, 0, StreamOffsetFromStart); + stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS); + + saved = true; + } while(0); + furi_string_free(file_dir); + furi_record_close(RECORD_STORAGE); + return saved; +} + +bool subrem_tx_start_sub( + SubGhzRemoteApp* app, + SubRemSubFilePreset* sub_preset, + SubGhzProtocolEncoderRAWCallbackEnd callback) { + furi_assert(app); + furi_assert(sub_preset); + bool ret = false; + + subrem_tx_stop_sub(app, true); + + if(sub_preset->type == SubGhzProtocolTypeUnknown) { + return false; + } + + FURI_LOG_I(TAG, "Send %s", furi_string_get_cstr(sub_preset->label)); + + subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK); + keeloq_reset_original_btn(); + subghz_custom_btns_reset(); + + do { + flipper_format_rewind(sub_preset->fff_data); // + + app->transmitter = subghz_transmitter_alloc_init( + app->environment, furi_string_get_cstr(sub_preset->protocaol_name)); + + if(app->transmitter) { + if(subghz_transmitter_deserialize(app->transmitter, sub_preset->fff_data) != + SubGhzProtocolStatusOk) { + FURI_LOG_E(TAG, "Deserialize error!"); + break; + } + furi_hal_subghz_reset(); + furi_hal_subghz_idle(); + furi_hal_subghz_load_custom_preset(sub_preset->freq_preset.data); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + + furi_hal_subghz_idle(); + + furi_hal_subghz_set_frequency_and_path(sub_preset->freq_preset.frequency); + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + + if(!furi_hal_subghz_tx()) { + FURI_LOG_E(TAG, "Sending not allowed"); + break; + } + + if(sub_preset->type == SubGhzProtocolTypeRAW) { + subghz_protocol_raw_file_encoder_worker_set_callback_end( + (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance( + app->transmitter), + callback, + app); + } + + furi_hal_subghz_start_async_tx(subghz_transmitter_yield, app->transmitter); + + ret = true; + } + } while(false); + + app->tx_running = ret; + + return ret; +} + +static void subghz_tx_stop(SubGhzRemoteApp* app) { + furi_assert(app); + + //Stop TX + furi_hal_subghz_stop_async_tx(); + + subghz_transmitter_stop(app->transmitter); + subghz_transmitter_free(app->transmitter); + furi_hal_subghz_idle(); +} + +bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced) { + furi_assert(app); + SubRemSubFilePreset* sub_preset = app->subs_preset[app->chusen_sub]; + + if(forced || (sub_preset->type != SubGhzProtocolTypeRAW)) { + // SubRemSubKeyTypeRawKey)) { + if(app->tx_running) { + subghz_tx_stop(app); + + if(sub_preset->type == SubGhzProtocolTypeDynamic) { + subrem_save_protocol_to_file( + sub_preset->fff_data, furi_string_get_cstr(sub_preset->file_path)); + + keeloq_reset_mfname(); + keeloq_reset_kl_type(); + keeloq_reset_original_btn(); + subghz_custom_btns_reset(); + star_line_reset_mfname(); + star_line_reset_kl_type(); + } + + app->tx_running = false; + return true; + } + } + + return false; +} + +static bool subrem_map_preset_check(SubGhzRemoteApp* app, FlipperFormat* fff_data_file) { + furi_assert(app); + FuriString* temp_str = furi_string_alloc(); + uint32_t temp_data32; + bool ret = false; + bool sub_preset_loaded = false; + SubRemSubFilePreset* sub_preset; + + for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) { + sub_preset = app->subs_preset[i]; + sub_preset_loaded = false; + if(furi_string_empty(sub_preset->file_path)) { + // FURI_LOG_I(TAG, "Empty file path"); + continue; + } + do { + if(!flipper_format_file_open_existing( + fff_data_file, furi_string_get_cstr(sub_preset->file_path))) { + FURI_LOG_W(TAG, "Error open file %s", furi_string_get_cstr(sub_preset->file_path)); + break; + } + + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + + if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) || + (!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) && + temp_data32 == SUBGHZ_KEY_FILE_VERSION) { + } else { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + //Load frequency + if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { + FURI_LOG_W(TAG, "Cannot read frequency. Set default frequency"); + sub_preset->freq_preset.frequency = + subghz_setting_get_default_frequency(app->setting); + } else if(!furi_hal_subghz_is_tx_allowed(temp_data32)) { + FURI_LOG_E(TAG, "This frequency can only be used for RX"); + break; + } else { + sub_preset->freq_preset.frequency = temp_data32; + } + + //Load preset + if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { + FURI_LOG_E(TAG, "Missing Preset"); + break; + } + + if(!subrem_set_preset_data( + app->setting, &sub_preset->freq_preset, furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Cannot load preset."); + break; + } + + //Load protocol + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + + FlipperFormat* fff_data = sub_preset->fff_data; + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { + //if RAW + subghz_protocol_raw_gen_fff_data( + fff_data, furi_string_get_cstr(sub_preset->file_path)); + } else { + stream_copy_full( + flipper_format_get_raw_stream(fff_data_file), + flipper_format_get_raw_stream(fff_data)); + } + + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(app->environment); + + const SubGhzProtocol* protocol = subghz_protocol_registry_get_by_name( + protocol_registry_items, furi_string_get_cstr(temp_str)); + + if(!protocol) { + FURI_LOG_E(TAG, "Protocol not found"); + break; + } else if(protocol->flag & SubGhzProtocolFlag_Send) { + if((protocol->type == SubGhzProtocolTypeStatic) || + (protocol->type == SubGhzProtocolTypeDynamic) || + // (protocol->type == SubGhzProtocolTypeBinRAW) || // TODO: BINRAW + (protocol->type == SubGhzProtocolTypeRAW)) { + sub_preset->type = protocol->type; + } else { + FURI_LOG_E(TAG, "Unsuported Protocol"); + break; + } + + furi_string_set(sub_preset->protocaol_name, temp_str); + } else { + FURI_LOG_E(TAG, "Protocol does not support transmission"); + } + + sub_preset_loaded = true; + ret |= true; +#if FURI_DEBUG + FURI_LOG_I(TAG, "%-16s - protocol Loaded", furi_string_get_cstr(sub_preset->label)); +#endif + } while(false); + + // TODO: + // Load file state logic + // Label depending on the state + + if(!sub_preset_loaded) { + furi_string_set_str(sub_preset->label, "N/A"); + } + + flipper_format_file_close(fff_data_file); + } + furi_string_free(temp_str); + + return ret; +} + +bool subrem_map_file_load(SubGhzRemoteApp* app, const char* file_path) { + furi_assert(app); + furi_assert(file_path); +#if FURI_DEBUG + FURI_LOG_I(TAG, "Load Map File Start"); +#endif + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + bool ret = false; +#if FURI_DEBUG + FURI_LOG_I(TAG, "Open Map File.."); +#endif + subrem_map_preset_reset(app); + + if(!flipper_format_file_open_existing(fff_data_file, file_path)) { + FURI_LOG_E(TAG, "Could not open MAP file %s", file_path); + } else { + if(!subrem_map_preset_load(app, fff_data_file)) { + FURI_LOG_E(TAG, "Could no Sub file path in MAP file"); + // ret = // error for popup + } else if( + (flipper_format_file_close(fff_data_file)) && + (subrem_map_preset_check(app, fff_data_file))) { + FURI_LOG_I(TAG, "Load Map File Seccesful"); + ret = true; + } + } + + // TODO: Popup for error or return error type + if(!ret) { + FURI_LOG_E(TAG, "Broken Map File"); + } + + flipper_format_file_close(fff_data_file); + flipper_format_free(fff_data_file); + + furi_record_close(RECORD_STORAGE); + + return ret; +} + +SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app) { + furi_assert(app); + + FuriString* file_path = furi_string_alloc(); + SubRemLoadMapState ret = SubRemLoadMapStateBack; + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, SUBREM_APP_EXTENSION, &I_sub1_10px); + browser_options.base_path = SUBREM_APP_FOLDER; + + // Input events and views are managed by file_select + if(!dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options)) { + } else if(subrem_map_file_load(app, furi_string_get_cstr(app->file_path))) { + ret = SubRemLoadMapStateOK; + } else { + ret = SubRemLoadMapStateError; + } + + furi_string_free(file_path); + + return ret; +} diff --git a/applications/external/subghz_remote/subghz_remote_app_i.h b/applications/external/subghz_remote/subghz_remote_app_i.h new file mode 100644 index 000000000..74413a55b --- /dev/null +++ b/applications/external/subghz_remote/subghz_remote_app_i.h @@ -0,0 +1,84 @@ +#pragma once + +#include "helpers/subrem_types.h" +#include + +#include "views/remote.h" + +#include "scenes/subrem_scene.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#define SUBREM_APP_FOLDER EXT_PATH("subghz/remote") +#define SUBREM_MAX_LEN_NAME 64 + +typedef struct { + uint32_t frequency; + uint8_t* data; +} FreqPreset; + +// Sub File preset +typedef struct { + FlipperFormat* fff_data; + FreqPreset freq_preset; + FuriString* file_path; + FuriString* protocaol_name; + FuriString* label; + SubGhzProtocolType type; +} SubRemSubFilePreset; + +SubRemSubFilePreset* subrem_sub_file_preset_alloc(); + +void subrem_sub_file_preset_free(SubRemSubFilePreset* sub_preset); + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + DialogsApp* dialogs; + Submenu* submenu; + FuriString* file_path; + char file_name_tmp[SUBREM_MAX_LEN_NAME]; + + SubRemViewRemote* subrem_remote_view; + + SubRemSubFilePreset* subs_preset[SubRemSubKeyNameMaxCount]; + + SubGhzSetting* setting; + SubGhzEnvironment* environment; + SubGhzReceiver* receiver; + SubGhzTransmitter* transmitter; + + bool tx_running; + + uint8_t chusen_sub; + + // TODO: LoadFileError +} SubGhzRemoteApp; + +SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app); + +bool subrem_tx_start_sub( + SubGhzRemoteApp* app, + SubRemSubFilePreset* sub_preset, + SubGhzProtocolEncoderRAWCallbackEnd callback); + +bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced); diff --git a/applications/external/subghz_remote/views/remote.c b/applications/external/subghz_remote/views/remote.c new file mode 100644 index 000000000..85c63568e --- /dev/null +++ b/applications/external/subghz_remote/views/remote.c @@ -0,0 +1,302 @@ +#include "remote.h" +#include "../subghz_remote_app_i.h" + +#include +#include + +#define SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH 16 + +struct SubRemViewRemote { + View* view; + SubRemViewRemoteCallback callback; + void* context; +}; + +// TODO: model +typedef struct { + // FuriString* up_label; + // FuriString* down_label; + // FuriString* left_label; + // FuriString* right_label; + // FuriString* ok_label; + + char* up_label; + char* down_label; + char* left_label; + char* right_label; + char* ok_label; + + SubRemViewRemoteState state; + + uint8_t pressed_btn; +} SubRemViewRemoteModel; + +void subrem_view_remote_set_callback( + SubRemViewRemote* subrem_view_remote, + SubRemViewRemoteCallback callback, + void* context) { + furi_assert(subrem_view_remote); + + subrem_view_remote->callback = callback; + subrem_view_remote->context = context; +} + +void subrem_view_remote_add_data_to_show( + SubRemViewRemote* subrem_view_remote, + const char* up_label, + const char* down_label, + const char* left_label, + const char* right_label, + const char* ok_label) { + furi_assert(subrem_view_remote); + + with_view_model( + subrem_view_remote->view, + SubRemViewRemoteModel * model, + { + strncpy(model->up_label, up_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH); + strncpy(model->down_label, down_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH); + strncpy(model->left_label, left_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH); + strncpy(model->right_label, right_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH); + strncpy(model->ok_label, ok_label, SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH); + + // furi_string_set(model->up_label, up_label); + // furi_string_set(model->down_label, down_label); + // furi_string_set(model->left_label, left_label); + // furi_string_set(model->right_label, right_label); + // furi_string_set(model->ok_label, ok_label); + }, + true); +} + +void subrem_view_remote_set_presed_btn(SubRemViewRemote* subrem_view_remote, uint8_t presed_btn) { + furi_assert(subrem_view_remote); + with_view_model( + subrem_view_remote->view, + SubRemViewRemoteModel * model, + { model->pressed_btn = presed_btn; }, + true); +} + +void subrem_view_remote_set_state( + SubRemViewRemote* subrem_view_remote, + SubRemViewRemoteState state) { + furi_assert(subrem_view_remote); + with_view_model( + subrem_view_remote->view, SubRemViewRemoteModel * model, { model->state = state; }, true); +} + +void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_clear(canvas); + + //Icons for Labels + //canvas_draw_icon(canvas, 0, 0, &I_SubGHzRemote_LeftAlignedButtons_9x64); + canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4); + canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4); + canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 2, 33, &I_ButtonRight_4x7); + canvas_draw_icon(canvas, 0, 42, &I_Ok_btn_9x9); + canvas_draw_icon(canvas, 0, 53, &I_back_10px); + + //Labels + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 10, 10, model->up_label); + canvas_draw_str(canvas, 10, 20, model->down_label); + canvas_draw_str(canvas, 10, 30, model->left_label); + canvas_draw_str(canvas, 10, 40, model->right_label); + canvas_draw_str(canvas, 10, 50, model->ok_label); + + // canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label)); + // canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label)); + // canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label)); + // canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label)); + // canvas_draw_str(canvas, 10, 10, furi_string_get_cstr(model->up_label)); + + canvas_draw_str_aligned(canvas, 11, 62, AlignLeft, AlignBottom, "Hold=Exit."); + + //Status text and indicator + canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); + + if(model->state == SubRemViewRemoteStateIdle) { + canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Idle"); + } else { + switch(model->state) { + case SubRemViewRemoteStateSending: + canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Send"); + break; + case SubRemViewRemoteStateLoading: + canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Load"); + break; + default: +#if FURI_DEBUG + canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Wrong_state"); +#endif + break; + } + + switch(model->pressed_btn) { + case SubRemSubKeyNameUp: + canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_up_7x9); + break; + case SubRemSubKeyNameDown: + canvas_draw_icon_ex(canvas, 116, 17, &I_Pin_arrow_up_7x9, IconRotation180); + break; + case SubRemSubKeyNameLeft: + canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation270); + break; + case SubRemSubKeyNameRight: + canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation90); + break; + case SubRemSubKeyNameOk: + canvas_draw_icon(canvas, 116, 18, &I_Pin_star_7x7); + break; + } + } + //Repeat indicator + //canvas_draw_str_aligned(canvas, 125, 40, AlignRight, AlignBottom, "Repeat:"); + //canvas_draw_icon(canvas, 115, 39, &I_SubGHzRemote_Repeat_12x14); + //canvas_draw_str_aligned(canvas, 125, 62, AlignRight, AlignBottom, int_to_char(app->repeat)); +} + +bool subrem_view_remote_input(InputEvent* event, void* context) { + furi_assert(context); + SubRemViewRemote* subrem_view_remote = context; + + if(event->key == InputKeyBack && event->type == InputTypeLong) { + with_view_model( + subrem_view_remote->view, + SubRemViewRemoteModel * model, + { + strcpy(model->up_label, "N/A"); + strcpy(model->down_label, "N/A"); + strcpy(model->left_label, "N/A"); + strcpy(model->right_label, "N/A"); + strcpy(model->ok_label, "N/A"); + }, + false); + subrem_view_remote->callback(SubRemCustomEventViewRemoteBack, subrem_view_remote->context); + return true; + } else if(event->key == InputKeyBack && event->type == InputTypeShort) { + with_view_model( + subrem_view_remote->view, + SubRemViewRemoteModel * model, + { model->pressed_btn = 0; }, + true); + subrem_view_remote->callback( + SubRemCustomEventViewRemoteForcedStop, subrem_view_remote->context); + return true; + } else if(event->key == InputKeyBack) { + return true; + } + // BACK button processing end + + if(event->key == InputKeyUp && event->type == InputTypePress) { + subrem_view_remote->callback( + SubRemCustomEventViewRemoteStartUP, subrem_view_remote->context); + return true; + } else if(event->key == InputKeyDown && event->type == InputTypePress) { + subrem_view_remote->callback( + SubRemCustomEventViewRemoteStartDOWN, subrem_view_remote->context); + return true; + } else if(event->key == InputKeyLeft && event->type == InputTypePress) { + subrem_view_remote->callback( + SubRemCustomEventViewRemoteStartLEFT, subrem_view_remote->context); + return true; + } else if(event->key == InputKeyRight && event->type == InputTypePress) { + subrem_view_remote->callback( + SubRemCustomEventViewRemoteStartRIGHT, subrem_view_remote->context); + return true; + } else if(event->key == InputKeyOk && event->type == InputTypePress) { + subrem_view_remote->callback( + SubRemCustomEventViewRemoteStartOK, subrem_view_remote->context); + return true; + } else if(event->type == InputTypeRelease) { + subrem_view_remote->callback(SubRemCustomEventViewRemoteStop, subrem_view_remote->context); + return true; + } + + return true; +} + +void subrem_view_remote_enter(void* context) { + furi_assert(context); +} + +void subrem_view_remote_exit(void* context) { + furi_assert(context); +} + +SubRemViewRemote* subrem_view_remote_alloc() { + SubRemViewRemote* subrem_view_remote = malloc(sizeof(SubRemViewRemote)); + + // View allocation and configuration + subrem_view_remote->view = view_alloc(); + view_allocate_model( + subrem_view_remote->view, ViewModelTypeLocking, sizeof(SubRemViewRemoteModel)); + view_set_context(subrem_view_remote->view, subrem_view_remote); + view_set_draw_callback(subrem_view_remote->view, (ViewDrawCallback)subrem_view_remote_draw); + view_set_input_callback(subrem_view_remote->view, subrem_view_remote_input); + view_set_enter_callback(subrem_view_remote->view, subrem_view_remote_enter); + view_set_exit_callback(subrem_view_remote->view, subrem_view_remote_exit); + + with_view_model( + subrem_view_remote->view, + SubRemViewRemoteModel * model, + { + model->state = SubRemViewRemoteStateIdle; + + model->up_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1); + model->down_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1); + model->left_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1); + model->right_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1); + model->ok_label = malloc(sizeof(char) * SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH + 1); + + strcpy(model->up_label, "N/A"); + strcpy(model->down_label, "N/A"); + strcpy(model->left_label, "N/A"); + strcpy(model->right_label, "N/A"); + strcpy(model->ok_label, "N/A"); + + // model->up_label = furi_string_alloc_set_str("N/A"); + // model->down_label = furi_string_alloc_set_str("N/A"); + // model->left_label = furi_string_alloc_set_str("N/A"); + // model->right_label = furi_string_alloc_set_str("N/A"); + // model->ok_label = furi_string_alloc_set_str("N/A"); + + model->pressed_btn = 0; + }, + true); + return subrem_view_remote; +} + +void subrem_view_remote_free(SubRemViewRemote* subghz_remote) { + furi_assert(subghz_remote); + + with_view_model( + subghz_remote->view, + SubRemViewRemoteModel * model, + { + free(model->up_label); + free(model->down_label); + free(model->left_label); + free(model->right_label); + free(model->ok_label); + + // furi_string_free(model->up_label); + // furi_string_free(model->down_label); + // furi_string_free(model->left_label); + // furi_string_free(model->right_label); + // furi_string_free(model->ok_label); + }, + true); + view_free(subghz_remote->view); + free(subghz_remote); +} + +View* subrem_view_remote_get_view(SubRemViewRemote* subrem_view_remote) { + furi_assert(subrem_view_remote); + return subrem_view_remote->view; +} \ No newline at end of file diff --git a/applications/external/subghz_remote/views/remote.h b/applications/external/subghz_remote/views/remote.h new file mode 100644 index 000000000..76121cf8a --- /dev/null +++ b/applications/external/subghz_remote/views/remote.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include "../helpers/subrem_custom_event.h" + +typedef enum { + SubRemViewRemoteStateIdle, + SubRemViewRemoteStateLoading, + SubRemViewRemoteStateSending, +} SubRemViewRemoteState; + +typedef struct SubRemViewRemote SubRemViewRemote; + +typedef void (*SubRemViewRemoteCallback)(SubRemCustomEvent event, void* context); + +void subrem_view_remote_set_callback( + SubRemViewRemote* subrem_view_remote, + SubRemViewRemoteCallback callback, + void* context); + +SubRemViewRemote* subrem_view_remote_alloc(); + +void subrem_view_remote_free(SubRemViewRemote* subrem_view_remote); + +View* subrem_view_remote_get_view(SubRemViewRemote* subrem_view_remote); + +void subrem_view_remote_add_data_to_show( + SubRemViewRemote* subrem_view_remote, + const char* up_label, + const char* down_label, + const char* left_label, + const char* right_label, + const char* ok_label); + +void subrem_view_remote_set_presed_btn(SubRemViewRemote* subrem_view_remote, uint8_t presed_btn); +void subrem_view_remote_set_state( + SubRemViewRemote* subrem_view_remote, + SubRemViewRemoteState state); \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c index 0ec122ad9..6f22d0adb 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c @@ -97,20 +97,13 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, - {"Sniff PMKID on channel", - {""}, - 1, - {"sniffpmkid -c"}, - INPUT_ARGS, + {"Sniff PMKID", + {"ap", "channel"}, + 2, + {"sniffpmkid -d -l", "sniffpmkid -c"}, + TOGGLE_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, - {"Sniff pmkid on selected aps", - {""}, - 1, - {"sniffpmkid -d -l"}, - NO_ARGS, - FOCUS_CONSOLE_END, - NO_TIP}, {"Channel", {"get", "set"}, 2, diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h index e37e5733b..2a16522bb 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h @@ -26,7 +26,7 @@ #include #include -#define NUM_MENU_ITEMS (19) +#define NUM_MENU_ITEMS (18) #define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096) #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512) diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index eacb05614..adf0b1a9c 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -187,11 +187,16 @@ int32_t lfrfid_app(char* args) { DOLPHIN_DEED(DolphinDeedRfidEmulate); } else { furi_string_set(app->file_path, args); - lfrfid_load_key_data(app, app->file_path, true); - view_dispatcher_attach_to_gui( - app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + if(lfrfid_load_key_data(app, app->file_path, true)) { + view_dispatcher_attach_to_gui( + app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + DOLPHIN_DEED(DolphinDeedRfidEmulate); + } else { + // TODO: exit properly + lfrfid_free(app); + return 0; + } } } else { diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 9ff82eaa4..e87f07012 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -296,6 +296,7 @@ int32_t nfc_app(char* p) { DOLPHIN_DEED(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); DOLPHIN_DEED(DolphinDeedNfcEmulate); diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 1a879d8a0..f7ec3feb6 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -204,7 +204,6 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); break; } - // Set tag general data } else if(type == FuriHalNfcTypeF) { // Set NFC-F data furi_string_cat_printf(temp_str, "ISO 18092 (NFC-F)\n"); @@ -260,7 +259,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { for(size_t i = 0; i < sizeof(nfc_data->f_data.pmm); i++) { furi_string_cat_printf(temp_str, " %02X", nfc_data->f_data.pmm[i]); } - } else { // FuriHalNfcTypeA + } else { + // Set tag general data char iso_type = FURI_BIT(nfc_data->a_data.sak, 5) ? '4' : '3'; furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); furi_string_cat_printf(temp_str, "UID:"); diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index 51d9cd82a..cd35afe55 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -10,7 +10,10 @@ App( "cli", "dialogs", ], - provides=["subghz_start"], + provides=[ + "subghz_start", + "subghz_load_extended_settings", + ], icon="A_Sub1ghz_14", stack_size=3 * 1024, order=10, @@ -23,3 +26,11 @@ App( requires=["subghz"], order=40, ) + +App( + appid="subghz_load_extended_settings", + apptype=FlipperAppType.STARTUP, + entry_point="subghz_extended_freq", + requires=["storage", "subghz"], + order=650, +) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index cd163a653..bf3c71d3a 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -36,6 +36,8 @@ typedef enum { SubmenuIndexCAME24bit868, SubmenuIndexCAMETwee, SubmenuIndexCAMESpace, + SubmenuIndexCameAtomo433, + SubmenuIndexCameAtomo868, SubmenuIndexPricenton433, SubmenuIndexPricenton315, SubmenuIndexBETT_433, diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c index 0631c7c15..4854bc882 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c @@ -266,6 +266,34 @@ bool subghz_txrx_gen_alutech_at_4n_protocol( return res; } +bool subghz_txrx_gen_came_atomo_protocol( + void* context, + const char* preset_name, + uint32_t frequency, + uint32_t serial, + uint16_t cnt) { + SubGhzTxRx* txrx = context; + + bool res = false; + + txrx->transmitter = + subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_CAME_ATOMO_NAME); + subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0); + + if(txrx->transmitter && subghz_protocol_came_atomo_create_data( + subghz_transmitter_get_protocol_instance(txrx->transmitter), + txrx->fff_data, + serial, + cnt, + txrx->preset)) { + res = true; + } + + subghz_transmitter_free(txrx->transmitter); + + return res; +} + bool subghz_txrx_gen_somfy_telis_protocol( void* context, const char* preset_name, diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h index a5fa2a802..dc7dfbe7e 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h @@ -108,6 +108,13 @@ bool subghz_txrx_gen_somfy_telis_protocol( uint8_t btn, uint16_t cnt); +bool subghz_txrx_gen_came_atomo_protocol( + void* context, + const char* preset_name, + uint32_t frequency, + uint32_t serial, + uint16_t cnt); + /** * Generate data SecPlus v2 protocol * diff --git a/applications/main/subghz/scenes/subghz_scene_radio_settings.c b/applications/main/subghz/scenes/subghz_scene_radio_settings.c index 7fea1de0a..77723fa94 100644 --- a/applications/main/subghz/scenes/subghz_scene_radio_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_radio_settings.c @@ -25,11 +25,14 @@ const char* const debug_pin_text[DEBUG_P_COUNT] = { "17(1W)", }; -#define DEBUG_COUNTER_COUNT 3 +#define DEBUG_COUNTER_COUNT 6 const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = { "+1", "+2", "+3", + "+4", + "+5", + "+10", }; static void subghz_scene_ext_module_changed(VariableItem* item) { @@ -71,6 +74,15 @@ static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) { case 2: furi_hal_subghz_set_rolling_counter_mult(3); break; + case 3: + furi_hal_subghz_set_rolling_counter_mult(4); + break; + case 4: + furi_hal_subghz_set_rolling_counter_mult(5); + break; + case 5: + furi_hal_subghz_set_rolling_counter_mult(10); + break; default: break; } @@ -140,24 +152,58 @@ void subghz_scene_radio_settings_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, timestamp_names_text[value_index]); - item = variable_item_list_add( - subghz->variable_item_list, - "Counter incr.", - DEBUG_COUNTER_COUNT, - subghz_scene_receiver_config_set_debug_counter, - subghz); - switch(furi_hal_subghz_get_rolling_counter_mult()) { - case 1: - value_index = 0; - break; - case 2: - value_index = 1; - break; - case 3: - value_index = 2; - break; - default: - break; + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + item = variable_item_list_add( + subghz->variable_item_list, + "Counter incr.", + DEBUG_COUNTER_COUNT, + subghz_scene_receiver_config_set_debug_counter, + subghz); + switch(furi_hal_subghz_get_rolling_counter_mult()) { + case 1: + value_index = 0; + break; + case 2: + value_index = 1; + break; + case 3: + value_index = 2; + break; + case 4: + value_index = 3; + break; + case 5: + value_index = 4; + break; + case 10: + value_index = 5; + break; + default: + break; + } + } else { + item = variable_item_list_add( + subghz->variable_item_list, + "Counter incr.", + 3, + subghz_scene_receiver_config_set_debug_counter, + subghz); + switch(furi_hal_subghz_get_rolling_counter_mult()) { + case 1: + value_index = 0; + break; + case 2: + value_index = 1; + break; + case 3: + value_index = 2; + break; + default: + // Reset to default value + value_index = 0; + furi_hal_subghz_set_rolling_counter_mult(1); + break; + } } variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, debug_counter_text[value_index]); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 9987449cd..26e92e983 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -227,12 +227,20 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case SubGhzCustomEventViewReceiverDeleteItem: + subghz->state_notifications = SubGhzNotificationStateRx; subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); + subghz_history_delete_item(subghz->history, subghz->idx_menu_chosen); subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); + subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); + subghz_scene_receiver_update_statusbar(subghz); + if(subghz_history_get_last_index(subghz->history) == 0) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + } consumed = true; break; case SubGhzCustomEventViewReceiverConfig: diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index ef53006da..14d8ba0d8 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -199,6 +199,18 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexCAMETwee, subghz_scene_set_type_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "CAME Atomo 433MHz", + SubmenuIndexCameAtomo433, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "CAME Atomo 868MHz", + SubmenuIndexCameAtomo868, + subghz_scene_set_type_submenu_callback, + subghz); submenu_add_item( subghz->submenu, "KL: CAME Space 433MHz", @@ -534,6 +546,14 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); } break; + case SubmenuIndexCameAtomo433: + generated_protocol = subghz_txrx_gen_came_atomo_protocol( + subghz->txrx, "AM650", 433920000, (key & 0x0FFFFFFF) | 0x10000000, 0x0003); + break; + case SubmenuIndexCameAtomo868: + generated_protocol = subghz_txrx_gen_came_atomo_protocol( + subghz->txrx, "AM650", 868350000, (key & 0x0FFFFFFF) | 0x10000000, 0x0003); + break; case SubmenuIndexBFTMitto: generated_protocol = subghz_txrx_gen_keeloq_bft_protocol( subghz->txrx, diff --git a/applications/main/subghz/subghz_extended_freq.c b/applications/main/subghz/subghz_extended_freq.c new file mode 100644 index 000000000..bbd44f639 --- /dev/null +++ b/applications/main/subghz/subghz_extended_freq.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +void subghz_extended_freq() { + bool is_extended_i = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + if(flipper_format_file_open_existing(file, "/ext/subghz/assets/extend_range.txt")) { + flipper_format_read_bool(file, "use_ext_range_at_own_risk", &is_extended_i, 1); + } + + furi_hal_subghz_set_extended_frequency(is_extended_i); + + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); +} diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 20afe8c12..d9904bfca 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -73,6 +73,7 @@ typedef struct { SubGhzViewReceiverMode mode; uint8_t u_rssi; size_t scroll_counter; + bool nodraw; } SubGhzViewReceiverModel; void subghz_view_receiver_set_mode( @@ -243,40 +244,48 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { bool scrollbar = model->history_item > 4; FuriString* str_buff = furi_string_alloc(); - SubGhzReceiverMenuItem* item_menu; + if(!model->nodraw) { + SubGhzReceiverMenuItem* item_menu; - for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { - size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0); - item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx); - furi_string_set(str_buff, item_menu->item_str); - size_t scroll_counter = model->scroll_counter; - if(model->idx == idx) { - subghz_view_receiver_draw_frame(canvas, i, scrollbar); - if(scroll_counter < SCROLL_DELAY) { - // Show time of signal one moment - furi_string_set(str_buff, item_menu->time); - scroll_counter = 0; - } else { - scroll_counter -= SCROLL_DELAY; + for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { + size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0); + item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx); + if(item_menu == NULL) { + break; } - } else { - canvas_set_color(canvas, ColorBlack); - scroll_counter = 0; + if(item_menu->type == 0) { + break; + } + furi_string_set(str_buff, item_menu->item_str); + size_t scroll_counter = model->scroll_counter; + if(model->idx == idx) { + subghz_view_receiver_draw_frame(canvas, i, scrollbar); + if(scroll_counter < SCROLL_DELAY) { + // Show time of signal one moment + furi_string_set(str_buff, item_menu->time); + scroll_counter = 0; + } else { + scroll_counter -= SCROLL_DELAY; + } + } else { + canvas_set_color(canvas, ColorBlack); + scroll_counter = 0; + } + canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); + elements_scrollable_text_line( + canvas, + 15, + 9 + i * FRAME_HEIGHT, + (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX), + str_buff, + scroll_counter, + (model->idx != idx), + false); + furi_string_reset(str_buff); + } + if(scrollbar) { + elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item); } - canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); - elements_scrollable_text_line( - canvas, - 15, - 9 + i * FRAME_HEIGHT, - (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX), - str_buff, - scroll_counter, - (model->idx != idx), - false); - furi_string_reset(str_buff); - } - if(scrollbar) { - elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item); } furi_string_free(str_buff); @@ -469,29 +478,12 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { SubGhzViewReceiverModel * model, { if(model->history_item != 0) { - SubGhzReceiverMenuItemArray_it_t it; - // SubGhzReceiverMenuItem* target_item = - // SubGhzReceiverMenuItemArray_get(model->history->data, model->idx); - SubGhzReceiverMenuItemArray_it_last(it, model->history->data); - while(!SubGhzReceiverMenuItemArray_end_p(it)) { - SubGhzReceiverMenuItem* item = SubGhzReceiverMenuItemArray_ref(it); - - if(it->index == (size_t)(model->idx)) { - furi_string_free(item->item_str); - furi_string_free(item->time); - item->type = 0; - SubGhzReceiverMenuItemArray_remove(model->history->data, it); - } - - SubGhzReceiverMenuItemArray_previous(it); - } - // Callback subghz_receiver->callback( SubGhzCustomEventViewReceiverDeleteItem, subghz_receiver->context); } }, - true); + false); } else if(event->key == InputKeyOk && event->type == InputTypeShort) { with_view_model( subghz_receiver->view, @@ -542,6 +534,7 @@ void subghz_view_receiver_exit(void* context) { model->idx = 0; model->list_offset = 0; model->history_item = 0; + model->nodraw = false; }, false); furi_timer_stop(subghz_receiver->timer); @@ -576,6 +569,7 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { model->history_stat_str = furi_string_alloc(); model->progress_str = furi_string_alloc(); model->bar_show = SubGhzViewReceiverBarShowDefault; + model->nodraw = false; model->history = malloc(sizeof(SubGhzReceiverHistory)); SubGhzReceiverMenuItemArray_init(model->history->data); }, @@ -633,6 +627,23 @@ void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_rec subghz_receiver->view, SubGhzViewReceiverModel * model, { + SubGhzReceiverMenuItemArray_it_t it; + // SubGhzReceiverMenuItem* target_item = + // SubGhzReceiverMenuItemArray_get(model->history->data, model->idx); + SubGhzReceiverMenuItemArray_it_last(it, model->history->data); + while(!SubGhzReceiverMenuItemArray_end_p(it)) { + SubGhzReceiverMenuItem* item = SubGhzReceiverMenuItemArray_ref(it); + + if(it->index == (size_t)(model->idx)) { + furi_string_free(item->item_str); + furi_string_free(item->time); + item->type = 0; + SubGhzReceiverMenuItemArray_remove(model->history->data, it); + } + + SubGhzReceiverMenuItemArray_previous(it); + } + if(model->history_item == 5) { if(model->idx >= 2) { model->idx = model->history_item - 1; @@ -645,7 +656,20 @@ void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_rec } }, true); - furi_delay_ms(200); +} + +void subghz_view_receiver_enable_draw_callback(SubGhzViewReceiver* subghz_receiver) { + furi_assert(subghz_receiver); + + with_view_model( + subghz_receiver->view, SubGhzViewReceiverModel * model, { model->nodraw = false; }, true); +} + +void subghz_view_receiver_disable_draw_callback(SubGhzViewReceiver* subghz_receiver) { + furi_assert(subghz_receiver); + + with_view_model( + subghz_receiver->view, SubGhzViewReceiverModel * model, { model->nodraw = true; }, true); } void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) { diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index 54c1b3e7d..f239331d5 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -49,4 +49,8 @@ void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint void subghz_view_receiver_delete_element_callback(SubGhzViewReceiver* subghz_receiver); +void subghz_view_receiver_enable_draw_callback(SubGhzViewReceiver* subghz_receiver); + +void subghz_view_receiver_disable_draw_callback(SubGhzViewReceiver* subghz_receiver); + void subghz_view_receiver_exit(void* context); diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c index b178b51f3..1bae6fdea 100644 --- a/applications/main/xtreme_app/xtreme_app.c +++ b/applications/main/xtreme_app/xtreme_app.c @@ -64,7 +64,19 @@ bool xtreme_app_apply(XtremeApp* app) { } if(app->save_subghz) { - furi_hal_subghz_set_is_extended(app->subghz_extend); + FlipperFormat* file = flipper_format_file_alloc(storage); + do { + if(!flipper_format_file_open_always(file, "/ext/subghz/assets/extend_range.txt")) + break; + if(!flipper_format_write_header_cstr(file, "Flipper SubGhz Setting File", 1)) break; + if(!flipper_format_write_comment_cstr( + file, "Whether to allow extended ranges that can break your flipper")) + break; + if(!flipper_format_write_bool( + file, "use_ext_range_at_own_risk", &app->subghz_extend, 1)) + break; + } while(0); + flipper_format_free(file); } if(app->save_name) { @@ -249,11 +261,13 @@ XtremeApp* xtreme_app_alloc() { } } } while(false); + + if(flipper_format_file_open_existing(file, "/ext/subghz/assets/extend_range.txt")) { + flipper_format_read_bool(file, "use_ext_range_at_own_risk", &app->subghz_extend, 1); + } flipper_format_free(file); furi_record_close(RECORD_STORAGE); - app->subghz_extend = furi_hal_subghz_get_is_extended(); - strlcpy(app->device_name, furi_hal_version_get_name_ptr(), FURI_HAL_VERSION_ARRAY_NAME_LENGTH); Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); diff --git a/assets/resources/badkb/assets/layouts/fr-FR-mac.kl b/assets/resources/badkb/assets/layouts/fr-FR-mac.kl new file mode 100644 index 0000000000000000000000000000000000000000..0906936547cd3c8accd9632bac82cb4193a24c25 GIT binary patch literal 256 zcmaLLM{dGU007a^2*c-7jOjgKAVDOih=6?{?tg}?*<^Na;Jp*y9N*W!`r*KahgW`G zvn8kCYGsczPfNdC`{Bl|xjXkB{IulBi;9;$9}G>b+c4NP+OloOuBmr3`wpx*a_q#Z zGgmHLIyaAHEaHW;H-;qCX%J`= 299999755 && value <= 350000335) && // was increased from 348 to 350 + if(!(allow_extended_for_int) && + !(value >= 299999755 && value <= 350000335) && // was increased from 348 to 350 !(value >= 386999938 && value <= 467750000) && // was increased from 464 to 467.75 - !(value >= 778999847 && value <= 928000000) && !(is_extended)) { + !(value >= 778999847 && value <= 928000000)) { FURI_LOG_I(TAG, "Frequency blocked - outside default range"); return false; } else if( + (allow_extended_for_int) && // !(value >= 281000000 && value <= 361000000) && !(value >= 378000000 && value <= 481000000) && - !(value >= 749000000 && value <= 962000000) && is_extended) { + !(value >= 749000000 && value <= 962000000)) { FURI_LOG_I(TAG, "Frequency blocked - outside extended range"); return false; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index 8383c27f2..d583c90f9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -78,8 +78,9 @@ typedef struct { FuriHalSpiBusHandle* spi_bus_handle; const GpioPin* cc1101_g0_pin; uint8_t rolling_counter_mult; - bool ext_module_power_disabled; - bool timestamp_file_names; + bool ext_module_power_disabled : 1; + bool timestamp_file_names : 1; + bool extended_frequency_i : 1; } FuriHalSubGhz; extern volatile FuriHalSubGhz furi_hal_subghz; @@ -225,18 +226,6 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value); */ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); -/** Read extend and bypass settings values into out params - * - * @return is_extended bool - */ -bool furi_hal_subghz_get_is_extended(); - -/** Set extend and bypass settings values to file - * - * @param is_extended bool for extend - */ -void furi_hal_subghz_set_is_extended(bool is_extended); - /** Сheck if transmission is allowed on this frequency with your current config * * @param value frequency in Hz diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz_i.h b/firmware/targets/f7/furi_hal/furi_hal_subghz_i.h new file mode 100644 index 000000000..9c9e432d6 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz_i.h @@ -0,0 +1,3 @@ +#pragma once + +void furi_hal_subghz_set_extended_frequency(bool state_i); diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index af0ef42ae..8c08509a5 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -991,11 +991,17 @@ bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { if(!flipper_format_get_value_count(file, "AID", &data_cnt)) break; data->aid_len = data_cnt; if(!flipper_format_read_hex(file, "AID", data->aid, data->aid_len)) break; - if(!flipper_format_read_string(file, "Name", temp_str)) break; + if(!flipper_format_read_string(file, "Name", temp_str)) { + furi_string_set_str(temp_str, "Unknown"); + } strlcpy(data->name, furi_string_get_cstr(temp_str), sizeof(data->name)); - if(!flipper_format_get_value_count(file, "Number", &data_cnt)) break; + if(!flipper_format_get_value_count(file, "Number", &data_cnt)) { + data_cnt = 0; + } data->number_len = data_cnt; - if(!flipper_format_read_hex(file, "Number", data->number, data->number_len)) break; + if(!flipper_format_read_hex(file, "Number", data->number, data->number_len)) { + memset(data->number, 0, sizeof(data->number)); + }; parsed = true; // Load optional data uint8_t exp_data[2] = {}; @@ -1401,9 +1407,8 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { break; nfc_device_prepare_format_string(dev, temp_str); if(!flipper_format_write_string(file, "Device type", temp_str)) break; - // Write UID, ATQA, SAK - if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) - break; + // Write UID + if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break; if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; if(dev->format != NfcDeviceSaveFormatNfcV) { diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 31c347021..799a73463 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -378,7 +378,8 @@ bool mf_classic_check_card_type(FuriHalNfcADevData* data) { uint8_t ATQA0 = data->atqa[0]; uint8_t ATQA1 = data->atqa[1]; uint8_t SAK = data->sak; - if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { + if((ATQA0 == 0x44 || ATQA0 == 0x04) && + (SAK == 0x08 || SAK == 0x88 || SAK == 0x09 || SAK == 0x89)) { return true; } else if((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01)) { //skylanders support @@ -397,7 +398,7 @@ MfClassicType mf_classic_get_classic_type(FuriHalNfcADevData* data) { if((ATQA0 == 0x44 || ATQA0 == 0x04)) { if((SAK == 0x08 || SAK == 0x88)) { return MfClassicType1k; - } else if(SAK == 0x09) { + } else if((SAK == 0x09 || SAK == 0x89)) { return MfClassicTypeMini; } } else if((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01)) { diff --git a/lib/nfc/protocols/mifare_common.c b/lib/nfc/protocols/mifare_common.c index cfb5e0a37..1f336cb51 100644 --- a/lib/nfc/protocols/mifare_common.c +++ b/lib/nfc/protocols/mifare_common.c @@ -10,7 +10,8 @@ MifareType mifare_common_get_type(FuriHalNfcADevData* data) { if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) { type = MifareTypeUltralight; } else if( - ((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) || + ((ATQA0 == 0x44 || ATQA0 == 0x04) && + (SAK == 0x08 || SAK == 0x88 || SAK == 0x09 || SAK == 0x89)) || ((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) || ((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01))) { type = MifareTypeClassic; diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 2e817a538..471993b63 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -15,12 +15,20 @@ #define TAG "NfcV" +/* macros to map "modulate field" flag to GPIO level */ +#define GPIO_LEVEL_MODULATED NFCV_LOAD_MODULATION_POLARITY +#define GPIO_LEVEL_UNMODULATED (!GPIO_LEVEL_MODULATED) + +/* timing macros */ +#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) +#define DIGITAL_SIGNAL_UNIT_US (100000.0f) + ReturnCode nfcv_inventory(uint8_t* uid) { uint16_t received = 0; rfalNfcvInventoryRes res; ReturnCode ret = ERR_NONE; - for(int tries = 0; tries < 5; tries++) { + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { /* TODO: needs proper abstraction via fury_hal(_ll)_* */ ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received); @@ -31,7 +39,7 @@ ReturnCode nfcv_inventory(uint8_t* uid) { if(ret == ERR_NONE) { if(uid != NULL) { - memcpy(uid, res.UID, 8); + memcpy(uid, res.UID, NFCV_UID_LENGTH); } } @@ -47,7 +55,7 @@ ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* nfcv_data) { FURI_LOG_D(TAG, "Reading block %d/%d", block, (nfcv_data->block_num - 1)); ReturnCode ret = ERR_NONE; - for(int tries = 0; tries < 5; tries++) { + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { ret = rfalNfcvPollerReadSingleBlock( RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block, rxBuf, sizeof(rxBuf), &received); @@ -80,7 +88,7 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { FURI_LOG_D(TAG, "Read SYSTEM INFORMATION..."); - for(int tries = 0; tries < 5; tries++) { + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { /* TODO: needs proper abstraction via fury_hal(_ll)_* */ ret = rfalNfcvPollerGetSystemInformation( RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, rxBuf, sizeof(rxBuf), &received); @@ -92,16 +100,16 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { if(ret == ERR_NONE) { nfc_data->type = FuriHalNfcTypeV; - nfc_data->uid_len = 8; + nfc_data->uid_len = NFCV_UID_LENGTH; /* UID is stored reversed in this response */ for(int pos = 0; pos < nfc_data->uid_len; pos++) { - nfc_data->uid[pos] = rxBuf[2 + (7 - pos)]; + nfc_data->uid[pos] = rxBuf[2 + (NFCV_UID_LENGTH - 1 - pos)]; } - nfcv_data->dsfid = rxBuf[10]; - nfcv_data->afi = rxBuf[11]; - nfcv_data->block_num = rxBuf[12] + 1; - nfcv_data->block_size = rxBuf[13] + 1; - nfcv_data->ic_ref = rxBuf[14]; + nfcv_data->dsfid = rxBuf[NFCV_UID_LENGTH + 2]; + nfcv_data->afi = rxBuf[NFCV_UID_LENGTH + 3]; + nfcv_data->block_num = rxBuf[NFCV_UID_LENGTH + 4] + 1; + nfcv_data->block_size = rxBuf[NFCV_UID_LENGTH + 5] + 1; + nfcv_data->ic_ref = rxBuf[NFCV_UID_LENGTH + 6]; FURI_LOG_D( TAG, " UID: %02X %02X %02X %02X %02X %02X %02X %02X", @@ -304,7 +312,7 @@ bool nfcv_emu_alloc(NfcVData* nfcv_data) { if(!nfcv_data->emu_air.nfcv_resp_unmod) { return false; } - nfcv_data->emu_air.nfcv_resp_unmod->start_level = false; + nfcv_data->emu_air.nfcv_resp_unmod->start_level = GPIO_LEVEL_UNMODULATED; nfcv_data->emu_air.nfcv_resp_unmod->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S); nfcv_data->emu_air.nfcv_resp_unmod->edge_cnt = 1; @@ -315,7 +323,7 @@ bool nfcv_emu_alloc(NfcVData* nfcv_data) { if(!nfcv_data->emu_air.nfcv_resp_pulse) { return false; } - nfcv_data->emu_air.nfcv_resp_pulse->start_level = true; + nfcv_data->emu_air.nfcv_resp_pulse->start_level = GPIO_LEVEL_MODULATED; nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[1] = @@ -329,7 +337,7 @@ bool nfcv_emu_alloc(NfcVData* nfcv_data) { if(!nfcv_data->emu_air.nfcv_resp_half_pulse) { return false; } - nfcv_data->emu_air.nfcv_resp_half_pulse->start_level = true; + nfcv_data->emu_air.nfcv_resp_half_pulse->start_level = GPIO_LEVEL_MODULATED; nfcv_data->emu_air.nfcv_resp_half_pulse->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); nfcv_data->emu_air.nfcv_resp_half_pulse->edge_cnt = 1; @@ -461,11 +469,10 @@ void nfcv_emu_send( digital_sequence_add(nfcv->emu_air.nfcv_signal, eof); } - FURI_CRITICAL_ENTER(); + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); digital_sequence_set_sendtime(nfcv->emu_air.nfcv_signal, send_time); digital_sequence_send(nfcv->emu_air.nfcv_signal); - FURI_CRITICAL_EXIT(); - furi_hal_gpio_write(&gpio_spi_r_mosi, false); + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); if(tx_rx->sniff_tx) { tx_rx->sniff_tx(data, length * 8, false, tx_rx->sniff_context); @@ -473,14 +480,14 @@ void nfcv_emu_send( } static void nfcv_revuidcpy(uint8_t* dst, uint8_t* src) { - for(int pos = 0; pos < 8; pos++) { - dst[pos] = src[7 - pos]; + for(int pos = 0; pos < NFCV_UID_LENGTH; pos++) { + dst[pos] = src[NFCV_UID_LENGTH - 1 - pos]; } } static int nfcv_revuidcmp(uint8_t* dst, uint8_t* src) { - for(int pos = 0; pos < 8; pos++) { - if(dst[pos] != src[7 - pos]) { + for(int pos = 0; pos < NFCV_UID_LENGTH; pos++) { + if(dst[pos] != src[NFCV_UID_LENGTH - 1 - pos]) { return 1; } } @@ -517,24 +524,26 @@ void nfcv_emu_handle_packet( /* parse the frame data for the upcoming part 3 handling */ ctx->flags = nfcv_data->frame[0]; ctx->command = nfcv_data->frame[1]; - ctx->selected = (ctx->flags & RFAL_NFCV_REQ_FLAG_SELECT); - ctx->addressed = !(ctx->flags & RFAL_NFCV_REQ_FLAG_INVENTORY) && - (ctx->flags & RFAL_NFCV_REQ_FLAG_ADDRESS); - ctx->advanced = (ctx->command >= 0xA0); + ctx->selected = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && (ctx->flags & NFCV_REQ_FLAG_SELECT); + ctx->addressed = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= NFCV_CMD_ADVANCED); ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); - ctx->payload_offset = ctx->address_offset + (ctx->addressed ? 8 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? NFCV_UID_LENGTH : 0); ctx->response_flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof; ctx->send_time = nfcv_data->eof_timestamp + NFCV_FDT_FC(4380); - if(ctx->flags & RFAL_NFCV_REQ_FLAG_DATA_RATE) { + if(ctx->flags & NFCV_REQ_FLAG_DATA_RATE) { ctx->response_flags |= NfcVSendFlagsHighRate; } - if(ctx->flags & RFAL_NFCV_REQ_FLAG_SUB_CARRIER) { + if(ctx->flags & NFCV_REQ_FLAG_SUB_CARRIER) { ctx->response_flags |= NfcVSendFlagsTwoSubcarrier; } if(ctx->payload_offset + 2 > nfcv_data->frame_length) { +#ifdef NFCV_VERBOSE FURI_LOG_D(TAG, "command 0x%02X, but packet is too short", ctx->command); +#endif return; } @@ -542,6 +551,7 @@ void nfcv_emu_handle_packet( if(ctx->addressed) { uint8_t* address = &nfcv_data->frame[ctx->address_offset]; if(nfcv_revuidcmp(address, nfc_data->uid)) { +#ifdef NFCV_VERBOSE FURI_LOG_D(TAG, "addressed command 0x%02X, but not for us:", ctx->command); FURI_LOG_D( TAG, @@ -565,15 +575,18 @@ void nfcv_emu_handle_packet( nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]); +#endif return; } } if(ctx->selected && !nfcv_data->selected) { +#ifdef NFCV_VERBOSE FURI_LOG_D( TAG, "selected card shall execute command 0x%02X, but we were not selected", ctx->command); +#endif return; } @@ -581,22 +594,42 @@ void nfcv_emu_handle_packet( if(ctx->emu_protocol_filter != NULL) { if(ctx->emu_protocol_filter(tx_rx, nfc_data, nfcv_data)) { if(strlen(nfcv_data->last_command) > 0) { +#ifdef NFCV_VERBOSE FURI_LOG_D( TAG, "Received command %s (handled by filter)", nfcv_data->last_command); +#endif } return; } } switch(ctx->command) { - case ISO15693_INVENTORY: { - if(!nfcv_data->quiet) { - ctx->response_buffer[0] = ISO15693_NOERROR; - ctx->response_buffer[1] = nfcv_data->dsfid; - nfcv_revuidcpy(&ctx->response_buffer[2], nfc_data->uid); + case NFCV_CMD_INVENTORY: { + bool respond = false; + + if(ctx->flags & NFCV_REQ_FLAG_AFI) { + uint8_t afi = nfcv_data->frame[ctx->payload_offset]; + if(afi == nfcv_data->afi) { + respond = true; + } + } else { + respond = true; + } + + if(!nfcv_data->quiet && respond) { + int buffer_pos = 0; + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + ctx->response_buffer[buffer_pos++] = nfcv_data->dsfid; + nfcv_revuidcpy(&ctx->response_buffer[buffer_pos], nfc_data->uid); + buffer_pos += NFCV_UID_LENGTH; nfcv_emu_send( - tx_rx, nfcv_data, ctx->response_buffer, 10, ctx->response_flags, ctx->send_time); + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); } else { snprintf( @@ -605,18 +638,18 @@ void nfcv_emu_handle_packet( break; } - case ISO15693_STAYQUIET: { + case NFCV_CMD_STAY_QUIET: { snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "STAYQUIET"); nfcv_data->quiet = true; break; } - case ISO15693_LOCKBLOCK: { + case NFCV_CMD_LOCK_BLOCK: { uint8_t block = nfcv_data->frame[ctx->payload_offset]; nfcv_data->security_status[block] |= 0x01; nfcv_data->modified = true; - ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); @@ -624,13 +657,13 @@ void nfcv_emu_handle_packet( break; } - case ISO15693_WRITE_DSFID: { + case NFCV_CMD_WRITE_DSFID: { uint8_t id = nfcv_data->frame[ctx->payload_offset]; if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { nfcv_data->dsfid = id; nfcv_data->modified = true; - ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); } @@ -639,13 +672,13 @@ void nfcv_emu_handle_packet( break; } - case ISO15693_WRITE_AFI: { + case NFCV_CMD_WRITE_AFI: { uint8_t id = nfcv_data->frame[ctx->payload_offset]; if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { nfcv_data->afi = id; nfcv_data->modified = true; - ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); } @@ -654,12 +687,12 @@ void nfcv_emu_handle_packet( break; } - case ISO15693_LOCK_DSFID: { + case NFCV_CMD_LOCK_DSFID: { if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { nfcv_data->security_status[0] |= NfcVLockBitDsfid; nfcv_data->modified = true; - ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); } @@ -668,12 +701,12 @@ void nfcv_emu_handle_packet( break; } - case ISO15693_LOCK_AFI: { + case NFCV_CMD_LOCK_AFI: { if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { nfcv_data->security_status[0] |= NfcVLockBitAfi; nfcv_data->modified = true; - ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); } @@ -682,8 +715,8 @@ void nfcv_emu_handle_packet( break; } - case ISO15693_SELECT: { - ctx->response_buffer[0] = ISO15693_NOERROR; + case NFCV_CMD_SELECT: { + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_data->selected = true; nfcv_data->quiet = false; nfcv_emu_send( @@ -692,8 +725,8 @@ void nfcv_emu_handle_packet( break; } - case ISO15693_RESET_TO_READY: { - ctx->response_buffer[0] = ISO15693_NOERROR; + case NFCV_CMD_RESET_TO_READY: { + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_data->quiet = false; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); @@ -701,24 +734,24 @@ void nfcv_emu_handle_packet( break; } - case ISO15693_READ_MULTI_BLOCK: - case ISO15693_READBLOCK: { + case NFCV_CMD_READ_MULTI_BLOCK: + case NFCV_CMD_READ_BLOCK: { uint8_t block = nfcv_data->frame[ctx->payload_offset]; uint8_t blocks = 1; - if(ctx->command == ISO15693_READ_MULTI_BLOCK) { + if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; } if(block + blocks <= nfcv_data->block_num) { uint8_t buffer_pos = 0; - ctx->response_buffer[buffer_pos++] = ISO15693_NOERROR; + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; for(int block_index = 0; block_index < blocks; block_index++) { int block_current = block + block_index; /* prepend security status */ - if(ctx->flags & RFAL_NFCV_REQ_FLAG_OPTION) { + if(ctx->flags & NFCV_REQ_FLAG_OPTION) { ctx->response_buffer[buffer_pos++] = nfcv_data->security_status[1 + block_current]; } @@ -736,19 +769,24 @@ void nfcv_emu_handle_packet( buffer_pos, ctx->response_flags, ctx->send_time); + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); } snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); break; } - case ISO15693_WRITE_MULTI_BLOCK: - case ISO15693_WRITEBLOCK: { + case NFCV_CMD_WRITE_MULTI_BLOCK: + case NFCV_CMD_WRITE_BLOCK: { uint8_t blocks = 1; uint8_t block = nfcv_data->frame[ctx->payload_offset]; uint8_t data_pos = ctx->payload_offset + 1; - if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) { + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { blocks = nfcv_data->frame[data_pos] + 1; data_pos++; } @@ -758,7 +796,7 @@ void nfcv_emu_handle_packet( if((block + blocks) <= nfcv_data->block_num && (data_pos + data_len + 2) == nfcv_data->frame_length) { - ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[0] = NFCV_NOERROR; memcpy( &nfcv_data->data[nfcv_data->block_size * block], &nfcv_data->frame[data_pos], @@ -767,9 +805,14 @@ void nfcv_emu_handle_packet( nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); } - if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) { + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), @@ -790,24 +833,32 @@ void nfcv_emu_handle_packet( break; } - case ISO15693_GET_SYSTEM_INFO: { - ctx->response_buffer[0] = ISO15693_NOERROR; - ctx->response_buffer[1] = 0x0F; - nfcv_revuidcpy(&ctx->response_buffer[2], nfc_data->uid); - ctx->response_buffer[10] = nfcv_data->dsfid; /* DSFID */ - ctx->response_buffer[11] = nfcv_data->afi; /* AFI */ - ctx->response_buffer[12] = nfcv_data->block_num - 1; /* number of blocks */ - ctx->response_buffer[13] = nfcv_data->block_size - 1; /* block size */ - ctx->response_buffer[14] = nfcv_data->ic_ref; /* IC reference */ + case NFCV_CMD_GET_SYSTEM_INFO: { + int buffer_pos = 0; + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + ctx->response_buffer[buffer_pos++] = NFCV_SYSINFO_FLAG_DSFID | NFCV_SYSINFO_FLAG_AFI | + NFCV_SYSINFO_FLAG_MEMSIZE | NFCV_SYSINFO_FLAG_ICREF; + nfcv_revuidcpy(&ctx->response_buffer[buffer_pos], nfc_data->uid); + buffer_pos += NFCV_UID_LENGTH; + ctx->response_buffer[buffer_pos++] = nfcv_data->dsfid; /* DSFID */ + ctx->response_buffer[buffer_pos++] = nfcv_data->afi; /* AFI */ + ctx->response_buffer[buffer_pos++] = nfcv_data->block_num - 1; /* number of blocks */ + ctx->response_buffer[buffer_pos++] = nfcv_data->block_size - 1; /* block size */ + ctx->response_buffer[buffer_pos++] = nfcv_data->ic_ref; /* IC reference */ nfcv_emu_send( - tx_rx, nfcv_data, ctx->response_buffer, 15, ctx->response_flags, ctx->send_time); + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO"); break; } - case ISO15693_CUST_ECHO_MODE: { - ctx->response_buffer[0] = ISO15693_NOERROR; + case NFCV_CMD_CUST_ECHO_MODE: { + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_data->echo_mode = true; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); @@ -815,7 +866,7 @@ void nfcv_emu_handle_packet( break; } - case ISO15693_CUST_ECHO_DATA: { + case NFCV_CMD_CUST_ECHO_DATA: { nfcv_emu_send( tx_rx, nfcv_data, @@ -837,7 +888,9 @@ void nfcv_emu_handle_packet( } if(strlen(nfcv_data->last_command) > 0) { +#ifdef NFCV_VERBOSE FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); +#endif } } @@ -859,12 +912,12 @@ void nfcv_emu_sniff_packet( /* parse the frame data for the upcoming part 3 handling */ ctx->flags = nfcv_data->frame[0]; ctx->command = nfcv_data->frame[1]; - ctx->selected = (ctx->flags & RFAL_NFCV_REQ_FLAG_SELECT); - ctx->addressed = !(ctx->flags & RFAL_NFCV_REQ_FLAG_INVENTORY) && - (ctx->flags & RFAL_NFCV_REQ_FLAG_ADDRESS); - ctx->advanced = (ctx->command >= 0xA0); + ctx->selected = (ctx->flags & NFCV_REQ_FLAG_SELECT); + ctx->addressed = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= NFCV_CMD_ADVANCED); ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); - ctx->payload_offset = ctx->address_offset + (ctx->addressed ? 8 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? NFCV_UID_LENGTH : 0); char flags_string[5]; @@ -872,28 +925,28 @@ void nfcv_emu_sniff_packet( flags_string, 5, "%c%c%c%d", - (ctx->flags & RFAL_NFCV_REQ_FLAG_INVENTORY) ? + (ctx->flags & NFCV_REQ_FLAG_INVENTORY) ? 'I' : (ctx->addressed ? 'A' : (ctx->selected ? 'S' : '*')), ctx->advanced ? 'X' : ' ', - (ctx->flags & RFAL_NFCV_REQ_FLAG_DATA_RATE) ? 'h' : 'l', - (ctx->flags & RFAL_NFCV_REQ_FLAG_SUB_CARRIER) ? 2 : 1); + (ctx->flags & NFCV_REQ_FLAG_DATA_RATE) ? 'h' : 'l', + (ctx->flags & NFCV_REQ_FLAG_SUB_CARRIER) ? 2 : 1); switch(ctx->command) { - case ISO15693_INVENTORY: { + case NFCV_CMD_INVENTORY: { snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s INVENTORY", flags_string); break; } - case ISO15693_STAYQUIET: { + case NFCV_CMD_STAY_QUIET: { snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s STAYQUIET", flags_string); nfcv_data->quiet = true; break; } - case ISO15693_LOCKBLOCK: { + case NFCV_CMD_LOCK_BLOCK: { uint8_t block = nfcv_data->frame[ctx->payload_offset]; snprintf( nfcv_data->last_command, @@ -904,7 +957,7 @@ void nfcv_emu_sniff_packet( break; } - case ISO15693_WRITE_DSFID: { + case NFCV_CMD_WRITE_DSFID: { uint8_t id = nfcv_data->frame[ctx->payload_offset]; snprintf( nfcv_data->last_command, @@ -915,7 +968,7 @@ void nfcv_emu_sniff_packet( break; } - case ISO15693_WRITE_AFI: { + case NFCV_CMD_WRITE_AFI: { uint8_t id = nfcv_data->frame[ctx->payload_offset]; snprintf( nfcv_data->last_command, @@ -926,7 +979,7 @@ void nfcv_emu_sniff_packet( break; } - case ISO15693_LOCK_DSFID: { + case NFCV_CMD_LOCK_DSFID: { snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), @@ -935,30 +988,30 @@ void nfcv_emu_sniff_packet( break; } - case ISO15693_LOCK_AFI: { + case NFCV_CMD_LOCK_AFI: { snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s LOCK AFI", flags_string); break; } - case ISO15693_SELECT: { + case NFCV_CMD_SELECT: { snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s SELECT", flags_string); break; } - case ISO15693_RESET_TO_READY: { + case NFCV_CMD_RESET_TO_READY: { snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s RESET", flags_string); break; } - case ISO15693_READ_MULTI_BLOCK: - case ISO15693_READBLOCK: { + case NFCV_CMD_READ_MULTI_BLOCK: + case NFCV_CMD_READ_BLOCK: { uint8_t block = nfcv_data->frame[ctx->payload_offset]; uint8_t blocks = 1; - if(ctx->command == ISO15693_READ_MULTI_BLOCK) { + if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; } @@ -973,20 +1026,20 @@ void nfcv_emu_sniff_packet( break; } - case ISO15693_WRITE_MULTI_BLOCK: - case ISO15693_WRITEBLOCK: { + case NFCV_CMD_WRITE_MULTI_BLOCK: + case NFCV_CMD_WRITE_BLOCK: { uint8_t block = nfcv_data->frame[ctx->payload_offset]; uint8_t blocks = 1; uint8_t data_pos = 1; - if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) { + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; data_pos++; } uint8_t* data = &nfcv_data->frame[ctx->payload_offset + data_pos]; - if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) { + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), @@ -1009,7 +1062,7 @@ void nfcv_emu_sniff_packet( break; } - case ISO15693_GET_SYSTEM_INFO: { + case NFCV_CMD_GET_SYSTEM_INFO: { snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), @@ -1051,13 +1104,24 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { /* everything is initialized */ nfcv_data->ready = true; + /* ensure the GPIO is already in unmodulated state */ + furi_hal_gpio_init(&gpio_spi_r_mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + rfal_platform_spi_acquire(); - /* configure for transparent and passive mode */ + /* stop operation to configure for transparent and passive mode */ st25r3916ExecuteCommand(ST25R3916_CMD_STOP); /* set enable, rx_enable and field detector enable */ - st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3); - /* target mode: ISO14443 passive mode */ - st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88); + st25r3916WriteRegister( + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + /* explicitely set the modulation resistor in case system config changes for some reason */ + st25r3916WriteRegister( + ST25R3916_REG_PT_MOD, + (0 << ST25R3916_REG_PT_MOD_ptm_res_shift) | (15 << ST25R3916_REG_PT_MOD_pt_res_shift)); + /* target mode: target, other fields do not have any effect as we use transparent mode */ + st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ); /* let us modulate the field using MOSI, read ASK modulation using IRQ */ st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); @@ -1112,11 +1176,12 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { } /* allocate a 512 edge buffer, more than enough */ - nfcv_data->emu_air.reader_signal = pulse_reader_alloc(&gpio_nfc_irq_rfid_pull, 512); + nfcv_data->emu_air.reader_signal = + pulse_reader_alloc(&gpio_nfc_irq_rfid_pull, NFCV_PULSE_BUFFER); /* timebase shall be 1 ns */ pulse_reader_set_timebase(nfcv_data->emu_air.reader_signal, PulseReaderUnitNanosecond); /* and configure to already calculate the number of bits */ - pulse_reader_set_bittime(nfcv_data->emu_air.reader_signal, PULSE_DURATION_NS); + pulse_reader_set_bittime(nfcv_data->emu_air.reader_signal, NFCV_PULSE_DURATION_NS); /* this IO is fed into the µC via a diode, so we need a pulldown */ pulse_reader_set_pull(nfcv_data->emu_air.reader_signal, GpioPullDown); @@ -1128,7 +1193,6 @@ void nfcv_emu_deinit(NfcVData* nfcv_data) { furi_assert(nfcv_data); furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); - rfal_platform_spi_release(); nfcv_emu_free(nfcv_data); if(nfcv_data->emu_protocol_ctx) { @@ -1139,6 +1203,7 @@ void nfcv_emu_deinit(NfcVData* nfcv_data) { /* set registers back to how we found them */ st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0x00); st25r3916WriteRegister(ST25R3916_REG_MODE, 0x08); + rfal_platform_spi_release(); } bool nfcv_emu_loop( @@ -1157,8 +1222,6 @@ bool nfcv_emu_loop( uint32_t byte_value = 0; uint32_t bits_received = 0; uint32_t timeout = timeout_ms * 1000; - uint32_t sof_timestamp = 0; - uint32_t eof_timestamp = 0; bool wait_for_pulse = false; if(!nfcv_data->ready) { @@ -1200,7 +1263,6 @@ bool nfcv_emu_loop( frame_state = NFCV_FRAME_STATE_SOF2; } else { frame_state = NFCV_FRAME_STATE_SOF1; - sof_timestamp = timestamp; break; } break; @@ -1234,7 +1296,6 @@ bool nfcv_emu_loop( break; } else if(periods == 2) { frame_state = NFCV_FRAME_STATE_EOF; - eof_timestamp = timestamp; break; } @@ -1275,7 +1336,6 @@ bool nfcv_emu_loop( periods_previous = 0; } else if(periods == 2) { frame_state = NFCV_FRAME_STATE_EOF; - eof_timestamp = timestamp; break; } else { frame_state = NFCV_FRAME_STATE_RESET; @@ -1310,27 +1370,13 @@ bool nfcv_emu_loop( } nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data); - /* determine readers fc by analyzing transmission duration */ - uint32_t duration = eof_timestamp - sof_timestamp; - float fc_1024 = (4.0f * duration) / (4 * (frame_pos * 4 + 1) + 1); - /* it should be 1024/fc in 64MHz ticks */ - float fact = fc_1024 / ((1000000.0f * 64.0f * 1024.0f) / NFCV_FC); - FURI_LOG_D(TAG, "1024/fc: %f -> %f %%", (double)fc_1024, (double)(fact * 100)); -#if 0 - if(fact > 0.99f && fact < 1.01f) { - static float avg_err = 0.0f; - - avg_err = (avg_err * 15.0f + (fact - 1.0f)) / 16.0f; - FURI_LOG_D(TAG, " ==> set %f %%", (double)((1.0f + avg_err) * 100)); - digital_sequence_timebase_correction(nfcv_data->emu_air.nfcv_signal, 1.0f + avg_err); - } -#endif - pulse_reader_start(nfcv_data->emu_air.reader_signal); ret = true; } else { if(frame_state != NFCV_FRAME_STATE_SOF1) { +#ifdef NFCV_VERBOSE FURI_LOG_T(TAG, "leaving while in state: %lu", frame_state); +#endif } } diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index 56e37f525..8ca6955d1 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -12,14 +12,13 @@ extern "C" { #endif +/* true: modulating releases load, false: modulating adds load resistor to field coil */ +#define NFCV_LOAD_MODULATION_POLARITY (false) + #define NFCV_FC (13560000.0f / 0.9998f) /* MHz */ #define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC / 32) / 2.0f) /* 1.1799 µs */ #define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */ - -#define PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) - -#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) -#define DIGITAL_SIGNAL_UNIT_US (100000.0f) +#define NFCV_PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) /* ISO/IEC 15693-3:2019(E) 10.4.12: maximum number of blocks is defined as 256 */ #define NFCV_BLOCKS_MAX 256 @@ -30,15 +29,20 @@ extern "C" { /* ISO/IEC 15693-3:2019(E) 7.1b: standard allows up to 8192, the maxium frame length that we are expected to receive/send is less */ #define NFCV_FRAMESIZE_MAX (1 + NFCV_MEMSIZE_MAX + NFCV_BLOCKS_MAX) +/* maximum string length for log messages */ #define NFCV_LOG_STR_LEN 128 +/* maximum of pulses to be buffered by pulse reader */ +#define NFCV_PULSE_BUFFER 512 -// #define NFCV_DIAGNOSTIC_DUMPS -// #define NFCV_DIAGNOSTIC_DUMP_SIZE 128 +//#define NFCV_DIAGNOSTIC_DUMPS +//#define NFCV_DIAGNOSTIC_DUMP_SIZE 256 +//#define NFCV_VERBOSE /* helpers to calculate the send time based on DWT->CYCCNT */ -#define NFCV_FDT_USEC(usec) (usec * 64) +#define NFCV_FDT_USEC(usec) ((usec)*64) #define NFCV_FDT_FC(ticks) ((ticks)*6400 / 1356) +/* state machine when receiving frame bits */ #define NFCV_FRAME_STATE_SOF1 0 #define NFCV_FRAME_STATE_SOF2 1 #define NFCV_FRAME_STATE_CODING_4 2 @@ -56,37 +60,83 @@ extern "C" { #define NFCV_SIG_LOW_BIT1 6 #define NFCV_SIG_LOW_EOF 7 +/* various constants */ +#define NFCV_COMMAND_RETRIES 5 +#define NFCV_UID_LENGTH 8 + +/* ISO15693 protocol flags */ +typedef enum { + /* ISO15693 protocol flags when INVENTORY is NOT set */ + NFCV_REQ_FLAG_SUB_CARRIER = (1 << 0), + NFCV_REQ_FLAG_DATA_RATE = (1 << 1), + NFCV_REQ_FLAG_INVENTORY = (1 << 2), + NFCV_REQ_FLAG_PROTOCOL_EXT = (1 << 3), + NFCV_REQ_FLAG_SELECT = (1 << 4), + NFCV_REQ_FLAG_ADDRESS = (1 << 5), + NFCV_REQ_FLAG_OPTION = (1 << 6), + /* ISO15693 protocol flags when INVENTORY flag is set */ + NFCV_REQ_FLAG_AFI = (1 << 4), + NFCV_REQ_FLAG_NB_SLOTS = (1 << 5) +} NfcVRequestFlags; + +/* ISO15693 protocol flags */ +typedef enum { + NFCV_RES_FLAG_ERROR = (1 << 0), + NFCV_RES_FLAG_VALIDITY = (1 << 1), + NFCV_RES_FLAG_FINAL = (1 << 2), + NFCV_RES_FLAG_PROTOCOL_EXT = (1 << 3), + NFCV_RES_FLAG_SEC_LEN1 = (1 << 4), + NFCV_RES_FLAG_SEC_LEN2 = (1 << 5), + NFCV_RES_FLAG_WAIT_EXT = (1 << 6), +} NfcVRsponseFlags; + +/* flags for SYSINFO response */ +typedef enum { + NFCV_SYSINFO_FLAG_DSFID = (1 << 0), + NFCV_SYSINFO_FLAG_AFI = (1 << 1), + NFCV_SYSINFO_FLAG_MEMSIZE = (1 << 2), + NFCV_SYSINFO_FLAG_ICREF = (1 << 3) +} NfcVSysinfoFlags; + /* ISO15693 command codes */ -#define ISO15693_INVENTORY 0x01 -#define ISO15693_STAYQUIET 0x02 -#define ISO15693_READBLOCK 0x20 -#define ISO15693_WRITEBLOCK 0x21 -#define ISO15693_LOCKBLOCK 0x22 -#define ISO15693_READ_MULTI_BLOCK 0x23 -#define ISO15693_WRITE_MULTI_BLOCK 0x24 -#define ISO15693_SELECT 0x25 -#define ISO15693_RESET_TO_READY 0x26 -#define ISO15693_WRITE_AFI 0x27 -#define ISO15693_LOCK_AFI 0x28 -#define ISO15693_WRITE_DSFID 0x29 -#define ISO15693_LOCK_DSFID 0x2A -#define ISO15693_GET_SYSTEM_INFO 0x2B -#define ISO15693_READ_MULTI_SECSTATUS 0x2C +typedef enum { + /* mandatory command codes */ + NFCV_CMD_INVENTORY = 0x01, + NFCV_CMD_STAY_QUIET = 0x02, + /* optional command codes */ + NFCV_CMD_READ_BLOCK = 0x20, + NFCV_CMD_WRITE_BLOCK = 0x21, + NFCV_CMD_LOCK_BLOCK = 0x22, + NFCV_CMD_READ_MULTI_BLOCK = 0x23, + NFCV_CMD_WRITE_MULTI_BLOCK = 0x24, + NFCV_CMD_SELECT = 0x25, + NFCV_CMD_RESET_TO_READY = 0x26, + NFCV_CMD_WRITE_AFI = 0x27, + NFCV_CMD_LOCK_AFI = 0x28, + NFCV_CMD_WRITE_DSFID = 0x29, + NFCV_CMD_LOCK_DSFID = 0x2A, + NFCV_CMD_GET_SYSTEM_INFO = 0x2B, + NFCV_CMD_READ_MULTI_SECSTATUS = 0x2C, + /* advanced command codes */ + NFCV_CMD_ADVANCED = 0xA0, + /* flipper zero custom command codes */ + NFCV_CMD_CUST_ECHO_MODE = 0xDE, + NFCV_CMD_CUST_ECHO_DATA = 0xDF +} NfcVCommands; -#define ISO15693_CUST_ECHO_MODE 0xDE -#define ISO15693_CUST_ECHO_DATA 0xDF - -/* ISO15693 RESPONSE ERROR CODES */ -#define ISO15693_NOERROR 0x00 -#define ISO15693_ERROR_CMD_NOT_SUP 0x01 // Command not supported -#define ISO15693_ERROR_CMD_NOT_REC 0x02 // Command not recognized (eg. parameter error) -#define ISO15693_ERROR_CMD_OPTION 0x03 // Command option not supported -#define ISO15693_ERROR_GENERIC 0x0F // No additional Info about this error -#define ISO15693_ERROR_BLOCK_UNAVAILABLE 0x10 -#define ISO15693_ERROR_BLOCK_LOCKED_ALREADY 0x11 // cannot lock again -#define ISO15693_ERROR_BLOCK_LOCKED 0x12 // cannot be changed -#define ISO15693_ERROR_BLOCK_WRITE 0x13 // Writing was unsuccessful -#define ISO15693_ERROR_BLOCL_WRITELOCK 0x14 // Locking was unsuccessful +/* ISO15693 Response error codes */ +typedef enum { + NFCV_NOERROR = 0x00, + NFCV_ERROR_CMD_NOT_SUP = 0x01, // Command not supported + NFCV_ERROR_CMD_NOT_REC = 0x02, // Command not recognized (eg. parameter error) + NFCV_ERROR_CMD_OPTION = 0x03, // Command option not supported + NFCV_ERROR_GENERIC = 0x0F, // No additional Info about this error + NFCV_ERROR_BLOCK_UNAVAILABLE = 0x10, + NFCV_ERROR_BLOCK_LOCKED_ALREADY = 0x11, // cannot lock again + NFCV_ERROR_BLOCK_LOCKED = 0x12, // cannot be changed + NFCV_ERROR_BLOCK_WRITE = 0x13, // Writing was unsuccessful + NFCV_ERROR_BLOCL_WRITELOCK = 0x14 // Locking was unsuccessful +} NfcVErrorcodes; typedef enum { NfcVLockBitDsfid = 1, diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c index eaa75509f..ec3afc248 100644 --- a/lib/nfc/protocols/slix.c +++ b/lib/nfc/protocols/slix.c @@ -59,9 +59,9 @@ ReturnCode slix_get_random(NfcVData* data) { uint8_t rxBuf[32]; ReturnCode ret = rfalNfcvPollerTransceiveReq( - ISO15693_CMD_NXP_GET_RANDOM_NUMBER, + NFCV_CMD_NXP_GET_RANDOM_NUMBER, RFAL_NFCV_REQ_FLAG_DEFAULT, - ISO15693_MANUFACTURER_NXP, + NFCV_MANUFACTURER_NXP, NULL, NULL, 0, @@ -124,9 +124,9 @@ ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) { } ReturnCode ret = rfalNfcvPollerTransceiveReq( - ISO15693_CMD_NXP_SET_PASSWORD, + NFCV_CMD_NXP_SET_PASSWORD, RFAL_NFCV_REQ_FLAG_DATA_RATE, - ISO15693_MANUFACTURER_NXP, + NFCV_MANUFACTURER_NXP, NULL, cmd_set_pass, sizeof(cmd_set_pass), @@ -150,8 +150,8 @@ bool slix_generic_protocol_filter( NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; NfcVSlixData* slix = &nfcv_data->sub_data.slix; - if(slix->privacy && ctx->command != ISO15693_CMD_NXP_GET_RANDOM_NUMBER && - ctx->command != ISO15693_CMD_NXP_SET_PASSWORD) { + if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && + ctx->command != NFCV_CMD_NXP_SET_PASSWORD) { snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), @@ -164,11 +164,11 @@ bool slix_generic_protocol_filter( bool handled = false; switch(ctx->command) { - case ISO15693_CMD_NXP_GET_RANDOM_NUMBER: { + case NFCV_CMD_NXP_GET_RANDOM_NUMBER: { slix->rand[0] = furi_hal_random_get(); slix->rand[1] = furi_hal_random_get(); - ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[0] = NFCV_NOERROR; ctx->response_buffer[1] = slix->rand[1]; ctx->response_buffer[2] = slix->rand[0]; @@ -185,7 +185,7 @@ bool slix_generic_protocol_filter( break; } - case ISO15693_CMD_NXP_SET_PASSWORD: { + case NFCV_CMD_NXP_SET_PASSWORD: { uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; if(!(password_id & password_supported)) { @@ -246,7 +246,7 @@ bool slix_generic_protocol_filter( default: break; } - ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); snprintf( @@ -268,15 +268,15 @@ bool slix_generic_protocol_filter( break; } - case ISO15693_CMD_NXP_ENABLE_PRIVACY: { - ctx->response_buffer[0] = ISO15693_NOERROR; + case NFCV_CMD_NXP_ENABLE_PRIVACY: { + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), - "ISO15693_CMD_NXP_ENABLE_PRIVACY"); + "NFCV_CMD_NXP_ENABLE_PRIVACY"); slix->privacy = true; handled = true; diff --git a/lib/nfc/protocols/slix.h b/lib/nfc/protocols/slix.h index 719fe6f43..701fa2f82 100644 --- a/lib/nfc/protocols/slix.h +++ b/lib/nfc/protocols/slix.h @@ -5,22 +5,22 @@ #include "nfc_util.h" #include -#define ISO15693_MANUFACTURER_NXP 0x04 +#define NFCV_MANUFACTURER_NXP 0x04 /* ISO15693-3 CUSTOM NXP COMMANDS */ -#define ISO15693_CMD_NXP_SET_EAS 0xA2 -#define ISO15693_CMD_NXP_RESET_EAS 0xA3 -#define ISO15693_CMD_NXP_LOCK_EAS 0xA4 -#define ISO15693_CMD_NXP_EAS_ALARM 0xA5 -#define ISO15693_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 -#define ISO15693_CMD_NXP_WRITE_EAS_ID 0xA7 -#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ 0xB0 -#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 -#define ISO15693_CMD_NXP_GET_RANDOM_NUMBER 0xB2 -#define ISO15693_CMD_NXP_SET_PASSWORD 0xB3 -#define ISO15693_CMD_NXP_WRITE_PASSWORD 0xB4 -#define ISO15693_CMD_NXP_DESTROY 0xB9 -#define ISO15693_CMD_NXP_ENABLE_PRIVACY 0xBA +#define NFCV_CMD_NXP_SET_EAS 0xA2 +#define NFCV_CMD_NXP_RESET_EAS 0xA3 +#define NFCV_CMD_NXP_LOCK_EAS 0xA4 +#define NFCV_CMD_NXP_EAS_ALARM 0xA5 +#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 +#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7 +#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0 +#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 +#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2 +#define NFCV_CMD_NXP_SET_PASSWORD 0xB3 +#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4 +#define NFCV_CMD_NXP_DESTROY 0xB9 +#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA /* available passwords */ #define SLIX_PASS_READ 0x01 diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 870afede3..4723ec99f 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -7,6 +7,8 @@ #include "../blocks/generic.h" #include "../blocks/math.h" +#include "../blocks/custom_btn.h" + #define TAG "SubGhzProtocoCameAtomo" static const SubGhzBlockConst subghz_protocol_came_atomo_const = { @@ -71,6 +73,13 @@ const SubGhzProtocol subghz_protocol_came_atomo = { static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* instance); +/** + * Defines the button value for the current btn_id + * Basic set | 0x0 | 0x2 | 0x4 | 0x6 | + * @return Button code + */ +static uint8_t subghz_protocol_came_atomo_get_btn_code(); + void* subghz_protocol_encoder_came_atomo_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolEncoderCameAtomo* instance = malloc(sizeof(SubGhzProtocolEncoderCameAtomo)); @@ -120,12 +129,53 @@ static LevelDuration return level_duration_make(data.level, data.duration); } +bool subghz_protocol_came_atomo_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderCameAtomo* instance = context; + instance->generic.btn = 0x1; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.cnt_2 = 0x7e; + instance->generic.data_count_bit = 62; + instance->generic.data_2 = + ((uint64_t)0x7e << 56 | (uint64_t)cnt << 40 | (uint64_t)serial << 8); + + uint8_t pack[8] = {}; + + pack[0] = (instance->generic.cnt_2); + pack[1] = (instance->generic.cnt >> 8); + pack[2] = (instance->generic.cnt & 0xFF); + pack[3] = ((instance->generic.data_2 >> 32) & 0xFF); + pack[4] = ((instance->generic.data_2 >> 24) & 0xFF); + pack[5] = ((instance->generic.data_2 >> 16) & 0xFF); + pack[6] = ((instance->generic.data_2 >> 8) & 0xFF); + pack[7] = (instance->generic.data_2 & 0xFF); + + atomo_encrypt(pack); + uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; + uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7]; + instance->generic.data = (uint64_t)hi << 32 | lo; + + instance->generic.data ^= 0xFFFFFFFFFFFFFFFF; + instance->generic.data >>= 4; + instance->generic.data &= 0xFFFFFFFFFFFFFFF; + + return SubGhzProtocolStatusOk == + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + /** * Generating an upload from data. * @param instance Pointer to a SubGhzProtocolEncoderCameAtomo instance */ -static void - subghz_protocol_encoder_came_atomo_get_upload(SubGhzProtocolEncoderCameAtomo* instance) { +static void subghz_protocol_encoder_came_atomo_get_upload( + SubGhzProtocolEncoderCameAtomo* instance, + uint8_t btn) { furi_assert(instance); size_t index = 0; @@ -145,6 +195,23 @@ static void instance->generic.cnt = 0; } + // Save original button for later use + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(btn); + } + + btn = subghz_protocol_came_atomo_get_btn_code(); + + if(btn == 0x1) { + btn = 0x0; + } else if(btn == 0x2) { + btn = 0x2; + } else if(btn == 0x3) { + btn = 0x4; + } else if(btn == 0x4) { + btn = 0x6; + } + //Send header instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)subghz_protocol_came_atomo_const.te_long * 15); @@ -159,7 +226,7 @@ static void pack[4] = ((instance->generic.data_2 >> 24) & 0xFF); pack[5] = ((instance->generic.data_2 >> 16) & 0xFF); pack[6] = ((instance->generic.data_2 >> 8) & 0xFF); - pack[7] = (instance->generic.data_2 & 0xFF); + pack[7] = (btn << 4); if(pack[0] == 0x7F) { pack[0] = 0; @@ -211,7 +278,7 @@ static void pack[4] = ((instance->generic.data_2 >> 24) & 0xFF); pack[5] = ((instance->generic.data_2 >> 16) & 0xFF); pack[6] = ((instance->generic.data_2 >> 8) & 0xFF); - pack[7] = (instance->generic.data_2 & 0xFF); + pack[7] = (btn << 4); atomo_encrypt(pack); uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; @@ -240,7 +307,7 @@ SubGhzProtocolStatus flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); subghz_protocol_came_atomo_remote_controller(&instance->generic); - subghz_protocol_encoder_came_atomo_get_upload(instance); + subghz_protocol_encoder_came_atomo_get_upload(instance, instance->generic.btn); if(!flipper_format_rewind(flipper_format)) { FURI_LOG_E(TAG, "Rewind error"); @@ -487,20 +554,23 @@ static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* ins uint8_t btn_decode = (pack[7] >> 4); if(btn_decode == 0x0) { instance->btn = 0x1; - } - if(btn_decode == 0x2) { + } else if(btn_decode == 0x2) { instance->btn = 0x2; - } - if(btn_decode == 0x4) { + } else if(btn_decode == 0x4) { instance->btn = 0x3; - } - if(btn_decode == 0x6) { + } else if(btn_decode == 0x6) { instance->btn = 0x4; } uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7]; instance->data_2 = (uint64_t)hi << 32 | lo; + + // Save original button for later use + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->btn); + } + subghz_custom_btn_set_max(3); } void atomo_encrypt(uint8_t* buff) { @@ -544,6 +614,74 @@ void atomo_decrypt(uint8_t* buff) { } } +static uint8_t subghz_protocol_came_atomo_get_btn_code() { + uint8_t custom_btn_id = subghz_custom_btn_get(); + uint8_t original_btn_code = subghz_custom_btn_get_original(); + uint8_t btn = original_btn_code; + + // Set custom button + if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) { + // Restore original button code + btn = original_btn_code; + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) { + switch(original_btn_code) { + case 0x1: + btn = 0x2; + break; + case 0x2: + btn = 0x1; + break; + case 0x3: + btn = 0x1; + break; + case 0x4: + btn = 0x1; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { + case 0x1: + btn = 0x3; + break; + case 0x2: + btn = 0x3; + break; + case 0x3: + btn = 0x2; + break; + case 0x4: + btn = 0x2; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { + case 0x1: + btn = 0x4; + break; + case 0x2: + btn = 0x4; + break; + case 0x3: + btn = 0x4; + break; + case 0x4: + btn = 0x3; + break; + + default: + break; + } + } + + return btn; +} + uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderCameAtomo* instance = context; @@ -581,7 +719,7 @@ void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* ou output, "%s %db\r\n" "Key:0x%08lX%08lX\r\n" - "Sn:0x%08lX Btn:0x%01X\r\n" + "Sn:0x%08lX Btn:%01X\r\n" "Pcl_Cnt:0x%04lX\r\n" "Btn_Cnt:0x%02X", diff --git a/lib/subghz/protocols/came_atomo.h b/lib/subghz/protocols/came_atomo.h index c5e45a68d..8183877b0 100644 --- a/lib/subghz/protocols/came_atomo.h +++ b/lib/subghz/protocols/came_atomo.h @@ -14,6 +14,22 @@ void atomo_decrypt(uint8_t* buff); void atomo_encrypt(uint8_t* buff); +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_came_atomo_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint16_t cnt, + SubGhzRadioPreset* preset); + /** * Allocate SubGhzProtocolEncoderCameAtomo. * @param environment Pointer to a SubGhzEnvironment instance diff --git a/lib/subghz/protocols/dooya.c b/lib/subghz/protocols/dooya.c index 47e95209e..816847840 100644 --- a/lib/subghz/protocols/dooya.c +++ b/lib/subghz/protocols/dooya.c @@ -308,7 +308,7 @@ void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t dura * Analysis of received data * @param instance Pointer to a SubGhzBlockGeneric* instance */ -static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { +static void subghz_protocol_dooya_check_remote_controller(SubGhzBlockGeneric* instance) { /* * serial s/m ch key * long press down X * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 @@ -416,7 +416,7 @@ void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) furi_assert(context); SubGhzProtocolDecoderDooya* instance = context; - subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); + subghz_protocol_dooya_check_remote_controller(&instance->generic); furi_string_cat_printf( output,