From 9914aa40bd589d767b850d81b0e4eda28de46be9 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 11 May 2023 05:25:06 +0300 Subject: [PATCH 01/71] [FL-3302] Part 2 of hooking C2 IPC (#2662) --- firmware/targets/f7/ble_glue/ble_glue.c | 15 +++++++++++++++ lib/stm32wb.scons | 5 ++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index 6b527cfca..1cb5501d9 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -2,6 +2,7 @@ #include "app_common.h" #include "ble_app.h" #include +#include #include #include @@ -72,6 +73,20 @@ void shci_register_io_bus(tSHciIO* fops) { fops->Send = ble_glue_TL_SYS_SendCmd; } +static int32_t ble_glue_TL_BLE_SendCmd(uint8_t* buffer, uint16_t size) { + if(furi_hal_bt_get_hardfault_info()) { + furi_crash("ST(R) Copro(R) HardFault"); + } + + return TL_BLE_SendCmd(buffer, size); +} + +void hci_register_io_bus(tHciIO* fops) { + /* Register IO bus services */ + fops->Init = TL_BLE_Init; + fops->Send = ble_glue_TL_BLE_SendCmd; +} + /////////////////////////////////////////////////////////////////////////////// void ble_glue_init() { diff --git a/lib/stm32wb.scons b/lib/stm32wb.scons index 7cb2fa8bd..94a1c7075 100644 --- a/lib/stm32wb.scons +++ b/lib/stm32wb.scons @@ -48,7 +48,10 @@ sources += Glob( ) sources += Glob( "stm32wb_copro/wpan/interface/patterns/ble_thread/tl/*_tl*.c", - exclude="stm32wb_copro/wpan/interface/patterns/ble_thread/tl/shci_tl_if.c", + exclude=[ + "stm32wb_copro/wpan/interface/patterns/ble_thread/tl/hci_tl_if.c", + "stm32wb_copro/wpan/interface/patterns/ble_thread/tl/shci_tl_if.c", + ], source=True, ) sources += [ From 9862876f06c967554c0979c035e1ed194f83f97d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 11 May 2023 06:10:20 +0300 Subject: [PATCH 02/71] SubGHz: Fix typos (#2661) Co-authored-by: hedger --- applications/debug/unit_tests/subghz/subghz_test.c | 8 ++++---- ...te_potocol_key.c => subghz_txrx_create_protocol_key.c} | 4 ++-- ...te_potocol_key.h => subghz_txrx_create_protocol_key.h} | 2 +- applications/main/subghz/scenes/subghz_scene_set_type.c | 6 +++--- lib/subghz/protocols/keeloq_common.c | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) rename applications/main/subghz/helpers/{subghz_txrx_create_potocol_key.c => subghz_txrx_create_protocol_key.c} (98%) rename applications/main/subghz/helpers/{subghz_txrx_create_potocol_key.h => subghz_txrx_create_protocol_key.h} (98%) diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index c7e9c96f1..f1ab92653 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -407,7 +407,7 @@ MU_TEST(subghz_decoder_ido_test) { "Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n"); } -MU_TEST(subghz_decoder_keelog_test) { +MU_TEST(subghz_decoder_keeloq_test) { mu_assert( subghz_decoder_test( EXT_PATH("unit_tests/subghz/doorhan_raw.sub"), SUBGHZ_PROTOCOL_KEELOQ_NAME), @@ -676,7 +676,7 @@ MU_TEST(subghz_encoder_nice_flo_test) { "Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); } -MU_TEST(subghz_encoder_keelog_test) { +MU_TEST(subghz_encoder_keeloq_test) { mu_assert( subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")), "Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); @@ -813,7 +813,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_gate_tx_test); MU_RUN_TEST(subghz_decoder_hormann_hsm_test); MU_RUN_TEST(subghz_decoder_ido_test); - MU_RUN_TEST(subghz_decoder_keelog_test); + MU_RUN_TEST(subghz_decoder_keeloq_test); MU_RUN_TEST(subghz_decoder_kia_seed_test); MU_RUN_TEST(subghz_decoder_nero_radio_test); MU_RUN_TEST(subghz_decoder_nero_sketch_test); @@ -852,7 +852,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_came_twee_test); MU_RUN_TEST(subghz_encoder_gate_tx_test); MU_RUN_TEST(subghz_encoder_nice_flo_test); - MU_RUN_TEST(subghz_encoder_keelog_test); + MU_RUN_TEST(subghz_encoder_keeloq_test); MU_RUN_TEST(subghz_encoder_linear_test); MU_RUN_TEST(subghz_encoder_linear_delta3_test); MU_RUN_TEST(subghz_encoder_megacode_test); diff --git a/applications/main/subghz/helpers/subghz_txrx_create_potocol_key.c b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c similarity index 98% rename from applications/main/subghz/helpers/subghz_txrx_create_potocol_key.c rename to applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c index 41e4f7c4e..06a855c23 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_potocol_key.c +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c @@ -1,5 +1,5 @@ #include "subghz_txrx_i.h" -#include "subghz_txrx_create_potocol_key.h" +#include "subghz_txrx_create_protocol_key.h" #include #include #include @@ -84,7 +84,7 @@ bool subghz_txrx_gen_data_protocol_and_te( return ret; } -bool subghz_txrx_gen_keelog_protocol( +bool subghz_txrx_gen_keeloq_protocol( SubGhzTxRx* instance, const char* name_preset, uint32_t frequency, diff --git a/applications/main/subghz/helpers/subghz_txrx_create_potocol_key.h b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h similarity index 98% rename from applications/main/subghz/helpers/subghz_txrx_create_potocol_key.h rename to applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h index 5eed93034..514a5733c 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_potocol_key.h +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h @@ -54,7 +54,7 @@ bool subghz_txrx_gen_data_protocol_and_te( * @param cnt Counter * @return bool True if success */ -bool subghz_txrx_gen_keelog_protocol( +bool subghz_txrx_gen_keeloq_protocol( SubGhzTxRx* instance, const char* name_preset, uint32_t frequency, diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 32e0d6588..d0571f1b1 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -1,5 +1,5 @@ #include "../subghz_i.h" -#include "../helpers/subghz_txrx_create_potocol_key.h" +#include "../helpers/subghz_txrx_create_protocol_key.h" #include #include @@ -172,7 +172,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24); break; case SubmenuIndexDoorHan_433_92: - generated_protocol = subghz_txrx_gen_keelog_protocol( + generated_protocol = subghz_txrx_gen_keeloq_protocol( subghz->txrx, "AM650", 433920000, "DoorHan", key, 0x2, 0x0003); if(!generated_protocol) { furi_string_set( @@ -181,7 +181,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { } break; case SubmenuIndexDoorHan_315_00: - generated_protocol = subghz_txrx_gen_keelog_protocol( + generated_protocol = subghz_txrx_gen_keeloq_protocol( subghz->txrx, "AM650", 315000000, "DoorHan", key, 0x2, 0x0003); if(!generated_protocol) { furi_string_set( diff --git a/lib/subghz/protocols/keeloq_common.c b/lib/subghz/protocols/keeloq_common.c index ddbf1c917..041494f90 100644 --- a/lib/subghz/protocols/keeloq_common.c +++ b/lib/subghz/protocols/keeloq_common.c @@ -23,7 +23,7 @@ inline uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const } /** Simple Learning Decrypt - * @param data - keelog encrypt data + * @param data - keeloq encrypt data * @param key - manufacture (64bit) * @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter */ From aa8a369e2a175fff226a84c75126eac8d24d625e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 11 May 2023 18:50:17 +0900 Subject: [PATCH 03/71] Rpc: add desktop service. Desktop: refactor locking routine. (#2665) * Rpc: add desktop service * Protobuf: sync to latest release. Desktop: rewrite PIN locking, finalize locking API * Desktop: cleanup code a little bit --- applications/services/desktop/desktop.c | 41 ++++- applications/services/desktop/desktop.h | 6 + .../services/desktop/desktop_settings.h | 3 +- applications/services/desktop/helpers/pin.c | 74 +++++++++ applications/services/desktop/helpers/pin.h | 11 ++ .../services/desktop/helpers/pin_lock.c | 140 ------------------ .../services/desktop/helpers/pin_lock.h | 21 --- .../desktop/scenes/desktop_scene_lock_menu.c | 19 +-- .../desktop/scenes/desktop_scene_locked.c | 4 +- .../desktop/scenes/desktop_scene_pin_input.c | 8 +- .../services/desktop/views/desktop_events.h | 1 - .../desktop/views/desktop_view_lock_menu.c | 14 +- .../desktop/views/desktop_view_lock_menu.h | 2 - .../views/desktop_view_pin_setup_done.c | 79 ---------- .../views/desktop_view_pin_setup_done.h | 15 -- applications/services/rpc/rpc.c | 4 + applications/services/rpc/rpc_desktop.c | 73 +++++++++ applications/services/rpc/rpc_i.h | 3 + .../scenes/desktop_settings_scene_pin_auth.c | 4 +- .../scenes/desktop_settings_scene_pin_error.c | 2 +- .../scenes/desktop_settings_scene_pin_setup.c | 4 +- assets/protobuf | 2 +- 22 files changed, 222 insertions(+), 308 deletions(-) create mode 100644 applications/services/desktop/helpers/pin.c create mode 100644 applications/services/desktop/helpers/pin.h delete mode 100644 applications/services/desktop/helpers/pin_lock.c delete mode 100644 applications/services/desktop/helpers/pin_lock.h delete mode 100644 applications/services/desktop/views/desktop_view_pin_setup_done.c delete mode 100644 applications/services/desktop/views/desktop_view_pin_setup_done.h create mode 100644 applications/services/rpc/rpc_desktop.c diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index bdb730099..28d09cc0d 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "animations/animation_manager.h" #include "desktop/scenes/desktop_scene.h" @@ -14,7 +16,7 @@ #include "desktop/views/desktop_view_pin_input.h" #include "desktop/views/desktop_view_pin_timeout.h" #include "desktop_i.h" -#include "helpers/pin_lock.h" +#include "helpers/pin.h" #include "helpers/slideshow_filename.h" #define TAG "Desktop" @@ -132,6 +134,15 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) { } void desktop_lock(Desktop* desktop) { + furi_hal_rtc_set_flag(FuriHalRtcFlagLock); + furi_hal_rtc_set_pin_fails(0); + + if(desktop->settings.pin_code.length) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_close(cli); + furi_record_close(RECORD_CLI); + } + desktop_auto_lock_inhibit(desktop); scene_manager_set_scene_state( desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); @@ -147,6 +158,13 @@ void desktop_unlock(Desktop* desktop) { desktop_view_locked_unlock(desktop->locked_view); scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); desktop_auto_lock_arm(desktop); + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + + if(desktop->settings.pin_code.length) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_open(cli, &cli_vcp); + furi_record_close(RECORD_CLI); + } } void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) { @@ -290,11 +308,14 @@ Desktop* desktop_alloc() { desktop->auto_lock_timer = furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop); + furi_record_create(RECORD_DESKTOP, desktop); + return desktop; } void desktop_free(Desktop* desktop) { furi_assert(desktop); + furi_check(furi_record_destroy(RECORD_DESKTOP)); furi_pubsub_unsubscribe( loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription); @@ -352,6 +373,16 @@ static bool desktop_check_file_flag(const char* flag_path) { return exists; } +bool desktop_api_is_locked(Desktop* instance) { + furi_assert(instance); + return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock); +} + +void desktop_api_unlock(Desktop* instance) { + furi_assert(instance); + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked); +} + int32_t desktop_srv(void* p) { UNUSED(p); @@ -375,14 +406,12 @@ int32_t desktop_srv(void* p) { scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); - desktop_pin_lock_init(&desktop->settings); - - if(!desktop_pin_lock_is_locked()) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { + desktop_lock(desktop); + } else { if(!loader_is_locked(desktop->loader)) { desktop_auto_lock_arm(desktop); } - } else { - desktop_lock(desktop); } if(desktop_check_file_flag(SLIDESHOW_FS_PATH)) { diff --git a/applications/services/desktop/desktop.h b/applications/services/desktop/desktop.h index f5608207d..5b12647b8 100644 --- a/applications/services/desktop/desktop.h +++ b/applications/services/desktop/desktop.h @@ -1,3 +1,9 @@ #pragma once typedef struct Desktop Desktop; + +#define RECORD_DESKTOP "desktop" + +bool desktop_api_is_locked(Desktop* instance); + +void desktop_api_unlock(Desktop* instance); diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h index e502c35f2..5d1b6126f 100644 --- a/applications/services/desktop/desktop_settings.h +++ b/applications/services/desktop/desktop_settings.h @@ -8,7 +8,7 @@ #include #include -#define DESKTOP_SETTINGS_VER (6) +#define DESKTOP_SETTINGS_VER (7) #define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) #define DESKTOP_SETTINGS_MAGIC (0x17) @@ -52,7 +52,6 @@ typedef struct { FavoriteApp favorite_primary; FavoriteApp favorite_secondary; PinCode pin_code; - uint8_t is_locked; uint32_t auto_lock_delay_ms; uint8_t dummy_mode; } DesktopSettings; diff --git a/applications/services/desktop/helpers/pin.c b/applications/services/desktop/helpers/pin.c new file mode 100644 index 000000000..8a79a1fb8 --- /dev/null +++ b/applications/services/desktop/helpers/pin.c @@ -0,0 +1,74 @@ +#include "pin.h" + +#include +#include +#include +#include +#include +#include + +#include "../desktop_i.h" + +static const NotificationSequence sequence_pin_fail = { + &message_display_backlight_on, + + &message_red_255, + &message_vibro_on, + &message_delay_100, + &message_vibro_off, + &message_red_0, + + &message_delay_250, + + &message_red_255, + &message_vibro_on, + &message_delay_100, + &message_vibro_off, + &message_red_0, + NULL, +}; + +static const uint8_t desktop_helpers_fails_timeout[] = { + 0, + 0, + 0, + 0, + 30, + 60, + 90, + 120, + 150, + 180, + /* +60 for every next fail */ +}; + +void desktop_pin_lock_error_notify() { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_pin_fail); + furi_record_close(RECORD_NOTIFICATION); +} + +uint32_t desktop_pin_lock_get_fail_timeout() { + uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); + uint32_t pin_timeout = 0; + uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1; + if(pin_fails <= max_index) { + pin_timeout = desktop_helpers_fails_timeout[pin_fails]; + } else { + pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60; + } + + return pin_timeout; +} + +bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2) { + furi_assert(pin_code1); + furi_assert(pin_code2); + bool result = false; + + if(pin_code1->length == pin_code2->length) { + result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length); + } + + return result; +} diff --git a/applications/services/desktop/helpers/pin.h b/applications/services/desktop/helpers/pin.h new file mode 100644 index 000000000..e5410723e --- /dev/null +++ b/applications/services/desktop/helpers/pin.h @@ -0,0 +1,11 @@ +#pragma once +#include +#include +#include "../desktop.h" +#include + +void desktop_pin_lock_error_notify(); + +uint32_t desktop_pin_lock_get_fail_timeout(); + +bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2); diff --git a/applications/services/desktop/helpers/pin_lock.c b/applications/services/desktop/helpers/pin_lock.c deleted file mode 100644 index 22fcabe7d..000000000 --- a/applications/services/desktop/helpers/pin_lock.c +++ /dev/null @@ -1,140 +0,0 @@ - -#include -#include -#include -#include -#include -#include - -#include "../helpers/pin_lock.h" -#include "../desktop_i.h" -#include -#include - -static const NotificationSequence sequence_pin_fail = { - &message_display_backlight_on, - - &message_red_255, - &message_vibro_on, - &message_delay_100, - &message_vibro_off, - &message_red_0, - - &message_delay_250, - - &message_red_255, - &message_vibro_on, - &message_delay_100, - &message_vibro_off, - &message_red_0, - NULL, -}; - -static const uint8_t desktop_helpers_fails_timeout[] = { - 0, - 0, - 0, - 0, - 30, - 60, - 90, - 120, - 150, - 180, - /* +60 for every next fail */ -}; - -void desktop_pin_lock_error_notify() { - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - notification_message(notification, &sequence_pin_fail); - furi_record_close(RECORD_NOTIFICATION); -} - -uint32_t desktop_pin_lock_get_fail_timeout() { - uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); - uint32_t pin_timeout = 0; - uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1; - if(pin_fails <= max_index) { - pin_timeout = desktop_helpers_fails_timeout[pin_fails]; - } else { - pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60; - } - - return pin_timeout; -} - -void desktop_pin_lock(DesktopSettings* settings) { - furi_assert(settings); - - furi_hal_rtc_set_pin_fails(0); - furi_hal_rtc_set_flag(FuriHalRtcFlagLock); - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_close(cli); - furi_record_close(RECORD_CLI); - settings->is_locked = 1; - DESKTOP_SETTINGS_SAVE(settings); -} - -void desktop_pin_unlock(DesktopSettings* settings) { - furi_assert(settings); - - furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); - settings->is_locked = 0; - DESKTOP_SETTINGS_SAVE(settings); -} - -void desktop_pin_lock_init(DesktopSettings* settings) { - furi_assert(settings); - - if(settings->pin_code.length > 0) { - if(settings->is_locked == 1) { - furi_hal_rtc_set_flag(FuriHalRtcFlagLock); - } else { - if(desktop_pin_lock_is_locked()) { - settings->is_locked = 1; - DESKTOP_SETTINGS_SAVE(settings); - } - } - } else { - furi_hal_rtc_set_pin_fails(0); - furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); - } - - if(desktop_pin_lock_is_locked()) { - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_close(cli); - furi_record_close(RECORD_CLI); - } -} - -bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered) { - bool result = false; - if(desktop_pins_are_equal(pin_set, pin_entered)) { - furi_hal_rtc_set_pin_fails(0); - result = true; - } else { - uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); - furi_hal_rtc_set_pin_fails(pin_fails + 1); - result = false; - } - return result; -} - -bool desktop_pin_lock_is_locked() { - return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock); -} - -bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) { - furi_assert(pin_code1); - furi_assert(pin_code2); - bool result = false; - - if(pin_code1->length == pin_code2->length) { - result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length); - } - - return result; -} diff --git a/applications/services/desktop/helpers/pin_lock.h b/applications/services/desktop/helpers/pin_lock.h deleted file mode 100644 index 028ae6d22..000000000 --- a/applications/services/desktop/helpers/pin_lock.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include -#include "../desktop.h" -#include - -void desktop_pin_lock_error_notify(); - -uint32_t desktop_pin_lock_get_fail_timeout(); - -void desktop_pin_lock(DesktopSettings* settings); - -void desktop_pin_unlock(DesktopSettings* settings); - -bool desktop_pin_lock_is_locked(); - -void desktop_pin_lock_init(DesktopSettings* settings); - -bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered); - -bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2); diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index bfaa8a036..105b2b37a 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -10,7 +10,7 @@ #include "../views/desktop_view_lock_menu.h" #include "desktop_scene_i.h" #include "desktop_scene.h" -#include "../helpers/pin_lock.h" +#include "../helpers/pin.h" #define TAG "DesktopSceneLock" @@ -25,7 +25,6 @@ void desktop_scene_lock_menu_on_enter(void* context) { DESKTOP_SETTINGS_LOAD(&desktop->settings); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); - desktop_lock_menu_set_pin_state(desktop->lock_menu, desktop->settings.pin_code.length > 0); desktop_lock_menu_set_dummy_mode_state(desktop->lock_menu, desktop->settings.dummy_mode); desktop_lock_menu_set_stealth_mode_state( desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); @@ -44,7 +43,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { if(check_pin_changed) { DESKTOP_SETTINGS_LOAD(&desktop->settings); if(desktop->settings.pin_code.length > 0) { - desktop_lock_menu_set_pin_state(desktop->lock_menu, true); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); } } @@ -55,21 +53,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { desktop_lock(desktop); consumed = true; break; - case DesktopLockMenuEventPinLock: - if(desktop->settings.pin_code.length > 0) { - desktop_pin_lock(&desktop->settings); - desktop_lock(desktop); - } else { - LoaderStatus status = - loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); - if(status == LoaderStatusOk) { - scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1); - } else { - FURI_LOG_E(TAG, "Unable to start desktop settings"); - } - } - consumed = true; - break; case DesktopLockMenuEventDummyModeOn: desktop_set_dummy_mode_state(desktop, true); scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/services/desktop/scenes/desktop_scene_locked.c b/applications/services/desktop/scenes/desktop_scene_locked.c index af19efc74..f64ef8371 100644 --- a/applications/services/desktop/scenes/desktop_scene_locked.c +++ b/applications/services/desktop/scenes/desktop_scene_locked.c @@ -7,7 +7,7 @@ #include "../desktop.h" #include "../desktop_i.h" -#include "../helpers/pin_lock.h" +#include "../helpers/pin.h" #include "../animations/animation_manager.h" #include "../views/desktop_events.h" #include "../views/desktop_view_pin_input.h" @@ -45,7 +45,7 @@ void desktop_scene_locked_on_enter(void* context) { bool switch_to_timeout_scene = false; uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked); if(state == SCENE_LOCKED_FIRST_ENTER) { - bool pin_locked = desktop_pin_lock_is_locked(); + bool pin_locked = desktop->settings.pin_code.length > 0; view_port_enabled_set(desktop->lock_icon_viewport, true); Gui* gui = furi_record_open(RECORD_GUI); gui_set_lockdown(gui, true); diff --git a/applications/services/desktop/scenes/desktop_scene_pin_input.c b/applications/services/desktop/scenes/desktop_scene_pin_input.c index 9392309e6..157acebf5 100644 --- a/applications/services/desktop/scenes/desktop_scene_pin_input.c +++ b/applications/services/desktop/scenes/desktop_scene_pin_input.c @@ -12,7 +12,7 @@ #include "../animations/animation_manager.h" #include "../views/desktop_events.h" #include "../views/desktop_view_pin_input.h" -#include "../helpers/pin_lock.h" +#include "../helpers/pin.h" #include "desktop_scene.h" #include "desktop_scene_i.h" @@ -54,9 +54,12 @@ static void desktop_scene_pin_input_back_callback(void* context) { static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) { Desktop* desktop = (Desktop*)context; - if(desktop_pin_lock_verify(&desktop->settings.pin_code, pin_code)) { + if(desktop_pin_compare(&desktop->settings.pin_code, pin_code)) { + furi_hal_rtc_set_pin_fails(0); view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked); } else { + uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); + furi_hal_rtc_set_pin_fails(pin_fails + 1); view_dispatcher_send_custom_event( desktop->view_dispatcher, DesktopPinInputEventUnlockFailed); } @@ -126,7 +129,6 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case DesktopPinInputEventUnlocked: - desktop_pin_unlock(&desktop->settings); desktop_unlock(desktop); consumed = true; break; diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h index 983e84438..e366885fe 100644 --- a/applications/services/desktop/views/desktop_events.h +++ b/applications/services/desktop/views/desktop_events.h @@ -31,7 +31,6 @@ typedef enum { DesktopDebugEventExit, DesktopLockMenuEventLock, - DesktopLockMenuEventPinLock, DesktopLockMenuEventDummyModeOn, DesktopLockMenuEventDummyModeOff, DesktopLockMenuEventStealthModeOn, diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 8b25a890f..f4790ebb8 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -23,14 +23,6 @@ void desktop_lock_menu_set_callback( lock_menu->context = context; } -void desktop_lock_menu_set_pin_state(DesktopLockMenuView* lock_menu, bool pin_is_set) { - with_view_model( - lock_menu->view, - DesktopLockMenuViewModel * model, - { model->pin_is_set = pin_is_set; }, - true); -} - void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode) { with_view_model( lock_menu->view, @@ -102,7 +94,6 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { bool consumed = false; bool dummy_mode = false; bool stealth_mode = false; - bool pin_is_set = false; bool update = false; with_view_model( @@ -131,15 +122,12 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { idx = model->idx; dummy_mode = model->dummy_mode; stealth_mode = model->stealth_mode; - pin_is_set = model->pin_is_set; }, update); if(event->key == InputKeyOk) { if((idx == DesktopLockMenuIndexLock)) { - if((pin_is_set) && (event->type == InputTypeShort)) { - lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context); - } else if((pin_is_set == false) && (event->type == InputTypeShort)) { + if((event->type == InputTypeShort)) { lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); } } else if(idx == DesktopLockMenuIndexStealth) { diff --git a/applications/services/desktop/views/desktop_view_lock_menu.h b/applications/services/desktop/views/desktop_view_lock_menu.h index 03ce6fa80..8ac3a7273 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.h +++ b/applications/services/desktop/views/desktop_view_lock_menu.h @@ -17,7 +17,6 @@ struct DesktopLockMenuView { typedef struct { uint8_t idx; - bool pin_is_set; bool dummy_mode; bool stealth_mode; } DesktopLockMenuViewModel; @@ -28,7 +27,6 @@ void desktop_lock_menu_set_callback( void* context); View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu); -void desktop_lock_menu_set_pin_state(DesktopLockMenuView* lock_menu, bool pin_is_set); void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode); void desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode); void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx); diff --git a/applications/services/desktop/views/desktop_view_pin_setup_done.c b/applications/services/desktop/views/desktop_view_pin_setup_done.c deleted file mode 100644 index 561b12861..000000000 --- a/applications/services/desktop/views/desktop_view_pin_setup_done.c +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../desktop_i.h" -#include "desktop_view_pin_setup_done.h" - -struct DesktopViewPinSetupDone { - View* view; - DesktopViewPinSetupDoneDoneCallback callback; - void* context; -}; - -static void desktop_view_pin_done_draw(Canvas* canvas, void* model) { - furi_assert(canvas); - UNUSED(model); - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned( - canvas, 64, 0, AlignCenter, AlignTop, "Prepare to use\narrows as\nPIN symbols"); - - canvas_set_font(canvas, FontSecondary); - elements_multiline_text(canvas, 58, 24, "Prepare to use\narrows as\nPIN symbols"); - - canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29); - elements_button_right(canvas, "Next"); -} - -static bool desktop_view_pin_done_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - - DesktopViewPinSetupDone* instance = context; - bool consumed = false; - - if((event->key == InputKeyRight) && (event->type == InputTypeShort)) { - instance->callback(instance->context); - consumed = true; - } - - return consumed; -} - -void desktop_view_pin_done_set_callback( - DesktopViewPinSetupDone* instance, - DesktopViewPinSetupDoneDoneCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - instance->callback = callback; - instance->context = context; -} - -DesktopViewPinSetupDone* desktop_view_pin_done_alloc() { - DesktopViewPinSetupDone* view = malloc(sizeof(DesktopViewPinSetupDone)); - view->view = view_alloc(); - view_set_context(view->view, view); - view_set_draw_callback(view->view, desktop_view_pin_done_draw); - view_set_input_callback(view->view, desktop_view_pin_done_input); - - return view; -} - -void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance) { - furi_assert(instance); - return instance->view; -} diff --git a/applications/services/desktop/views/desktop_view_pin_setup_done.h b/applications/services/desktop/views/desktop_view_pin_setup_done.h deleted file mode 100644 index b55677dc5..000000000 --- a/applications/services/desktop/views/desktop_view_pin_setup_done.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -typedef struct DesktopViewPinSetupDone DesktopViewPinSetupDone; - -typedef void (*DesktopViewPinSetupDoneDoneCallback)(void*); - -void desktop_view_pin_done_set_callback( - DesktopViewPinSetupDone* instance, - DesktopViewPinSetupDoneDoneCallback callback, - void* context); -DesktopViewPinSetupDone* desktop_view_pin_done_alloc(); -void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance); -View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance); diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index a759a12a9..b3ed4417c 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -57,6 +57,10 @@ static RpcSystemCallbacks rpc_systems[] = { .alloc = rpc_system_property_alloc, .free = NULL, }, + { + .alloc = rpc_desktop_alloc, + .free = rpc_desktop_free, + }, }; struct RpcSession { diff --git a/applications/services/rpc/rpc_desktop.c b/applications/services/rpc/rpc_desktop.c new file mode 100644 index 000000000..dbf9796ec --- /dev/null +++ b/applications/services/rpc/rpc_desktop.c @@ -0,0 +1,73 @@ +#include "flipper.pb.h" +#include "rpc_i.h" +#include +#include "desktop.pb.h" + +#define TAG "RpcDesktop" + +typedef struct { + RpcSession* session; + Desktop* desktop; +} RpcDesktop; + +static void rpc_desktop_on_is_locked_request(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_desktop_is_locked_request_tag); + + FURI_LOG_D(TAG, "IsLockedRequest"); + RpcDesktop* rpc_desktop = context; + RpcSession* session = rpc_desktop->session; + + PB_CommandStatus ret = desktop_api_is_locked(rpc_desktop->desktop) ? PB_CommandStatus_OK : + PB_CommandStatus_ERROR; + + rpc_send_and_release_empty(session, request->command_id, ret); +} + +static void rpc_desktop_on_unlock_request(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_desktop_unlock_request_tag); + + FURI_LOG_D(TAG, "UnlockRequest"); + RpcDesktop* rpc_desktop = context; + RpcSession* session = rpc_desktop->session; + + desktop_api_unlock(rpc_desktop->desktop); + + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); +} + +void* rpc_desktop_alloc(RpcSession* session) { + furi_assert(session); + + RpcDesktop* rpc_desktop = malloc(sizeof(RpcDesktop)); + rpc_desktop->desktop = furi_record_open(RECORD_DESKTOP); + rpc_desktop->session = session; + + RpcHandler rpc_handler = { + .message_handler = NULL, + .decode_submessage = NULL, + .context = rpc_desktop, + }; + + rpc_handler.message_handler = rpc_desktop_on_is_locked_request; + rpc_add_handler(session, PB_Main_desktop_is_locked_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_desktop_on_unlock_request; + rpc_add_handler(session, PB_Main_desktop_unlock_request_tag, &rpc_handler); + + return rpc_desktop; +} + +void rpc_desktop_free(void* context) { + furi_assert(context); + RpcDesktop* rpc_desktop = context; + + furi_assert(rpc_desktop->desktop); + furi_record_close(RECORD_DESKTOP); + + rpc_desktop->session = NULL; + free(rpc_desktop); +} \ No newline at end of file diff --git a/applications/services/rpc/rpc_i.h b/applications/services/rpc/rpc_i.h index 91a176da8..16e5e594d 100644 --- a/applications/services/rpc/rpc_i.h +++ b/applications/services/rpc/rpc_i.h @@ -36,6 +36,9 @@ void* rpc_system_gpio_alloc(RpcSession* session); void rpc_system_gpio_free(void* ctx); void* rpc_system_property_alloc(RpcSession* session); +void* rpc_desktop_alloc(RpcSession* session); +void rpc_desktop_free(void* ctx); + void rpc_debug_print_message(const PB_Main* message); void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c index 5fed235ce..be2ee4825 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include "../desktop_settings_app.h" #include #include @@ -18,7 +18,7 @@ static void pin_auth_done_callback(const PinCode* pin_code, void* context) { DesktopSettingsApp* app = context; app->pincode_buffer = *pin_code; - if(desktop_pins_are_equal(&app->settings.pin_code, pin_code)) { + if(desktop_pin_compare(&app->settings.pin_code, pin_code)) { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); } else { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c index dd1e85795..508992cee 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c @@ -6,7 +6,7 @@ #include #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" -#include +#include #include "../desktop_settings_app.h" #define SCENE_EVENT_EXIT (0U) diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c index bf0f48ae6..1603aa337 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c @@ -7,7 +7,7 @@ #include #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" -#include +#include #define SCENE_EVENT_EXIT (0U) #define SCENE_EVENT_1ST_PIN_ENTERED (1U) @@ -25,7 +25,7 @@ static void pin_setup_done_callback(const PinCode* pin_code, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_1ST_PIN_ENTERED); } else { app->pincode_buffer_filled = false; - if(desktop_pins_are_equal(&app->pincode_buffer, pin_code)) { + if(desktop_pin_compare(&app->pincode_buffer, pin_code)) { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); } else { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); diff --git a/assets/protobuf b/assets/protobuf index 1f6b4a08c..a13c5ddd0 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 1f6b4a08c5d05c2b17926a3ba79f60109638932f +Subproject commit a13c5ddd0397511bd4c6de4afdd1031a5b6f5bca From ec7e6a0ac13598532878f484855a8f0ecbfc77cc Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Thu, 11 May 2023 13:22:02 +0300 Subject: [PATCH 04/71] Using scene manager functions in DecodeRAW --- applications/main/subghz/helpers/subghz_types.h | 6 ++++++ .../subghz/scenes/subghz_scene_decode_raw.c | 17 +++++++++-------- .../subghz/scenes/subghz_scene_receiver_info.c | 9 ++------- .../main/subghz/scenes/subghz_scene_save_name.c | 2 +- .../subghz/scenes/subghz_scene_save_success.c | 11 ++++------- applications/main/subghz/subghz.c | 3 --- applications/main/subghz/subghz_i.h | 10 ---------- 7 files changed, 22 insertions(+), 36 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 700049237..1cddfc8d5 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -91,3 +91,9 @@ typedef enum { SubGhzViewReceiverModeLive, SubGhzViewReceiverModeFile, } SubGhzViewReceiverMode; + +typedef enum { + SubGhzDecodeRawStateStart, + SubGhzDecodeRawStateLoading, + SubGhzDecodeRawStateLoaded, +} SubGhzDecodeRawState; \ No newline at end of file diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 54c729cc3..e88da3749 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -120,7 +120,8 @@ bool subghz_scene_decode_raw_next(SubGhz* subghz) { uint32_t duration = level_duration_get_duration(level_duration); subghz_receiver_decode(receiver, level, duration); } else { - subghz->decode_raw_state = SubGhzDecodeRawStateLoaded; + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateLoaded); subghz->state_notifications = SubGhzNotificationStateIDLE; subghz_view_receiver_add_data_progress(subghz->subghz_receiver, "Done!"); @@ -155,11 +156,13 @@ void subghz_scene_decode_raw_on_enter(void* context) { subghz_txrx_receiver_set_filter(subghz->txrx, SubGhzProtocolFlag_Decodable); - if(subghz->decode_raw_state == SubGhzDecodeRawStateStart) { + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneDecodeRAW) == + SubGhzDecodeRawStateStart) { //Decode RAW to history subghz_history_reset(subghz->history); if(subghz_scene_decode_raw_start(subghz)) { - subghz->decode_raw_state = SubGhzDecodeRawStateLoading; + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateLoading); subghz->state_notifications = SubGhzNotificationStateRx; } } else { @@ -192,10 +195,9 @@ bool subghz_scene_decode_raw_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case SubGhzCustomEventViewReceiverBack: - subghz->decode_raw_state = SubGhzDecodeRawStateStart; + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateStart); subghz->idx_menu_chosen = 0; - subghz->in_decoder_scene = false; - subghz->in_decoder_scene_skip = false; subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz); @@ -214,7 +216,6 @@ bool subghz_scene_decode_raw_on_event(void* context, SceneManagerEvent event) { case SubGhzCustomEventViewReceiverOK: subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz->state_notifications = SubGhzNotificationStateIDLE; - subghz->in_decoder_scene = true; scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); consumed = true; break; @@ -242,7 +243,7 @@ bool subghz_scene_decode_raw_on_event(void* context, SceneManagerEvent event) { break; } - switch(subghz->decode_raw_state) { + switch(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneDecodeRAW)) { case SubGhzDecodeRawStateLoading: subghz_scene_decode_raw_next(subghz); break; diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 370638931..2d89de731 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -141,7 +141,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_scene_receiver_info_draw_widget(subghz); subghz_txrx_stop(subghz->txrx); - if(!subghz->in_decoder_scene) { + if(!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneDecodeRAW)) { subghz_txrx_rx_start(subghz->txrx); subghz_txrx_hopper_unpause(subghz->txrx); @@ -161,9 +161,6 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { subghz_file_name_clear(subghz); - if(subghz->in_decoder_scene) { - subghz->in_decoder_scene_skip = true; - } scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); } return true; @@ -192,9 +189,7 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) void subghz_scene_receiver_info_on_exit(void* context) { SubGhz* subghz = context; - if(subghz->in_decoder_scene && !subghz->in_decoder_scene_skip) { - subghz->in_decoder_scene = false; - } + widget_reset(subghz->widget); keeloq_reset_mfname(); keeloq_reset_kl_type(); diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index b0c3a1b12..74a861efb 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -127,7 +127,7 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(!(strcmp(subghz->file_name_tmp, "") == 0) || scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != SubGhzCustomEventManagerNoSet) { - if(!subghz->in_decoder_scene) { + if(!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneDecodeRAW)) { furi_string_set(subghz->file_path, subghz->file_path_tmp); } } diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c index 0d0f9059e..6a5346492 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -24,7 +24,7 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventSceneSaveSuccess) { - if(!subghz->in_decoder_scene) { + if(!scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneDecodeRAW)) { if(!scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneReceiver)) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWSave); @@ -38,7 +38,9 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) } } } else { - subghz->decode_raw_state = SubGhzDecodeRawStateStart; + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateStart); + subghz->idx_menu_chosen = 0; subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz); @@ -64,11 +66,6 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) void subghz_scene_save_success_on_exit(void* context) { SubGhz* subghz = context; - if(subghz->in_decoder_scene) { - subghz->in_decoder_scene = false; - subghz->in_decoder_scene_skip = false; - } - // Clear view Popup* popup = subghz->popup; diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 4f4393efb..d30bd79f2 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -60,9 +60,6 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { // GUI subghz->gui = furi_record_open(RECORD_GUI); - subghz->in_decoder_scene = false; - subghz->in_decoder_scene_skip = false; - // View Dispatcher subghz->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(subghz->view_dispatcher); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 785a6708f..3d1c2db9c 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -54,12 +54,6 @@ typedef struct { uint8_t seed[4]; } SecureData; -typedef enum { - SubGhzDecodeRawStateStart, - SubGhzDecodeRawStateLoading, - SubGhzDecodeRawStateLoaded, -} SubGhzDecodeRawState; - struct SubGhz { Gui* gui; NotificationApp* notifications; @@ -98,16 +92,12 @@ struct SubGhz { FuriString* error_str; SubGhzLock lock; - bool in_decoder_scene; - bool in_decoder_scene_skip; - bool ignore_starline; bool ignore_auto_alarms; bool ignore_magellan; SecureData* secure_data; - SubGhzDecodeRawState decode_raw_state; SubGhzFileEncoderWorker* decode_raw_file_worker_encoder; SubGhzThresholdRssi* threshold_rssi; From a7691b2d3be2e9a43fd9f34b34cc02155f9ad4ba Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 11 May 2023 17:57:17 +0300 Subject: [PATCH 05/71] Fix merge issues --- applications/services/desktop/scenes/desktop_scene_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index 1cf00bb0c..f504646bf 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -9,7 +9,6 @@ #include "../views/desktop_view_main.h" #include "desktop_scene.h" #include "desktop_scene_i.h" -#include "../helpers/pin_lock.h" #define TAG "DesktopSrv" From 8012f9cd44c8431da011f6431a7c5e46dc433a1e Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Fri, 12 May 2023 23:38:09 +0300 Subject: [PATCH 06/71] Fix offset in some files --- lib/subghz/protocols/keeloq.c | 483 ++++++++++++++++--------------- lib/subghz/protocols/star_line.c | 216 +++++++------- 2 files changed, 368 insertions(+), 331 deletions(-) diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 4900e7e49..c9e603297 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -247,64 +247,69 @@ static bool subghz_protocol_keeloq_gen_data( } else if(strcmp(instance->manufacture_name, "HCS101") == 0) { hop = instance->generic.cnt << 16 | (btn & 0xF) << 12 | 0x000; } else { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); - if(res == 0) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - //Simple Learning - hop = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); - break; - case KEELOQ_LEARNING_NORMAL: - //Simple Learning - man = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - break; - case KEELOQ_LEARNING_SECURE: - //Secure Learning - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->generic.seed, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - break; - case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: - //Magic XOR type-1 Learning - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - instance->generic.serial, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - break; - case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: - //Magic Serial Type 1 learning - man = subghz_protocol_keeloq_common_magic_serial_type1_learning( - fix, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - break; - case KEELOQ_LEARNING_UNKNOWN: - if(kl_type == 1) { - hop = - subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); + for + M_EACH( + manufacture_code, + *subghz_keystore_get_data(instance->keystore), + SubGhzKeyArray_t) { + res = strcmp( + furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + //Simple Learning + hop = subghz_protocol_keeloq_common_encrypt( + decrypt, manufacture_code->key); + break; + case KEELOQ_LEARNING_NORMAL: + //Simple Learning + man = subghz_protocol_keeloq_common_normal_learning( + fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_SECURE: + //Secure Learning + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->generic.seed, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: + //Magic XOR type-1 Learning + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + instance->generic.serial, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: + //Magic Serial Type 1 learning + man = subghz_protocol_keeloq_common_magic_serial_type1_learning( + fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_UNKNOWN: + if(kl_type == 1) { + hop = subghz_protocol_keeloq_common_encrypt( + decrypt, manufacture_code->key); + } + if(kl_type == 2) { + man = subghz_protocol_keeloq_common_normal_learning( + fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + if(kl_type == 3) { + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->generic.seed, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + if(kl_type == 4) { + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + instance->generic.serial, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + break; + } + break; } - if(kl_type == 2) { - man = subghz_protocol_keeloq_common_normal_learning( - fix, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - } - if(kl_type == 3) { - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->generic.seed, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - } - if(kl_type == 4) { - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - instance->generic.serial, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - } - break; } - break; - } - } } } if(hop) { @@ -835,179 +840,8 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( } if(strcmp(mfname, "") == 0) { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_NORMAL: - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_SECURE: - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->seed, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: - man = subghz_protocol_keeloq_common_magic_serial_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: - man = subghz_protocol_keeloq_common_magic_serial_type2_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: - man = subghz_protocol_keeloq_common_magic_serial_type3_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_UNKNOWN: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - - // Check for mirrored man - uint64_t man_rev = 0; - uint64_t man_rev_byte = 0; - for(uint8_t i = 0; i < 64; i += 8) { - man_rev_byte = (uint8_t)(manufacture_code->key >> i); - man_rev = man_rev | man_rev_byte << (56 - i); - } - - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - - //########################### - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - - // Secure Learning - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->seed, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 3; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_secure_learning(fix, instance->seed, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 3; - return 1; - } - - // Magic xor type1 learning - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 4; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_magic_xor_type1_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 4; - return 1; - } - - break; - } - } - } else if(strcmp(mfname, "Unknown") == 0) { - return 1; - } else { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); - if(res == 0) { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { switch(manufacture_code->type) { case KEELOQ_LEARNING_SIMPLE: // Simple Learning @@ -1060,6 +894,26 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( return 1; } break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: + man = subghz_protocol_keeloq_common_magic_serial_type2_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: + man = subghz_protocol_keeloq_common_magic_serial_type3_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; case KEELOQ_LEARNING_UNKNOWN: // Simple Learning decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); @@ -1069,6 +923,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( kl_type = 1; return 1; } + // Check for mirrored man uint64_t man_rev = 0; uint64_t man_rev_byte = 0; @@ -1076,6 +931,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( man_rev_byte = (uint8_t)(manufacture_code->key >> i); man_rev = man_rev | man_rev_byte << (56 - i); } + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { *manufacture_name = furi_string_get_cstr(manufacture_code->name); @@ -1083,6 +939,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( kl_type = 1; return 1; } + //########################### // Normal Learning // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 @@ -1152,7 +1009,173 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( break; } } - } + } else if(strcmp(mfname, "Unknown") == 0) { + return 1; + } else { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + // Simple Learning + decrypt = + subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_NORMAL: + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man = subghz_protocol_keeloq_common_normal_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_SECURE: + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: + man = subghz_protocol_keeloq_common_magic_serial_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_UNKNOWN: + // Simple Learning + decrypt = + subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + // Check for mirrored man + uint64_t man_rev = 0; + uint64_t man_rev_byte = 0; + for(uint8_t i = 0; i < 64; i += 8) { + man_rev_byte = (uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56 - i); + } + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + //########################### + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man = subghz_protocol_keeloq_common_normal_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Secure Learning + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 3; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 3; + return 1; + } + + // Magic xor type1 learning + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 4; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_magic_xor_type1_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 4; + return 1; + } + + break; + } + } + } } *manufacture_name = "Unknown"; diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index cb9b26760..792622412 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -163,36 +163,41 @@ static bool instance->generic.data, instance->generic.data_count_bit); hop = code_found_reverse & 0x00000000ffffffff; } else { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); - if(res == 0) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - //Simple Learning - hop = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); - break; - case KEELOQ_LEARNING_NORMAL: - //Normal Learning - man = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - break; - case KEELOQ_LEARNING_UNKNOWN: - if(kl_type == 1) { + for + M_EACH( + manufacture_code, + *subghz_keystore_get_data(instance->keystore), + SubGhzKeyArray_t) { + res = strcmp( + furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + //Simple Learning hop = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); - } - if(kl_type == 2) { + break; + case KEELOQ_LEARNING_NORMAL: + //Normal Learning man = subghz_protocol_keeloq_common_normal_learning( fix, manufacture_code->key); hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_UNKNOWN: + if(kl_type == 1) { + hop = subghz_protocol_keeloq_common_encrypt( + decrypt, manufacture_code->key); + } + if(kl_type == 2) { + man = subghz_protocol_keeloq_common_normal_learning( + fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + break; } break; } - break; } - } } if(hop) { uint64_t yek = (uint64_t)fix << 32 | hop; @@ -523,84 +528,8 @@ static uint8_t subghz_protocol_star_line_check_remote_controller_selector( } if(strcmp(mfname, "") == 0) { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_NORMAL: - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man_normal_learning = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_UNKNOWN: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - // Check for mirrored man - uint64_t man_rev = 0; - uint64_t man_rev_byte = 0; - for(uint8_t i = 0; i < 64; i += 8) { - man_rev_byte = (uint8_t)(manufacture_code->key >> i); - man_rev = man_rev | man_rev_byte << (56 - i); - } - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - //########################### - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man_normal_learning = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - // Check for mirrored man - man_normal_learning = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - break; - } - } - } else if(strcmp(mfname, "Unknown") == 0) { - return 1; - } else { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); - if(res == 0) { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { switch(manufacture_code->type) { case KEELOQ_LEARNING_SIMPLE: // Simple Learning @@ -663,7 +592,6 @@ static uint8_t subghz_protocol_star_line_check_remote_controller_selector( kl_type = 2; return 1; } - // Check for mirrored man man_normal_learning = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); @@ -678,7 +606,93 @@ static uint8_t subghz_protocol_star_line_check_remote_controller_selector( break; } } - } + } else if(strcmp(mfname, "Unknown") == 0) { + return 1; + } else { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + // Simple Learning + decrypt = + subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_NORMAL: + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = subghz_protocol_keeloq_common_normal_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_UNKNOWN: + // Simple Learning + decrypt = + subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + // Check for mirrored man + uint64_t man_rev = 0; + uint64_t man_rev_byte = 0; + for(uint8_t i = 0; i < 64; i += 8) { + man_rev_byte = (uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56 - i); + } + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + //########################### + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = subghz_protocol_keeloq_common_normal_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Check for mirrored man + man_normal_learning = + subghz_protocol_keeloq_common_normal_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + break; + } + } + } } *manufacture_name = "Unknown"; From 849f14e480eec3e28a2fbebaa190a6679f7cb172 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 13 May 2023 00:14:22 +0300 Subject: [PATCH 07/71] Bad BT plugin, Submenu locked elements, API updates, etc. Thanks to WillyJL, ClaraCrazy, and XFW contributors --- applications/external/bad_bt/application.fam | 17 + applications/external/bad_bt/bad_bt_app.c | 333 ++++++++ applications/external/bad_bt/bad_bt_app.h | 39 + .../external/bad_bt/helpers/ducky_script.c | 777 ++++++++++++++++++ .../external/bad_bt/helpers/ducky_script.h | 154 ++++ .../bad_bt/helpers/ducky_script_commands.c | 201 +++++ .../external/bad_bt/helpers/ducky_script_i.h | 44 + .../bad_bt/helpers/ducky_script_keycodes.c | 78 ++ .../external/bad_bt/images/badbt_10px.png | Bin 0 -> 576 bytes .../external/bad_bt/scenes/bad_bt_scene.c | 30 + .../external/bad_bt/scenes/bad_bt_scene.h | 29 + .../bad_bt/scenes/bad_bt_scene_config.c | 104 +++ .../bad_bt/scenes/bad_bt_scene_config.h | 7 + .../scenes/bad_bt_scene_config_layout.c | 47 ++ .../bad_bt/scenes/bad_bt_scene_config_mac.c | 47 ++ .../bad_bt/scenes/bad_bt_scene_config_name.c | 45 + .../bad_bt/scenes/bad_bt_scene_error.c | 61 ++ .../bad_bt/scenes/bad_bt_scene_file_select.c | 49 ++ .../bad_bt/scenes/bad_bt_scene_work.c | 56 ++ .../external/bad_bt/views/bad_bt_view.c | 233 ++++++ .../external/bad_bt/views/bad_bt_view.h | 29 + applications/external/totp/features_config.h | 2 +- applications/services/bt/bt_service/bt.c | 88 ++ applications/services/bt/bt_service/bt.h | 33 + applications/services/bt/bt_service/bt_i.h | 2 + applications/services/gui/elements.c | 58 ++ applications/services/gui/elements.h | 10 + applications/services/gui/modules/submenu.c | 98 ++- applications/services/gui/modules/submenu.h | 20 + .../services/gui/modules/variable_item_list.c | 154 +++- .../services/gui/modules/variable_item_list.h | 8 + .../bt_settings_scene_forget_dev_confirm.c | 9 + firmware/targets/f7/api_symbols.csv | 22 + firmware/targets/f7/ble_glue/gap.c | 59 +- firmware/targets/f7/ble_glue/gap.h | 2 + firmware/targets/f7/ble_glue/hid_service.c | 88 +- firmware/targets/f7/ble_glue/hid_service.h | 4 + firmware/targets/f7/furi_hal/furi_hal_bt.c | 106 ++- .../targets/f7/furi_hal/furi_hal_bt_hid.c | 75 +- .../targets/furi_hal_include/furi_hal_bt.h | 29 + .../furi_hal_include/furi_hal_bt_hid.h | 7 + .../furi_hal_include/furi_hal_version.h | 1 + 42 files changed, 3211 insertions(+), 44 deletions(-) create mode 100644 applications/external/bad_bt/application.fam create mode 100644 applications/external/bad_bt/bad_bt_app.c create mode 100644 applications/external/bad_bt/bad_bt_app.h create mode 100644 applications/external/bad_bt/helpers/ducky_script.c create mode 100644 applications/external/bad_bt/helpers/ducky_script.h create mode 100644 applications/external/bad_bt/helpers/ducky_script_commands.c create mode 100644 applications/external/bad_bt/helpers/ducky_script_i.h create mode 100644 applications/external/bad_bt/helpers/ducky_script_keycodes.c create mode 100644 applications/external/bad_bt/images/badbt_10px.png create mode 100644 applications/external/bad_bt/scenes/bad_bt_scene.c create mode 100644 applications/external/bad_bt/scenes/bad_bt_scene.h create mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_config.c create mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_config.h create mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_config_layout.c create mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_config_mac.c create mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_config_name.c create mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_error.c create mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_file_select.c create mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_work.c create mode 100644 applications/external/bad_bt/views/bad_bt_view.c create mode 100644 applications/external/bad_bt/views/bad_bt_view.h diff --git a/applications/external/bad_bt/application.fam b/applications/external/bad_bt/application.fam new file mode 100644 index 000000000..ddd8eee19 --- /dev/null +++ b/applications/external/bad_bt/application.fam @@ -0,0 +1,17 @@ +App( + appid="bad_bt", + name="Bad BT", + apptype=FlipperAppType.EXTERNAL, + entry_point="bad_bt_app", + requires=[ + "gui", + "dialogs", + ], + stack_size=2 * 1024, + order=70, + fap_libs=["assets"], + fap_category="Tools", + fap_icon="images/badbt_10px.png", + fap_icon_assets="images", + fap_icon_assets_symbol="bad_bt", +) diff --git a/applications/external/bad_bt/bad_bt_app.c b/applications/external/bad_bt/bad_bt_app.c new file mode 100644 index 000000000..6ac7d4fa4 --- /dev/null +++ b/applications/external/bad_bt/bad_bt_app.c @@ -0,0 +1,333 @@ +#include "bad_bt_app.h" +#include +#include +#include +#include +#include + +#include +#include + +#define BAD_BT_SETTINGS_FILE_NAME ".badbt.settings" +#define BAD_BT_APP_PATH_BOUND_KEYS_FOLDER EXT_PATH("badbt") +#define BAD_BT_APP_PATH_BOUND_KEYS_FILE BAD_BT_APP_PATH_BOUND_KEYS_FOLDER "/.badbt.keys" + +#define BAD_BT_SETTINGS_PATH BAD_BT_APP_BASE_CONFIG_FOLDER "/" BAD_BT_SETTINGS_FILE_NAME + +static bool bad_bt_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + BadBtApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool bad_bt_app_back_event_callback(void* context) { + furi_assert(context); + BadBtApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void bad_bt_app_tick_event_callback(void* context) { + furi_assert(context); + BadBtApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +static void bad_bt_load_settings(BadBtApp* app) { + furi_string_reset(app->keyboard_layout); + strcpy(app->config.bt_name, ""); + memcpy( + app->config.bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_BT_MAC_ADDRESS_LEN); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_existing(file, BAD_BT_SETTINGS_PATH)) { + FuriString* tmp_str = furi_string_alloc(); + if(!flipper_format_read_string(file, "Keyboard_Layout", app->keyboard_layout)) { + furi_string_reset(app->keyboard_layout); + } + if(!flipper_format_read_bool(file, "BT_Remember", &(app->bt_remember), 1)) { + app->bt_remember = false; + } + if(flipper_format_read_string(file, "Bt_Name", tmp_str) && !furi_string_empty(tmp_str)) { + strcpy(app->config.bt_name, furi_string_get_cstr(tmp_str)); + } else { + strcpy(app->config.bt_name, ""); + } + if(!flipper_format_read_hex( + file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN)) { + memcpy( + app->config.bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_BT_MAC_ADDRESS_LEN); + } + furi_string_free(tmp_str); + flipper_format_file_close(file); + } + flipper_format_free(file); + + if(!furi_string_empty(app->keyboard_layout)) { + FileInfo layout_file_info; + FS_Error file_check_err = storage_common_stat( + storage, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); + if(file_check_err != FSE_OK) { + furi_string_reset(app->keyboard_layout); + return; + } + if(layout_file_info.size != 256) { + furi_string_reset(app->keyboard_layout); + } + } + + furi_record_close(RECORD_STORAGE); +} + +static void bad_bt_save_settings(BadBtApp* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_always(file, BAD_BT_SETTINGS_PATH)) { + flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout); + flipper_format_write_bool(file, "BT_Remember", &(app->bt_remember), 1); + flipper_format_write_string_cstr(file, "Bt_Name", app->config.bt_name); + flipper_format_write_hex( + file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN); + flipper_format_file_close(file); + } + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); +} + +void bad_bt_reload_worker(BadBtApp* app) { + bad_bt_script_close(app->bad_bt_script); + app->bad_bt_script = bad_bt_script_open(app->file_path, app->bt, app); + bad_bt_script_set_keyboard_layout(app->bad_bt_script, app->keyboard_layout); +} + +void bad_kb_config_refresh_menu(BadBtApp* app) { + scene_manager_next_scene(app->scene_manager, BadBtSceneConfig); + scene_manager_previous_scene(app->scene_manager); +} + +int32_t bad_bt_config_switch_mode(BadBtApp* app) { + bad_bt_reload_worker(app); + furi_hal_bt_start_advertising(); + bad_kb_config_refresh_menu(app); + return 0; +} + +void bad_bt_config_switch_remember_mode(BadBtApp* app) { + if(app->bt_remember) { + furi_hal_bt_set_profile_pairing_method( + FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + bt_set_profile_mac_address(app->bt, (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS); + bt_enable_peer_key_update(app->bt); + } else { + furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); + bt_set_profile_mac_address(app->bt, app->config.bt_mac); + bt_disable_peer_key_update(app->bt); + } + bad_bt_reload_worker(app); +} + +int32_t bad_bt_connection_init(BadBtApp* app) { + // Set original name and mac address in prev config + strcpy( + app->prev_config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); + + memcpy(app->prev_config.bt_mac, furi_hal_version_get_ble_mac(), BAD_BT_MAC_ADDRESS_LEN); + + bt_timeout = bt_hid_delays[LevelRssi39_0]; + bt_disconnect(app->bt); + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + bt_keys_storage_set_storage_path(app->bt, BAD_BT_APP_PATH_BOUND_KEYS_FILE); + if(strcmp(app->config.bt_name, "") != 0) { + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name); + } + if(app->bt_remember) { + furi_hal_bt_set_profile_mac_addr( + FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS); + furi_hal_bt_set_profile_pairing_method( + FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + } else { + if(memcmp( + app->config.bt_mac, (uint8_t*)&BAD_BT_EMPTY_MAC_ADDRESS, BAD_BT_MAC_ADDRESS_LEN) != + 0) { + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); + } + furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); + } + bt_set_profile(app->bt, BtProfileHidKeyboard); + if(strcmp(app->config.bt_name, "") == 0) { + strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); + } + if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_BT_EMPTY_MAC_ADDRESS, BAD_BT_MAC_ADDRESS_LEN) == + 0) { + memcpy( + app->config.bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_BT_MAC_ADDRESS_LEN); + } + + furi_hal_bt_start_advertising(); + if(app->bt_remember) { + bt_enable_peer_key_update(app->bt); + } else { + bt_disable_peer_key_update(app->bt); + } + + return 0; +} + +void bad_bt_connection_deinit(BadBtApp* app) { + bt_disconnect(app->bt); + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + bt_keys_storage_set_default_path(app->bt); + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name); + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac); + furi_hal_bt_set_profile_pairing_method( + FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + bt_set_profile(app->bt, BtProfileSerial); + bt_enable_peer_key_update(app->bt); +} + +BadBtApp* bad_bt_app_alloc(char* arg) { + BadBtApp* app = malloc(sizeof(BadBtApp)); + + app->bad_bt_script = NULL; + + app->file_path = furi_string_alloc(); + app->keyboard_layout = furi_string_alloc(); + if(arg && strlen(arg)) { + furi_string_set(app->file_path, arg); + } + + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_mkdir(storage, BAD_BT_APP_BASE_CONFIG_FOLDER); + furi_record_close(RECORD_STORAGE); + + bad_bt_load_settings(app); + + app->gui = furi_record_open(RECORD_GUI); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + app->dialogs = furi_record_open(RECORD_DIALOGS); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&bad_bt_scene_handlers, app); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, bad_bt_app_tick_event_callback, 500); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, bad_bt_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, bad_bt_app_back_event_callback); + + Bt* bt = furi_record_open(RECORD_BT); + app->bt = bt; + app->bt->suppress_pin_screen = true; + + // Custom Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadBtAppViewError, widget_get_view(app->widget)); + + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadBtAppViewConfig, variable_item_list_get_view(app->var_item_list)); + + app->bad_bt_view = bad_bt_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadBtAppViewWork, bad_bt_get_view(app->bad_bt_view)); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadBtAppViewConfigName, text_input_get_view(app->text_input)); + + app->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadBtAppViewConfigMac, byte_input_get_view(app->byte_input)); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->conn_init_thread = furi_thread_alloc_ex( + "BadBtConnInit", 1024, (FuriThreadCallback)bad_bt_connection_init, app); + furi_thread_start(app->conn_init_thread); + if(!furi_string_empty(app->file_path)) { + app->bad_bt_script = bad_bt_script_open(app->file_path, app->bt, app); + bad_bt_script_set_keyboard_layout(app->bad_bt_script, app->keyboard_layout); + scene_manager_next_scene(app->scene_manager, BadBtSceneWork); + } else { + furi_string_set(app->file_path, BAD_BT_APP_BASE_FOLDER); + scene_manager_next_scene(app->scene_manager, BadBtSceneFileSelect); + } + + return app; +} + +void bad_bt_app_free(BadBtApp* app) { + furi_assert(app); + + if(app->bad_bt_script) { + bad_bt_script_close(app->bad_bt_script); + app->bad_bt_script = NULL; + } + + // Views + view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewWork); + bad_bt_free(app->bad_bt_view); + + // Custom Widget + view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewError); + widget_free(app->widget); + + // Variable item list + view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfig); + variable_item_list_free(app->var_item_list); + + // Text Input + view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfigName); + text_input_free(app->text_input); + + // Byte Input + view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfigMac); + byte_input_free(app->byte_input); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Restore bt config + app->bt->suppress_pin_screen = false; + if(app->conn_init_thread) { + furi_thread_join(app->conn_init_thread); + furi_thread_free(app->conn_init_thread); + bad_bt_connection_deinit(app); + } + + // Close records + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_BT); + + bad_bt_save_settings(app); + + furi_string_free(app->file_path); + furi_string_free(app->keyboard_layout); + + free(app); +} + +int32_t bad_bt_app(void* p) { + BadBtApp* bad_bt_app = bad_bt_app_alloc((char*)p); + + view_dispatcher_run(bad_bt_app->view_dispatcher); + + bad_bt_app_free(bad_bt_app); + return 0; +} diff --git a/applications/external/bad_bt/bad_bt_app.h b/applications/external/bad_bt/bad_bt_app.h new file mode 100644 index 000000000..13b0844b0 --- /dev/null +++ b/applications/external/bad_bt/bad_bt_app.h @@ -0,0 +1,39 @@ +#pragma once + +#include "scenes/bad_bt_scene.h" +#include "helpers/ducky_script.h" + +#include +#include +#include +#include +#include +#include "bad_bt_icons.h" + +#define BAD_BT_APP_BASE_FOLDER EXT_PATH("badusb") +#define BAD_BT_APP_BASE_CONFIG_FOLDER EXT_PATH("badbt") +#define BAD_BT_APP_PATH_LAYOUT_FOLDER BAD_BT_APP_BASE_FOLDER "/assets/layouts" +#define BAD_BT_APP_SCRIPT_EXTENSION ".txt" +#define BAD_BT_APP_LAYOUT_EXTENSION ".kl" + +typedef enum BadBtCustomEvent { + BadBtAppCustomEventTextEditResult, + BadBtAppCustomEventByteInputDone, + BadBtCustomEventErrorBack +} BadBtCustomEvent; + +typedef enum { + BadBtAppViewError, + BadBtAppViewWork, + BadBtAppViewConfig, + BadBtAppViewConfigMac, + BadBtAppViewConfigName +} BadBtAppView; + +void bad_bt_config_switch_remember_mode(BadBtApp* app); + +int32_t bad_bt_connection_init(BadBtApp* app); + +void bad_bt_connection_deinit(BadBtApp* app); + +void bad_kb_config_refresh_menu(BadBtApp* app); \ No newline at end of file diff --git a/applications/external/bad_bt/helpers/ducky_script.c b/applications/external/bad_bt/helpers/ducky_script.c new file mode 100644 index 000000000..a59d377f2 --- /dev/null +++ b/applications/external/bad_bt/helpers/ducky_script.c @@ -0,0 +1,777 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "ducky_script.h" +#include "ducky_script_i.h" +#include +#include +#include "../bad_bt_app.h" + +const uint8_t BAD_BT_BOUND_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN] = + {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; +const uint8_t BAD_BT_EMPTY_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN] = + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +#define TAG "BadBT" +#define WORKER_TAG TAG "Worker" + +#define BADBT_ASCII_TO_KEY(script, x) \ + (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) + +/** + * Delays for waiting between HID key press and key release +*/ +const uint8_t bt_hid_delays[LevelRssiNum] = { + 30, // LevelRssi122_100 + 25, // LevelRssi99_80 + 20, // LevelRssi79_60 + 17, // LevelRssi59_40 + 14, // LevelRssi39_0 +}; + +uint8_t bt_timeout = 0; + +static LevelRssiRange bt_remote_rssi_range(Bt* bt) { + uint8_t rssi; + + if(!bt_remote_rssi(bt, &rssi)) return LevelRssiError; + + if(rssi <= 39) + return LevelRssi39_0; + else if(rssi <= 59) + return LevelRssi59_40; + else if(rssi <= 79) + return LevelRssi79_60; + else if(rssi <= 99) + return LevelRssi99_80; + else if(rssi <= 122) + return LevelRssi122_100; + + return LevelRssiError; +} + +static inline void update_bt_timeout(Bt* bt) { + LevelRssiRange r = bt_remote_rssi_range(bt); + if(r < LevelRssiNum) { + bt_timeout = bt_hid_delays[r]; + FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); + } +} + +typedef enum { + WorkerEvtToggle = (1 << 0), + WorkerEvtEnd = (1 << 1), + WorkerEvtConnect = (1 << 2), + WorkerEvtDisconnect = (1 << 3), +} WorkerEvtFlags; + +static const char ducky_cmd_id[] = {"ID"}; +static const char ducky_cmd_bt_id[] = {"BT_ID"}; + +static const uint8_t numpad_keys[10] = { + HID_KEYPAD_0, + HID_KEYPAD_1, + HID_KEYPAD_2, + HID_KEYPAD_3, + HID_KEYPAD_4, + HID_KEYPAD_5, + HID_KEYPAD_6, + HID_KEYPAD_7, + HID_KEYPAD_8, + HID_KEYPAD_9, +}; + +uint32_t ducky_get_command_len(const char* line) { + uint32_t len = strlen(line); + for(uint32_t i = 0; i < len; i++) { + if(line[i] == ' ') return i; + } + return 0; +} + +bool ducky_is_line_end(const char chr) { + return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); +} + +uint16_t ducky_get_keycode(BadBtScript* bad_bt, const char* param, bool accept_chars) { + uint16_t keycode = ducky_get_keycode_by_name(param); + if(keycode != HID_KEYBOARD_NONE) { + return keycode; + } + + if((accept_chars) && (strlen(param) > 0)) { + return (BADBT_ASCII_TO_KEY(bad_bt, param[0]) & 0xFF); + } + return 0; +} + +bool ducky_get_number(const char* param, uint32_t* val) { + uint32_t value = 0; + if(sscanf(param, "%lu", &value) == 1) { + *val = value; + return true; + } + return false; +} + +void ducky_numlock_on(BadBtScript* bad_bt) { + UNUSED(bad_bt); + if((furi_hal_bt_hid_get_led_state() & HID_KB_LED_NUM) == 0) { + furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); + } +} + +bool ducky_numpad_press(BadBtScript* bad_bt, const char num) { + UNUSED(bad_bt); + if((num < '0') || (num > '9')) return false; + + uint16_t key = numpad_keys[num - '0']; + furi_hal_bt_hid_kb_press(key); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(key); + + return true; +} + +bool ducky_altchar(BadBtScript* bad_bt, const char* charcode) { + uint8_t i = 0; + bool state = false; + + furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT); + + while(!ducky_is_line_end(charcode[i])) { + state = ducky_numpad_press(bad_bt, charcode[i]); + if(state == false) break; + i++; + } + + furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT); + + return state; +} + +bool ducky_altstring(BadBtScript* bad_bt, const char* param) { + uint32_t i = 0; + bool state = false; + + while(param[i] != '\0') { + if((param[i] < ' ') || (param[i] > '~')) { + i++; + continue; // Skip non-printable chars + } + + char temp_str[4]; + snprintf(temp_str, 4, "%u", param[i]); + + state = ducky_altchar(bad_bt, temp_str); + if(state == false) break; + i++; + } + return state; +} + +int32_t ducky_error(BadBtScript* bad_bt, const char* text, ...) { + va_list args; + va_start(args, text); + + vsnprintf(bad_bt->st.error, sizeof(bad_bt->st.error), text, args); + + va_end(args); + return SCRIPT_STATE_ERROR; +} + +bool ducky_string(BadBtScript* bad_bt, const char* param) { + uint32_t i = 0; + + while(param[i] != '\0') { + if(param[i] != '\n') { + uint16_t keycode = BADBT_ASCII_TO_KEY(bad_bt, param[i]); + if(keycode != HID_KEYBOARD_NONE) { + furi_hal_bt_hid_kb_press(keycode); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(keycode); + } + } else { + furi_hal_bt_hid_kb_press(HID_KEYBOARD_RETURN); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(HID_KEYBOARD_RETURN); + } + i++; + } + bad_bt->stringdelay = 0; + return true; +} + +static bool ducky_string_next(BadBtScript* bad_bt) { + if(bad_bt->string_print_pos >= furi_string_size(bad_bt->string_print)) { + return true; + } + + char print_char = furi_string_get_char(bad_bt->string_print, bad_bt->string_print_pos); + + if(print_char != '\n') { + uint16_t keycode = BADBT_ASCII_TO_KEY(bad_bt, print_char); + if(keycode != HID_KEYBOARD_NONE) { + furi_hal_bt_hid_kb_press(keycode); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(keycode); + } + } else { + furi_hal_bt_hid_kb_press(HID_KEYBOARD_RETURN); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(HID_KEYBOARD_RETURN); + } + + bad_bt->string_print_pos++; + + return false; +} + +static int32_t ducky_parse_line(BadBtScript* bad_bt, FuriString* line) { + uint32_t line_len = furi_string_size(line); + const char* line_tmp = furi_string_get_cstr(line); + + if(line_len == 0) { + return SCRIPT_STATE_NEXT_LINE; // Skip empty lines + } + FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); + + // Ducky Lang Functions + int32_t cmd_result = ducky_execute_cmd(bad_bt, line_tmp); + if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) { + return cmd_result; + } + + // Special keys + modifiers + uint16_t key = ducky_get_keycode(bad_bt, line_tmp, false); + if(key == HID_KEYBOARD_NONE) { + return ducky_error(bad_bt, "No keycode defined for %s", line_tmp); + } + if((key & 0xFF00) != 0) { + // It's a modifier key + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + key |= ducky_get_keycode(bad_bt, line_tmp, true); + } + furi_hal_bt_hid_kb_press(key); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(key); + + return 0; +} + +static bool ducky_set_bt_id(BadBtScript* bad_bt, const char* line) { + size_t line_len = strlen(line); + size_t mac_len = BAD_BT_MAC_ADDRESS_LEN * 3; + if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name + + uint8_t mac[BAD_BT_MAC_ADDRESS_LEN]; + for(size_t i = 0; i < BAD_BT_MAC_ADDRESS_LEN; i++) { + char a = line[i * 3]; + char b = line[i * 3 + 1]; + if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') || + (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &mac[i])) { + return false; + } + } + + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, line + mac_len); + bt_set_profile_mac_address(bad_bt->bt, mac); + return true; +} + +static bool ducky_script_preload(BadBtScript* bad_bt, File* script_file) { + uint8_t ret = 0; + uint32_t line_len = 0; + + furi_string_reset(bad_bt->line); + + do { + ret = storage_file_read(script_file, bad_bt->file_buf, FILE_BUFFER_LEN); + for(uint16_t i = 0; i < ret; i++) { + if(bad_bt->file_buf[i] == '\n' && line_len > 0) { + bad_bt->st.line_nb++; + line_len = 0; + } else { + if(bad_bt->st.line_nb == 0) { // Save first line + furi_string_push_back(bad_bt->line, bad_bt->file_buf[i]); + } + line_len++; + } + } + if(storage_file_eof(script_file)) { + if(line_len > 0) { + bad_bt->st.line_nb++; + break; + } + } + } while(ret > 0); + + const char* line_tmp = furi_string_get_cstr(bad_bt->line); + if(bad_bt->app->switch_mode_thread) { + furi_thread_join(bad_bt->app->switch_mode_thread); + furi_thread_free(bad_bt->app->switch_mode_thread); + bad_bt->app->switch_mode_thread = NULL; + } + // Looking for ID or BT_ID command at first line + bad_bt->set_usb_id = false; + bad_bt->set_bt_id = false; + bad_bt->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0; + // TODO: We setting has_usb_id to its value but ignoring it for now and not using anywhere here, may be used in a future to detect script type + bad_bt->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0; + if(bad_bt->has_bt_id) { + if(!bad_bt->app->bt_remember) { + bad_bt->set_bt_id = ducky_set_bt_id(bad_bt, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); + } + } + + bad_kb_config_refresh_menu(bad_bt->app); + + if(!bad_bt->set_bt_id) { + const char* bt_name = bad_bt->app->config.bt_name; + const uint8_t* bt_mac = bad_bt->app->bt_remember ? (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS : + bad_bt->app->config.bt_mac; + bool reset_name = strncmp( + bt_name, + furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard), + BAD_BT_ADV_NAME_MAX_LEN); + bool reset_mac = memcmp( + bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_BT_MAC_ADDRESS_LEN); + if(reset_name && reset_mac) { + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bt_name); + } else if(reset_name) { + bt_set_profile_adv_name(bad_bt->bt, bt_name); + } + if(reset_mac) { + bt_set_profile_mac_address(bad_bt->bt, bt_mac); + } + } + + storage_file_seek(script_file, 0, true); + furi_string_reset(bad_bt->line); + + return true; +} + +static int32_t ducky_script_execute_next(BadBtScript* bad_bt, File* script_file) { + int32_t delay_val = 0; + + if(bad_bt->repeat_cnt > 0) { + bad_bt->repeat_cnt--; + delay_val = ducky_parse_line(bad_bt, bad_bt->line_prev); + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays + return delay_val; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button + return delay_val; + } else if(delay_val < 0) { // Script error + bad_bt->st.error_line = bad_bt->st.line_cur - 1; + FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_bt->st.line_cur - 1U); + return SCRIPT_STATE_ERROR; + } else { + return (delay_val + bad_bt->defdelay); + } + } + + furi_string_set(bad_bt->line_prev, bad_bt->line); + furi_string_reset(bad_bt->line); + + while(1) { + if(bad_bt->buf_len == 0) { + bad_bt->buf_len = storage_file_read(script_file, bad_bt->file_buf, FILE_BUFFER_LEN); + if(storage_file_eof(script_file)) { + if((bad_bt->buf_len < FILE_BUFFER_LEN) && (bad_bt->file_end == false)) { + bad_bt->file_buf[bad_bt->buf_len] = '\n'; + bad_bt->buf_len++; + bad_bt->file_end = true; + } + } + + bad_bt->buf_start = 0; + if(bad_bt->buf_len == 0) return SCRIPT_STATE_END; + } + for(uint8_t i = bad_bt->buf_start; i < (bad_bt->buf_start + bad_bt->buf_len); i++) { + if(bad_bt->file_buf[i] == '\n' && furi_string_size(bad_bt->line) > 0) { + bad_bt->st.line_cur++; + bad_bt->buf_len = bad_bt->buf_len + bad_bt->buf_start - (i + 1); + bad_bt->buf_start = i + 1; + furi_string_trim(bad_bt->line); + delay_val = ducky_parse_line(bad_bt, bad_bt->line); + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays + return delay_val; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button + return delay_val; + } else if(delay_val < 0) { + bad_bt->st.error_line = bad_bt->st.line_cur; + FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_bt->st.line_cur); + return SCRIPT_STATE_ERROR; + } else { + return (delay_val + bad_bt->defdelay); + } + } else { + furi_string_push_back(bad_bt->line, bad_bt->file_buf[i]); + } + } + bad_bt->buf_len = 0; + if(bad_bt->file_end) return SCRIPT_STATE_END; + } + + return 0; +} + +static void bad_bt_bt_hid_state_callback(BtStatus status, void* context) { + furi_assert(context); + BadBtScript* bad_bt = context; + bool state = (status == BtStatusConnected); + + if(state == true) { + LevelRssiRange r = bt_remote_rssi_range(bad_bt->bt); + if(r != LevelRssiError) { + bt_timeout = bt_hid_delays[r]; + } + furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtConnect); + } else { + furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtDisconnect); + } +} + +static uint32_t bad_bt_flags_get(uint32_t flags_mask, uint32_t timeout) { + uint32_t flags = furi_thread_flags_get(); + furi_check((flags & FuriFlagError) == 0); + if(flags == 0) { + flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout); + furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout)); + } else { + uint32_t state = furi_thread_flags_clear(flags); + furi_check((state & FuriFlagError) == 0); + } + return flags; +} + +static int32_t bad_bt_worker(void* context) { + BadBtScript* bad_bt = context; + + BadBtWorkerState worker_state = BadBtStateInit; + int32_t delay_val = 0; + + FURI_LOG_I(WORKER_TAG, "Init"); + File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + bad_bt->line = furi_string_alloc(); + bad_bt->line_prev = furi_string_alloc(); + bad_bt->string_print = furi_string_alloc(); + + bt_set_status_changed_callback(bad_bt->bt, bad_bt_bt_hid_state_callback, bad_bt); + + while(1) { + if(worker_state == BadBtStateInit) { // State: initialization + if(storage_file_open( + script_file, + furi_string_get_cstr(bad_bt->file_path), + FSAM_READ, + FSOM_OPEN_EXISTING)) { + if((ducky_script_preload(bad_bt, script_file)) && (bad_bt->st.line_nb > 0)) { + if(furi_hal_bt_is_connected()) { + worker_state = BadBtStateIdle; // Ready to run + } else { + worker_state = BadBtStateNotConnected; // Not connected + } + + } else { + worker_state = BadBtStateScriptError; // Script preload error + } + } else { + FURI_LOG_E(WORKER_TAG, "File open error"); + worker_state = BadBtStateFileError; // File open error + } + bad_bt->st.state = worker_state; + + } else if(worker_state == BadBtStateNotConnected) { // State: Not connected + uint32_t flags = bad_bt_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { + worker_state = BadBtStateIdle; // Ready to run + } else if(flags & WorkerEvtToggle) { + worker_state = BadBtStateWillRun; // Will run when connected + } + bad_bt->st.state = worker_state; + + } else if(worker_state == BadBtStateIdle) { // State: ready to start + uint32_t flags = bad_bt_flags_get( + WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriWaitForever); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtToggle) { // Start executing script + delay_val = 0; + bad_bt->buf_len = 0; + bad_bt->st.line_cur = 0; + bad_bt->defdelay = 0; + bad_bt->stringdelay = 0; + bad_bt->repeat_cnt = 0; + bad_bt->key_hold_nb = 0; + bad_bt->file_end = false; + storage_file_seek(script_file, 0, true); + bad_bt_script_set_keyboard_layout(bad_bt, bad_bt->keyboard_layout); + worker_state = BadBtStateRunning; + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadBtStateNotConnected; // Disconnected + } + bad_bt->st.state = worker_state; + + } else if(worker_state == BadBtStateWillRun) { // State: start on connection + uint32_t flags = bad_bt_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriWaitForever); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { // Start executing script + delay_val = 0; + bad_bt->buf_len = 0; + bad_bt->st.line_cur = 0; + bad_bt->defdelay = 0; + bad_bt->stringdelay = 0; + bad_bt->repeat_cnt = 0; + bad_bt->file_end = false; + storage_file_seek(script_file, 0, true); + // extra time for PC to recognize Flipper as keyboard + flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtToggle, + FuriFlagWaitAny | FuriFlagNoClear, + 1500); + if(flags == (unsigned)FuriFlagErrorTimeout) { + // If nothing happened - start script execution + worker_state = BadBtStateRunning; + } else if(flags & WorkerEvtToggle) { + worker_state = BadBtStateIdle; + furi_thread_flags_clear(WorkerEvtToggle); + } + + update_bt_timeout(bad_bt->bt); + + bad_bt_script_set_keyboard_layout(bad_bt, bad_bt->keyboard_layout); + } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution + worker_state = BadBtStateNotConnected; + } + bad_bt->st.state = worker_state; + + } else if(worker_state == BadBtStateRunning) { // State: running + uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); + + delay_val -= delay_cur; + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtToggle) { + worker_state = BadBtStateIdle; // Stop executing script + + furi_hal_bt_hid_kb_release_all(); + + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadBtStateNotConnected; // Disconnected + + furi_hal_bt_hid_kb_release_all(); + } + bad_bt->st.state = worker_state; + continue; + } else if( + (flags == (unsigned)FuriFlagErrorTimeout) || + (flags == (unsigned)FuriFlagErrorResource)) { + if(delay_val > 0) { + bad_bt->st.delay_remain--; + continue; + } + bad_bt->st.state = BadBtStateRunning; + delay_val = ducky_script_execute_next(bad_bt, script_file); + if(delay_val == SCRIPT_STATE_ERROR) { // Script error + delay_val = 0; + worker_state = BadBtStateScriptError; + bad_bt->st.state = worker_state; + + furi_hal_bt_hid_kb_release_all(); + + } else if(delay_val == SCRIPT_STATE_END) { // End of script + delay_val = 0; + worker_state = BadBtStateIdle; + bad_bt->st.state = BadBtStateDone; + + furi_hal_bt_hid_kb_release_all(); + + continue; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays + delay_val = bad_bt->defdelay; + bad_bt->string_print_pos = 0; + worker_state = BadBtStateStringDelay; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input + worker_state = BadBtStateWaitForBtn; + bad_bt->st.state = BadBtStateWaitForBtn; // Show long delays + } else if(delay_val > 1000) { + bad_bt->st.state = BadBtStateDelay; // Show long delays + bad_bt->st.delay_remain = delay_val / 1000; + } + } else { + furi_check((flags & FuriFlagError) == 0); + } + } else if(worker_state == BadBtStateWaitForBtn) { // State: Wait for button Press + uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtToggle) { + delay_val = 0; + worker_state = BadBtStateRunning; + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadBtStateNotConnected; // Disconnected + furi_hal_hid_kb_release_all(); + } + bad_bt->st.state = worker_state; + continue; + } + } else if(worker_state == BadBtStateStringDelay) { // State: print string with delays + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, + FuriFlagWaitAny, + bad_bt->stringdelay); + + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtToggle) { + worker_state = BadBtStateIdle; // Stop executing script + + furi_hal_bt_hid_kb_release_all(); + + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadBtStateNotConnected; // Disconnected + + furi_hal_bt_hid_kb_release_all(); + } + bad_bt->st.state = worker_state; + continue; + } else if( + (flags == (unsigned)FuriFlagErrorTimeout) || + (flags == (unsigned)FuriFlagErrorResource)) { + bool string_end = ducky_string_next(bad_bt); + if(string_end) { + bad_bt->stringdelay = 0; + worker_state = BadBtStateRunning; + } + } else { + furi_check((flags & FuriFlagError) == 0); + } + } else if( + (worker_state == BadBtStateFileError) || + (worker_state == BadBtStateScriptError)) { // State: error + uint32_t flags = + bad_bt_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command + + if(flags & WorkerEvtEnd) { + break; + } + } + + update_bt_timeout(bad_bt->bt); + } + + bt_set_status_changed_callback(bad_bt->bt, NULL, NULL); + + storage_file_close(script_file); + storage_file_free(script_file); + furi_string_free(bad_bt->line); + furi_string_free(bad_bt->line_prev); + furi_string_free(bad_bt->string_print); + + FURI_LOG_I(WORKER_TAG, "End"); + + return 0; +} + +static void bad_bt_script_set_default_keyboard_layout(BadBtScript* bad_bt) { + furi_assert(bad_bt); + furi_string_set_str(bad_bt->keyboard_layout, ""); + memset(bad_bt->layout, HID_KEYBOARD_NONE, sizeof(bad_bt->layout)); + memcpy(bad_bt->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_bt->layout))); +} + +BadBtScript* bad_bt_script_open(FuriString* file_path, Bt* bt, BadBtApp* app) { + furi_assert(file_path); + + BadBtScript* bad_bt = malloc(sizeof(BadBtScript)); + bad_bt->app = app; + bad_bt->file_path = furi_string_alloc(); + furi_string_set(bad_bt->file_path, file_path); + bad_bt->keyboard_layout = furi_string_alloc(); + bad_bt_script_set_default_keyboard_layout(bad_bt); + + bad_bt->st.state = BadBtStateInit; + bad_bt->st.error[0] = '\0'; + + bad_bt->bt = bt; + + bad_bt->thread = furi_thread_alloc_ex("BadBtWorker", 2048, bad_bt_worker, bad_bt); + furi_thread_start(bad_bt->thread); + return bad_bt; +} + +void bad_bt_script_close(BadBtScript* bad_bt) { + furi_assert(bad_bt); + furi_record_close(RECORD_STORAGE); + furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtEnd); + furi_thread_join(bad_bt->thread); + furi_thread_free(bad_bt->thread); + furi_string_free(bad_bt->file_path); + furi_string_free(bad_bt->keyboard_layout); + free(bad_bt); +} + +void bad_bt_script_set_keyboard_layout(BadBtScript* bad_bt, FuriString* layout_path) { + furi_assert(bad_bt); + + if((bad_bt->st.state == BadBtStateRunning) || (bad_bt->st.state == BadBtStateDelay)) { + // do not update keyboard layout while a script is running + return; + } + + File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(!furi_string_empty(layout_path)) { //-V1051 + furi_string_set(bad_bt->keyboard_layout, layout_path); + if(storage_file_open( + layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint16_t layout[128]; + if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { + memcpy(bad_bt->layout, layout, sizeof(layout)); + } + } + storage_file_close(layout_file); + } else { + bad_bt_script_set_default_keyboard_layout(bad_bt); + } + storage_file_free(layout_file); +} + +void bad_bt_script_toggle(BadBtScript* bad_bt) { + furi_assert(bad_bt); + furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtToggle); +} + +BadBtState* bad_bt_script_get_state(BadBtScript* bad_bt) { + furi_assert(bad_bt); + return &(bad_bt->st); +} \ No newline at end of file diff --git a/applications/external/bad_bt/helpers/ducky_script.h b/applications/external/bad_bt/helpers/ducky_script.h new file mode 100644 index 000000000..ea0f91040 --- /dev/null +++ b/applications/external/bad_bt/helpers/ducky_script.h @@ -0,0 +1,154 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include "../views/bad_bt_view.h" + +#define FILE_BUFFER_LEN 16 + +typedef enum { + LevelRssi122_100, + LevelRssi99_80, + LevelRssi79_60, + LevelRssi59_40, + LevelRssi39_0, + LevelRssiNum, + LevelRssiError = 0xFF, +} LevelRssiRange; + +extern const uint8_t bt_hid_delays[LevelRssiNum]; + +extern uint8_t bt_timeout; + +typedef enum { + BadBtStateInit, + BadBtStateNotConnected, + BadBtStateIdle, + BadBtStateWillRun, + BadBtStateRunning, + BadBtStateDelay, + BadBtStateStringDelay, + BadBtStateWaitForBtn, + BadBtStateDone, + BadBtStateScriptError, + BadBtStateFileError, +} BadBtWorkerState; + +struct BadBtState { + BadBtWorkerState state; + uint32_t pin; + uint16_t line_cur; + uint16_t line_nb; + uint32_t delay_remain; + uint16_t error_line; + char error[64]; +}; + +typedef struct BadBtApp BadBtApp; + +typedef struct { + FuriHalUsbHidConfig hid_cfg; + FuriThread* thread; + BadBtState st; + + FuriString* file_path; + FuriString* keyboard_layout; + uint8_t file_buf[FILE_BUFFER_LEN + 1]; + uint8_t buf_start; + uint8_t buf_len; + bool file_end; + + uint32_t defdelay; + uint32_t stringdelay; + uint16_t layout[128]; + + FuriString* line; + FuriString* line_prev; + uint32_t repeat_cnt; + uint8_t key_hold_nb; + + bool set_usb_id; + bool set_bt_id; + bool has_usb_id; + bool has_bt_id; + + FuriString* string_print; + size_t string_print_pos; + + Bt* bt; + BadBtApp* app; +} BadBtScript; + +BadBtScript* bad_bt_script_open(FuriString* file_path, Bt* bt, BadBtApp* app); + +void bad_bt_script_close(BadBtScript* bad_bt); + +void bad_bt_script_set_keyboard_layout(BadBtScript* bad_bt, FuriString* layout_path); + +void bad_bt_script_start(BadBtScript* bad_bt); + +void bad_bt_script_stop(BadBtScript* bad_bt); + +void bad_bt_script_toggle(BadBtScript* bad_bt); + +BadBtState* bad_bt_script_get_state(BadBtScript* bad_bt); + +#define BAD_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH +#define BAD_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE + +// this is the MAC address used when we do not forget paired device (BOUND STATE) +extern const uint8_t BAD_BT_BOUND_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN]; +extern const uint8_t BAD_BT_EMPTY_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN]; + +typedef enum { + BadBtAppErrorNoFiles, + BadBtAppErrorCloseRpc, +} BadBtAppError; + +typedef struct { + char bt_name[BAD_BT_ADV_NAME_MAX_LEN]; + uint8_t bt_mac[BAD_BT_MAC_ADDRESS_LEN]; + GapPairing bt_mode; +} BadBtConfig; + +struct BadBtApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + DialogsApp* dialogs; + Widget* widget; + VariableItemList* var_item_list; + TextInput* text_input; + ByteInput* byte_input; + + BadBtAppError error; + FuriString* file_path; + FuriString* keyboard_layout; + BadBt* bad_bt_view; + BadBtScript* bad_bt_script; + + Bt* bt; + bool bt_remember; + BadBtConfig config; + BadBtConfig prev_config; + FuriThread* conn_init_thread; + FuriThread* switch_mode_thread; +}; + +int32_t bad_bt_config_switch_mode(BadBtApp* app); + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/bad_bt/helpers/ducky_script_commands.c b/applications/external/bad_bt/helpers/ducky_script_commands.c new file mode 100644 index 000000000..eddab96ac --- /dev/null +++ b/applications/external/bad_bt/helpers/ducky_script_commands.c @@ -0,0 +1,201 @@ +#include +#include +#include "ducky_script.h" +#include "ducky_script_i.h" + +typedef int32_t (*DuckyCmdCallback)(BadBtScript* bad_bt, const char* line, int32_t param); + +typedef struct { + char* name; + DuckyCmdCallback callback; + int32_t param; +} DuckyCmd; + +static int32_t ducky_fnc_delay(BadBtScript* bad_bt, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint32_t delay_val = 0; + bool state = ducky_get_number(line, &delay_val); + if((state) && (delay_val > 0)) { + return (int32_t)delay_val; + } + + return ducky_error(bad_bt, "Invalid number %s", line); +} + +static int32_t ducky_fnc_defdelay(BadBtScript* bad_bt, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_bt->defdelay); + if(!state) { + return ducky_error(bad_bt, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_strdelay(BadBtScript* bad_bt, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_bt->stringdelay); + if(!state) { + return ducky_error(bad_bt, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_string(BadBtScript* bad_bt, const char* line, int32_t param) { + line = &line[ducky_get_command_len(line) + 1]; + furi_string_set_str(bad_bt->string_print, line); + if(param == 1) { + furi_string_cat(bad_bt->string_print, "\n"); + } + + if(bad_bt->stringdelay == 0) { // stringdelay not set - run command immidiately + bool state = ducky_string(bad_bt, furi_string_get_cstr(bad_bt->string_print)); + if(!state) { + return ducky_error(bad_bt, "Invalid string %s", line); + } + } else { // stringdelay is set - run command in thread to keep handling external events + return SCRIPT_STATE_STRING_START; + } + + return 0; +} + +static int32_t ducky_fnc_repeat(BadBtScript* bad_bt, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_bt->repeat_cnt); + if((!state) || (bad_bt->repeat_cnt == 0)) { + return ducky_error(bad_bt, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_sysrq(BadBtScript* bad_bt, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_keycode(bad_bt, line, true); + + furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + furi_hal_bt_hid_kb_press(key); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(key); + furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + return 0; +} + +static int32_t ducky_fnc_altchar(BadBtScript* bad_bt, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + ducky_numlock_on(bad_bt); + bool state = ducky_altchar(bad_bt, line); + if(!state) { + return ducky_error(bad_bt, "Invalid altchar %s", line); + } + return 0; +} + +static int32_t ducky_fnc_altstring(BadBtScript* bad_bt, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + ducky_numlock_on(bad_bt); + bool state = ducky_altstring(bad_bt, line); + if(!state) { + return ducky_error(bad_bt, "Invalid altstring %s", line); + } + return 0; +} + +static int32_t ducky_fnc_hold(BadBtScript* bad_bt, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_keycode(bad_bt, line, true); + if(key == HID_KEYBOARD_NONE) { + return ducky_error(bad_bt, "No keycode defined for %s", line); + } + bad_bt->key_hold_nb++; + if(bad_bt->key_hold_nb > (HID_KB_MAX_KEYS - 1)) { + return ducky_error(bad_bt, "Too many keys are hold"); + } + furi_hal_bt_hid_kb_press(key); + + return 0; +} + +static int32_t ducky_fnc_release(BadBtScript* bad_bt, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_keycode(bad_bt, line, true); + if(key == HID_KEYBOARD_NONE) { + return ducky_error(bad_bt, "No keycode defined for %s", line); + } + if(bad_bt->key_hold_nb == 0) { + return ducky_error(bad_bt, "No keys are hold"); + } + bad_bt->key_hold_nb--; + furi_hal_bt_hid_kb_release(key); + return 0; +} + +static int32_t ducky_fnc_waitforbutton(BadBtScript* bad_bt, const char* line, int32_t param) { + UNUSED(param); + UNUSED(bad_bt); + UNUSED(line); + + return SCRIPT_STATE_WAIT_FOR_BTN; +} + +static const DuckyCmd ducky_commands[] = { + {"REM", NULL, -1}, + {"ID", NULL, -1}, + {"BT_ID", NULL, -1}, + {"DELAY", ducky_fnc_delay, -1}, + {"STRING", ducky_fnc_string, 0}, + {"STRINGLN", ducky_fnc_string, 1}, + {"DEFAULT_DELAY", ducky_fnc_defdelay, -1}, + {"DEFAULTDELAY", ducky_fnc_defdelay, -1}, + {"STRINGDELAY", ducky_fnc_strdelay, -1}, + {"STRING_DELAY", ducky_fnc_strdelay, -1}, + {"REPEAT", ducky_fnc_repeat, -1}, + {"SYSRQ", ducky_fnc_sysrq, -1}, + {"ALTCHAR", ducky_fnc_altchar, -1}, + {"ALTSTRING", ducky_fnc_altstring, -1}, + {"ALTCODE", ducky_fnc_altstring, -1}, + {"HOLD", ducky_fnc_hold, -1}, + {"RELEASE", ducky_fnc_release, -1}, + {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, +}; + +#define TAG "BadBT" +#define WORKER_TAG TAG "Worker" + +int32_t ducky_execute_cmd(BadBtScript* bad_bt, const char* line) { + size_t cmd_word_len = strcspn(line, " "); + for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { + size_t cmd_compare_len = strlen(ducky_commands[i].name); + + if(cmd_compare_len != cmd_word_len) { + continue; + } + + if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) { + if(ducky_commands[i].callback == NULL) { + return 0; + } else { + return ((ducky_commands[i].callback)(bad_bt, line, ducky_commands[i].param)); + } + } + } + + return SCRIPT_STATE_CMD_UNKNOWN; +} diff --git a/applications/external/bad_bt/helpers/ducky_script_i.h b/applications/external/bad_bt/helpers/ducky_script_i.h new file mode 100644 index 000000000..08afa65a4 --- /dev/null +++ b/applications/external/bad_bt/helpers/ducky_script_i.h @@ -0,0 +1,44 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "ducky_script.h" + +#define SCRIPT_STATE_ERROR (-1) +#define SCRIPT_STATE_END (-2) +#define SCRIPT_STATE_NEXT_LINE (-3) +#define SCRIPT_STATE_CMD_UNKNOWN (-4) +#define SCRIPT_STATE_STRING_START (-5) +#define SCRIPT_STATE_WAIT_FOR_BTN (-6) + +uint16_t ducky_get_keycode(BadBtScript* bad_bt, const char* param, bool accept_chars); + +uint32_t ducky_get_command_len(const char* line); + +bool ducky_is_line_end(const char chr); + +uint16_t ducky_get_keycode_by_name(const char* param); + +bool ducky_get_number(const char* param, uint32_t* val); + +void ducky_numlock_on(BadBtScript* bad_bt); + +bool ducky_numpad_press(BadBtScript* bad_bt, const char num); + +bool ducky_altchar(BadBtScript* bad_bt, const char* charcode); + +bool ducky_altstring(BadBtScript* bad_bt, const char* param); + +bool ducky_string(BadBtScript* bad_bt, const char* param); + +int32_t ducky_execute_cmd(BadBtScript* bad_bt, const char* line); + +int32_t ducky_error(BadBtScript* bad_bt, const char* text, ...); + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/bad_bt/helpers/ducky_script_keycodes.c b/applications/external/bad_bt/helpers/ducky_script_keycodes.c new file mode 100644 index 000000000..55c52810f --- /dev/null +++ b/applications/external/bad_bt/helpers/ducky_script_keycodes.c @@ -0,0 +1,78 @@ +#include +#include "ducky_script_i.h" + +typedef struct { + char* name; + uint16_t keycode; +} DuckyKey; + +static const DuckyKey ducky_keys[] = { + {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, + {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, + {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, + {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, + {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, + {"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL}, + + {"CTRL", KEY_MOD_LEFT_CTRL}, + {"CONTROL", KEY_MOD_LEFT_CTRL}, + {"SHIFT", KEY_MOD_LEFT_SHIFT}, + {"ALT", KEY_MOD_LEFT_ALT}, + {"GUI", KEY_MOD_LEFT_GUI}, + {"WINDOWS", KEY_MOD_LEFT_GUI}, + + {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW}, + {"DOWN", HID_KEYBOARD_DOWN_ARROW}, + {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW}, + {"LEFT", HID_KEYBOARD_LEFT_ARROW}, + {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW}, + {"RIGHT", HID_KEYBOARD_RIGHT_ARROW}, + {"UPARROW", HID_KEYBOARD_UP_ARROW}, + {"UP", HID_KEYBOARD_UP_ARROW}, + + {"ENTER", HID_KEYBOARD_RETURN}, + {"BREAK", HID_KEYBOARD_PAUSE}, + {"PAUSE", HID_KEYBOARD_PAUSE}, + {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, + {"DELETE", HID_KEYBOARD_DELETE_FORWARD}, + {"BACKSPACE", HID_KEYBOARD_DELETE}, + {"END", HID_KEYBOARD_END}, + {"ESC", HID_KEYBOARD_ESCAPE}, + {"ESCAPE", HID_KEYBOARD_ESCAPE}, + {"HOME", HID_KEYBOARD_HOME}, + {"INSERT", HID_KEYBOARD_INSERT}, + {"NUMLOCK", HID_KEYPAD_NUMLOCK}, + {"PAGEUP", HID_KEYBOARD_PAGE_UP}, + {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, + {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, + {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK}, + {"SPACE", HID_KEYBOARD_SPACEBAR}, + {"TAB", HID_KEYBOARD_TAB}, + {"MENU", HID_KEYBOARD_APPLICATION}, + {"APP", HID_KEYBOARD_APPLICATION}, + + {"F1", HID_KEYBOARD_F1}, + {"F2", HID_KEYBOARD_F2}, + {"F3", HID_KEYBOARD_F3}, + {"F4", HID_KEYBOARD_F4}, + {"F5", HID_KEYBOARD_F5}, + {"F6", HID_KEYBOARD_F6}, + {"F7", HID_KEYBOARD_F7}, + {"F8", HID_KEYBOARD_F8}, + {"F9", HID_KEYBOARD_F9}, + {"F10", HID_KEYBOARD_F10}, + {"F11", HID_KEYBOARD_F11}, + {"F12", HID_KEYBOARD_F12}, +}; + +uint16_t ducky_get_keycode_by_name(const char* param) { + for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) { + size_t key_cmd_len = strlen(ducky_keys[i].name); + if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && + (ducky_is_line_end(param[key_cmd_len]))) { + return ducky_keys[i].keycode; + } + } + + return HID_KEYBOARD_NONE; +} diff --git a/applications/external/bad_bt/images/badbt_10px.png b/applications/external/bad_bt/images/badbt_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..037474aa3bc9c2e1aca79a68483e69980432bcf5 GIT binary patch literal 576 zcmV-G0>AxEX>4Tx04R}tkv&MmKpe$i(`rSk4t5Z6$WWau6cusQDionYs1;guFuC*#nlvOW zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0Yan9G%FATG`(u3 z5^*t;T@{0`5D-8=V(6BcWz0!Z5}xDh9zMR_MR}I@xj#prnzI<-6NzV;VOEJZh^IHJ z2Iqa^Fe}O`@j3ChNf#u3C`-Nm{=@yu+qV-Xlle$#1U1~DPPFA zta9Gstd(o5bx;1nP)=W2<~q$0B(R7jND!f*h7!uCB1)@HiiH&I$36VRj$a~|Laq`R zITlcX2HEk0|H1EWt^DMKn-q!zT`#u%F$x5Cfo9#dzmILZc>?&Kfh)c3uQY&}Ptxmc zEph}5Yy%h9ZB5w&E_Z;TCqp)6NAlAY@_FF>jJ_!g4Bi60Yi@6?eVjf3Y3eF@0~{Oz zV+G1y_jq?tXK(+WY4!I5C=YUpXXIhH00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3ljhU3ljkVnw%H_000McNliru<^lu`HWXkp{t5s906j@WK~xyijZi@f05Awj>HlAL zr$MwDdI>{Qf+U53tOUR#xOeyy)jcQo#JNRv)7r6DVVK|+*(cmT+R+EbO(O#X#REG4 O0000 + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) BadBtScene##id, +typedef enum { +#include "bad_bt_scene_config.h" + BadBtSceneNum, +} BadBtScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers bad_bt_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "bad_bt_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 "bad_bt_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 "bad_bt_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_config.c b/applications/external/bad_bt/scenes/bad_bt_scene_config.c new file mode 100644 index 000000000..5fc9c0012 --- /dev/null +++ b/applications/external/bad_bt/scenes/bad_bt_scene_config.c @@ -0,0 +1,104 @@ +#include "../bad_bt_app.h" +#include "../helpers/ducky_script.h" +#include "furi_hal_power.h" + +enum VarItemListIndex { + VarItemListIndexKeyboardLayout, + VarItemListIndexBtRemember, + VarItemListIndexBtDeviceName, + VarItemListIndexBtMacAddress, + VarItemListIndexRandomizeBtMac, +}; + +void bad_bt_scene_config_bt_remember_callback(VariableItem* item) { + BadBtApp* bad_bt = variable_item_get_context(item); + bad_bt->bt_remember = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, bad_bt->bt_remember ? "ON" : "OFF"); + view_dispatcher_send_custom_event(bad_bt->view_dispatcher, VarItemListIndexBtRemember); +} + +void bad_bt_scene_config_var_item_list_callback(void* context, uint32_t index) { + BadBtApp* bad_bt = context; + view_dispatcher_send_custom_event(bad_bt->view_dispatcher, index); +} + +void bad_bt_scene_config_on_enter(void* context) { + BadBtApp* bad_bt = context; + VariableItemList* var_item_list = bad_bt->var_item_list; + VariableItem* item; + + item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_bt); + + item = variable_item_list_add( + var_item_list, "BT Remember", 2, bad_bt_scene_config_bt_remember_callback, bad_bt); + variable_item_set_current_value_index(item, bad_bt->bt_remember); + variable_item_set_current_value_text(item, bad_bt->bt_remember ? "ON" : "OFF"); + + item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_bt); + if(bad_bt->bad_bt_script->set_bt_id) { + variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset Name!"); + } + + item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_bt); + if(bad_bt->bt_remember) { + variable_item_set_locked(item, true, "Remember\nmust be Off!"); + } else if(bad_bt->bad_bt_script->set_bt_id) { + variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); + } + + item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_bt); + if(bad_bt->bt_remember) { + variable_item_set_locked(item, true, "Remember\nmust be Off!"); + } else if(bad_bt->bad_bt_script->set_bt_id) { + variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); + } + + variable_item_list_set_enter_callback( + var_item_list, bad_bt_scene_config_var_item_list_callback, bad_bt); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(bad_bt->scene_manager, BadBtSceneConfig)); + + view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfig); +} + +bool bad_bt_scene_config_on_event(void* context, SceneManagerEvent event) { + BadBtApp* bad_bt = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_bt->scene_manager, BadBtSceneConfig, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexKeyboardLayout: + scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigLayout); + break; + case VarItemListIndexBtRemember: + bad_bt_config_switch_remember_mode(bad_bt); + scene_manager_previous_scene(bad_bt->scene_manager); + scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfig); + break; + case VarItemListIndexBtDeviceName: + scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigName); + break; + case VarItemListIndexBtMacAddress: + scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigMac); + break; + case VarItemListIndexRandomizeBtMac: + furi_hal_random_fill_buf(bad_bt->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN); + bt_set_profile_mac_address(bad_bt->bt, bad_bt->config.bt_mac); + break; + default: + break; + } + } + + return consumed; +} + +void bad_bt_scene_config_on_exit(void* context) { + BadBtApp* bad_bt = context; + VariableItemList* var_item_list = bad_bt->var_item_list; + + variable_item_list_reset(var_item_list); +} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_config.h b/applications/external/bad_bt/scenes/bad_bt_scene_config.h new file mode 100644 index 000000000..f7914e6dd --- /dev/null +++ b/applications/external/bad_bt/scenes/bad_bt_scene_config.h @@ -0,0 +1,7 @@ +ADD_SCENE(bad_bt, file_select, FileSelect) +ADD_SCENE(bad_bt, work, Work) +ADD_SCENE(bad_bt, error, Error) +ADD_SCENE(bad_bt, config, Config) +ADD_SCENE(bad_bt, config_layout, ConfigLayout) +ADD_SCENE(bad_bt, config_name, ConfigName) +ADD_SCENE(bad_bt, config_mac, ConfigMac) diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_config_layout.c b/applications/external/bad_bt/scenes/bad_bt_scene_config_layout.c new file mode 100644 index 000000000..b0ce2d084 --- /dev/null +++ b/applications/external/bad_bt/scenes/bad_bt_scene_config_layout.c @@ -0,0 +1,47 @@ +#include "../bad_bt_app.h" +#include "furi_hal_power.h" +#include + +static bool bad_bt_layout_select(BadBtApp* bad_bt) { + furi_assert(bad_bt); + + FuriString* predefined_path; + predefined_path = furi_string_alloc(); + if(!furi_string_empty(bad_bt->keyboard_layout)) { + furi_string_set(predefined_path, bad_bt->keyboard_layout); + } else { + furi_string_set(predefined_path, BAD_BT_APP_PATH_LAYOUT_FOLDER); + } + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_BT_APP_LAYOUT_EXTENSION, &I_keyboard_10px); + browser_options.base_path = BAD_BT_APP_PATH_LAYOUT_FOLDER; + browser_options.skip_assets = false; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_bt->dialogs, bad_bt->keyboard_layout, predefined_path, &browser_options); + + furi_string_free(predefined_path); + return res; +} + +void bad_bt_scene_config_layout_on_enter(void* context) { + BadBtApp* bad_bt = context; + + if(bad_bt_layout_select(bad_bt)) { + bad_bt_script_set_keyboard_layout(bad_bt->bad_bt_script, bad_bt->keyboard_layout); + } + scene_manager_previous_scene(bad_bt->scene_manager); +} + +bool bad_bt_scene_config_layout_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void bad_bt_scene_config_layout_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_config_mac.c b/applications/external/bad_bt/scenes/bad_bt_scene_config_mac.c new file mode 100644 index 000000000..47f63e08c --- /dev/null +++ b/applications/external/bad_bt/scenes/bad_bt_scene_config_mac.c @@ -0,0 +1,47 @@ +#include "../bad_bt_app.h" + +#define TAG "BadBtConfigMac" + +void bad_bt_scene_config_mac_byte_input_callback(void* context) { + BadBtApp* bad_bt = context; + + view_dispatcher_send_custom_event(bad_bt->view_dispatcher, BadBtAppCustomEventByteInputDone); +} + +void bad_bt_scene_config_mac_on_enter(void* context) { + BadBtApp* bad_bt = context; + + // Setup view + ByteInput* byte_input = bad_bt->byte_input; + byte_input_set_header_text(byte_input, "Set BT MAC address"); + byte_input_set_result_callback( + byte_input, + bad_bt_scene_config_mac_byte_input_callback, + NULL, + bad_bt, + bad_bt->config.bt_mac, + GAP_MAC_ADDR_SIZE); + view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfigMac); +} + +bool bad_bt_scene_config_mac_on_event(void* context, SceneManagerEvent event) { + BadBtApp* bad_bt = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == BadBtAppCustomEventByteInputDone) { + bt_set_profile_mac_address(bad_bt->bt, bad_bt->config.bt_mac); + scene_manager_previous_scene(bad_bt->scene_manager); + consumed = true; + } + } + return consumed; +} + +void bad_bt_scene_config_mac_on_exit(void* context) { + BadBtApp* bad_bt = context; + + // Clear view + byte_input_set_result_callback(bad_bt->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(bad_bt->byte_input, ""); +} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_config_name.c b/applications/external/bad_bt/scenes/bad_bt_scene_config_name.c new file mode 100644 index 000000000..61d198b8c --- /dev/null +++ b/applications/external/bad_bt/scenes/bad_bt_scene_config_name.c @@ -0,0 +1,45 @@ +#include "../bad_bt_app.h" + +static void bad_bt_scene_config_name_text_input_callback(void* context) { + BadBtApp* bad_bt = context; + + view_dispatcher_send_custom_event(bad_bt->view_dispatcher, BadBtAppCustomEventTextEditResult); +} + +void bad_bt_scene_config_name_on_enter(void* context) { + BadBtApp* bad_bt = context; + TextInput* text_input = bad_bt->text_input; + + text_input_set_header_text(text_input, "Set BT device name"); + + text_input_set_result_callback( + text_input, + bad_bt_scene_config_name_text_input_callback, + bad_bt, + bad_bt->config.bt_name, + BAD_BT_ADV_NAME_MAX_LEN, + true); + + view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfigName); +} + +bool bad_bt_scene_config_name_on_event(void* context, SceneManagerEvent event) { + BadBtApp* bad_bt = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadBtAppCustomEventTextEditResult) { + bt_set_profile_adv_name(bad_bt->bt, bad_bt->config.bt_name); + } + scene_manager_previous_scene(bad_bt->scene_manager); + } + return consumed; +} + +void bad_bt_scene_config_name_on_exit(void* context) { + BadBtApp* bad_bt = context; + TextInput* text_input = bad_bt->text_input; + + text_input_reset(text_input); +} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_error.c b/applications/external/bad_bt/scenes/bad_bt_scene_error.c new file mode 100644 index 000000000..e25703e7d --- /dev/null +++ b/applications/external/bad_bt/scenes/bad_bt_scene_error.c @@ -0,0 +1,61 @@ +#include "../bad_bt_app.h" + +static void + bad_bt_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + BadBtApp* app = context; + + if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event(app->view_dispatcher, BadBtCustomEventErrorBack); + } +} + +void bad_bt_scene_error_on_enter(void* context) { + BadBtApp* app = context; + + if(app->error == BadBtAppErrorNoFiles) { + widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); + widget_add_string_multiline_element( + app->widget, + 81, + 4, + AlignCenter, + AlignTop, + FontSecondary, + "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", bad_bt_scene_error_event_callback, app); + } else if(app->error == BadBtAppErrorCloseRpc) { + widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); + widget_add_string_multiline_element( + app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); + widget_add_string_multiline_element( + app->widget, + 3, + 30, + AlignLeft, + AlignTop, + FontSecondary, + "Disconnect from\nPC or phone to\nuse this function."); + } + + view_dispatcher_switch_to_view(app->view_dispatcher, BadBtAppViewError); +} + +bool bad_bt_scene_error_on_event(void* context, SceneManagerEvent event) { + BadBtApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == BadBtCustomEventErrorBack) { + view_dispatcher_stop(app->view_dispatcher); + consumed = true; + } + } + return consumed; +} + +void bad_bt_scene_error_on_exit(void* context) { + BadBtApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_file_select.c b/applications/external/bad_bt/scenes/bad_bt_scene_file_select.c new file mode 100644 index 000000000..b86dc6d71 --- /dev/null +++ b/applications/external/bad_bt/scenes/bad_bt_scene_file_select.c @@ -0,0 +1,49 @@ +#include "../bad_bt_app.h" +#include +#include + +static bool bad_bt_file_select(BadBtApp* bad_bt) { + furi_assert(bad_bt); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_BT_APP_SCRIPT_EXTENSION, &I_badbt_10px); + browser_options.base_path = BAD_BT_APP_BASE_FOLDER; + browser_options.skip_assets = true; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_bt->dialogs, bad_bt->file_path, bad_bt->file_path, &browser_options); + + return res; +} + +void bad_bt_scene_file_select_on_enter(void* context) { + BadBtApp* bad_bt = context; + + if(bad_bt->bad_bt_script) { + bad_bt_script_close(bad_bt->bad_bt_script); + bad_bt->bad_bt_script = NULL; + } + + if(bad_bt_file_select(bad_bt)) { + bad_bt->bad_bt_script = bad_bt_script_open(bad_bt->file_path, bad_bt->bt, bad_bt); + bad_bt_script_set_keyboard_layout(bad_bt->bad_bt_script, bad_bt->keyboard_layout); + + scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneWork); + } else { + view_dispatcher_stop(bad_bt->view_dispatcher); + } +} + +bool bad_bt_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + // BadBtApp* bad_bt = context; + return false; +} + +void bad_bt_scene_file_select_on_exit(void* context) { + UNUSED(context); + // BadBtApp* bad_bt = context; +} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_work.c b/applications/external/bad_bt/scenes/bad_bt_scene_work.c new file mode 100644 index 000000000..684bb8b74 --- /dev/null +++ b/applications/external/bad_bt/scenes/bad_bt_scene_work.c @@ -0,0 +1,56 @@ +#include "../helpers/ducky_script.h" +#include "../bad_bt_app.h" +#include "../views/bad_bt_view.h" +#include +#include "toolbox/path.h" + +void bad_bt_scene_work_button_callback(InputKey key, void* context) { + furi_assert(context); + BadBtApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, key); +} + +bool bad_bt_scene_work_on_event(void* context, SceneManagerEvent event) { + BadBtApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InputKeyLeft) { + if(bad_bt_is_idle_state(app->bad_bt_view)) { + scene_manager_next_scene(app->scene_manager, BadBtSceneConfig); + } + consumed = true; + } else if(event.event == InputKeyOk) { + bad_bt_script_toggle(app->bad_bt_script); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeTick) { + bad_bt_set_state(app->bad_bt_view, bad_bt_script_get_state(app->bad_bt_script)); + } + return consumed; +} + +void bad_bt_scene_work_on_enter(void* context) { + BadBtApp* app = context; + + FuriString* file_name; + file_name = furi_string_alloc(); + path_extract_filename(app->file_path, file_name, true); + bad_bt_set_file_name(app->bad_bt_view, furi_string_get_cstr(file_name)); + furi_string_free(file_name); + + FuriString* layout; + layout = furi_string_alloc(); + path_extract_filename(app->keyboard_layout, layout, true); + bad_bt_set_layout(app->bad_bt_view, furi_string_get_cstr(layout)); + furi_string_free(layout); + + bad_bt_set_state(app->bad_bt_view, bad_bt_script_get_state(app->bad_bt_script)); + + bad_bt_set_button_callback(app->bad_bt_view, bad_bt_scene_work_button_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, BadBtAppViewWork); +} + +void bad_bt_scene_work_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/external/bad_bt/views/bad_bt_view.c b/applications/external/bad_bt/views/bad_bt_view.c new file mode 100644 index 000000000..4a9bf589c --- /dev/null +++ b/applications/external/bad_bt/views/bad_bt_view.c @@ -0,0 +1,233 @@ +#include "bad_bt_view.h" +#include "../helpers/ducky_script.h" +#include "../bad_bt_app.h" +#include +#include +#include + +#define MAX_NAME_LEN 64 + +typedef struct { + char file_name[MAX_NAME_LEN]; + char layout[MAX_NAME_LEN]; + BadBtState state; + uint8_t anim_frame; +} BadBtModel; + +static void bad_bt_draw_callback(Canvas* canvas, void* _model) { + BadBtModel* model = _model; + + FuriString* disp_str; + disp_str = furi_string_alloc_set("(BT) "); + furi_string_cat_str(disp_str, model->file_name); + elements_string_fit_width(canvas, disp_str, 128 - 2); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); + + if(strlen(model->layout) == 0) { + furi_string_set(disp_str, "(default)"); + } else { + furi_string_reset(disp_str); + furi_string_push_back(disp_str, '('); + for(size_t i = 0; i < strlen(model->layout); i++) + furi_string_push_back(disp_str, model->layout[i]); + furi_string_push_back(disp_str, ')'); + } + if(model->state.pin) { + furi_string_cat_printf(disp_str, " PIN: %ld", model->state.pin); + } + elements_string_fit_width(canvas, disp_str, 128 - 2); + canvas_draw_str( + canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); + + furi_string_reset(disp_str); + + if((model->state.state == BadBtStateIdle) || (model->state.state == BadBtStateDone) || + (model->state.state == BadBtStateNotConnected)) { + elements_button_center(canvas, "Run"); + elements_button_left(canvas, "Config"); + } else if((model->state.state == BadBtStateRunning) || (model->state.state == BadBtStateDelay)) { + elements_button_center(canvas, "Stop"); + } else if(model->state.state == BadBtStateWaitForBtn) { + elements_button_center(canvas, "Press to continue"); + } else if(model->state.state == BadBtStateWillRun) { + elements_button_center(canvas, "Cancel"); + } + + if(model->state.state == BadBtStateNotConnected) { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect to"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "a device"); + } else if(model->state.state == BadBtStateWillRun) { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); + } else if(model->state.state == BadBtStateFileError) { + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); + } else if(model->state.state == BadBtStateScriptError) { + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); + canvas_set_font(canvas, FontSecondary); + furi_string_printf(disp_str, "line %u", model->state.error_line); + canvas_draw_str_aligned( + canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + furi_string_set_str(disp_str, model->state.error); + elements_string_fit_width(canvas, disp_str, canvas_width(canvas)); + canvas_draw_str_aligned( + canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + } else if(model->state.state == BadBtStateIdle) { + canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + } else if(model->state.state == BadBtStateRunning) { + if(model->anim_frame == 0) { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); + } else { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); + } + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + } else if(model->state.state == BadBtStateDone) { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + } else if(model->state.state == BadBtStateDelay) { + if(model->anim_frame == 0) { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); + } else { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); + } + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_set_font(canvas, FontSecondary); + furi_string_printf(disp_str, "delay %lus", model->state.delay_remain); + canvas_draw_str_aligned( + canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + } else { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + } + + furi_string_free(disp_str); +} + +static bool bad_bt_input_callback(InputEvent* event, void* context) { + furi_assert(context); + BadBt* bad_bt = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) { + consumed = true; + furi_assert(bad_bt->callback); + bad_bt->callback(event->key, bad_bt->context); + } + } + + return consumed; +} + +BadBt* bad_bt_alloc() { + BadBt* bad_bt = malloc(sizeof(BadBt)); + + bad_bt->view = view_alloc(); + view_allocate_model(bad_bt->view, ViewModelTypeLocking, sizeof(BadBtModel)); + view_set_context(bad_bt->view, bad_bt); + view_set_draw_callback(bad_bt->view, bad_bt_draw_callback); + view_set_input_callback(bad_bt->view, bad_bt_input_callback); + + return bad_bt; +} + +void bad_bt_free(BadBt* bad_bt) { + furi_assert(bad_bt); + view_free(bad_bt->view); + free(bad_bt); +} + +View* bad_bt_get_view(BadBt* bad_bt) { + furi_assert(bad_bt); + return bad_bt->view; +} + +void bad_bt_set_button_callback(BadBt* bad_bt, BadBtButtonCallback callback, void* context) { + furi_assert(bad_bt); + furi_assert(callback); + with_view_model( + bad_bt->view, + BadBtModel * model, + { + UNUSED(model); + bad_bt->callback = callback; + bad_bt->context = context; + }, + true); +} + +void bad_bt_set_file_name(BadBt* bad_bt, const char* name) { + furi_assert(name); + with_view_model( + bad_bt->view, BadBtModel * model, { strlcpy(model->file_name, name, MAX_NAME_LEN); }, true); +} + +void bad_bt_set_layout(BadBt* bad_bt, const char* layout) { + furi_assert(layout); + with_view_model( + bad_bt->view, BadBtModel * model, { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true); +} + +void bad_bt_set_state(BadBt* bad_bt, BadBtState* st) { + furi_assert(st); + uint32_t pin = 0; + if(bad_bt->context != NULL) { + BadBtApp* app = bad_bt->context; + if(app->bt != NULL) { + pin = app->bt->pin; + } + } + st->pin = pin; + with_view_model( + bad_bt->view, + BadBtModel * model, + { + memcpy(&(model->state), st, sizeof(BadBtState)); + model->anim_frame ^= 1; + }, + true); +} + +bool bad_bt_is_idle_state(BadBt* bad_bt) { + bool is_idle = false; + with_view_model( + bad_bt->view, + BadBtModel * model, + { + if((model->state.state == BadBtStateIdle) || (model->state.state == BadBtStateDone) || + (model->state.state == BadBtStateNotConnected)) { + is_idle = true; + } + }, + false); + return is_idle; +} diff --git a/applications/external/bad_bt/views/bad_bt_view.h b/applications/external/bad_bt/views/bad_bt_view.h new file mode 100644 index 000000000..850a71057 --- /dev/null +++ b/applications/external/bad_bt/views/bad_bt_view.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +typedef void (*BadBtButtonCallback)(InputKey key, void* context); + +typedef struct { + View* view; + BadBtButtonCallback callback; + void* context; +} BadBt; + +typedef struct BadBtState BadBtState; + +BadBt* bad_bt_alloc(); + +void bad_bt_free(BadBt* bad_bt); + +View* bad_bt_get_view(BadBt* bad_bt); + +void bad_bt_set_button_callback(BadBt* bad_bt, BadBtButtonCallback callback, void* context); + +void bad_bt_set_file_name(BadBt* bad_bt, const char* name); + +void bad_bt_set_layout(BadBt* bad_bt, const char* layout); + +void bad_bt_set_state(BadBt* bad_bt, BadBtState* st); + +bool bad_bt_is_idle_state(BadBt* bad_bt); diff --git a/applications/external/totp/features_config.h b/applications/external/totp/features_config.h index 845ec1d35..683eff2fc 100644 --- a/applications/external/totp/features_config.h +++ b/applications/external/totp/features_config.h @@ -11,4 +11,4 @@ // End of list // Target firmware to build for -#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_OFFICIAL_DEV \ No newline at end of file +#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME \ No newline at end of file diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 2dcea3485..a842aea45 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -61,6 +61,7 @@ static ViewPort* bt_pin_code_view_port_alloc(Bt* bt) { static void bt_pin_code_show(Bt* bt, uint32_t pin_code) { bt->pin_code = pin_code; + if(bt->suppress_pin_screen) return; notification_message(bt->notification, &sequence_display_backlight_on); gui_view_port_send_to_front(bt->gui, bt->pin_code_view_port); view_port_enabled_set(bt->pin_code_view_port, true); @@ -75,6 +76,8 @@ static void bt_pin_code_hide(Bt* bt) { static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) { furi_assert(bt); + bt->pin_code = pin; + if(bt->suppress_pin_screen) return true; notification_message(bt->notification, &sequence_display_backlight_on); FuriString* pin_str; dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0); @@ -149,6 +152,8 @@ Bt* bt_alloc() { // API evnent bt->api_event = furi_event_flag_alloc(); + bt->pin = 0; + return bt; } @@ -214,6 +219,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { furi_assert(context); Bt* bt = context; bool ret = false; + bt->pin = 0; if(event.type == GapEventTypeConnected) { // Update status bar @@ -270,12 +276,14 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); ret = true; } else if(event.type == GapEventTypePinCodeShow) { + bt->pin = event.data.pin_code; BtMessage message = { .type = BtMessageTypePinCodeShow, .data.pin_code = event.data.pin_code}; furi_check( furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); ret = true; } else if(event.type == GapEventTypePinCodeVerify) { + bt->pin = event.data.pin_code; ret = bt_pin_code_verify_event_handler(bt, event.data.pin_code); } else if(event.type == GapEventTypeUpdateMTU) { bt->max_packet_size = event.data.max_packet_size; @@ -368,6 +376,86 @@ static void bt_close_connection(Bt* bt) { furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); } +static inline FuriHalBtProfile get_hal_bt_profile(BtProfile profile) { + if(profile == BtProfileHidKeyboard) { + return FuriHalBtProfileHidKeyboard; + } else { + return FuriHalBtProfileSerial; + } +} + +void bt_restart(Bt* bt) { + furi_hal_bt_change_app(get_hal_bt_profile(bt->profile), bt_on_gap_event_callback, bt); + furi_hal_bt_start_advertising(); +} + +void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...) { + furi_assert(bt); + furi_assert(fmt); + + char name[FURI_HAL_BT_ADV_NAME_LENGTH]; + va_list args; + va_start(args, fmt); + vsnprintf(name, sizeof(name), fmt, args); + va_end(args); + furi_hal_bt_set_profile_adv_name(get_hal_bt_profile(bt->profile), name); + + bt_restart(bt); +} + +const char* bt_get_profile_adv_name(Bt* bt) { + furi_assert(bt); + return furi_hal_bt_get_profile_adv_name(get_hal_bt_profile(bt->profile)); +} + +void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]) { + furi_assert(bt); + furi_assert(mac); + + furi_hal_bt_set_profile_mac_addr(get_hal_bt_profile(bt->profile), mac); + + bt_restart(bt); +} + +const uint8_t* bt_get_profile_mac_address(Bt* bt) { + furi_assert(bt); + return furi_hal_bt_get_profile_mac_addr(get_hal_bt_profile(bt->profile)); +} + +bool bt_remote_rssi(Bt* bt, uint8_t* rssi) { + furi_assert(bt); + + uint8_t rssi_val; + uint32_t since = furi_hal_bt_get_conn_rssi(&rssi_val); + + if(since == 0) return false; + + *rssi = rssi_val; + + return true; +} + +void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method) { + furi_assert(bt); + furi_hal_bt_set_profile_pairing_method(get_hal_bt_profile(bt->profile), pairing_method); + bt_restart(bt); +} + +GapPairing bt_get_profile_pairing_method(Bt* bt) { + furi_assert(bt); + return furi_hal_bt_get_profile_pairing_method(get_hal_bt_profile(bt->profile)); +} + +void bt_disable_peer_key_update(Bt* bt) { + UNUSED(bt); + furi_hal_bt_set_key_storage_change_callback(NULL, NULL); +} + +void bt_enable_peer_key_update(Bt* bt) { + furi_assert(bt); + furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); +} + int32_t bt_srv(void* p) { UNUSED(p); Bt* bt = bt_alloc(); diff --git a/applications/services/bt/bt_service/bt.h b/applications/services/bt/bt_service/bt.h index ca47936db..3d96ce603 100644 --- a/applications/services/bt/bt_service/bt.h +++ b/applications/services/bt/bt_service/bt.h @@ -3,6 +3,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -23,6 +25,11 @@ typedef enum { BtProfileHidKeyboard, } BtProfile; +typedef struct { + uint8_t rssi; + uint32_t since; +} BtRssi; + typedef void (*BtStatusChangedCallback)(BtStatus status, void* context); /** Change BLE Profile @@ -69,6 +76,32 @@ void bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path); */ void bt_keys_storage_set_default_path(Bt* bt); +// New methods + +void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...); + +const char* bt_get_profile_adv_name(Bt* bt); + +void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]); + +const uint8_t* bt_get_profile_mac_address(Bt* bt); + +bool bt_remote_rssi(Bt* bt, uint8_t* rssi); + +void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method); +GapPairing bt_get_profile_pairing_method(Bt* bt); + +/** Stop saving new peer key to flash (in .bt.keys file) + * +*/ +void bt_disable_peer_key_update(Bt* bt); + +/** Enable saving peer key to internal flash (enable by default) + * + * @note This function should be called if bt_disable_peer_key_update was called before +*/ +void bt_enable_peer_key_update(Bt* bt); + #ifdef __cplusplus } #endif diff --git a/applications/services/bt/bt_service/bt_i.h b/applications/services/bt/bt_service/bt_i.h index c8a0e9965..41e5bcd8c 100644 --- a/applications/services/bt/bt_service/bt_i.h +++ b/applications/services/bt/bt_service/bt_i.h @@ -76,4 +76,6 @@ struct Bt { FuriEventFlag* api_event; BtStatusChangedCallback status_changed_cb; void* status_changed_ctx; + uint32_t pin; + bool suppress_pin_screen; }; diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index 90682d68e..4d990c14e 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -573,6 +573,64 @@ void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width } } +void elements_scrollable_text_line_str( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + const char* string, + size_t scroll, + bool ellipsis, + bool centered) { + FuriString* line = furi_string_alloc_set_str(string); + + size_t len_px = canvas_string_width(canvas, furi_string_get_cstr(line)); + if(len_px > width) { + if(centered) { + centered = false; + x -= width / 2; + } + + if(ellipsis) { + width -= canvas_string_width(canvas, "..."); + } + + // Calculate scroll size + size_t scroll_size = furi_string_size(line); + size_t right_width = 0; + for(size_t i = scroll_size - 1; i > 0; i--) { + right_width += canvas_glyph_width(canvas, furi_string_get_char(line, i)); + if(right_width > width) break; + scroll_size--; + if(!scroll_size) break; + } + // Ensure that we have something to scroll + if(scroll_size) { + scroll_size += 3; + scroll = scroll % scroll_size; + furi_string_right(line, scroll); + } + + len_px = canvas_string_width(canvas, furi_string_get_cstr(line)); + while(len_px > width) { + furi_string_left(line, furi_string_size(line) - 1); + len_px = canvas_string_width(canvas, furi_string_get_cstr(line)); + } + + if(ellipsis) { + furi_string_cat(line, "..."); + } + } + + if(centered) { + canvas_draw_str_aligned( + canvas, x, y, AlignCenter, AlignBottom, furi_string_get_cstr(line)); + } else { + canvas_draw_str(canvas, x, y, furi_string_get_cstr(line)); + } + furi_string_free(line); +} + void elements_scrollable_text_line( Canvas* canvas, uint8_t x, diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 04ca357b8..bdc43d09a 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -228,6 +228,16 @@ void elements_scrollable_text_line( size_t scroll, bool ellipsis); +void elements_scrollable_text_line_str( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + const char* string, + size_t scroll, + bool ellipsis, + bool centered); + /** Draw text box element * * @param canvas Canvas instance diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 66b264458..5f47ac179 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -1,11 +1,14 @@ #include "submenu.h" +#include +#include #include #include #include struct Submenu { View* view; + FuriTimer* locked_timer; }; typedef struct { @@ -13,6 +16,8 @@ typedef struct { uint32_t index; SubmenuItemCallback callback; void* callback_context; + bool locked; + FuriString* locked_message; } SubmenuItem; static void SubmenuItem_init(SubmenuItem* item) { @@ -20,6 +25,8 @@ static void SubmenuItem_init(SubmenuItem* item) { item->index = 0; item->callback = NULL; item->callback_context = NULL; + item->locked = false; + item->locked_message = furi_string_alloc(); } static void SubmenuItem_init_set(SubmenuItem* item, const SubmenuItem* src) { @@ -27,6 +34,8 @@ static void SubmenuItem_init_set(SubmenuItem* item, const SubmenuItem* src) { item->index = src->index; item->callback = src->callback; item->callback_context = src->callback_context; + item->locked = src->locked; + item->locked_message = furi_string_alloc_set(src->locked_message); } static void SubmenuItem_set(SubmenuItem* item, const SubmenuItem* src) { @@ -34,10 +43,13 @@ static void SubmenuItem_set(SubmenuItem* item, const SubmenuItem* src) { item->index = src->index; item->callback = src->callback; item->callback_context = src->callback_context; + item->locked = src->locked; + furi_string_set(item->locked_message, src->locked_message); } static void SubmenuItem_clear(SubmenuItem* item) { furi_string_free(item->label); + furi_string_free(item->locked_message); } ARRAY_DEF( @@ -53,6 +65,7 @@ typedef struct { FuriString* header; size_t position; size_t window_position; + bool locked_message_visible; } SubmenuModel; static void submenu_process_up(Submenu* submenu); @@ -63,7 +76,12 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { SubmenuModel* model = _model; const uint8_t item_height = 16; - const uint8_t item_width = 123; + uint8_t item_width = 123; + + if(canvas->orientation == CanvasOrientationVertical || + canvas->orientation == CanvasOrientationVerticalFlip) { + item_width = 60; + } canvas_clear(canvas); @@ -96,9 +114,18 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { canvas_set_color(canvas, ColorBlack); } + if(SubmenuItemArray_cref(it)->locked) { + canvas_draw_icon( + canvas, + 110, + y_offset + (item_position * item_height) + item_height - 12, + &I_Lock_7x8); + } + FuriString* disp_str; disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label); - elements_string_fit_width(canvas, disp_str, item_width - 11); + elements_string_fit_width( + canvas, disp_str, item_width - (SubmenuItemArray_cref(it)->locked ? 25 : 11)); canvas_draw_str( canvas, @@ -113,6 +140,23 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { } elements_scrollbar(canvas, model->position, SubmenuItemArray_size(model->items)); + + if(model->locked_message_visible) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 8, 10, 110, 48); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42); + canvas_draw_rframe(canvas, 8, 8, 112, 50, 3); + canvas_draw_rframe(canvas, 9, 9, 110, 48, 2); + elements_multiline_text_aligned( + canvas, + 84, + 32, + AlignCenter, + AlignCenter, + furi_string_get_cstr( + SubmenuItemArray_get(model->items, model->position)->locked_message)); + } } static bool submenu_view_input_callback(InputEvent* event, void* context) { @@ -120,7 +164,19 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) { furi_assert(submenu); bool consumed = false; - if(event->type == InputTypeShort) { + bool locked_message_visible = false; + with_view_model( + submenu->view, + SubmenuModel * model, + { locked_message_visible = model->locked_message_visible; }, + false); + + if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && + locked_message_visible) { + with_view_model( + submenu->view, SubmenuModel * model, { model->locked_message_visible = false; }, true); + consumed = true; + } else if(event->type == InputTypeShort) { switch(event->key) { case InputKeyUp: consumed = true; @@ -150,6 +206,14 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) { return consumed; } +void submenu_timer_callback(void* context) { + furi_assert(context); + Submenu* submenu = context; + + with_view_model( + submenu->view, SubmenuModel * model, { model->locked_message_visible = false; }, true); +} + Submenu* submenu_alloc() { Submenu* submenu = malloc(sizeof(Submenu)); submenu->view = view_alloc(); @@ -158,6 +222,8 @@ Submenu* submenu_alloc() { view_set_draw_callback(submenu->view, submenu_view_draw_callback); view_set_input_callback(submenu->view, submenu_view_input_callback); + submenu->locked_timer = furi_timer_alloc(submenu_timer_callback, FuriTimerTypeOnce, submenu); + with_view_model( submenu->view, SubmenuModel * model, @@ -183,6 +249,8 @@ void submenu_free(Submenu* submenu) { SubmenuItemArray_clear(model->items); }, true); + furi_timer_stop(submenu->locked_timer); + furi_timer_free(submenu->locked_timer); view_free(submenu->view); free(submenu); } @@ -198,9 +266,23 @@ void submenu_add_item( uint32_t index, SubmenuItemCallback callback, void* callback_context) { + submenu_add_lockable_item(submenu, label, index, callback, callback_context, false, NULL); +} + +void submenu_add_lockable_item( + Submenu* submenu, + const char* label, + uint32_t index, + SubmenuItemCallback callback, + void* callback_context, + bool locked, + const char* locked_message) { SubmenuItem* item = NULL; furi_assert(label); furi_assert(submenu); + if(locked) { + furi_assert(locked_message); + } with_view_model( submenu->view, @@ -211,6 +293,10 @@ void submenu_add_item( item->index = index; item->callback = callback; item->callback_context = callback_context; + item->locked = locked; + if(locked) { + furi_string_set_str(item->locked_message, locked_message); + } }, true); } @@ -328,10 +414,14 @@ void submenu_process_ok(Submenu* submenu) { if(model->position < items_size) { item = SubmenuItemArray_get(model->items, model->position); } + if(item && item->locked) { + model->locked_message_visible = true; + furi_timer_start(submenu->locked_timer, furi_kernel_get_tick_frequency() * 3); + } }, true); - if(item && item->callback) { + if(item && !item->locked && item->callback) { item->callback(item->callback_context, item->index); } } diff --git a/applications/services/gui/modules/submenu.h b/applications/services/gui/modules/submenu.h index f68abe83a..e7252eb33 100644 --- a/applications/services/gui/modules/submenu.h +++ b/applications/services/gui/modules/submenu.h @@ -53,6 +53,26 @@ void submenu_add_item( SubmenuItemCallback callback, void* callback_context); +/** Add lockable item to submenu + * + * @param submenu Submenu instance + * @param label menu item label + * @param index menu item index, used for callback, may be + * the same with other items + * @param callback menu item callback + * @param callback_context menu item callback context + * @param locked menu item locked status + * @param locked_message menu item locked message + */ +void submenu_add_lockable_item( + Submenu* submenu, + const char* label, + uint32_t index, + SubmenuItemCallback callback, + void* callback_context, + bool locked, + const char* locked_message); + /** Remove all items from submenu * * @param submenu Submenu instance diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index cf7f64ca3..93948f7a4 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -11,6 +12,8 @@ struct VariableItem { FuriString* current_value_text; uint8_t values_count; VariableItemChangeCallback change_callback; + bool locked; + FuriString* locked_message; void* context; }; @@ -20,12 +23,16 @@ struct VariableItemList { View* view; VariableItemListEnterCallback callback; void* context; + FuriTimer* scroll_timer; + FuriTimer* locked_timer; }; typedef struct { VariableItemArray_t items; uint8_t position; uint8_t window_position; + size_t scroll_counter; + bool locked_message_visible; } VariableItemListModel; static void variable_item_list_process_up(VariableItemList* variable_item_list); @@ -56,31 +63,50 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { const VariableItem* item = VariableItemArray_cref(it); uint8_t item_y = y_offset + (item_position * item_height); uint8_t item_text_y = item_y + item_height - 4; + size_t scroll_counter = 0; if(position == model->position) { canvas_set_color(canvas, ColorBlack); elements_slightly_rounded_box(canvas, 0, item_y + 1, item_width, item_height - 2); canvas_set_color(canvas, ColorWhite); + scroll_counter = model->scroll_counter; + if(scroll_counter < 1) { + scroll_counter = 0; + } else { + scroll_counter -= 1; + } } else { canvas_set_color(canvas, ColorBlack); } - canvas_draw_str(canvas, 6, item_text_y, item->label); - - if(item->current_value_index > 0) { - canvas_draw_str(canvas, 73, item_text_y, "<"); + if(item->current_value_index == 0 && furi_string_empty(item->current_value_text)) { + // Only left text, no right text + canvas_draw_str(canvas, 6, item_text_y, item->label); + } else { + elements_scrollable_text_line_str( + canvas, 6, item_text_y, 66, item->label, scroll_counter, false, false); } - canvas_draw_str_aligned( - canvas, - (115 + 73) / 2 + 1, - item_text_y, - AlignCenter, - AlignBottom, - furi_string_get_cstr(item->current_value_text)); + if(item->locked) { + canvas_draw_icon(canvas, 110, item_text_y - 8, &I_Lock_7x8); + } else { + if(item->current_value_index > 0) { + canvas_draw_str(canvas, 73, item_text_y, "<"); + } - if(item->current_value_index < (item->values_count - 1)) { - canvas_draw_str(canvas, 115, item_text_y, ">"); + elements_scrollable_text_line_str( + canvas, + (115 + 73) / 2 + 1, + item_text_y, + 37, + furi_string_get_cstr(item->current_value_text), + scroll_counter, + false, + true); + + if(item->current_value_index < (item->values_count - 1)) { + canvas_draw_str(canvas, 115, item_text_y, ">"); + } } } @@ -88,6 +114,23 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { } elements_scrollbar(canvas, model->position, VariableItemArray_size(model->items)); + + if(model->locked_message_visible) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 8, 10, 110, 48); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42); + canvas_draw_rframe(canvas, 8, 8, 112, 50, 3); + canvas_draw_rframe(canvas, 9, 9, 110, 48, 2); + elements_multiline_text_aligned( + canvas, + 84, + 32, + AlignCenter, + AlignCenter, + furi_string_get_cstr( + VariableItemArray_get(model->items, model->position)->locked_message)); + } } void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index) { @@ -130,7 +173,22 @@ static bool variable_item_list_input_callback(InputEvent* event, void* context) furi_assert(variable_item_list); bool consumed = false; - if(event->type == InputTypeShort) { + bool locked_message_visible = false; + with_view_model( + variable_item_list->view, + VariableItemListModel * model, + { locked_message_visible = model->locked_message_visible; }, + false); + + if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && + locked_message_visible) { + with_view_model( + variable_item_list->view, + VariableItemListModel * model, + { model->locked_message_visible = false; }, + true); + consumed = true; + } else if(event->type == InputTypeShort) { switch(event->key) { case InputKeyUp: consumed = true; @@ -198,6 +256,7 @@ void variable_item_list_process_up(VariableItemList* variable_item_list) { model->window_position = model->position - (items_on_screen - 1); } } + model->scroll_counter = 0; }, true); } @@ -219,6 +278,7 @@ void variable_item_list_process_down(VariableItemList* variable_item_list) { model->position = 0; model->window_position = 0; } + model->scroll_counter = 0; }, true); } @@ -248,8 +308,13 @@ void variable_item_list_process_left(VariableItemList* variable_item_list) { VariableItemListModel * model, { VariableItem* item = variable_item_list_get_selected_item(model); - if(item->current_value_index > 0) { + if(item->locked) { + model->locked_message_visible = true; + furi_timer_start( + variable_item_list->locked_timer, furi_kernel_get_tick_frequency() * 3); + } else if(item->current_value_index > 0) { item->current_value_index--; + model->scroll_counter = 0; if(item->change_callback) { item->change_callback(item); } @@ -264,8 +329,13 @@ void variable_item_list_process_right(VariableItemList* variable_item_list) { VariableItemListModel * model, { VariableItem* item = variable_item_list_get_selected_item(model); - if(item->current_value_index < (item->values_count - 1)) { + if(item->locked) { + model->locked_message_visible = true; + furi_timer_start( + variable_item_list->locked_timer, furi_kernel_get_tick_frequency() * 3); + } else if(item->current_value_index < (item->values_count - 1)) { item->current_value_index++; + model->scroll_counter = 0; if(item->change_callback) { item->change_callback(item); } @@ -279,11 +349,36 @@ void variable_item_list_process_ok(VariableItemList* variable_item_list) { variable_item_list->view, VariableItemListModel * model, { - if(variable_item_list->callback) { + VariableItem* item = variable_item_list_get_selected_item(model); + if(item->locked) { + model->locked_message_visible = true; + furi_timer_start( + variable_item_list->locked_timer, furi_kernel_get_tick_frequency() * 3); + } else if(variable_item_list->callback) { variable_item_list->callback(variable_item_list->context, model->position); } }, - false); + true); +} + +static void variable_item_list_scroll_timer_callback(void* context) { + VariableItemList* variable_item_list = context; + with_view_model( + variable_item_list->view, + VariableItemListModel * model, + { model->scroll_counter++; }, + true); +} + +void variable_item_list_locked_timer_callback(void* context) { + furi_assert(context); + VariableItemList* variable_item_list = context; + + with_view_model( + variable_item_list->view, + VariableItemListModel * model, + { model->locked_message_visible = false; }, + true); } VariableItemList* variable_item_list_alloc() { @@ -295,6 +390,9 @@ VariableItemList* variable_item_list_alloc() { view_set_draw_callback(variable_item_list->view, variable_item_list_draw_callback); view_set_input_callback(variable_item_list->view, variable_item_list_input_callback); + variable_item_list->locked_timer = furi_timer_alloc( + variable_item_list_locked_timer_callback, FuriTimerTypeOnce, variable_item_list); + with_view_model( variable_item_list->view, VariableItemListModel * model, @@ -302,8 +400,12 @@ VariableItemList* variable_item_list_alloc() { VariableItemArray_init(model->items); model->position = 0; model->window_position = 0; + model->scroll_counter = 0; }, true); + variable_item_list->scroll_timer = furi_timer_alloc( + variable_item_list_scroll_timer_callback, FuriTimerTypePeriodic, variable_item_list); + furi_timer_start(variable_item_list->scroll_timer, 333); return variable_item_list; } @@ -319,10 +421,15 @@ void variable_item_list_free(VariableItemList* variable_item_list) { for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it); VariableItemArray_next(it)) { furi_string_free(VariableItemArray_ref(it)->current_value_text); + furi_string_free(VariableItemArray_ref(it)->locked_message); } VariableItemArray_clear(model->items); }, false); + furi_timer_stop(variable_item_list->scroll_timer); + furi_timer_free(variable_item_list->scroll_timer); + furi_timer_stop(variable_item_list->locked_timer); + furi_timer_free(variable_item_list->locked_timer); view_free(variable_item_list->view); free(variable_item_list); } @@ -338,6 +445,7 @@ void variable_item_list_reset(VariableItemList* variable_item_list) { for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it); VariableItemArray_next(it)) { furi_string_free(VariableItemArray_ref(it)->current_value_text); + furi_string_free(VariableItemArray_ref(it)->locked_message); } VariableItemArray_reset(model->items); }, @@ -370,6 +478,8 @@ VariableItem* variable_item_list_add( item->context = context; item->current_value_index = 0; item->current_value_text = furi_string_alloc(); + item->locked = false; + item->locked_message = furi_string_alloc(); }, true); @@ -404,6 +514,14 @@ void variable_item_set_current_value_text(VariableItem* item, const char* curren furi_string_set(item->current_value_text, current_value_text); } +void variable_item_set_locked(VariableItem* item, bool locked, const char* locked_message) { + item->locked = locked; + if(locked) { + furi_assert(locked_message); + furi_string_set(item->locked_message, locked_message); + } +} + uint8_t variable_item_get_current_value_index(VariableItem* item) { return item->current_value_index; } diff --git a/applications/services/gui/modules/variable_item_list.h b/applications/services/gui/modules/variable_item_list.h index db2a58993..db8b1788f 100644 --- a/applications/services/gui/modules/variable_item_list.h +++ b/applications/services/gui/modules/variable_item_list.h @@ -95,6 +95,14 @@ void variable_item_set_values_count(VariableItem* item, uint8_t values_count); */ void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text); +/** Set item locked state and text + * + * @param item VariableItem* instance + * @param locked Is item locked boolean + * @param locked_message The locked message text + */ +void variable_item_set_locked(VariableItem* item, bool locked, const char* locked_message); + /** Get item current selected index * * @param item VariableItem* instance diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c index 31921b9f3..d009ae2d5 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c @@ -1,5 +1,6 @@ #include "../bt_settings_app.h" #include +#include void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void* context) { furi_assert(context); @@ -30,6 +31,14 @@ bool bt_settings_scene_forget_dev_confirm_on_event(void* context, SceneManagerEv consumed = scene_manager_previous_scene(app->scene_manager); } else if(event.event == DialogExResultRight) { bt_forget_bonded_devices(app->bt); + + // Also remove keys of BadBT, Bluetooth Remote, TOTP Authenticator + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_remove(storage, EXT_PATH("badbt/.badbt.keys")); + storage_simply_remove(storage, EXT_PATH("apps_data/hid_ble/.bt_hid.keys")); + storage_simply_remove(storage, EXT_PATH("authenticator/.bt_hid.keys")); + furi_record_close(RECORD_STORAGE); + scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneForgetDevSuccess); consumed = true; } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 96006c43c..7eac9ab00 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -586,11 +586,20 @@ Function,+,ble_glue_start,_Bool, Function,+,ble_glue_thread_stop,void, Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" +Function,+,bt_disable_peer_key_update,void,Bt* Function,+,bt_disconnect,void,Bt* +Function,+,bt_enable_peer_key_update,void,Bt* Function,+,bt_forget_bonded_devices,void,Bt* +Function,+,bt_get_profile_adv_name,const char*,Bt* +Function,+,bt_get_profile_mac_address,const uint8_t*,Bt* +Function,+,bt_get_profile_pairing_method,GapPairing,Bt* Function,+,bt_keys_storage_set_default_path,void,Bt* Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" +Function,+,bt_remote_rssi,_Bool,"Bt*, uint8_t*" Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" +Function,+,bt_set_profile_adv_name,void,"Bt*, const char*, ..." +Function,+,bt_set_profile_mac_address,void,"Bt*, const uint8_t[6]" +Function,+,bt_set_profile_pairing_method,void,"Bt*, GapPairing" Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" Function,+,buffered_file_stream_alloc,Stream*,Storage* Function,+,buffered_file_stream_close,_Bool,Stream* @@ -816,6 +825,7 @@ Function,+,elements_multiline_text_framed,void,"Canvas*, uint8_t, uint8_t, const Function,+,elements_progress_bar,void,"Canvas*, uint8_t, uint8_t, uint8_t, float" Function,+,elements_progress_bar_with_text,void,"Canvas*, uint8_t, uint8_t, uint8_t, float, const char*" Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool" +Function,+,elements_scrollable_text_line_str,void,"Canvas*, uint8_t, uint8_t, uint8_t, const char*, size_t, _Bool, _Bool" Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t" Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t" Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" @@ -1037,14 +1047,19 @@ Function,+,furi_hal_bt_change_app,_Bool,"FuriHalBtProfile, GapEventCallback, voi Function,+,furi_hal_bt_clear_white_list,_Bool, Function,+,furi_hal_bt_dump_state,void,FuriString* Function,+,furi_hal_bt_ensure_c2_mode,_Bool,BleGlueC2Mode +Function,+,furi_hal_bt_get_conn_rssi,uint32_t,uint8_t* Function,-,furi_hal_bt_get_hardfault_info,const FuriHalBtHardfaultInfo*, Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" +Function,+,furi_hal_bt_get_profile_adv_name,const char*,FuriHalBtProfile +Function,+,furi_hal_bt_get_profile_mac_addr,const uint8_t*,FuriHalBtProfile +Function,+,furi_hal_bt_get_profile_pairing_method,GapPairing,FuriHalBtProfile Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack, Function,+,furi_hal_bt_get_rssi,float, Function,+,furi_hal_bt_get_transmitted_packets,uint32_t, Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool, +Function,+,furi_hal_bt_hid_get_led_state,uint8_t, Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t Function,+,furi_hal_bt_hid_kb_release_all,_Bool, @@ -1059,6 +1074,7 @@ Function,-,furi_hal_bt_init,void, Function,+,furi_hal_bt_is_active,_Bool, Function,+,furi_hal_bt_is_alive,_Bool, Function,+,furi_hal_bt_is_ble_gatt_gap_supported,_Bool, +Function,+,furi_hal_bt_is_connected,_Bool, Function,+,furi_hal_bt_is_testing_supported,_Bool, Function,+,furi_hal_bt_lock_core2,void, Function,+,furi_hal_bt_nvm_sram_sem_acquire,void, @@ -1071,6 +1087,9 @@ Function,+,furi_hal_bt_serial_start,void, Function,+,furi_hal_bt_serial_stop,void, Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*" +Function,+,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 18 + 1 )]" +Function,+,furi_hal_bt_set_profile_mac_addr,void,"FuriHalBtProfile, const uint8_t[( 6 )]" +Function,+,furi_hal_bt_set_profile_pairing_method,void,"FuriHalBtProfile, GapPairing" Function,+,furi_hal_bt_start_advertising,void, Function,+,furi_hal_bt_start_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" Function,+,furi_hal_bt_start_packet_rx,void,"uint8_t, uint8_t" @@ -1633,6 +1652,7 @@ Function,-,gamma,double,double Function,-,gamma_r,double,"double, int*" Function,-,gammaf,float,float Function,-,gammaf_r,float,"float, int*" +Function,+,gap_get_remote_conn_rssi,uint32_t,int8_t* Function,-,gap_get_state,GapState, Function,-,gap_init,_Bool,"GapConfig*, GapEventCallback, void*" Function,-,gap_start_advertising,void, @@ -3334,6 +3354,7 @@ Function,+,subghz_worker_set_pair_callback,void,"SubGhzWorker*, SubGhzWorkerPair Function,+,subghz_worker_start,void,SubGhzWorker* Function,+,subghz_worker_stop,void,SubGhzWorker* Function,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*" +Function,+,submenu_add_lockable_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*, _Bool, const char*" Function,+,submenu_alloc,Submenu*, Function,+,submenu_free,void,Submenu* Function,+,submenu_get_view,View*,Submenu* @@ -4557,6 +4578,7 @@ Function,+,variable_item_list_set_enter_callback,void,"VariableItemList*, Variab Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t" Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t" Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*" +Function,+,variable_item_set_locked,void,"VariableItem*, _Bool, const char*" Function,+,variable_item_set_values_count,void,"VariableItem*, uint8_t" Function,-,vasiprintf,int,"char**, const char*, __gnuc_va_list" Function,-,vasniprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index f0a9ced3c..f2fb3e388 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -28,6 +28,8 @@ typedef struct { GapConfig* config; GapConnectionParams connection_params; GapState state; + int8_t conn_rssi; + uint32_t time_rssi_sample; FuriMutex* state_mutex; GapEventCallback on_event_cb; void* context; @@ -56,6 +58,19 @@ static Gap* gap = NULL; static void gap_advertise_start(GapState new_state); static int32_t gap_app(void* context); +/** function for updating rssi informations in global Gap object + * +*/ +static inline void fetch_rssi() { + uint8_t ret_rssi = 127; + if(hci_read_rssi(gap->service.connection_handle, &ret_rssi) == BLE_STATUS_SUCCESS) { + gap->conn_rssi = (int8_t)ret_rssi; + gap->time_rssi_sample = furi_get_tick(); + return; + } + FURI_LOG_D(TAG, "Failed to read RSSI"); +} + static void gap_verify_connection_parameters(Gap* gap) { furi_assert(gap); @@ -128,6 +143,9 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->connection_params.supervisor_timeout = event->Supervision_Timeout; FURI_LOG_I(TAG, "Connection parameters event complete"); gap_verify_connection_parameters(gap); + + // Save rssi for current connection + fetch_rssi(); break; } @@ -162,6 +180,9 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->service.connection_handle = event->Connection_Handle; gap_verify_connection_parameters(gap); + + // Save rssi for current connection + fetch_rssi(); // Start pairing by sending security request aci_gap_slave_security_req(event->Connection_Handle); } break; @@ -242,6 +263,9 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { pairing_complete->Status); aci_gap_terminate(gap->service.connection_handle, 5); } else { + // Save RSSI + fetch_rssi(); + FURI_LOG_I(TAG, "Pairing complete"); GapEvent event = {.type = GapEventTypeConnected}; gap->on_event_cb(event, gap->context); //-V595 @@ -310,7 +334,7 @@ static void gap_init_svc(Gap* gap) { // Initialize GATT interface aci_gatt_init(); // Initialize GAP interface - // Skip fist symbol AD_TYPE_COMPLETE_LOCAL_NAME + // Skip first symbol AD_TYPE_COMPLETE_LOCAL_NAME char* name = gap->service.adv_name + 1; aci_gap_init( GAP_PERIPHERAL_ROLE, @@ -345,21 +369,34 @@ static void gap_init_svc(Gap* gap) { hci_le_set_default_phy(ALL_PHYS_PREFERENCE, TX_2M_PREFERRED, RX_2M_PREFERRED); // Set I/O capability bool keypress_supported = false; + // New things below + uint8_t conf_mitm = CFG_MITM_PROTECTION; + uint8_t conf_used_fixed_pin = CFG_USED_FIXED_PIN; + bool conf_bonding = gap->config->bonding_mode; + if(gap->config->pairing_method == GapPairingPinCodeShow) { aci_gap_set_io_capability(IO_CAP_DISPLAY_ONLY); } else if(gap->config->pairing_method == GapPairingPinCodeVerifyYesNo) { aci_gap_set_io_capability(IO_CAP_DISPLAY_YES_NO); keypress_supported = true; + } else if(gap->config->pairing_method == GapPairingNone) { + // Just works pairing method (IOS accept it, it seems android and linux doesn't) + conf_mitm = 0; + conf_used_fixed_pin = 0; + conf_bonding = false; + // if just works isn't supported, we want the numeric comparaison method + aci_gap_set_io_capability(IO_CAP_DISPLAY_YES_NO); + keypress_supported = true; } // Setup authentication aci_gap_set_authentication_requirement( - gap->config->bonding_mode, - CFG_MITM_PROTECTION, + conf_bonding, + conf_mitm, CFG_SC_SUPPORT, keypress_supported, CFG_ENCRYPTION_KEY_SIZE_MIN, CFG_ENCRYPTION_KEY_SIZE_MAX, - CFG_USED_FIXED_PIN, + conf_used_fixed_pin, // 0x0 for no pin 0, CFG_IDENTITY_ADDRESS); // Configure whitelist @@ -488,6 +525,9 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap->service.connection_handle = 0xFFFF; gap->enable_adv = true; + gap->conn_rssi = 127; + gap->time_rssi_sample = 0; + // Thread configuration gap->thread = furi_thread_alloc_ex("BleGapDriver", 1024, gap_app, gap); furi_thread_start(gap->thread); @@ -507,6 +547,17 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { return true; } +// Get RSSI +uint32_t gap_get_remote_conn_rssi(int8_t* rssi) { + if(gap && gap->state == GapStateConnected) { + fetch_rssi(); + *rssi = gap->conn_rssi; + + if(gap->time_rssi_sample) return furi_get_tick() - gap->time_rssi_sample; + } + return 0; +} + GapState gap_get_state() { GapState state; if(gap) { diff --git a/firmware/targets/f7/ble_glue/gap.h b/firmware/targets/f7/ble_glue/gap.h index 1e207299f..7b317e06c 100644 --- a/firmware/targets/f7/ble_glue/gap.h +++ b/firmware/targets/f7/ble_glue/gap.h @@ -81,6 +81,8 @@ GapState gap_get_state(); void gap_thread_stop(); +uint32_t gap_get_remote_conn_rssi(int8_t* rssi); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c index 47d242d4d..a31d6015f 100644 --- a/firmware/targets/f7/ble_glue/hid_service.c +++ b/firmware/targets/f7/ble_glue/hid_service.c @@ -14,6 +14,11 @@ typedef struct { uint16_t report_map_char_handle; uint16_t info_char_handle; uint16_t ctrl_point_char_handle; + // led state + uint16_t led_state_char_handle; + uint16_t led_state_desc_handle; + HidLedStateEventCallback led_state_event_callback; + void* led_state_ctx; } HIDSvc; static HIDSvc* hid_svc = NULL; @@ -30,6 +35,32 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { // Process notification confirmation ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { + // Process write request + aci_gatt_write_permit_req_event_rp0* req = + (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; + + furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); + + // this check is likely to be incorrect, it will actually work in our case + // but we need to investigate gatt api to see what is the rules + // that specify attibute handle value from char handle (or the reverse) + if(req->Attribute_Handle == (hid_svc->led_state_char_handle + 1)) { + hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); + aci_gatt_write_resp( + req->Connection_Handle, + req->Attribute_Handle, + 0x00, /* write_status = 0 (no error))*/ + 0x00, /* err_code */ + req->Data_Length, + req->Data); + aci_gatt_write_char_value( + req->Connection_Handle, + hid_svc->led_state_char_handle, + req->Data_Length, + req->Data); + ret = SVCCTL_EvtAckFlowEnable; + } } } return ret; @@ -55,8 +86,8 @@ void hid_svc_start() { PRIMARY_SERVICE, 2 + /* protocol mode */ (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + - (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + - 2, /* Service + Report Map + HID Information + HID Control Point */ + (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + + 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ &hid_svc->svc_handle); if(status) { FURI_LOG_E(TAG, "Failed to add HID service: %d", status); @@ -198,6 +229,43 @@ void hid_svc_start() { } } #endif + // Add led state output report + char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + 1, + CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, + ATTR_PERMISSION_NONE, + GATT_NOTIFY_ATTRIBUTE_WRITE | GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, + 10, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->led_state_char_handle)); + if(status) { + FURI_LOG_E(TAG, "Failed to add led state characteristic: %d", status); + } + + // Add led state char descriptor specifying it is an output report + uint8_t buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; + desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; + status = aci_gatt_add_char_desc( + hid_svc->svc_handle, + hid_svc->led_state_char_handle, + UUID_TYPE_16, + &desc_uuid, + HID_SVC_REPORT_REF_LEN, + HID_SVC_REPORT_REF_LEN, + buf, + ATTR_PERMISSION_NONE, + ATTR_ACCESS_READ_WRITE, + GATT_DONT_NOTIFY_EVENTS, + MIN_ENCRY_KEY_SIZE, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->led_state_desc_handle)); + if(status) { + FURI_LOG_E(TAG, "Failed to add led state descriptor: %d", status); + } // Add Report Map characteristic char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; status = aci_gatt_add_char( @@ -247,6 +315,9 @@ void hid_svc_start() { if(status) { FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status); } + + hid_svc->led_state_event_callback = NULL; + hid_svc->led_state_ctx = NULL; } bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { @@ -288,6 +359,15 @@ bool hid_svc_update_info(uint8_t* data, uint16_t len) { return true; } +void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { + furi_assert(hid_svc); + furi_assert(callback); + furi_assert(context); + + hid_svc->led_state_event_callback = callback; + hid_svc->led_state_ctx = context; +} + bool hid_svc_is_started() { return hid_svc != NULL; } @@ -320,6 +400,10 @@ void hid_svc_stop() { if(status) { FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status); } + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->led_state_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete led state characteristic: %d", status); + } // Delete service status = aci_gatt_del_service(hid_svc->svc_handle); if(status) { diff --git a/firmware/targets/f7/ble_glue/hid_service.h b/firmware/targets/f7/ble_glue/hid_service.h index 723460d49..b8f6b244d 100644 --- a/firmware/targets/f7/ble_glue/hid_service.h +++ b/firmware/targets/f7/ble_glue/hid_service.h @@ -15,6 +15,8 @@ #define HID_SVC_REPORT_COUNT \ (HID_SVC_INPUT_REPORT_COUNT + HID_SVC_OUTPUT_REPORT_COUNT + HID_SVC_FEATURE_REPORT_COUNT) +typedef uint16_t (*HidLedStateEventCallback)(uint8_t state, void* ctx); + void hid_svc_start(); void hid_svc_stop(); @@ -26,3 +28,5 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len); bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); bool hid_svc_update_info(uint8_t* data, uint16_t len); + +void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 048a8b309..4e8b3091f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -199,26 +199,39 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, FURI_LOG_E(TAG, "Can't start Ble App - unsupported radio stack"); break; } - // Set mac address - memcpy( - profile_config[profile].config.mac_address, - furi_hal_version_get_ble_mac(), - sizeof(profile_config[profile].config.mac_address)); - // Set advertise name - strlcpy( - profile_config[profile].config.adv_name, - furi_hal_version_get_ble_local_device_name_ptr(), - FURI_HAL_VERSION_DEVICE_NAME_LENGTH); - // Configure GAP GapConfig* config = &profile_config[profile].config; + // Configure GAP if(profile == FuriHalBtProfileSerial) { + // Set mac address + memcpy( + config->mac_address, furi_hal_version_get_ble_mac(), sizeof(config->mac_address)); + // Set advertise name + strlcpy( + config->adv_name, + furi_hal_version_get_ble_local_device_name_ptr(), + FURI_HAL_VERSION_DEVICE_NAME_LENGTH); + config->adv_service_uuid |= furi_hal_version_get_hw_color(); } else if(profile == FuriHalBtProfileHidKeyboard) { // Change MAC address for HID profile - config->mac_address[2]++; + uint8_t default_mac[sizeof(config->mac_address)] = FURI_HAL_BT_DEFAULT_MAC_ADDR; + const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); + if(memcmp(config->mac_address, default_mac, sizeof(config->mac_address)) == 0) { + memcpy(config->mac_address, normal_mac, sizeof(config->mac_address)); + } + if(memcmp(config->mac_address, normal_mac, sizeof(config->mac_address)) == 0) { + config->mac_address[2]++; + } // Change name Flipper -> Control - const char* clicker_str = "Control"; - memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str)); + if(strnlen(config->adv_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH) < 2 || + strnlen(config->adv_name + 1, FURI_HAL_VERSION_DEVICE_NAME_LENGTH) < 1) { + snprintf( + config->adv_name, + FURI_HAL_VERSION_DEVICE_NAME_LENGTH, + "%cControl %s", + *furi_hal_version_get_ble_local_device_name_ptr(), + furi_hal_version_get_ble_local_device_name_ptr() + 9); + } } if(!gap_init(config, event_cb, context)) { gap_thread_stop(); @@ -280,6 +293,10 @@ bool furi_hal_bt_is_active() { return gap_get_state() > GapStateIdle; } +bool furi_hal_bt_is_connected() { + return gap_get_state() == GapStateConnected; +} + void furi_hal_bt_start_advertising() { if(gap_get_state() == GapStateIdle) { gap_start_advertising(); @@ -418,6 +435,67 @@ float furi_hal_bt_get_rssi() { return val; } +/** fill the RSSI of the remote host of the bt connection and returns the last + * time the RSSI was updated + * +*/ +uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi) { + int8_t ret_rssi = 0; + uint32_t since = gap_get_remote_conn_rssi(&ret_rssi); + + if(ret_rssi == 127 || since == 0) return 0; + + *rssi = (uint8_t)abs(ret_rssi); + + return since; +} + +void furi_hal_bt_set_profile_adv_name( + FuriHalBtProfile profile, + const char name[FURI_HAL_BT_ADV_NAME_LENGTH]) { + furi_assert(profile < FuriHalBtProfileNumber); + furi_assert(name); + + if(strlen(name) == 0) { + memset( + &(profile_config[profile].config.adv_name[1]), + 0, + strlen(&(profile_config[profile].config.adv_name[1]))); + } else { + profile_config[profile].config.adv_name[0] = AD_TYPE_COMPLETE_LOCAL_NAME; + memcpy(&(profile_config[profile].config.adv_name[1]), name, FURI_HAL_BT_ADV_NAME_LENGTH); + } +} + +const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile) { + furi_assert(profile < FuriHalBtProfileNumber); + return &(profile_config[profile].config.adv_name[1]); +} + +void furi_hal_bt_set_profile_mac_addr( + FuriHalBtProfile profile, + const uint8_t mac_addr[GAP_MAC_ADDR_SIZE]) { + furi_assert(profile < FuriHalBtProfileNumber); + furi_assert(mac_addr); + + memcpy(profile_config[profile].config.mac_address, mac_addr, GAP_MAC_ADDR_SIZE); +} + +const uint8_t* furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile) { + furi_assert(profile < FuriHalBtProfileNumber); + return profile_config[profile].config.mac_address; +} + +void furi_hal_bt_set_profile_pairing_method(FuriHalBtProfile profile, GapPairing pairing_method) { + furi_assert(profile < FuriHalBtProfileNumber); + profile_config[profile].config.pairing_method = pairing_method; +} + +GapPairing furi_hal_bt_get_profile_pairing_method(FuriHalBtProfile profile) { + furi_assert(profile < FuriHalBtProfileNumber); + return profile_config[profile].config.pairing_method; +} + uint32_t furi_hal_bt_get_transmitted_packets() { uint32_t packets = 0; aci_hal_le_tx_test_packet_number(&packets); diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 8259be2f6..860edfcd4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -20,6 +20,7 @@ enum HidReportId { ReportIdKeyboard = 1, ReportIdMouse = 2, ReportIdConsumer = 3, + ReportIdLEDState = 4, }; // Report numbers corresponded to the report id with an offset of 1 enum HidInputNumber { @@ -77,6 +78,13 @@ static const uint8_t furi_hal_bt_hid_report_map_data[] = { HID_USAGE_MINIMUM(0), HID_USAGE_MAXIMUM(101), HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), + HID_REPORT_ID(ReportIdLEDState), + HID_USAGE_PAGE(HID_PAGE_LED), + HID_REPORT_COUNT(8), + HID_REPORT_SIZE(1), + HID_USAGE_MINIMUM(1), + HID_USAGE_MAXIMUM(8), + HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), HID_END_COLLECTION, // Mouse Report HID_USAGE_PAGE(HID_PAGE_DESKTOP), @@ -125,6 +133,63 @@ FuriHalBtHidKbReport* kb_report = NULL; FuriHalBtHidMouseReport* mouse_report = NULL; FuriHalBtHidConsumerReport* consumer_report = NULL; +typedef struct { +// shortcuts +#define s_undefined data.bits.b_undefined +#define s_num_lock data.bits.b_num_lock +#define s_caps_lock data.bits.b_caps_lock +#define s_scroll_lock data.bits.b_scroll_lock +#define s_compose data.bits.b_compose +#define s_kana data.bits.b_kana +#define s_power data.bits.b_power +#define s_shift data.bits.b_shift +#define s_value data.value + union { + struct { + uint8_t b_undefined : 1; + uint8_t b_num_lock : 1; + uint8_t b_caps_lock : 1; + uint8_t b_scroll_lock : 1; + uint8_t b_compose : 1; + uint8_t b_kana : 1; + uint8_t b_power : 1; + uint8_t b_shift : 1; + } bits; + uint8_t value; + } data; +} __attribute__((__packed__)) FuriHalBtHidLedState; + +FuriHalBtHidLedState hid_host_led_state = {.s_value = 0}; + +uint16_t furi_hal_bt_hid_led_state_cb(uint8_t state, void* ctx) { + FuriHalBtHidLedState* led_state = (FuriHalBtHidLedState*)ctx; + + //FURI_LOG_D("HalBtHid", "LED state updated !"); + + led_state->s_value = state; + + return 0; +} + +uint8_t furi_hal_bt_hid_get_led_state(void) { + /*FURI_LOG_D( + "HalBtHid", + "LED state: RFU=%d NUMLOCK=%d CAPSLOCK=%d SCROLLLOCK=%d COMPOSE=%d KANA=%d POWER=%d SHIFT=%d", + hid_host_led_state.s_undefined, + hid_host_led_state.s_num_lock, + hid_host_led_state.s_caps_lock, + hid_host_led_state.s_scroll_lock, + hid_host_led_state.s_compose, + hid_host_led_state.s_kana, + hid_host_led_state.s_power, + hid_host_led_state.s_shift); + */ + + return (hid_host_led_state.s_value >> 1); // bit 0 is undefined (after shift bit location + // match with HID led state bits defines) + // see bad_bt_script.c (ducky_numlock_on function) +} + void furi_hal_bt_hid_start() { // Start device info if(!dev_info_svc_is_started()) { @@ -139,6 +204,8 @@ void furi_hal_bt_hid_start() { hid_svc_start(); } // Configure HID Keyboard + hid_svc_register_led_state_callback(furi_hal_bt_hid_led_state_cb, &hid_host_led_state); + kb_report = malloc(sizeof(FuriHalBtHidKbReport)); mouse_report = malloc(sizeof(FuriHalBtHidMouseReport)); consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport)); @@ -160,6 +227,8 @@ void furi_hal_bt_hid_stop() { furi_assert(kb_report); furi_assert(mouse_report); furi_assert(consumer_report); + + hid_svc_register_led_state_callback(NULL, NULL); // Stop all services if(dev_info_svc_is_started()) { dev_info_svc_stop(); @@ -180,12 +249,16 @@ void furi_hal_bt_hid_stop() { bool furi_hal_bt_hid_kb_press(uint16_t button) { furi_assert(kb_report); - for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { + uint8_t i; + for(i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { if(kb_report->key[i] == 0) { kb_report->key[i] = button & 0xFF; break; } } + if(i == FURI_HAL_BT_HID_KB_MAX_KEYS) { + return false; + } kb_report->mods |= (button >> 8); return hid_svc_update_input_report( ReportNumberKeyboard, (uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport)); diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 6ba38cb5e..f128b1064 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -218,6 +218,35 @@ float furi_hal_bt_get_rssi(); */ uint32_t furi_hal_bt_get_transmitted_packets(); +// BadBT stuff +/** Modify profile advertisement name and restart bluetooth + * @param[in] profile profile type + * @param[in] name new adv name +*/ +void furi_hal_bt_set_profile_adv_name( + FuriHalBtProfile profile, + const char name[FURI_HAL_BT_ADV_NAME_LENGTH]); + +const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile); + +/** Modify profile mac address and restart bluetooth + * @param[in] profile profile type + * @param[in] mac new mac address +*/ +void furi_hal_bt_set_profile_mac_addr( + FuriHalBtProfile profile, + const uint8_t mac_addr[GAP_MAC_ADDR_SIZE]); + +const uint8_t* furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile); + +uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi); + +void furi_hal_bt_set_profile_pairing_method(FuriHalBtProfile profile, GapPairing pairing_method); + +GapPairing furi_hal_bt_get_profile_pairing_method(FuriHalBtProfile profile); + +bool furi_hal_bt_is_connected(void); + /** Check & switch C2 to given mode * * @param[in] mode mode to switch into diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_hid.h b/firmware/targets/furi_hal_include/furi_hal_bt_hid.h index 4e74bbda7..56a8b4e48 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt_hid.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt_hid.h @@ -86,6 +86,13 @@ bool furi_hal_bt_hid_consumer_key_release(uint16_t button); */ bool furi_hal_bt_hid_consumer_key_release_all(); +/** Retrieves LED state from remote BT HID host + * + * @return (look at HID usage page to know what each bit of the returned byte means) + * NB: RFU bit has been shifted out in the returned octet so USB defines should work +*/ +uint8_t furi_hal_bt_hid_get_led_state(void); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index bfe691aeb..6a3e3154d 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -16,6 +16,7 @@ extern "C" { #define FURI_HAL_VERSION_NAME_LENGTH 8 #define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1) +#define FURI_HAL_BT_ADV_NAME_LENGTH (18 + 1) // 18 characters + null terminator /** BLE symbol + "Flipper " + name */ #define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + 8 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH) From 5c259fa0e21ef79b83087acce9ad0e10a8a13f98 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 13 May 2023 00:17:28 +0300 Subject: [PATCH 08/71] Set path just in case --- .../scenes/bt_settings_scene_forget_dev_confirm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c index d009ae2d5..150c3ef9f 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c @@ -30,6 +30,7 @@ bool bt_settings_scene_forget_dev_confirm_on_event(void* context, SceneManagerEv if(event.event == DialogExResultLeft) { consumed = scene_manager_previous_scene(app->scene_manager); } else if(event.event == DialogExResultRight) { + bt_keys_storage_set_default_path(app->bt); bt_forget_bonded_devices(app->bt); // Also remove keys of BadBT, Bluetooth Remote, TOTP Authenticator From e5c2e8e5d795971ca0b26e4ba9f8da3b36753df1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 13 May 2023 00:42:29 +0300 Subject: [PATCH 09/71] Added sniff pmkid on selected aps from 0.10.4 update by @clipboard1 https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/13/files --- .../scenes/wifi_marauder_scene_start.c | 7 +++++++ .../external/wifi_marauder_companion/wifi_marauder_app.h | 2 +- .../external/wifi_marauder_companion/wifi_marauder_app_i.h | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) 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 c77543fd0..0ec122ad9 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 @@ -104,6 +104,13 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { INPUT_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.h b/applications/external/wifi_marauder_companion/wifi_marauder_app.h index 2f8b2f9a6..303fb59b4 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.h @@ -4,7 +4,7 @@ extern "C" { #endif -#define WIFI_MARAUDER_APP_VERSION "v0.3.4" +#define WIFI_MARAUDER_APP_VERSION "v0.3.5" typedef struct WifiMarauderApp WifiMarauderApp; 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 2a16522bb..e37e5733b 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 (18) +#define NUM_MENU_ITEMS (19) #define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096) #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512) From c0a26cf1eb0d8aa143798543f43e8eeef62978b4 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 13 May 2023 14:43:11 +0300 Subject: [PATCH 10/71] SubGhz custom btn improved code readability some "if" optimization --- .../subghz/scenes/subghz_scene_transmitter.c | 4 +-- applications/main/subghz/views/transmitter.c | 18 +++++------ .../main/subghz_remote/subghz_remote_app.c | 2 +- lib/subghz/blocks/custom_btn.c | 14 ++++----- lib/subghz/blocks/custom_btn.h | 11 +++++-- lib/subghz/protocols/alutech_at_4n.c | 28 ++++++++--------- lib/subghz/protocols/keeloq.c | 31 +++++++++---------- lib/subghz/protocols/nice_flor_s.c | 24 +++++++------- lib/subghz/protocols/secplus_v2.c | 24 +++++++------- lib/subghz/protocols/somfy_telis.c | 24 +++++++------- 10 files changed, 90 insertions(+), 90 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index b07785c06..15a53603d 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -76,8 +76,8 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { subghz->state_notifications = SubGhzNotificationStateIDLE; subghz_txrx_stop(subghz->txrx); - if(subghz_custom_btn_get() != 0) { - subghz_custom_btn_set(0); + if(subghz_custom_btn_get() != SUBGHZ_CUSTOM_BTN_OK) { + subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK); uint8_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult(); furi_hal_subghz_set_rolling_counter_mult(0); // Calling restore! diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index 5ae1a6e2c..ad2b3efd7 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -137,7 +137,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { true); if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { - subghz_custom_btn_set(0); + subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK); with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, @@ -157,14 +157,14 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { // Temp Buttons (UP) if(can_be_sent && event->key == InputKeyUp && event->type == InputTypePress) { - subghz_custom_btn_set(1); + subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_UP); with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, { furi_string_reset(model->temp_button_id); if(subghz_custom_btn_get_original() != 0) { - if(subghz_custom_btn_get() == 1) { + if(subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_UP) { furi_string_printf( model->temp_button_id, "%01X", subghz_custom_btn_get_original()); model->draw_temp_button = true; @@ -182,14 +182,14 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { } // Down if(can_be_sent && event->key == InputKeyDown && event->type == InputTypePress) { - subghz_custom_btn_set(2); + subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_DOWN); with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, { furi_string_reset(model->temp_button_id); if(subghz_custom_btn_get_original() != 0) { - if(subghz_custom_btn_get() == 2) { + if(subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_DOWN) { furi_string_printf( model->temp_button_id, "%01X", subghz_custom_btn_get_original()); model->draw_temp_button = true; @@ -207,14 +207,14 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { } // Left if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypePress) { - subghz_custom_btn_set(3); + subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_LEFT); with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, { furi_string_reset(model->temp_button_id); if(subghz_custom_btn_get_original() != 0) { - if(subghz_custom_btn_get() == 3) { + if(subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_LEFT) { furi_string_printf( model->temp_button_id, "%01X", subghz_custom_btn_get_original()); model->draw_temp_button = true; @@ -232,14 +232,14 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { } // Right if(can_be_sent && event->key == InputKeyRight && event->type == InputTypePress) { - subghz_custom_btn_set(4); + subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_RIGHT); with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, { furi_string_reset(model->temp_button_id); if(subghz_custom_btn_get_original() != 0) { - if(subghz_custom_btn_get() == 4) { + if(subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_RIGHT) { furi_string_printf( model->temp_button_id, "%01X", subghz_custom_btn_get_original()); model->draw_temp_button = true; diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index d215a1d0e..dd23533cc 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -354,7 +354,7 @@ bool subghz_remote_key_load( bool res = false; - subghz_custom_btn_set(0); + subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK); keeloq_reset_original_btn(); subghz_custom_btns_reset(); diff --git a/lib/subghz/blocks/custom_btn.c b/lib/subghz/blocks/custom_btn.c index 3023e09ea..158c10a2f 100644 --- a/lib/subghz/blocks/custom_btn.c +++ b/lib/subghz/blocks/custom_btn.c @@ -1,14 +1,14 @@ #include "custom_btn.h" -static uint8_t custom_btn_id = 0; +static uint8_t custom_btn_id = SUBGHZ_CUSTOM_BTN_OK; static uint8_t custom_btn_original = 0; static uint8_t custom_btn_max_btns = 0; -void subghz_custom_btn_set(uint8_t b) { - if(b > custom_btn_max_btns) { - custom_btn_id = 0; +void subghz_custom_btn_set(uint8_t btn_id) { + if(btn_id > custom_btn_max_btns) { + custom_btn_id = SUBGHZ_CUSTOM_BTN_OK; } else { - custom_btn_id = b; + custom_btn_id = btn_id; } } @@ -16,8 +16,8 @@ uint8_t subghz_custom_btn_get() { return custom_btn_id; } -void subghz_custom_btn_set_original(uint8_t b) { - custom_btn_original = b; +void subghz_custom_btn_set_original(uint8_t btn_code) { + custom_btn_original = btn_code; } uint8_t subghz_custom_btn_get_original() { diff --git a/lib/subghz/blocks/custom_btn.h b/lib/subghz/blocks/custom_btn.h index 0ea5ee654..a92567321 100644 --- a/lib/subghz/blocks/custom_btn.h +++ b/lib/subghz/blocks/custom_btn.h @@ -4,11 +4,18 @@ #include #include -void subghz_custom_btn_set(uint8_t b); +// Default btn ID +#define SUBGHZ_CUSTOM_BTN_OK (0U) +#define SUBGHZ_CUSTOM_BTN_UP (1U) +#define SUBGHZ_CUSTOM_BTN_DOWN (2U) +#define SUBGHZ_CUSTOM_BTN_LEFT (3U) +#define SUBGHZ_CUSTOM_BTN_RIGHT (4U) + +void subghz_custom_btn_set(uint8_t btn_id); uint8_t subghz_custom_btn_get(); -void subghz_custom_btn_set_original(uint8_t b); +void subghz_custom_btn_set_original(uint8_t btn_code); uint8_t subghz_custom_btn_get_original(); diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index 47cf37d6b..6733613c4 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -334,11 +334,15 @@ static bool subghz_protocol_encoder_alutech_at_4n_get_upload( } uint8_t custom_btn_id = subghz_custom_btn_get(); - uint8_t original_btn_num = subghz_custom_btn_get_original(); + uint8_t original_btn_code = subghz_custom_btn_get_original(); // Set custom button - if(custom_btn_id == 1) { - switch(original_btn_num) { + // Basic set | 0x11 | 0x22 | 0xFF | 0x44 | 0x33 | + 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 0x11: btn = 0x22; break; @@ -358,9 +362,8 @@ static bool subghz_protocol_encoder_alutech_at_4n_get_upload( default: break; } - } - if(custom_btn_id == 2) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { case 0x11: btn = 0x44; break; @@ -380,9 +383,8 @@ static bool subghz_protocol_encoder_alutech_at_4n_get_upload( default: break; } - } - if(custom_btn_id == 3) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { case 0x11: btn = 0x33; break; @@ -402,9 +404,8 @@ static bool subghz_protocol_encoder_alutech_at_4n_get_upload( default: break; } - } - if(custom_btn_id == 4) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_RIGHT) { + switch(original_btn_code) { case 0x11: btn = 0xFF; break; @@ -426,9 +427,6 @@ static bool subghz_protocol_encoder_alutech_at_4n_get_upload( } } - if((custom_btn_id == 0) && (original_btn_num != 0)) { - btn = original_btn_num; - } // Gen new key if(!subghz_protocol_alutech_at_4n_gen_data(instance, btn)) { return false; diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index c9e603297..37e8c30e1 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -397,10 +397,15 @@ static bool } uint8_t custom_btn_id = subghz_custom_btn_get(); - uint8_t original_btn_num = subghz_custom_btn_get_original(); + uint8_t original_btn_code = subghz_custom_btn_get_original(); + // Set custom button - if(custom_btn_id == 1) { - switch(original_btn_num) { + // Basic set | 0x1 | 0x2 | 0x4 | 0x8 | 0xA or Special Learning Code | + 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; @@ -424,9 +429,8 @@ static bool btn = 0x1; break; } - } - if(custom_btn_id == 2) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { case 0x1: btn = 0x4; break; @@ -450,9 +454,8 @@ static bool btn = 0x4; break; } - } - if(custom_btn_id == 3) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { case 0x1: btn = 0x8; break; @@ -476,9 +479,8 @@ static bool btn = 0x8; break; } - } - if(custom_btn_id == 4) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_RIGHT) { + switch(original_btn_code) { case 0x1: btn = klq_last_custom_btn; break; @@ -503,11 +505,8 @@ static bool break; } } - if((custom_btn_id == 0) && (original_btn_num != 0)) { - btn = original_btn_num; - } + // Generate new key - if(subghz_protocol_keeloq_gen_data(instance, btn, true)) { // OK } else { diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index 5d8e7f587..f337ca105 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -136,11 +136,15 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( } uint8_t custom_btn_id = subghz_custom_btn_get(); - uint8_t original_btn_num = subghz_custom_btn_get_original(); + uint8_t original_btn_code = subghz_custom_btn_get_original(); // Set custom button - if(custom_btn_id == 1) { - switch(original_btn_num) { + // Basic set | 0x1 | 0x2 | 0x4 | 0x8 | + 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; @@ -157,9 +161,8 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( default: break; } - } - if(custom_btn_id == 2) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { case 0x1: btn = 0x4; break; @@ -176,9 +179,8 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( default: break; } - } - if(custom_btn_id == 3) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { case 0x1: btn = 0x8; break; @@ -197,10 +199,6 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( } } - if((custom_btn_id == 0) && (original_btn_num != 0)) { - btn = original_btn_num; - } - size_t size_upload = ((instance->generic.data_count_bit * 2) + ((37 + 2 + 2) * 2) * 16); if(size_upload > instance->encoder.size_upload) { FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 5d0ecb0dd..97631ce34 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -387,11 +387,15 @@ static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* i } uint8_t custom_btn_id = subghz_custom_btn_get(); - uint8_t original_btn_num = subghz_custom_btn_get_original(); + uint8_t original_btn_code = subghz_custom_btn_get_original(); // Set custom button - if(custom_btn_id == 1) { - switch(original_btn_num) { + // Basic set | 0x68 | 0x80 | 0x81 | 0xE2 | + if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) { + // Restore original button code + instance->generic.btn = original_btn_code; + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) { + switch(original_btn_code) { case 0x68: instance->generic.btn = 0x80; break; @@ -408,9 +412,8 @@ static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* i default: break; } - } - if(custom_btn_id == 2) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { case 0x68: instance->generic.btn = 0x81; break; @@ -427,9 +430,8 @@ static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* i default: break; } - } - if(custom_btn_id == 3) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { case 0x68: instance->generic.btn = 0xE2; break; @@ -447,9 +449,7 @@ static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* i break; } } - if((custom_btn_id == 0) && (original_btn_num != 0)) { - instance->generic.btn = original_btn_num; - } + uint32_t fixed_1[1] = {instance->generic.btn << 12 | instance->generic.serial >> 20}; uint32_t fixed_2[1] = {instance->generic.serial & 0xFFFFF}; uint8_t rolling_digits[18] = {0}; diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index 57c6c510b..fc4034d1f 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -116,11 +116,15 @@ static bool subghz_protocol_somfy_telis_gen_data( } uint8_t custom_btn_id = subghz_custom_btn_get(); - uint8_t original_btn_num = subghz_custom_btn_get_original(); + uint8_t original_btn_code = subghz_custom_btn_get_original(); // Set custom button - if(custom_btn_id == 1) { - switch(original_btn_num) { + // Basic set | 0x1 | 0x2 | 0x4 | 0x8 | + 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; @@ -137,9 +141,8 @@ static bool subghz_protocol_somfy_telis_gen_data( default: break; } - } - if(custom_btn_id == 2) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { case 0x1: btn = 0x4; break; @@ -156,9 +159,8 @@ static bool subghz_protocol_somfy_telis_gen_data( default: break; } - } - if(custom_btn_id == 3) { - switch(original_btn_num) { + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { case 0x1: btn = 0x8; break; @@ -177,10 +179,6 @@ static bool subghz_protocol_somfy_telis_gen_data( } } - if((custom_btn_id == 0) && (original_btn_num != 0)) { - btn = original_btn_num; - } - if(instance->generic.cnt < 0xFFFF) { if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { instance->generic.cnt = 0; From 23a24c42c10712f0a97da3c7a66c68570b335294 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 13 May 2023 16:48:51 +0300 Subject: [PATCH 11/71] SubGhz: Fix add manual Alutech AT 4N --- lib/subghz/protocols/alutech_at_4n.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index 6733613c4..83d98b7aa 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -308,8 +308,8 @@ bool subghz_protocol_alutech_at_4n_create_data( instance->generic.data_count_bit = 72; bool res = subghz_protocol_alutech_at_4n_gen_data(instance, btn); if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - if((res == SubGhzProtocolStatusOk) && + if((subghz_block_generic_serialize(&instance->generic, flipper_format, preset) != + SubGhzProtocolStatusOk) || !flipper_format_write_uint32(flipper_format, "CRC", &instance->crc, 1)) { FURI_LOG_E(TAG, "Unable to add CRC"); res = false; From a74461d903c7bfa9cc2db0c792a70cc0c3ebe4e1 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 13 May 2023 16:55:35 +0300 Subject: [PATCH 12/71] Removed duplicate code in transmitter.c --- applications/main/subghz/views/transmitter.c | 174 ++++++------------- 1 file changed, 57 insertions(+), 117 deletions(-) diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index ad2b3efd7..08db03334 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -124,7 +124,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { }, false); return false; - } + } // Finish "Back" key processing with_view_model( subghz_transmitter->view, @@ -136,124 +136,64 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { }, true); - if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { - subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK); - with_view_model( - subghz_transmitter->view, - SubGhzViewTransmitterModel * model, - { - furi_string_reset(model->temp_button_id); - model->draw_temp_button = false; - }, - true); - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); - return true; - } else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) { - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); - return true; - } + if(can_be_sent) { + if(event->key == InputKeyOk && event->type == InputTypePress) { + subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_OK); + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + furi_string_reset(model->temp_button_id); + model->draw_temp_button = false; + }, + true); + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); + return true; + } else if(event->key == InputKeyOk && event->type == InputTypeRelease) { + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); + return true; + } // Finish "OK" key processing - // Temp Buttons (UP) - if(can_be_sent && event->key == InputKeyUp && event->type == InputTypePress) { - subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_UP); - with_view_model( - subghz_transmitter->view, - SubGhzViewTransmitterModel * model, - { - furi_string_reset(model->temp_button_id); - if(subghz_custom_btn_get_original() != 0) { - if(subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_UP) { - furi_string_printf( - model->temp_button_id, "%01X", subghz_custom_btn_get_original()); - model->draw_temp_button = true; + uint8_t temp_btn_id; + if(event->key == InputKeyUp) { + temp_btn_id = SUBGHZ_CUSTOM_BTN_UP; + } else if(event->key == InputKeyDown) { + temp_btn_id = SUBGHZ_CUSTOM_BTN_DOWN; + } else if(event->key == InputKeyLeft) { + temp_btn_id = SUBGHZ_CUSTOM_BTN_LEFT; + } else if(event->key == InputKeyRight) { + temp_btn_id = SUBGHZ_CUSTOM_BTN_RIGHT; + } else { + // Finish processing if the button is different + return true; + } + + if(event->type == InputTypePress) { + subghz_custom_btn_set(temp_btn_id); + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + furi_string_reset(model->temp_button_id); + if(subghz_custom_btn_get_original() != 0) { + if(subghz_custom_btn_get() == temp_btn_id) { + furi_string_printf( + model->temp_button_id, "%01X", subghz_custom_btn_get_original()); + model->draw_temp_button = true; + } } - } - }, - true); - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); - return true; - } else if(can_be_sent && event->key == InputKeyUp && event->type == InputTypeRelease) { - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); - return true; - } - // Down - if(can_be_sent && event->key == InputKeyDown && event->type == InputTypePress) { - subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_DOWN); - with_view_model( - subghz_transmitter->view, - SubGhzViewTransmitterModel * model, - { - furi_string_reset(model->temp_button_id); - if(subghz_custom_btn_get_original() != 0) { - if(subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_DOWN) { - furi_string_printf( - model->temp_button_id, "%01X", subghz_custom_btn_get_original()); - model->draw_temp_button = true; - } - } - }, - true); - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); - return true; - } else if(can_be_sent && event->key == InputKeyDown && event->type == InputTypeRelease) { - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); - return true; - } - // Left - if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypePress) { - subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_LEFT); - with_view_model( - subghz_transmitter->view, - SubGhzViewTransmitterModel * model, - { - furi_string_reset(model->temp_button_id); - if(subghz_custom_btn_get_original() != 0) { - if(subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_LEFT) { - furi_string_printf( - model->temp_button_id, "%01X", subghz_custom_btn_get_original()); - model->draw_temp_button = true; - } - } - }, - true); - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); - return true; - } else if(can_be_sent && event->key == InputKeyLeft && event->type == InputTypeRelease) { - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); - return true; - } - // Right - if(can_be_sent && event->key == InputKeyRight && event->type == InputTypePress) { - subghz_custom_btn_set(SUBGHZ_CUSTOM_BTN_RIGHT); - with_view_model( - subghz_transmitter->view, - SubGhzViewTransmitterModel * model, - { - furi_string_reset(model->temp_button_id); - if(subghz_custom_btn_get_original() != 0) { - if(subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_RIGHT) { - furi_string_printf( - model->temp_button_id, "%01X", subghz_custom_btn_get_original()); - model->draw_temp_button = true; - } - } - }, - true); - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); - return true; - } else if(can_be_sent && event->key == InputKeyRight && event->type == InputTypeRelease) { - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); - return true; + }, + true); + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); + return true; + } else if(event->type == InputTypeRelease) { + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); + return true; + } } return true; From 07b2f0109a758255237bb397a65f73c8b673e136 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 13 May 2023 21:57:25 +0300 Subject: [PATCH 13/71] SubGhz: keloq --- lib/subghz/protocols/keeloq.c | 152 ++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 73 deletions(-) diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 37e8c30e1..bbe97237e 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -14,6 +14,10 @@ #define TAG "SubGhzProtocolKeeloq" +#define KEELOQ_PROG_MODE_OFF (0U) +#define KEELOQ_PROG_MODE_BFT (1U) +#define KEELOQ_PROG_MODE_APRIMATIC (2U) + static const SubGhzBlockConst subghz_protocol_keeloq_const = { .te_short = 400, .te_long = 800, @@ -92,10 +96,9 @@ static uint8_t klq_prog_mode; static uint16_t temp_counter; void keeloq_reset_original_btn() { - subghz_custom_btn_set_original(0); - subghz_custom_btn_set_max(0); + subghz_custom_btns_reset(); temp_counter = 0; - klq_prog_mode = 0; + klq_prog_mode = KEELOQ_PROG_MODE_OFF; } void keeloq_reset_mfname() { @@ -146,6 +149,7 @@ void subghz_protocol_encoder_keeloq_free(void* context) { * Key generation from simple data * @param instance Pointer to a SubGhzProtocolEncoderKeeloq* instance * @param btn Button number, 4 bit + * @param counter_up increasing the counter if the value is true */ static bool subghz_protocol_keeloq_gen_data( SubGhzProtocolEncoderKeeloq* instance, @@ -160,29 +164,30 @@ static bool subghz_protocol_keeloq_gen_data( instance->manufacture_name = ""; } - // BFT programming mode on / off conditions - if((strcmp(instance->manufacture_name, "BFT") == 0) && (btn == 0xF)) { - klq_prog_mode = 1; - } - if((strcmp(instance->manufacture_name, "BFT") == 0) && (btn != 0xF) && (klq_prog_mode == 1)) { - klq_prog_mode = 0; - } - // Aprimatic programming mode on / off conditions - if((strcmp(instance->manufacture_name, "Aprimatic") == 0) && (btn == 0xF)) { - klq_prog_mode = 2; - } - if((strcmp(instance->manufacture_name, "Aprimatic") == 0) && (btn != 0xF) && - (klq_prog_mode == 2)) { - klq_prog_mode = 0; + // programming mode on / off conditions + if(strcmp(instance->manufacture_name, "BFT") == 0) { + // BFT programming mode on / off conditions + if(btn == 0xF) { + klq_prog_mode = KEELOQ_PROG_MODE_BFT; + } else if(klq_prog_mode == KEELOQ_PROG_MODE_BFT) { + klq_prog_mode = KEELOQ_PROG_MODE_OFF; + } + } else if(strcmp(instance->manufacture_name, "Aprimatic") == 0) { + // Aprimatic programming mode on / off conditions + if(btn == 0xF) { + klq_prog_mode = KEELOQ_PROG_MODE_APRIMATIC; + } else if(klq_prog_mode == KEELOQ_PROG_MODE_APRIMATIC) { + klq_prog_mode = KEELOQ_PROG_MODE_OFF; + } } // If we using BFT programming mode we will trasmit its seed in hop part like original remote - if(klq_prog_mode == 1) { + if(klq_prog_mode == KEELOQ_PROG_MODE_BFT) { hop = instance->generic.seed; - } else if(klq_prog_mode == 2) { + } else if(klq_prog_mode == KEELOQ_PROG_MODE_APRIMATIC) { // If we using Aprimatic programming mode we will trasmit some strange looking hop value, why? cuz manufacturer did it this way :) hop = 0x1A2B3C4D; } - if(counter_up && klq_prog_mode == 0) { + if(counter_up && klq_prog_mode == KEELOQ_PROG_MODE_OFF) { if(instance->generic.cnt < 0xFFFF) { if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { instance->generic.cnt = 0; @@ -193,50 +198,8 @@ static bool subghz_protocol_keeloq_gen_data( instance->generic.cnt = 0; } } - if(klq_prog_mode == 0) { - uint32_t decrypt = (uint32_t)btn << 28 | - (instance->generic.serial & 0x3FF) - << 16 | //ToDo in some protocols the discriminator is 0 - instance->generic.cnt; - - if(strcmp(instance->manufacture_name, "Aprimatic") == 0) { - // Aprimatic uses 12bit serial number + 2bit APR1 "parity" bit in front of it replacing first 2 bits of serial - // Thats in theory! We need to check if this is true for all Aprimatic remotes but we got only 3 recordings to test - // For now lets assume that this is true for all Aprimatic remotes, if not we will need to add some more code here - uint32_t apri_serial = instance->generic.serial; - uint8_t apr1 = 0; - for(uint16_t i = 1; i != 0b10000000000; i <<= 1) { - if(apri_serial & i) apr1++; - } - apri_serial &= 0b00001111111111; - if(apr1 % 2 == 0) { - apri_serial |= 0b110000000000; - } - decrypt = btn << 28 | (apri_serial & 0xFFF) << 16 | instance->generic.cnt; - } - - // DTM Neo, Came_Space uses 12bit -> simple learning -- FAAC_RC,XT , Mutanco_Mutancode, Stilmatic(Schellenberg) -> 12bit normal learning - if((strcmp(instance->manufacture_name, "DTM_Neo") == 0) || - (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || - (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0) || - (strcmp(instance->manufacture_name, "Stilmatic") == 0) || - (strcmp(instance->manufacture_name, "Came_Space") == 0)) { - decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | instance->generic.cnt; - } - - // Nice Smilo, MHouse, JCM, Normstahl -> 8bit serial - simple learning - if((strcmp(instance->manufacture_name, "NICE_Smilo") == 0) || - (strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0) || - (strcmp(instance->manufacture_name, "JCM_Tech") == 0) || - (strcmp(instance->manufacture_name, "Normstahl") == 0)) { - decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; - } - - // Beninca / Allmatic -> no serial - simple XOR - if(strcmp(instance->manufacture_name, "Beninca") == 0) { - decrypt = btn << 28 | (0x000) << 16 | instance->generic.cnt; - } - + if(klq_prog_mode == KEELOQ_PROG_MODE_OFF) { + // Protocols that do not use encryption if(strcmp(instance->manufacture_name, "Unknown") == 0) { code_found_reverse = subghz_protocol_blocks_reverse_key( instance->generic.data, instance->generic.data_count_bit); @@ -247,6 +210,49 @@ static bool subghz_protocol_keeloq_gen_data( } else if(strcmp(instance->manufacture_name, "HCS101") == 0) { hop = instance->generic.cnt << 16 | (btn & 0xF) << 12 | 0x000; } else { + // Protocols that use encryption + uint32_t decrypt = (uint32_t)btn << 28 | + (instance->generic.serial & 0x3FF) + << 16 | //ToDo in some protocols the discriminator is 0 + instance->generic.cnt; + + if(strcmp(instance->manufacture_name, "Aprimatic") == 0) { + // Aprimatic uses 12bit serial number + 2bit APR1 "parity" bit in front of it replacing first 2 bits of serial + // Thats in theory! We need to check if this is true for all Aprimatic remotes but we got only 3 recordings to test + // For now lets assume that this is true for all Aprimatic remotes, if not we will need to add some more code here + uint32_t apri_serial = instance->generic.serial; + uint8_t apr1 = 0; + for(uint16_t i = 1; i != 0b10000000000; i <<= 1) { + if(apri_serial & i) apr1++; + } + apri_serial &= 0b00001111111111; + if(apr1 % 2 == 0) { + apri_serial |= 0b110000000000; + } + decrypt = btn << 28 | (apri_serial & 0xFFF) << 16 | instance->generic.cnt; + } else if( + (strcmp(instance->manufacture_name, "DTM_Neo") == 0) || + (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || + (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0) || + (strcmp(instance->manufacture_name, "Stilmatic") == 0) || + (strcmp(instance->manufacture_name, "Came_Space") == 0)) { + // DTM Neo, Came_Space uses 12bit serial -> simple learning + // FAAC_RC,XT , Mutanco_Mutancode, Stilmatic(Schellenberg) 12bit serial -> normal learning + decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | + instance->generic.cnt; + } else if( + (strcmp(instance->manufacture_name, "NICE_Smilo") == 0) || + (strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0) || + (strcmp(instance->manufacture_name, "JCM_Tech") == 0) || + (strcmp(instance->manufacture_name, "Normstahl") == 0)) { + // Nice Smilo, MHouse, JCM, Normstahl -> 8bit serial - simple learning + decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 | + instance->generic.cnt; + } else if(strcmp(instance->manufacture_name, "Beninca") == 0) { + decrypt = btn << 28 | (0x000) << 16 | instance->generic.cnt; + // Beninca / Allmatic -> no serial - simple XOR + } + for M_EACH( manufacture_code, @@ -316,8 +322,8 @@ static bool subghz_protocol_keeloq_gen_data( uint64_t yek = (uint64_t)fix << 32 | hop; instance->generic.data = subghz_protocol_blocks_reverse_key(yek, instance->generic.data_count_bit); - } - return true; + } // What should happen if seed = 0 in bft programming mode + return true; // Always return true } bool subghz_protocol_keeloq_create_data( @@ -385,9 +391,9 @@ static bool if(instance->manufacture_name == 0x0) { instance->manufacture_name = ""; } - if(klq_prog_mode == 1) { + if(klq_prog_mode == KEELOQ_PROG_MODE_BFT) { instance->manufacture_name = "BFT"; - } else if(klq_prog_mode == 2) { + } else if(klq_prog_mode == KEELOQ_PROG_MODE_APRIMATIC) { instance->manufacture_name = "Aprimatic"; } uint8_t klq_last_custom_btn = 0xA; @@ -673,7 +679,7 @@ void* subghz_protocol_decoder_keeloq_alloc(SubGhzEnvironment* environment) { instance->keystore = subghz_environment_get_keystore(environment); instance->manufacture_from_file = furi_string_alloc(); - klq_prog_mode = 0; + klq_prog_mode = KEELOQ_PROG_MODE_OFF; return instance; } @@ -1192,8 +1198,8 @@ static void subghz_protocol_keeloq_check_remote_controller( uint32_t key_fix = key >> 32; uint32_t key_hop = key & 0x00000000ffffffff; - // If we are in BFT programming mode we will set previous remembered counter and skip mf keys check - if(klq_prog_mode == 0) { + // If we are in BFT / Aprimatic programming mode we will set previous remembered counter and skip mf keys check + if(klq_prog_mode == KEELOQ_PROG_MODE_OFF) { // Check key AN-Motors if((key_hop >> 24) == ((key_hop >> 16) & 0x00ff) && (key_fix >> 28) == ((key_hop >> 12) & 0x0f) && (key_hop & 0xFFF) == 0x404) { @@ -1210,11 +1216,11 @@ static void subghz_protocol_keeloq_check_remote_controller( } temp_counter = instance->cnt; - } else if(klq_prog_mode == 1) { + } else if(klq_prog_mode == KEELOQ_PROG_MODE_BFT) { *manufacture_name = "BFT"; mfname = *manufacture_name; instance->cnt = temp_counter; - } else if(klq_prog_mode == 2) { + } else if(klq_prog_mode == KEELOQ_PROG_MODE_APRIMATIC) { *manufacture_name = "Aprimatic"; mfname = *manufacture_name; instance->cnt = temp_counter; From 6b56de5c93adaf13157ef137ebb92e4e574198ec Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 13 May 2023 23:16:28 +0300 Subject: [PATCH 14/71] SubGhz some changes --- .../main/subghz_remote/subghz_remote_app.c | 4 +- lib/subghz/protocols/alutech_at_4n.c | 199 ++++++++------- lib/subghz/protocols/keeloq.c | 237 ++++++++++-------- lib/subghz/protocols/nice_flor_s.c | 139 +++++----- lib/subghz/protocols/secplus_v2.c | 139 +++++----- lib/subghz/protocols/somfy_telis.c | 139 +++++----- 6 files changed, 465 insertions(+), 392 deletions(-) diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index dd23533cc..8cef4da3a 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -12,7 +12,9 @@ #include #include -#include +#include +#include +#include #include #include diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index 83d98b7aa..281e820f0 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -318,6 +318,13 @@ bool subghz_protocol_alutech_at_4n_create_data( return res; } +/** + * Defines the button value for the current btn_id + * Basic set | 0x11 | 0x22 | 0xFF | 0x44 | 0x33 | + * @return Button code + */ +static uint8_t subghz_protocol_alutech_at_4n_get_btn_code(); + /** * Generating an upload from data. * @param instance Pointer to a SubGhzProtocolEncoderAlutech instance @@ -333,99 +340,7 @@ static bool subghz_protocol_encoder_alutech_at_4n_get_upload( subghz_custom_btn_set_original(btn); } - uint8_t custom_btn_id = subghz_custom_btn_get(); - uint8_t original_btn_code = subghz_custom_btn_get_original(); - - // Set custom button - // Basic set | 0x11 | 0x22 | 0xFF | 0x44 | 0x33 | - 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 0x11: - btn = 0x22; - break; - case 0x22: - btn = 0x11; - break; - case 0xFF: - btn = 0x11; - break; - case 0x44: - btn = 0x11; - break; - case 0x33: - btn = 0x11; - break; - - default: - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { - switch(original_btn_code) { - case 0x11: - btn = 0x44; - break; - case 0x22: - btn = 0x44; - break; - case 0xFF: - btn = 0x44; - break; - case 0x44: - btn = 0xFF; - break; - case 0x33: - btn = 0x44; - break; - - default: - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { - switch(original_btn_code) { - case 0x11: - btn = 0x33; - break; - case 0x22: - btn = 0x33; - break; - case 0xFF: - btn = 0x33; - break; - case 0x44: - btn = 0x33; - break; - case 0x33: - btn = 0x22; - break; - - default: - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_RIGHT) { - switch(original_btn_code) { - case 0x11: - btn = 0xFF; - break; - case 0x22: - btn = 0xFF; - break; - case 0xFF: - btn = 0x22; - break; - case 0x44: - btn = 0x22; - break; - case 0x33: - btn = 0xFF; - break; - - default: - break; - } - } + btn = subghz_protocol_alutech_at_4n_get_btn_code(); // Gen new key if(!subghz_protocol_alutech_at_4n_gen_data(instance, btn)) { @@ -786,6 +701,104 @@ SubGhzProtocolStatus subghz_protocol_decoder_alutech_at_4n_deserialize( return ret; } +static uint8_t subghz_protocol_alutech_at_4n_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 0x11: + btn = 0x22; + break; + case 0x22: + btn = 0x11; + break; + case 0xFF: + btn = 0x11; + break; + case 0x44: + btn = 0x11; + break; + case 0x33: + btn = 0x11; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { + case 0x11: + btn = 0x44; + break; + case 0x22: + btn = 0x44; + break; + case 0xFF: + btn = 0x44; + break; + case 0x44: + btn = 0xFF; + break; + case 0x33: + btn = 0x44; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { + case 0x11: + btn = 0x33; + break; + case 0x22: + btn = 0x33; + break; + case 0xFF: + btn = 0x33; + break; + case 0x44: + btn = 0x33; + break; + case 0x33: + btn = 0x22; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_RIGHT) { + switch(original_btn_code) { + case 0x11: + btn = 0xFF; + break; + case 0x22: + btn = 0xFF; + break; + case 0xFF: + btn = 0x22; + break; + case 0x44: + btn = 0x22; + break; + case 0x33: + btn = 0xFF; + break; + + default: + break; + } + } + + return btn; +} + void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderAlutech_at_4n* instance = context; diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index bbe97237e..6c7e074fe 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -374,6 +374,14 @@ bool subghz_protocol_keeloq_bft_create_data( return res; } +/** + * Defines the button value for the current btn_id + * Basic set | 0x1 | 0x2 | 0x4 | 0x8 | 0xA or Special Learning Code | + * @param last_btn_code Candidate for the last button + * @return Button code + */ +static uint8_t subghz_protocol_keeloq_get_btn_code(uint8_t last_btn_code); + /** * Generating an upload from data. * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance @@ -402,115 +410,7 @@ static bool klq_last_custom_btn = 0xF; } - uint8_t custom_btn_id = subghz_custom_btn_get(); - uint8_t original_btn_code = subghz_custom_btn_get_original(); - - // Set custom button - // Basic set | 0x1 | 0x2 | 0x4 | 0x8 | 0xA or Special Learning Code | - 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 0xA: - btn = 0x1; - break; - case 0x4: - btn = 0x1; - break; - case 0x8: - btn = 0x1; - break; - case 0xF: - btn = 0x1; - break; - - default: - btn = 0x1; - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { - switch(original_btn_code) { - case 0x1: - btn = 0x4; - break; - case 0x2: - btn = 0x4; - break; - case 0xA: - btn = 0x4; - break; - case 0x4: - btn = klq_last_custom_btn; - break; - case 0x8: - btn = 0x4; - break; - case 0xF: - btn = 0x4; - break; - - default: - btn = 0x4; - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { - switch(original_btn_code) { - case 0x1: - btn = 0x8; - break; - case 0x2: - btn = 0x8; - break; - case 0xA: - btn = 0x8; - break; - case 0x4: - btn = 0x8; - break; - case 0x8: - btn = 0x2; - break; - case 0xF: - btn = 0x8; - break; - - default: - btn = 0x8; - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_RIGHT) { - switch(original_btn_code) { - case 0x1: - btn = klq_last_custom_btn; - break; - case 0x2: - btn = klq_last_custom_btn; - break; - case 0xA: - btn = 0x2; - break; - case 0x4: - btn = 0x2; - break; - case 0x8: - btn = klq_last_custom_btn; - break; - case 0xF: - btn = 0x2; - break; - - default: - btn = 0x2; - break; - } - } + btn = subghz_protocol_keeloq_get_btn_code(klq_last_custom_btn); // Generate new key if(subghz_protocol_keeloq_gen_data(instance, btn, true)) { @@ -1330,6 +1230,125 @@ SubGhzProtocolStatus return res; } +static uint8_t subghz_protocol_keeloq_get_btn_code(uint8_t last_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; + + if(last_btn_code == 0) { + last_btn_code = 0xA; + } + + // Set custom button + // Basic set | 0x1 | 0x2 | 0x4 | 0x8 | 0xA or Special Learning Code | + 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 0xA: + btn = 0x1; + break; + case 0x4: + btn = 0x1; + break; + case 0x8: + btn = 0x1; + break; + case 0xF: + btn = 0x1; + break; + + default: + btn = 0x1; + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { + case 0x1: + btn = 0x4; + break; + case 0x2: + btn = 0x4; + break; + case 0xA: + btn = 0x4; + break; + case 0x4: + btn = last_btn_code; + break; + case 0x8: + btn = 0x4; + break; + case 0xF: + btn = 0x4; + break; + + default: + btn = 0x4; + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { + case 0x1: + btn = 0x8; + break; + case 0x2: + btn = 0x8; + break; + case 0xA: + btn = 0x8; + break; + case 0x4: + btn = 0x8; + break; + case 0x8: + btn = 0x2; + break; + case 0xF: + btn = 0x8; + break; + + default: + btn = 0x8; + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_RIGHT) { + switch(original_btn_code) { + case 0x1: + btn = last_btn_code; + break; + case 0x2: + btn = last_btn_code; + break; + case 0xA: + btn = 0x2; + break; + case 0x4: + btn = 0x2; + break; + case 0x8: + btn = last_btn_code; + break; + case 0xF: + btn = 0x2; + break; + + default: + btn = 0x2; + break; + } + } + + return btn; +} + void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderKeeloq* instance = context; diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index f337ca105..5eaa21274 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -117,6 +117,13 @@ void subghz_protocol_encoder_nice_flor_s_free(void* context) { static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit); +/** + * Defines the button value for the current btn_id + * Basic set | 0x1 | 0x2 | 0x4 | 0x8 | + * @return Button code + */ +static uint8_t subghz_protocol_nice_flor_s_get_btn_code(); + /** * Generating an upload from data. * @param instance Pointer to a SubGhzProtocolEncoderNiceFlorS instance @@ -135,69 +142,7 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( subghz_custom_btn_set_original(btn); } - uint8_t custom_btn_id = subghz_custom_btn_get(); - uint8_t original_btn_code = subghz_custom_btn_get_original(); - - // Set custom button - // Basic set | 0x1 | 0x2 | 0x4 | 0x8 | - 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 0x4: - btn = 0x1; - break; - case 0x8: - btn = 0x1; - break; - - default: - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { - switch(original_btn_code) { - case 0x1: - btn = 0x4; - break; - case 0x2: - btn = 0x4; - break; - case 0x4: - btn = 0x2; - break; - case 0x8: - btn = 0x4; - break; - - default: - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { - switch(original_btn_code) { - case 0x1: - btn = 0x8; - break; - case 0x2: - btn = 0x8; - break; - case 0x4: - btn = 0x8; - break; - case 0x8: - btn = 0x2; - break; - - default: - break; - } - } + btn = subghz_protocol_nice_flor_s_get_btn_code(); size_t size_upload = ((instance->generic.data_count_bit * 2) + ((37 + 2 + 2) * 2) * 16); if(size_upload > instance->encoder.size_upload) { @@ -806,6 +751,74 @@ SubGhzProtocolStatus return ret; } +static uint8_t subghz_protocol_nice_flor_s_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 0x4: + btn = 0x1; + break; + case 0x8: + btn = 0x1; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { + case 0x1: + btn = 0x4; + break; + case 0x2: + btn = 0x4; + break; + case 0x4: + btn = 0x2; + break; + case 0x8: + btn = 0x4; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { + case 0x1: + btn = 0x8; + break; + case 0x2: + btn = 0x8; + break; + case 0x4: + btn = 0x8; + break; + case 0x8: + btn = 0x2; + break; + + default: + break; + } + } + + return btn; +} + void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 97631ce34..d39f794ab 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -375,6 +375,13 @@ static uint64_t subghz_protocol_secplus_v2_encode_half(uint8_t roll_array[], uin return data; } +/** + * Defines the button value for the current btn_id + * Basic set | 0x68 | 0x80 | 0x81 | 0xE2 | + * @return Button code + */ +static uint8_t subghz_protocol_secplus_v2_get_btn_code(); + /** * Security+ 2.0 message encoding * @param instance SubGhzProtocolEncoderSecPlus_v2* @@ -386,69 +393,7 @@ static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* i subghz_custom_btn_set_original(instance->generic.btn); } - uint8_t custom_btn_id = subghz_custom_btn_get(); - uint8_t original_btn_code = subghz_custom_btn_get_original(); - - // Set custom button - // Basic set | 0x68 | 0x80 | 0x81 | 0xE2 | - if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) { - // Restore original button code - instance->generic.btn = original_btn_code; - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) { - switch(original_btn_code) { - case 0x68: - instance->generic.btn = 0x80; - break; - case 0x80: - instance->generic.btn = 0x68; - break; - case 0x81: - instance->generic.btn = 0x80; - break; - case 0xE2: - instance->generic.btn = 0x80; - break; - - default: - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { - switch(original_btn_code) { - case 0x68: - instance->generic.btn = 0x81; - break; - case 0x80: - instance->generic.btn = 0x81; - break; - case 0x81: - instance->generic.btn = 0x68; - break; - case 0xE2: - instance->generic.btn = 0x81; - break; - - default: - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { - switch(original_btn_code) { - case 0x68: - instance->generic.btn = 0xE2; - break; - case 0x80: - instance->generic.btn = 0xE2; - break; - case 0x81: - instance->generic.btn = 0xE2; - break; - case 0xE2: - instance->generic.btn = 0x68; - break; - - default: - break; - } - } + instance->generic.btn = subghz_protocol_secplus_v2_get_btn_code(); uint32_t fixed_1[1] = {instance->generic.btn << 12 | instance->generic.serial >> 20}; uint32_t fixed_2[1] = {instance->generic.serial & 0xFFFFF}; @@ -888,6 +833,74 @@ SubGhzProtocolStatus return ret; } +static uint8_t subghz_protocol_secplus_v2_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 0x68: + btn = 0x80; + break; + case 0x80: + btn = 0x68; + break; + case 0x81: + btn = 0x80; + break; + case 0xE2: + btn = 0x80; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { + case 0x68: + btn = 0x81; + break; + case 0x80: + btn = 0x81; + break; + case 0x81: + btn = 0x68; + break; + case 0xE2: + btn = 0x81; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { + case 0x68: + btn = 0xE2; + break; + case 0x80: + btn = 0xE2; + break; + case 0x81: + btn = 0xE2; + break; + case 0xE2: + btn = 0x68; + break; + + default: + break; + } + } + + return btn; +} + void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v2* instance = context; diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index fc4034d1f..6a159a7ea 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -97,6 +97,13 @@ void subghz_protocol_encoder_somfy_telis_free(void* context) { free(instance); } +/** + * Defines the button value for the current btn_id + * Basic set | 0x1 | 0x2 | 0x4 | 0x8 | + * @return Button code + */ +static uint8_t subghz_protocol_somfy_telis_get_btn_code(); + static bool subghz_protocol_somfy_telis_gen_data( SubGhzProtocolEncoderSomfyTelis* instance, uint8_t btn, @@ -115,69 +122,7 @@ static bool subghz_protocol_somfy_telis_gen_data( subghz_custom_btn_set_original(btn); } - uint8_t custom_btn_id = subghz_custom_btn_get(); - uint8_t original_btn_code = subghz_custom_btn_get_original(); - - // Set custom button - // Basic set | 0x1 | 0x2 | 0x4 | 0x8 | - 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 0x4: - btn = 0x1; - break; - case 0x8: - btn = 0x1; - break; - - default: - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { - switch(original_btn_code) { - case 0x1: - btn = 0x4; - break; - case 0x2: - btn = 0x4; - break; - case 0x4: - btn = 0x2; - break; - case 0x8: - btn = 0x4; - break; - - default: - break; - } - } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { - switch(original_btn_code) { - case 0x1: - btn = 0x8; - break; - case 0x2: - btn = 0x8; - break; - case 0x4: - btn = 0x8; - break; - case 0x8: - btn = 0x2; - break; - - default: - break; - } - } + btn = subghz_protocol_somfy_telis_get_btn_code(); if(instance->generic.cnt < 0xFFFF) { if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { @@ -723,6 +668,74 @@ SubGhzProtocolStatus subghz_protocol_somfy_telis_const.min_count_bit_for_found); } +static uint8_t subghz_protocol_somfy_telis_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 0x4: + btn = 0x1; + break; + case 0x8: + btn = 0x1; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { + case 0x1: + btn = 0x4; + break; + case 0x2: + btn = 0x4; + break; + case 0x4: + btn = 0x2; + break; + case 0x8: + btn = 0x4; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { + case 0x1: + btn = 0x8; + break; + case 0x2: + btn = 0x8; + break; + case 0x4: + btn = 0x8; + break; + case 0x8: + btn = 0x2; + break; + + default: + break; + } + } + + return btn; +} + void subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderSomfyTelis* instance = context; From 71f48ea39ef148c4c7ca97680be2e210964eae21 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sun, 14 May 2023 00:48:06 +0300 Subject: [PATCH 15/71] Keeloq and Star line remove duplicate code --- lib/subghz/protocols/keeloq.c | 216 +++++-------------------------- lib/subghz/protocols/star_line.c | 102 ++------------- 2 files changed, 40 insertions(+), 278 deletions(-) diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 6c7e074fe..9b86eb5e8 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -739,14 +739,19 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( uint8_t btn = (uint8_t)(fix >> 28); uint32_t decrypt = 0; uint64_t man; - int res = 0; + bool mf_not_set = false; if(mfname == 0x0) { mfname = ""; } - if(strcmp(mfname, "") == 0) { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + if(strcmp(mfname, "Unknown") == 0) { + return 1; + } else if(strcmp(mfname, "") == 0) { + mf_not_set = true; + } + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + if(mf_not_set || (strcmp(furi_string_get_cstr(manufacture_code->name), mfname) == 0)) { switch(manufacture_code->type) { case KEELOQ_LEARNING_SIMPLE: // Simple Learning @@ -800,23 +805,29 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( } break; case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: - man = subghz_protocol_keeloq_common_magic_serial_type2_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; + if(mf_not_set) { + man = subghz_protocol_keeloq_common_magic_serial_type2_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } } break; case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: - man = subghz_protocol_keeloq_common_magic_serial_type3_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; + if(mf_not_set) { + man = subghz_protocol_keeloq_common_magic_serial_type3_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } } break; case KEELOQ_LEARNING_UNKNOWN: @@ -914,174 +925,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( break; } } - } else if(strcmp(mfname, "Unknown") == 0) { - return 1; - } else { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); - if(res == 0) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - // Simple Learning - decrypt = - subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_NORMAL: - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man = subghz_protocol_keeloq_common_normal_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_SECURE: - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->seed, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: - man = subghz_protocol_keeloq_common_magic_serial_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_UNKNOWN: - // Simple Learning - decrypt = - subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - // Check for mirrored man - uint64_t man_rev = 0; - uint64_t man_rev_byte = 0; - for(uint8_t i = 0; i < 64; i += 8) { - man_rev_byte = (uint8_t)(manufacture_code->key >> i); - man_rev = man_rev | man_rev_byte << (56 - i); - } - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - //########################### - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man = subghz_protocol_keeloq_common_normal_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - - // Secure Learning - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->seed, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 3; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->seed, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 3; - return 1; - } - - // Magic xor type1 learning - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 4; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_magic_xor_type1_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 4; - return 1; - } - - break; - } - } - } - } + } *manufacture_name = "Unknown"; mfname = "Unknown"; diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 792622412..83a4846cd 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -522,14 +522,19 @@ static uint8_t subghz_protocol_star_line_check_remote_controller_selector( uint8_t btn = (uint8_t)(fix >> 24); uint32_t decrypt = 0; uint64_t man_normal_learning; - int res = 0; + bool mf_not_set = false; if(mfname == 0x0) { mfname = ""; } - if(strcmp(mfname, "") == 0) { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + if(strcmp(mfname, "Unknown") == 0) { + return 1; + } else if(strcmp(mfname, "") == 0) { + mf_not_set = true; + } + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + if(mf_not_set || strcmp(furi_string_get_cstr(manufacture_code->name), mfname)) { switch(manufacture_code->type) { case KEELOQ_LEARNING_SIMPLE: // Simple Learning @@ -606,94 +611,7 @@ static uint8_t subghz_protocol_star_line_check_remote_controller_selector( break; } } - } else if(strcmp(mfname, "Unknown") == 0) { - return 1; - } else { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); - if(res == 0) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - // Simple Learning - decrypt = - subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_NORMAL: - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man_normal_learning = subghz_protocol_keeloq_common_normal_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_UNKNOWN: - // Simple Learning - decrypt = - subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - // Check for mirrored man - uint64_t man_rev = 0; - uint64_t man_rev_byte = 0; - for(uint8_t i = 0; i < 64; i += 8) { - man_rev_byte = (uint8_t)(manufacture_code->key >> i); - man_rev = man_rev | man_rev_byte << (56 - i); - } - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - //########################### - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man_normal_learning = subghz_protocol_keeloq_common_normal_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - - // Check for mirrored man - man_normal_learning = - subghz_protocol_keeloq_common_normal_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - break; - } - } - } - } + } *manufacture_name = "Unknown"; mfname = "Unknown"; From d87df3e6c25dddc71891c255dffdf05a7e81ea4d Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sun, 14 May 2023 01:34:43 +0300 Subject: [PATCH 16/71] Change custom btn sending logic --- applications/main/subghz/views/transmitter.c | 73 ++++++++++---------- firmware/targets/f7/api_symbols.csv | 5 +- lib/subghz/blocks/custom_btn.c | 8 ++- lib/subghz/blocks/custom_btn.h | 6 +- 4 files changed, 52 insertions(+), 40 deletions(-) diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index 08db03334..7ae865064 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -156,43 +156,46 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { return true; } // Finish "OK" key processing - uint8_t temp_btn_id; - if(event->key == InputKeyUp) { - temp_btn_id = SUBGHZ_CUSTOM_BTN_UP; - } else if(event->key == InputKeyDown) { - temp_btn_id = SUBGHZ_CUSTOM_BTN_DOWN; - } else if(event->key == InputKeyLeft) { - temp_btn_id = SUBGHZ_CUSTOM_BTN_LEFT; - } else if(event->key == InputKeyRight) { - temp_btn_id = SUBGHZ_CUSTOM_BTN_RIGHT; - } else { - // Finish processing if the button is different - return true; - } + if(subghz_custom_btn_is_allowed()) { + uint8_t temp_btn_id; + if(event->key == InputKeyUp) { + temp_btn_id = SUBGHZ_CUSTOM_BTN_UP; + } else if(event->key == InputKeyDown) { + temp_btn_id = SUBGHZ_CUSTOM_BTN_DOWN; + } else if(event->key == InputKeyLeft) { + temp_btn_id = SUBGHZ_CUSTOM_BTN_LEFT; + } else if(event->key == InputKeyRight) { + temp_btn_id = SUBGHZ_CUSTOM_BTN_RIGHT; + } else { + // Finish processing if the button is different + return true; + } - if(event->type == InputTypePress) { - subghz_custom_btn_set(temp_btn_id); - with_view_model( - subghz_transmitter->view, - SubGhzViewTransmitterModel * model, - { - furi_string_reset(model->temp_button_id); - if(subghz_custom_btn_get_original() != 0) { - if(subghz_custom_btn_get() == temp_btn_id) { - furi_string_printf( - model->temp_button_id, "%01X", subghz_custom_btn_get_original()); - model->draw_temp_button = true; + if(event->type == InputTypePress) { + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + furi_string_reset(model->temp_button_id); + if(subghz_custom_btn_get_original() != 0) { + if(subghz_custom_btn_set(temp_btn_id)) { + furi_string_printf( + model->temp_button_id, + "%01X", + subghz_custom_btn_get_original()); + model->draw_temp_button = true; + } } - } - }, - true); - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); - return true; - } else if(event->type == InputTypeRelease) { - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); - return true; + }, + true); + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); + return true; + } else if(event->type == InputTypeRelease) { + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); + return true; + } } } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 7eac9ab00..4bf96d2a9 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,26.2,, +Version,+,26.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2703,7 +2703,8 @@ Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" Function,+,subghz_block_generic_serialize,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_custom_btn_get,uint8_t, Function,-,subghz_custom_btn_get_original,uint8_t, -Function,-,subghz_custom_btn_set,void,uint8_t +Function,-,subghz_custom_btn_is_allowed,_Bool, +Function,-,subghz_custom_btn_set,_Bool,uint8_t Function,-,subghz_custom_btn_set_max,void,uint8_t Function,-,subghz_custom_btn_set_original,void,uint8_t Function,-,subghz_custom_btns_reset,void, diff --git a/lib/subghz/blocks/custom_btn.c b/lib/subghz/blocks/custom_btn.c index 158c10a2f..275a4c930 100644 --- a/lib/subghz/blocks/custom_btn.c +++ b/lib/subghz/blocks/custom_btn.c @@ -4,11 +4,13 @@ static uint8_t custom_btn_id = SUBGHZ_CUSTOM_BTN_OK; static uint8_t custom_btn_original = 0; static uint8_t custom_btn_max_btns = 0; -void subghz_custom_btn_set(uint8_t btn_id) { +bool subghz_custom_btn_set(uint8_t btn_id) { if(btn_id > custom_btn_max_btns) { custom_btn_id = SUBGHZ_CUSTOM_BTN_OK; + return false; } else { custom_btn_id = btn_id; + return true; } } @@ -31,4 +33,8 @@ void subghz_custom_btn_set_max(uint8_t b) { void subghz_custom_btns_reset() { custom_btn_original = 0; custom_btn_max_btns = 0; +} + +bool subghz_custom_btn_is_allowed() { + return custom_btn_max_btns != 0; } \ No newline at end of file diff --git a/lib/subghz/blocks/custom_btn.h b/lib/subghz/blocks/custom_btn.h index a92567321..c13842fa8 100644 --- a/lib/subghz/blocks/custom_btn.h +++ b/lib/subghz/blocks/custom_btn.h @@ -11,7 +11,7 @@ #define SUBGHZ_CUSTOM_BTN_LEFT (3U) #define SUBGHZ_CUSTOM_BTN_RIGHT (4U) -void subghz_custom_btn_set(uint8_t btn_id); +bool subghz_custom_btn_set(uint8_t btn_id); uint8_t subghz_custom_btn_get(); @@ -21,4 +21,6 @@ uint8_t subghz_custom_btn_get_original(); void subghz_custom_btn_set_max(uint8_t b); -void subghz_custom_btns_reset(); \ No newline at end of file +void subghz_custom_btns_reset(); + +bool subghz_custom_btn_is_allowed(); \ No newline at end of file From f08dd2fd03db28324e391b30909a92f99d16abaa Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 14 May 2023 01:44:00 +0300 Subject: [PATCH 17/71] Text Input improvements, added cursor and ability to set minimal length thanks to WillyJL --- .../services/gui/modules/text_input.c | 160 +++++++++++++----- .../services/gui/modules/text_input.h | 2 + firmware/targets/f7/api_symbols.csv | 1 + 3 files changed, 124 insertions(+), 39 deletions(-) diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 5fd306aa6..1152f496c 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -18,7 +18,12 @@ typedef struct { const char* header; char* text_buffer; size_t text_buffer_size; + size_t minimum_length; bool clear_default_text; + FuriString* temp_str; + + bool cursor_select; + size_t cursor_pos; TextInputCallback callback; void* callback_context; @@ -146,9 +151,14 @@ static char char_to_uppercase(const char letter) { } static void text_input_backspace_cb(TextInputModel* model) { - uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer); - if(text_length > 0) { - model->text_buffer[text_length - 1] = 0; + if(model->clear_default_text) { + model->text_buffer[0] = 0; + model->cursor_pos = 0; + } else if(model->cursor_pos > 0) { + furi_string_set_str(model->temp_str, model->text_buffer); + furi_string_replace_at(model->temp_str, model->cursor_pos - 1, 1, ""); + model->cursor_pos--; + strcpy(model->text_buffer, furi_string_get_cstr(model->temp_str)); } } @@ -158,7 +168,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { uint8_t needed_string_width = canvas_width(canvas) - 8; uint8_t start_pos = 4; - const char* text = model->text_buffer; + model->cursor_pos = model->cursor_pos > text_length ? text_length : model->cursor_pos; canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); @@ -166,25 +176,43 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str(canvas, 2, 8, model->header); elements_slightly_rounded_frame(canvas, 1, 12, 126, 15); - if(canvas_string_width(canvas, text) > needed_string_width) { - canvas_draw_str(canvas, start_pos, 22, "..."); - start_pos += 6; - needed_string_width -= 8; - } - - while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) { - text++; + FuriString* str = model->temp_str; + if(model->text_buffer) { + furi_string_set_str(str, model->text_buffer); + } else { + furi_string_reset(str); } + const char* cstr = furi_string_get_cstr(str); if(model->clear_default_text) { elements_slightly_rounded_box( - canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10); + canvas, start_pos - 1, 14, canvas_string_width(canvas, cstr) + 2, 10); canvas_set_color(canvas, ColorWhite); } else { - canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 22, "|"); - canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 22, "|"); + furi_string_replace_at(str, model->cursor_pos, 0, "|"); } - canvas_draw_str(canvas, start_pos, 22, text); + + if(model->cursor_pos > 0 && canvas_string_width(canvas, cstr) > needed_string_width) { + canvas_draw_str(canvas, start_pos, 22, "..."); + start_pos += 6; + needed_string_width -= 8; + for(uint32_t off = 0; + !furi_string_empty(str) && canvas_string_width(canvas, cstr) > needed_string_width && + off < model->cursor_pos; + off++) { + furi_string_right(str, 1); + } + } + + if(canvas_string_width(canvas, cstr) > needed_string_width) { + needed_string_width -= 4; + while(!furi_string_empty(str) && canvas_string_width(canvas, cstr) > needed_string_width) { + furi_string_left(str, furi_string_size(str) - 1); + } + furi_string_cat_str(str, "..."); + } + + canvas_draw_str(canvas, start_pos, 22, cstr); canvas_set_font(canvas, FontKeyboard); @@ -193,9 +221,12 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { const TextInputKey* keys = get_row(row); for(size_t column = 0; column < column_count; column++) { + bool selected = !model->cursor_select && model->selected_row == row && + model->selected_column == column; + if(keys[column].text == ENTER_KEY) { canvas_set_color(canvas, ColorBlack); - if(model->selected_row == row && model->selected_column == column) { + if(selected) { canvas_draw_icon( canvas, keyboard_origin_x + keys[column].x, @@ -210,7 +241,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { } } else if(keys[column].text == BACKSPACE_KEY) { canvas_set_color(canvas, ColorBlack); - if(model->selected_row == row && model->selected_column == column) { + if(selected) { canvas_draw_icon( canvas, keyboard_origin_x + keys[column].x, @@ -224,7 +255,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { &I_KeyBackspace_16x9); } } else { - if(model->selected_row == row && model->selected_column == column) { + if(selected) { canvas_set_color(canvas, ColorBlack); canvas_draw_box( canvas, @@ -273,12 +304,17 @@ static void text_input_handle_up(TextInput* text_input, TextInputModel* model) { if(model->selected_column > get_row_size(model->selected_row) - 6) { model->selected_column = model->selected_column + 1; } + } else { + model->cursor_select = true; + model->clear_default_text = false; } } static void text_input_handle_down(TextInput* text_input, TextInputModel* model) { UNUSED(text_input); - if(model->selected_row < keyboard_row_count - 1) { + if(model->cursor_select) { + model->cursor_select = false; + } else if(model->selected_row < keyboard_row_count - 1) { model->selected_row++; if(model->selected_column > get_row_size(model->selected_row) - 4) { model->selected_column = model->selected_column - 1; @@ -288,7 +324,11 @@ static void text_input_handle_down(TextInput* text_input, TextInputModel* model) static void text_input_handle_left(TextInput* text_input, TextInputModel* model) { UNUSED(text_input); - if(model->selected_column > 0) { + if(model->cursor_select) { + if(model->cursor_pos > 0) { + model->cursor_pos = CLAMP(model->cursor_pos - 1, strlen(model->text_buffer), 0u); + } + } else if(model->selected_column > 0) { model->selected_column--; } else { model->selected_column = get_row_size(model->selected_row) - 1; @@ -297,14 +337,19 @@ static void text_input_handle_left(TextInput* text_input, TextInputModel* model) static void text_input_handle_right(TextInput* text_input, TextInputModel* model) { UNUSED(text_input); - if(model->selected_column < get_row_size(model->selected_row) - 1) { + if(model->cursor_select) { + model->cursor_pos = CLAMP(model->cursor_pos + 1, strlen(model->text_buffer), 0u); + } else if(model->selected_column < get_row_size(model->selected_row) - 1) { model->selected_column++; } else { model->selected_column = 0; } } -static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, bool shift) { +static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, InputType type) { + if(model->cursor_select) return; + bool shift = type == InputTypeLong; + bool repeat = type == InputTypeRepeat; char selected = get_selected_char(model); size_t text_length = strlen(model->text_buffer); @@ -314,24 +359,34 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, b model->text_buffer, model->validator_text, model->validator_callback_context))) { model->validator_message_visible = true; furi_timer_start(text_input->timer, furi_kernel_get_tick_frequency() * 4); - } else if(model->callback != 0 && text_length > 0) { + } else if(model->callback != 0 && text_length >= model->minimum_length) { model->callback(model->callback_context); } - } else if(selected == BACKSPACE_KEY) { - text_input_backspace_cb(model); } else { - if(model->clear_default_text) { - text_length = 0; - } - if(text_length < (model->text_buffer_size - 1)) { - if(shift != (text_length == 0)) { - selected = char_to_uppercase(selected); + if(selected == BACKSPACE_KEY) { + text_input_backspace_cb(model); + } else if(!repeat) { + if(model->clear_default_text) { + text_length = 0; + } + if(text_length < (model->text_buffer_size - 1)) { + if(shift != (text_length == 0)) { + selected = char_to_uppercase(selected); + } + const char replace[2] = {selected, 0}; + if(model->clear_default_text) { + furi_string_set_str(model->temp_str, replace); + model->cursor_pos = 1; + } else { + furi_string_set_str(model->temp_str, model->text_buffer); + furi_string_replace_at(model->temp_str, model->cursor_pos, 0, replace); + model->cursor_pos++; + } + strcpy(model->text_buffer, furi_string_get_cstr(model->temp_str)); } - model->text_buffer[text_length] = selected; - model->text_buffer[text_length + 1] = 0; } + model->clear_default_text = false; } - model->clear_default_text = false; } static bool text_input_view_input_callback(InputEvent* event, void* context) { @@ -363,7 +418,7 @@ static bool text_input_view_input_callback(InputEvent* event, void* context) { text_input_handle_right(text_input, model); break; case InputKeyOk: - text_input_handle_ok(text_input, model, false); + text_input_handle_ok(text_input, model, event->type); break; default: consumed = false; @@ -385,7 +440,7 @@ static bool text_input_view_input_callback(InputEvent* event, void* context) { text_input_handle_right(text_input, model); break; case InputKeyOk: - text_input_handle_ok(text_input, model, true); + text_input_handle_ok(text_input, model, event->type); break; case InputKeyBack: text_input_backspace_cb(model); @@ -409,6 +464,9 @@ static bool text_input_view_input_callback(InputEvent* event, void* context) { case InputKeyRight: text_input_handle_right(text_input, model); break; + case InputKeyOk: + text_input_handle_ok(text_input, model, event->type); + break; case InputKeyBack: text_input_backspace_cb(model); break; @@ -448,7 +506,13 @@ TextInput* text_input_alloc() { with_view_model( text_input->view, TextInputModel * model, - { model->validator_text = furi_string_alloc(); }, + { + model->validator_text = furi_string_alloc(); + model->temp_str = furi_string_alloc(); + model->minimum_length = 1; + model->cursor_pos = 0; + model->cursor_select = false; + }, false); text_input_reset(text_input); @@ -461,7 +525,10 @@ void text_input_free(TextInput* text_input) { with_view_model( text_input->view, TextInputModel * model, - { furi_string_free(model->validator_text); }, + { + furi_string_free(model->validator_text); + furi_string_free(model->temp_str); + }, false); // Send stop command @@ -483,7 +550,10 @@ void text_input_reset(TextInput* text_input) { model->header = ""; model->selected_row = 0; model->selected_column = 0; + model->minimum_length = 1; model->clear_default_text = false; + model->cursor_pos = 0; + model->cursor_select = false; model->text_buffer = NULL; model->text_buffer_size = 0; model->callback = NULL; @@ -517,15 +587,27 @@ void text_input_set_result_callback( model->text_buffer = text_buffer; model->text_buffer_size = text_buffer_size; model->clear_default_text = clear_default_text; + model->cursor_select = false; if(text_buffer && text_buffer[0] != '\0') { + model->cursor_pos = strlen(text_buffer); // Set focus on Save model->selected_row = 2; model->selected_column = 8; + } else { + model->cursor_pos = 0; } }, true); } +void text_input_set_minimum_length(TextInput* text_input, size_t minimum_length) { + with_view_model( + text_input->view, + TextInputModel * model, + { model->minimum_length = minimum_length; }, + true); +} + void text_input_set_validator( TextInput* text_input, TextInputValidatorCallback callback, diff --git a/applications/services/gui/modules/text_input.h b/applications/services/gui/modules/text_input.h index 218df3141..65207532e 100644 --- a/applications/services/gui/modules/text_input.h +++ b/applications/services/gui/modules/text_input.h @@ -70,6 +70,8 @@ void text_input_set_validator( TextInputValidatorCallback callback, void* callback_context); +void text_input_set_minimum_length(TextInput* text_input, size_t minimum_length); + TextInputValidatorCallback text_input_get_validator_callback(TextInput* text_input); void* text_input_get_validator_callback_context(TextInput* text_input); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 7eac9ab00..b79c8271b 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -3399,6 +3399,7 @@ Function,+,text_input_get_validator_callback_context,void*,TextInput* Function,+,text_input_get_view,View*,TextInput* Function,+,text_input_reset,void,TextInput* Function,+,text_input_set_header_text,void,"TextInput*, const char*" +Function,+,text_input_set_minimum_length,void,"TextInput*, size_t" Function,+,text_input_set_result_callback,void,"TextInput*, TextInputCallback, void*, char*, size_t, _Bool" Function,+,text_input_set_validator,void,"TextInput*, TextInputValidatorCallback, void*" Function,-,tga_save,void,const char* From 2efecc25a75b9c20af5d2cbab430f1f289735788 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 14 May 2023 02:23:12 +0300 Subject: [PATCH 18/71] bump subbrute --- applications/external/subbrute | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/subbrute b/applications/external/subbrute index c94efdf88..d1317392a 160000 --- a/applications/external/subbrute +++ b/applications/external/subbrute @@ -1 +1 @@ -Subproject commit c94efdf88ac2ed51b88caf288ab3c50afaaec9bb +Subproject commit d1317392a4a7dadd34157e66c73fee6990fd2100 From 43e5daded277d01108816e9adb1eb7c96cc147c0 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 14 May 2023 04:11:26 +0300 Subject: [PATCH 19/71] fix small bugs --- lib/subghz/protocols/keeloq.c | 34 +++++++++++++------------------- lib/subghz/protocols/star_line.c | 2 +- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 9b86eb5e8..26b5a20a9 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -805,29 +805,23 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( } break; case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: - if(mf_not_set) { - man = subghz_protocol_keeloq_common_magic_serial_type2_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } + man = subghz_protocol_keeloq_common_magic_serial_type2_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; } break; case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: - if(mf_not_set) { - man = subghz_protocol_keeloq_common_magic_serial_type3_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } + man = subghz_protocol_keeloq_common_magic_serial_type3_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; } break; case KEELOQ_LEARNING_UNKNOWN: diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 83a4846cd..362e34f3c 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -534,7 +534,7 @@ static uint8_t subghz_protocol_star_line_check_remote_controller_selector( } for M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - if(mf_not_set || strcmp(furi_string_get_cstr(manufacture_code->name), mfname)) { + if(mf_not_set || (strcmp(furi_string_get_cstr(manufacture_code->name), mfname) == 0)) { switch(manufacture_code->type) { case KEELOQ_LEARNING_SIMPLE: // Simple Learning From c0b3a18316b01fd3cbb1cebab559e170811dbac9 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 14 May 2023 04:27:25 +0300 Subject: [PATCH 20/71] api --- firmware/targets/f7/api_symbols.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index bbe92e30f..b69a251e0 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,26.3,, +Version,+,26.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, From c2713eff87a111a1a207ed049b5d8fa6eb6105bf Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 14 May 2023 05:38:00 +0300 Subject: [PATCH 21/71] New way of changing device name Settings scene, char Validator, Idea -> by Willy-JL Implemented by me Kod plohoi, ochen plohoi, kto mozhet - sdelaite kak nado, esli smozhete :) Ya luche varianta ne pridumal poka 4to --- applications/services/desktop/desktop.c | 23 ++++++ applications/services/gui/modules/popup.h | 2 +- .../desktop_settings/desktop_settings_app.c | 41 ++++++++++ .../desktop_settings/desktop_settings_app.h | 6 ++ .../desktop_settings_scene_change_name.c | 75 +++++++++++++++++++ .../scenes/desktop_settings_scene_config.h | 3 + .../desktop_settings_scene_name_popup.c | 54 +++++++++++++ .../scenes/desktop_settings_scene_start.c | 7 ++ documentation/CustomFlipperName.md | 37 ++------- firmware/targets/f7/api_symbols.csv | 2 + .../targets/f7/furi_hal/furi_hal_version.c | 2 +- .../furi_hal_include/furi_hal_version.h | 4 + lib/toolbox/namechanger.c | 53 +++++++++++++ lib/toolbox/namechanger.h | 18 +++++ lib/toolbox/version.c | 12 +-- lib/toolbox/version.h | 10 ++- scripts/sconsdist.py | 7 -- scripts/version.py | 26 ++----- site_scons/commandline.scons | 5 -- site_scons/environ.scons | 1 - 20 files changed, 316 insertions(+), 72 deletions(-) create mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_change_name.c create mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_name_popup.c create mode 100644 lib/toolbox/namechanger.c create mode 100644 lib/toolbox/namechanger.h diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 28d09cc0d..f4d1b6301 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -9,6 +9,9 @@ #include #include +#include +#include + #include "animations/animation_manager.h" #include "desktop/scenes/desktop_scene.h" #include "desktop/scenes/desktop_scene_i.h" @@ -426,6 +429,26 @@ int32_t desktop_srv(void* p) { scene_manager_next_scene(desktop->scene_manager, DesktopSceneFault); } + // I added some very bydlo kod here, and thrown some delays to make it worse, pls don't look at it, it will make you cry from laugh + if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { + if(NameChanger_Init()) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_close(cli); + furi_delay_ms(2); + cli_session_open(cli, &cli_vcp); + furi_record_close(RECORD_CLI); + + furi_delay_ms(3); + Bt* bt = furi_record_open(RECORD_BT); + if(!bt_set_profile(bt, BtProfileSerial)) { + FURI_LOG_D(TAG, "Failed to touch bluetooth to name change"); + } + furi_record_close(RECORD_BT); + bt = NULL; + furi_delay_ms(3); + } + } + view_dispatcher_run(desktop->view_dispatcher); desktop_free(desktop); diff --git a/applications/services/gui/modules/popup.h b/applications/services/gui/modules/popup.h index 13371a05d..ca783fdb7 100644 --- a/applications/services/gui/modules/popup.h +++ b/applications/services/gui/modules/popup.h @@ -41,7 +41,7 @@ void popup_free(Popup* popup); */ View* popup_get_view(Popup* popup); -/** Set popup header text +/** Set popup callback function * * @param popup Popup instance * @param callback PopupCallback diff --git a/applications/settings/desktop_settings/desktop_settings_app.c b/applications/settings/desktop_settings/desktop_settings_app.c index afb5d59ec..317d57654 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -1,6 +1,9 @@ #include #include #include +#include +#include +#include #include "desktop_settings_app.h" #include "scenes/desktop_settings_scene.h" @@ -62,11 +65,41 @@ DesktopSettingsApp* desktop_settings_app_alloc() { app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2, desktop_settings_view_pin_setup_howto2_get_view(app->pin_setup_howto2_view)); + + // Text Input + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + DesktopSettingsAppViewTextInput, + text_input_get_view(app->text_input)); + return app; } void desktop_settings_app_free(DesktopSettingsApp* app) { furi_assert(app); + + bool temp_save_name = app->save_name; + // Save name if set or remove file + if(temp_save_name) { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(strcmp(app->device_name, "") == 0) { + storage_simply_remove(storage, NAMECHANGER_PATH); + } else { + FlipperFormat* file = flipper_format_file_alloc(storage); + + do { + if(!flipper_format_file_open_always(file, NAMECHANGER_PATH)) break; + if(!flipper_format_write_header_cstr(file, NAMECHANGER_HEADER, NAMECHANGER_VERSION)) + break; + if(!flipper_format_write_string_cstr(file, "Name", app->device_name)) break; + } while(0); + + flipper_format_free(file); + } + furi_record_close(RECORD_STORAGE); + } + // Variable item list view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList); @@ -74,6 +107,10 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto); view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2); + // TextInput + view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewTextInput); + text_input_free(app->text_input); + variable_item_list_free(app->variable_item_list); submenu_free(app->submenu); popup_free(app->popup); @@ -87,6 +124,10 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { furi_record_close(RECORD_DIALOGS); furi_record_close(RECORD_GUI); free(app); + + if(temp_save_name) { + power_reboot(PowerBootModeNormal); + } } extern int32_t desktop_settings_app(void* p) { diff --git a/applications/settings/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h index 6f97564c9..76f23e22f 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,7 @@ typedef enum { DesktopSettingsAppViewIdPinInput, DesktopSettingsAppViewIdPinSetupHowto, DesktopSettingsAppViewIdPinSetupHowto2, + DesktopSettingsAppViewTextInput, } DesktopSettingsAppView; typedef struct { @@ -32,6 +34,7 @@ typedef struct { ViewDispatcher* view_dispatcher; VariableItemList* variable_item_list; Submenu* submenu; + TextInput* text_input; Popup* popup; DesktopViewPinInput* pin_input_view; DesktopSettingsViewPinSetupHowto* pin_setup_howto_view; @@ -40,5 +43,8 @@ typedef struct { PinCode pincode_buffer; bool pincode_buffer_filled; + bool save_name; + char device_name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; + uint8_t menu_idx; } DesktopSettingsApp; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_change_name.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_change_name.c new file mode 100644 index 000000000..11080b2ec --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_change_name.c @@ -0,0 +1,75 @@ +#include "../desktop_settings_app.h" +#include +#include "desktop_settings_scene.h" + +enum TextInputIndex { + TextInputResultOk, +}; + +static void desktop_settings_scene_change_name_text_input_callback(void* context) { + DesktopSettingsApp* app = context; + + app->save_name = true; + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +static bool desktop_settings_scene_change_name_validator( + const char* text, + FuriString* error, + void* context) { + UNUSED(context); + + for(; *text; ++text) { + const char c = *text; + if((c < '0' || c > '9') && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) { + furi_string_printf(error, "Please only\nenter letters\nand numbers!"); + return false; + } + } + + return true; +} + +void desktop_settings_scene_change_name_on_enter(void* context) { + DesktopSettingsApp* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Leave empty for default"); + + text_input_set_validator(text_input, desktop_settings_scene_change_name_validator, NULL); + + text_input_set_minimum_length(text_input, 0); + + text_input_set_result_callback( + text_input, + desktop_settings_scene_change_name_text_input_callback, + app, + app->device_name, + FURI_HAL_VERSION_ARRAY_NAME_LENGTH, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewTextInput); +} + +bool desktop_settings_scene_change_name_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneNamePopup); + break; + default: + break; + } + } + + return consumed; +} + +void desktop_settings_scene_change_name_on_exit(void* context) { + DesktopSettingsApp* app = context; + text_input_reset(app->text_input); +} \ No newline at end of file diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h index 5bc52172b..b85ab36af 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h @@ -9,3 +9,6 @@ ADD_SCENE(desktop_settings, pin_setup, PinSetup) ADD_SCENE(desktop_settings, pin_setup_howto, PinSetupHowto) ADD_SCENE(desktop_settings, pin_setup_howto2, PinSetupHowto2) ADD_SCENE(desktop_settings, pin_setup_done, PinSetupDone) + +ADD_SCENE(desktop_settings, change_name, ChangeName) +ADD_SCENE(desktop_settings, name_popup, NamePopup) diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_name_popup.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_name_popup.c new file mode 100644 index 000000000..2f03a9bfd --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_name_popup.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#include "../desktop_settings_app.h" +#include +#include "desktop_settings_scene.h" + +#define SCENE_EVENT_EXIT (0U) + +static void name_popup_back_callback(void* context) { + furi_assert(context); + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT); +} + +void desktop_settings_scene_name_popup_on_enter(void* context) { + furi_assert(context); + DesktopSettingsApp* app = context; + + popup_set_context(app->popup, app); + popup_set_callback(app->popup, name_popup_back_callback); + popup_set_header(app->popup, "Name is set!", 64, 20, AlignCenter, AlignCenter); + popup_set_text( + app->popup, "Your Flipper will be\nrestarted on exit.", 64, 40, AlignCenter, AlignCenter); + popup_set_timeout(app->popup, 2100); + popup_enable_timeout(app->popup); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup); +} + +bool desktop_settings_scene_name_popup_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SCENE_EVENT_EXIT: + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppSceneStart); + consumed = true; + break; + + default: + consumed = true; + break; + } + } + return consumed; +} + +void desktop_settings_scene_name_popup_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 2f3cdf914..9d63f6628 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -11,6 +11,7 @@ #define SCENE_EVENT_SELECT_PIN_SETUP 3 #define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 4 #define SCENE_EVENT_SELECT_BATTERY_DISPLAY 5 +#define SCENE_EVENT_SELECT_CHANGE_NAME 6 #define AUTO_LOCK_DELAY_COUNT 9 const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { @@ -105,6 +106,8 @@ void desktop_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, battery_view_count_text[value_index]); + variable_item_list_add(variable_item_list, "Change Flipper Name", 0, NULL, app); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList); } @@ -139,6 +142,10 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even case SCENE_EVENT_SELECT_BATTERY_DISPLAY: consumed = true; break; + case SCENE_EVENT_SELECT_CHANGE_NAME: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneChangeName); + consumed = true; + break; } } return consumed; diff --git a/documentation/CustomFlipperName.md b/documentation/CustomFlipperName.md index d16c35b58..54a6f7ad8 100644 --- a/documentation/CustomFlipperName.md +++ b/documentation/CustomFlipperName.md @@ -1,35 +1,10 @@ # How to change Flipper name: ## Instruction -1. Read [How to build](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToBuild.md) and [How to install](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md) to know how to build and install firmware -2. Follow how to build instructions to prepare all things before continuing -3. Run release build to verify all is ok - `./fbt COMPACT=1 DEBUG=0 updater_package` -4. Clear build files - `./fbt COMPACT=1 DEBUG=0 updater_package -c` -5. Run command with extra environment var before `./fbt` that variable should contain your custom name in alphanumeric characters - max length 8 chars - `CUSTOM_FLIPPER_NAME=Name ./fbt COMPACT=1 DEBUG=0 updater_package` - where `Name` write your custom name -6. Copy `dist/f7-C/f7-update-local` folder to microSD `update/myfw/` and run `update` file on flipper from file manager app (Archive) -7. Flash from microSD card only!!!! .dfu update from qFlipper will not work properly since name and serial number will be changed -8. Done, you will have custom name, serial number and bluetooth mac address +1. Go to Settings -> Desktop -> Change Flipper Name +2. Enter your new custom name for your flipper and click `Save`, **name will be saved on microSD card, and will stay same after firmware updates** +3. You will see a message `Name is set!` and when you exit from settings -> flipper will automatically reboot! +4. After reboot you will see your new custom name in device info and right screen `passport` +5. Done! -9. Also you can skip 5-7 and flash with `CUSTOM_FLIPPER_NAME=Name ./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_usb_full` - - -## Troubleshooting -### I'm using Windows and name changing / building firmware doesn't work -- Use PowerShell or VSCode terminal(powershell by default) -- Clear build files - `.\fbt.cmd COMPACT=1 DEBUG=0 updater_package -c` -- Enter this in same terminal `$Env:CUSTOM_FLIPPER_NAME="Name"` -- Run release build - `.\fbt.cmd COMPACT=1 DEBUG=0 updater_package` -- Flash as described before (see 6.) -- If something still not work - Run powershell or VSCode as Admin -### Name stays same for every new build -- Clear build files - `./fbt COMPACT=1 DEBUG=0 updater_package -c` -- Try again -### I want to return my original name and serial number -- Flash stock FW or any CFW using microSD card offline update method - -Or -- Clear build files - `./fbt COMPACT=1 DEBUG=0 updater_package -c` -- Run release build - `./fbt COMPACT=1 DEBUG=0 updater_package` -- Copy `dist/f7-C/f7-update-local` folder to microSD `update/myfw/` and run `update` file on flipper from file manager app (Archive) -- Flash from microSD card only, .dfu update from qFlipper will not work properly since name and serial number will be changed \ No newline at end of file +**To reset device name to default - do same steps but do not enter any characters, leave it empty and click** `Save` \ No newline at end of file diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b69a251e0..a3d2efa45 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1486,6 +1486,7 @@ Function,+,furi_hal_version_get_model_name,const char*, Function,+,furi_hal_version_get_name_ptr,const char*, Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, Function,-,furi_hal_version_init,void, +Function,-,furi_hal_version_set_name,void,const char* Function,+,furi_hal_version_uid,const uint8_t*, Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, @@ -4597,6 +4598,7 @@ Function,+,version_get_gitbranchnum,const char*,const Version* Function,+,version_get_githash,const char*,const Version* Function,+,version_get_target,uint8_t,const Version* Function,+,version_get_version,const char*,const Version* +Function,-,version_set_custom_name,void,"Version*, const char*" Function,-,vfiprintf,int,"FILE*, const char*, __gnuc_va_list" Function,-,vfiscanf,int,"FILE*, const char*, __gnuc_va_list" Function,-,vfprintf,int,"FILE*, const char*, __gnuc_va_list" diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/firmware/targets/f7/furi_hal/furi_hal_version.c index 7e0046e52..d0857f288 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version.c @@ -90,7 +90,7 @@ typedef struct { static FuriHalVersion furi_hal_version = {0}; -static void furi_hal_version_set_name(const char* name) { +void furi_hal_version_set_name(const char* name) { if(name != NULL) { strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH); snprintf( diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index 6a3e3154d..f5e5ca49a 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -176,6 +176,10 @@ const char* furi_hal_version_get_device_name_ptr(); */ const char* furi_hal_version_get_ble_local_device_name_ptr(); +/** Set flipper name + */ +void furi_hal_version_set_name(const char* name); + /** Get BLE MAC address * * @return pointer to BLE MAC address diff --git a/lib/toolbox/namechanger.c b/lib/toolbox/namechanger.c new file mode 100644 index 000000000..33842bcd7 --- /dev/null +++ b/lib/toolbox/namechanger.c @@ -0,0 +1,53 @@ +#include "namechanger.h" +#include +#include + +#define TAG "NameChanger" + +bool NameChanger_Init() { + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriString* str = furi_string_alloc(); + FlipperFormat* file = flipper_format_file_alloc(storage); + + bool res = false; + + do { + uint32_t version; + if(!flipper_format_file_open_existing(file, NAMECHANGER_PATH)) break; + if(!flipper_format_read_header(file, str, &version)) break; + if(furi_string_cmp_str(str, NAMECHANGER_HEADER)) break; + if(version != NAMECHANGER_VERSION) break; + + if(!flipper_format_read_string(file, "Name", str)) break; + // Check for size + size_t temp_string_size = furi_string_size(str); + if(temp_string_size > (size_t)8) break; + if(temp_string_size < (size_t)2) break; + + // Check for forbidden characters + const char* name_ptr = furi_string_get_cstr(str); + bool chars_check_failed = false; + + for(; *name_ptr; ++name_ptr) { + const char c = *name_ptr; + if((c < '0' || c > '9') && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) { + chars_check_failed = true; + break; + } + } + + if(chars_check_failed) break; + + // If all checks was good we can set the name + version_set_custom_name(NULL, strdup(furi_string_get_cstr(str))); + furi_hal_version_set_name(version_get_custom_name(NULL)); + + res = true; + } while(false); + + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + furi_string_free(str); + + return res; +} \ No newline at end of file diff --git a/lib/toolbox/namechanger.h b/lib/toolbox/namechanger.h new file mode 100644 index 000000000..bfaadec15 --- /dev/null +++ b/lib/toolbox/namechanger.h @@ -0,0 +1,18 @@ +#pragma once + +#define NAMECHANGER_HEADER "Flipper Name File" +#define NAMECHANGER_VERSION 1 +#define NAMECHANGER_PATH EXT_PATH("dolphin/name.settings") + +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Initializes the name changer. (Load name file, apply changes) +bool NameChanger_Init(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/toolbox/version.c b/lib/toolbox/version.c index e0a6e6197..ae3dc851a 100644 --- a/lib/toolbox/version.c +++ b/lib/toolbox/version.c @@ -24,18 +24,14 @@ struct Version { }; /* version of current running firmware (bootloader/flipper) */ -static const Version version = { +static Version version = { .magic = VERSION_MAGIC, .major = VERSION_MAJOR, .minor = VERSION_MINOR, .git_hash = GIT_COMMIT, .git_branch = GIT_BRANCH, .build_date = BUILD_DATE, -#ifdef FURI_CUSTOM_FLIPPER_NAME - .custom_flipper_name = FURI_CUSTOM_FLIPPER_NAME, -#else .custom_flipper_name = NULL, -#endif .version = VERSION #ifdef FURI_RAM_EXEC " (RAM)" @@ -74,6 +70,12 @@ const char* version_get_custom_name(const Version* v) { return v ? v->custom_flipper_name : version.custom_flipper_name; } +void version_set_custom_name(Version* v, const char* name) { + Version* ver = v ? v : &version; + ver->custom_flipper_name = name; + return; +} + uint8_t version_get_target(const Version* v) { return v ? v->target : version.target; } diff --git a/lib/toolbox/version.h b/lib/toolbox/version.h index ed54631b3..2aa72bc28 100644 --- a/lib/toolbox/version.h +++ b/lib/toolbox/version.h @@ -65,7 +65,7 @@ const char* version_get_builddate(const Version* v); */ const char* version_get_version(const Version* v); -/** Get custom flipper name if set in ENV +/** Get custom flipper name if set in version_set_custom_name * * @param v pointer to Version data. NULL for currently running * software. @@ -74,6 +74,14 @@ const char* version_get_version(const Version* v); */ const char* version_get_custom_name(const Version* v); +/** Set custom flipper name + * + * @param v pointer to Version data. NULL for currently running + * software. + * @param name Custom name or NULL + */ +void version_set_custom_name(Version* v, const char* name); + /** Get hardware target this firmware was built for * * @param v pointer to Version data. NULL for currently running diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index 0f0b22082..a3b466898 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -250,13 +250,6 @@ class Main(App): ) bundle_args.extend(self.other_args) - log_custom_fz_name = ( - environ.get("CUSTOM_FLIPPER_NAME", None) - or "" - ) - if (log_custom_fz_name != "") and (len(log_custom_fz_name) <= 8) and (log_custom_fz_name.isalnum()) and (log_custom_fz_name.isascii()): - self.logger.info(f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars") - if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0: self.note_dist_component("update", "dir", bundle_dir) self.logger.info( diff --git a/scripts/version.py b/scripts/version.py index 27772e9c2..149c5d99e 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -40,11 +40,6 @@ class GitVersion: or "unknown" ) - custom_fz_name = ( - os.environ.get("CUSTOM_FLIPPER_NAME", None) - or "" - ) - force_no_dirty = ( os.environ.get("FORCE_NO_DIRTY", None) or "" @@ -52,21 +47,12 @@ class GitVersion: if (force_no_dirty != ""): dirty = False - if (custom_fz_name != "") and (len(custom_fz_name) <= 8) and (custom_fz_name.isalnum()) and (custom_fz_name.isascii()): - return { - "GIT_COMMIT": commit, - "GIT_BRANCH": branch, - "FURI_CUSTOM_FLIPPER_NAME": custom_fz_name, - "VERSION": version, - "BUILD_DIRTY": dirty and 1 or 0, - } - else: - return { - "GIT_COMMIT": commit, - "GIT_BRANCH": branch, - "VERSION": version, - "BUILD_DIRTY": dirty and 1 or 0, - } + return { + "GIT_COMMIT": commit, + "GIT_BRANCH": branch, + "VERSION": version, + "BUILD_DIRTY": dirty and 1 or 0, + } def _exec_git(self, args): cmd = ["git"] diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 84ef6ce19..54bcbc568 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -100,11 +100,6 @@ vars.AddVariables( "Version string for updater package", "${DIST_SUFFIX}", ), - ( - "CUSTOM_FLIPPER_NAME", - "Replaces OTP flipper name with custom string of 8 chars", - "", - ), ( "FORCE_NO_DIRTY", "Force disable dirty status of the build", diff --git a/site_scons/environ.scons b/site_scons/environ.scons index 0ffc8a6a2..0cdc45557 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -20,7 +20,6 @@ variables_to_forward = [ # CI/CD variables "WORKFLOW_BRANCH_OR_TAG", "DIST_SUFFIX", - "CUSTOM_FLIPPER_NAME", "FORCE_NO_DIRTY", # Python & other tools "HOME", From 523c63fec7698cee417e987929cc26d7c6d1f588 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 14 May 2023 07:40:48 +0300 Subject: [PATCH 22/71] Update readme and changelog unrandomize detect reader for now --- CHANGELOG.md | 14 ++++++++++++++ ReadMe.md | 4 +++- lib/nfc/helpers/reader_analyzer.c | 8 ++------ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd3ff9a6b..ae9a20161 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ ### New changes +* New way of changing device name -> **Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL) +* Plugins: BadBT plugin (BT version of BadKB) [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/ClaraCrazy/Flipper-Xtreme/tree/dev/applications/main/bad_kb) +* Plugins: WiFi Marauder -> Added sniff pmkid on selected aps from 0.10.4 update (by @clipboard1) +* Plugins: SubGHz Bruteforcer - Increase delay just a little bit to fix some cases when receiver will not get codes and decrease manual transmit delay +* SubGHz: App refactoring (OFW code ported + our code was refactored/cleaned up too) (by @gid9798 and @xMasterX | PR #461) +* SubGHz: Using scene manager functions in DecodeRAW (by @gid9798 | PR #462) +* SubGHz: Protocols and custom buttons refactoring (by @gid9798 | PR #465) +* SubGHz: Move `counter increase` setting out of debug, change max value +* GUI: Submenu locked elements (by @Willy-JL and @giacomoferretti) +* GUI: Text Input improvements, added cursor and ability to set minimal length (by @Willy-JL) +* BT API: Functions that allow to change bt mac address and device broadcasted name (by @Willy-JL and XFW contributors) +* Infrared: `External output` move out of debug and add power option for external modules * Extra pack: Some app fixes +* OFW: Rpc: add desktop service. Desktop: refactor locking routine. +* OFW: Part 2 of hooking C2 IPC * OFW: ble: attempt to handle hardfaulted c2 * OFW: Add Mfkey32 application * OFW: Added DigitalSequence and PulseReader diff --git a/ReadMe.md b/ReadMe.md index 91ca8235d..350db11d6 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -43,7 +43,8 @@ Our Discord Community: * Picopass/iClass plugin included in releases * Recompiled IR TV Universal Remote for ALL buttons * Universal remote for Projectors, Fans, A/Cs and Audio(soundbars, etc.) -* Customizable Flipper name +* Customizable Flipper name **Update! Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL) +* Text Input UI element -> Cursor feature (by @Willy-JL) - BadUSB -> Keyboard layouts [(by rien > dummy-decoy)](https://github.com/dummy-decoy/flipperzero-firmware/tree/dummy_decoy/bad_usb_keyboard_layout) - Sub-GHz -> External CC1101 module support - [(by quen0n)](https://github.com/DarkFlippers/unleashed-firmware/pull/307) - Sub-GHz -> `Add manually` menu extended with new protocols @@ -162,6 +163,7 @@ You can support us by using links or addresses below: - **ProtoView** [(by antirez)](https://github.com/antirez/protoview) - **SWD Probe** [(by g3gg0)](https://github.com/g3gg0/flipper-swd_probe) - IR Scope [(by kallanreed)](https://github.com/DarkFlippers/unleashed-firmware/pull/407) +- BadBT plugin (BT version of BadKB) [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/ClaraCrazy/Flipper-Xtreme/tree/dev/applications/main/bad_kb) Games: - DOOM (fixed) [(by p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/) diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c index 0063b13e7..9bf37a60d 100644 --- a/lib/nfc/helpers/reader_analyzer.c +++ b/lib/nfc/helpers/reader_analyzer.c @@ -2,7 +2,6 @@ #include #include #include -#include #include "mfkey32.h" #include "nfc_debug_pcap.h" @@ -38,8 +37,7 @@ struct ReaderAnalyzer { NfcDebugPcap* pcap; }; -static FuriHalNfcDevData reader_analyzer_nfc_data[] = { - //XXX +const FuriHalNfcDevData reader_analyzer_nfc_data[] = { [ReaderAnalyzerNfcDataMfClassic] = {.sak = 0x08, .atqa = {0x44, 0x00}, @@ -100,9 +98,7 @@ int32_t reader_analyzer_thread(void* context) { ReaderAnalyzer* reader_analyzer_alloc() { ReaderAnalyzer* instance = malloc(sizeof(ReaderAnalyzer)); - reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic].cuid = rand(); //XXX - furi_hal_random_fill_buf( - (uint8_t*)&reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic].uid, 7); + instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic]; instance->alive = false; instance->stream = From c496962f950e2d38384b86d7ae5b590f744e4a79 Mon Sep 17 00:00:00 2001 From: end-me-please <90796271+end-me-please@users.noreply.github.com> Date: Sun, 14 May 2023 08:12:30 +0200 Subject: [PATCH 23/71] fix typo in FuriHalDebuging.md (#2667) --- documentation/FuriHalDebuging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/FuriHalDebuging.md b/documentation/FuriHalDebuging.md index 8ff770163..e7f2d8f2a 100644 --- a/documentation/FuriHalDebuging.md +++ b/documentation/FuriHalDebuging.md @@ -1,6 +1,6 @@ # Furi HAL Debugging -Some Furi subsystem got additional debugging features that can be enabled by adding additional defines to firmware compilation. +Some Furi subsystems got additional debugging features that can be enabled by adding additional defines to firmware compilation. Usually they are used for low level tracing and profiling or signal redirection/duplication. @@ -23,4 +23,4 @@ There are 3 signals that will be exposed to external GPIO pins: There are 2 signals that will be exposed to external GPIO pins: - `WFI` - `PB2` - Light sleep (wait for interrupt) used. Basically this is lightest and most non-breaking things power save mode. All function and debug should work correctly in this mode. -- `STOP` - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible. \ No newline at end of file +- `STOP` - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible. From 8d1f5b04b32a55141359ba6053a895a56c008c4c Mon Sep 17 00:00:00 2001 From: hedger Date: Sun, 14 May 2023 14:49:52 +0300 Subject: [PATCH 24/71] [FL-3317] fbt: allow strings for fap_version field in app manifests (#2672) --- documentation/AppManifests.md | 2 +- scripts/debug/flipperapps.py | 5 ++++- scripts/fbt/appmanifest.py | 9 ++++++++- .../ufbt/project_template/app_template/application.fam | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/documentation/AppManifests.md b/documentation/AppManifests.md index 99f6386b2..b48a6b8ed 100644 --- a/documentation/AppManifests.md +++ b/documentation/AppManifests.md @@ -47,7 +47,7 @@ Only two parameters are mandatory: **_appid_** and **_apptype_**. Others are opt The following parameters are used only for [FAPs](./AppsOnSDCard.md): - **sources**: list of strings, file name masks used for gathering sources within the app folder. The default value of `["*.c*"]` includes C and C++ source files. Applications cannot use the `"lib"` folder for their own source code, as it is reserved for **fap_private_libs**. -- **fap_version**: tuple, 2 numbers in the form of (x,y): application version to be embedded within .fap file. The default value is (0,1), meaning version "0.1". +- **fap_version**: string, application version. The default value is "0.1". You can also use a tuple of 2 numbers in the form of (x,y) to specify the version. It is also possible to add more dot-separated parts to the version, like patch number, but only major and minor version numbers are stored in the built .fap. - **fap_icon**: name of a `.png` file, 1-bit color depth, 10x10px, to be embedded within `.fap` file. - **fap_libs**: list of extra libraries to link the application against. Provides access to extra functions that are not exported as a part of main firmware at the expense of increased `.fap` file size and RAM consumption. - **fap_category**: string, may be empty. App subcategory, also determines the path of the FAP within the apps folder in the file system. diff --git a/scripts/debug/flipperapps.py b/scripts/debug/flipperapps.py index 90582c1e4..608c30412 100644 --- a/scripts/debug/flipperapps.py +++ b/scripts/debug/flipperapps.py @@ -196,7 +196,10 @@ class FlipperAppStateHelper: self.set_debug_mode(False) def set_debug_mode(self, mode: bool) -> None: - gdb.execute(f"set variable furi_hal_debug_gdb_session_active = {int(mode)}") + try: + gdb.execute(f"set variable furi_hal_debug_gdb_session_active = {int(mode)}") + except gdb.error as e: + print(f"Failed to set debug mode: {e}") # Init additional 'fap-set-debug-elf-root' command and set up hooks diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 5e41f4c0e..ed1654e36 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -56,7 +56,7 @@ class FlipperApplication: # .fap-specific sources: List[str] = field(default_factory=lambda: ["*.c*"]) - fap_version: Tuple[int] = field(default_factory=lambda: (0, 1)) + fap_version: str | Tuple[int] = "0.1" fap_icon: Optional[str] = None fap_libs: List[str] = field(default_factory=list) fap_category: str = "" @@ -84,6 +84,13 @@ class FlipperApplication: def __post_init__(self): if self.apptype == FlipperAppType.PLUGIN: self.stack_size = 0 + if isinstance(self.fap_version, str): + try: + self.fap_version = tuple(int(v) for v in self.fap_version.split(".")) + except ValueError: + raise FlipperManifestException( + f"Invalid version string '{self.fap_version}'. Must be in the form 'major.minor'" + ) class AppManager: diff --git a/scripts/ufbt/project_template/app_template/application.fam b/scripts/ufbt/project_template/app_template/application.fam index 37a4ce665..a2d23ef46 100644 --- a/scripts/ufbt/project_template/app_template/application.fam +++ b/scripts/ufbt/project_template/app_template/application.fam @@ -8,7 +8,7 @@ App( stack_size=2 * 1024, fap_category="Examples", # Optional values - # fap_version=(0, 1), # (major, minor) + # fap_version="0.1", fap_icon="@FBT_APPID@.png", # 10x10 1-bit PNG # fap_description="A simple app", # fap_author="J. Doe", From 341cd5e860a67b70b46a87d79d705caa1d457155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Sun, 14 May 2023 21:04:03 +0900 Subject: [PATCH 25/71] [FL-3312] fix PIN retry count reset on reboot (#2671) --- applications/services/desktop/desktop.c | 2 +- applications/services/desktop/scenes/desktop_scene_pin_input.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 28d09cc0d..77999dfcc 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -135,7 +135,6 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) { void desktop_lock(Desktop* desktop) { furi_hal_rtc_set_flag(FuriHalRtcFlagLock); - furi_hal_rtc_set_pin_fails(0); if(desktop->settings.pin_code.length) { Cli* cli = furi_record_open(RECORD_CLI); @@ -159,6 +158,7 @@ void desktop_unlock(Desktop* desktop) { scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); desktop_auto_lock_arm(desktop); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + furi_hal_rtc_set_pin_fails(0); if(desktop->settings.pin_code.length) { Cli* cli = furi_record_open(RECORD_CLI); diff --git a/applications/services/desktop/scenes/desktop_scene_pin_input.c b/applications/services/desktop/scenes/desktop_scene_pin_input.c index 157acebf5..e062c1b97 100644 --- a/applications/services/desktop/scenes/desktop_scene_pin_input.c +++ b/applications/services/desktop/scenes/desktop_scene_pin_input.c @@ -55,7 +55,6 @@ static void desktop_scene_pin_input_back_callback(void* context) { static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) { Desktop* desktop = (Desktop*)context; if(desktop_pin_compare(&desktop->settings.pin_code, pin_code)) { - furi_hal_rtc_set_pin_fails(0); view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked); } else { uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); From d4bfc3f2255f16b4d5d3ff8805478c8b07bc7aa1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 14 May 2023 20:19:47 +0300 Subject: [PATCH 26/71] Fix debugging --- lib/toolbox/version.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/toolbox/version.c b/lib/toolbox/version.c index ae3dc851a..70386d96d 100644 --- a/lib/toolbox/version.c +++ b/lib/toolbox/version.c @@ -16,11 +16,11 @@ struct Version { const char* git_hash; const char* git_branch; const char* build_date; - const char* custom_flipper_name; const char* version; // Payload bits and pieces const uint8_t target; const bool build_is_dirty; + const char* custom_flipper_name; }; /* version of current running firmware (bootloader/flipper) */ @@ -31,7 +31,6 @@ static Version version = { .git_hash = GIT_COMMIT, .git_branch = GIT_BRANCH, .build_date = BUILD_DATE, - .custom_flipper_name = NULL, .version = VERSION #ifdef FURI_RAM_EXEC " (RAM)" @@ -39,6 +38,7 @@ static Version version = { , .target = TARGET, .build_is_dirty = BUILD_DIRTY, + .custom_flipper_name = NULL, }; const Version* version_get(void) { From 575f863942bd9f727d0bdeba168e07bddcab6e53 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 15 May 2023 01:27:45 +0300 Subject: [PATCH 27/71] Fix UART terminal crashes --- .../uart_terminal/uart_terminal_uart.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/applications/external/uart_terminal/uart_terminal_uart.c b/applications/external/uart_terminal/uart_terminal_uart.c index 136bd5d38..e906c9e8e 100644 --- a/applications/external/uart_terminal/uart_terminal_uart.c +++ b/applications/external/uart_terminal/uart_terminal_uart.c @@ -38,8 +38,6 @@ void uart_terminal_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) static int32_t uart_worker(void* context) { UART_TerminalUart* uart = (void*)context; - uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1); - while(1) { uint32_t events = furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); @@ -64,6 +62,16 @@ void uart_terminal_uart_tx(uint8_t* data, size_t len) { UART_TerminalUart* uart_terminal_uart_init(UART_TerminalApp* app) { UART_TerminalUart* uart = malloc(sizeof(UART_TerminalUart)); + uart->app = app; + // Init all rx stream and thread early to avoid crashes + uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1); + uart->rx_thread = furi_thread_alloc(); + furi_thread_set_name(uart->rx_thread, "UART_TerminalUartRxThread"); + furi_thread_set_stack_size(uart->rx_thread, 1024); + furi_thread_set_context(uart->rx_thread, uart); + furi_thread_set_callback(uart->rx_thread, uart_worker); + + furi_thread_start(uart->rx_thread); furi_hal_console_disable(); if(app->BAUDRATE == 0) { @@ -72,14 +80,6 @@ UART_TerminalUart* uart_terminal_uart_init(UART_TerminalApp* app) { furi_hal_uart_set_br(UART_CH, app->BAUDRATE); furi_hal_uart_set_irq_cb(UART_CH, uart_terminal_uart_on_irq_cb, uart); - uart->app = app; - uart->rx_thread = furi_thread_alloc(); - furi_thread_set_name(uart->rx_thread, "UART_TerminalUartRxThread"); - furi_thread_set_stack_size(uart->rx_thread, 1024); - furi_thread_set_context(uart->rx_thread, uart); - furi_thread_set_callback(uart->rx_thread, uart_worker); - - furi_thread_start(uart->rx_thread); return uart; } From 89c69ed622e8529156481437385e08c4339cb3f7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 15 May 2023 01:29:24 +0300 Subject: [PATCH 28/71] Dont use temp str for manipulating text input by @Willy-JL --- applications/services/gui/modules/text_input.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 1152f496c..e14639614 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -155,10 +155,9 @@ static void text_input_backspace_cb(TextInputModel* model) { model->text_buffer[0] = 0; model->cursor_pos = 0; } else if(model->cursor_pos > 0) { - furi_string_set_str(model->temp_str, model->text_buffer); - furi_string_replace_at(model->temp_str, model->cursor_pos - 1, 1, ""); + char* move = model->text_buffer + model->cursor_pos; + memmove(move - 1, move, strlen(move) + 1); model->cursor_pos--; - strcpy(model->text_buffer, furi_string_get_cstr(model->temp_str)); } } @@ -373,16 +372,16 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, I if(shift != (text_length == 0)) { selected = char_to_uppercase(selected); } - const char replace[2] = {selected, 0}; if(model->clear_default_text) { - furi_string_set_str(model->temp_str, replace); + model->text_buffer[0] = selected; + model->text_buffer[1] = '\0'; model->cursor_pos = 1; } else { - furi_string_set_str(model->temp_str, model->text_buffer); - furi_string_replace_at(model->temp_str, model->cursor_pos, 0, replace); + char* move = model->text_buffer + model->cursor_pos; + memmove(move + 1, move, strlen(move) + 1); + model->text_buffer[model->cursor_pos] = selected; model->cursor_pos++; } - strcpy(model->text_buffer, furi_string_get_cstr(model->temp_str)); } } model->clear_default_text = false; From 07c0889d102d1caa9e1ce4370a02efec0d449ff9 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 15 May 2023 03:11:20 +0300 Subject: [PATCH 29/71] Update changelog --- CHANGELOG.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae9a20161..85d918843 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ -### New changes +### New changes +* **Warning! After installing, Desktop settings (Favoutite apps, PIN Code, AutoLock time..) will be resetted to default due to OFW changes, Please set your PIN code, Favourite apps again in Settings->Desktop** * New way of changing device name -> **Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL) * Plugins: BadBT plugin (BT version of BadKB) [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/ClaraCrazy/Flipper-Xtreme/tree/dev/applications/main/bad_kb) * Plugins: WiFi Marauder -> Added sniff pmkid on selected aps from 0.10.4 update (by @clipboard1) -* Plugins: SubGHz Bruteforcer - Increase delay just a little bit to fix some cases when receiver will not get codes and decrease manual transmit delay +* Plugins: SubGHz Bruteforcer -> Increase delay just a little bit to fix some cases when receiver will not get codes and decrease manual transmit delay +* Plugins: UART Terminal -> Fix crashes on plugin load with RX connected * SubGHz: App refactoring (OFW code ported + our code was refactored/cleaned up too) (by @gid9798 and @xMasterX | PR #461) * SubGHz: Using scene manager functions in DecodeRAW (by @gid9798 | PR #462) * SubGHz: Protocols and custom buttons refactoring (by @gid9798 | PR #465) @@ -12,7 +14,9 @@ * BT API: Functions that allow to change bt mac address and device broadcasted name (by @Willy-JL and XFW contributors) * Infrared: `External output` move out of debug and add power option for external modules * Extra pack: Some app fixes -* OFW: Rpc: add desktop service. Desktop: refactor locking routine. +* OFW: fix PIN retry count reset on reboot +* OFW: fbt: allow strings for fap_version field in app manifests +* OFW: Rpc: add desktop service. Desktop: refactor locking routine. **Now PIN lock is actually cannot be bypassed by reboot!** / **Desktop settings will be reset, please set your PIN and favourite apps again!** * OFW: Part 2 of hooking C2 IPC * OFW: ble: attempt to handle hardfaulted c2 * OFW: Add Mfkey32 application From 89ca69b111ce9f33d82fab52f4c21d2fa3d19080 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 15 May 2023 05:56:44 +0300 Subject: [PATCH 30/71] Fix keyboard buffer issues by @Willy-JL --- .../services/gui/modules/text_input.c | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index e14639614..3e521c356 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -20,7 +20,6 @@ typedef struct { size_t text_buffer_size; size_t minimum_length; bool clear_default_text; - FuriString* temp_str; bool cursor_select; size_t cursor_pos; @@ -168,6 +167,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { uint8_t start_pos = 4; model->cursor_pos = model->cursor_pos > text_length ? text_length : model->cursor_pos; + size_t cursor_pos = model->cursor_pos; canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); @@ -175,43 +175,44 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str(canvas, 2, 8, model->header); elements_slightly_rounded_frame(canvas, 1, 12, 126, 15); - FuriString* str = model->temp_str; + char buf[model->text_buffer_size + 1]; if(model->text_buffer) { - furi_string_set_str(str, model->text_buffer); - } else { - furi_string_reset(str); + strlcpy(buf, model->text_buffer, sizeof(buf)); } - const char* cstr = furi_string_get_cstr(str); + char* str = buf; if(model->clear_default_text) { elements_slightly_rounded_box( - canvas, start_pos - 1, 14, canvas_string_width(canvas, cstr) + 2, 10); + canvas, start_pos - 1, 14, canvas_string_width(canvas, str) + 2, 10); canvas_set_color(canvas, ColorWhite); } else { - furi_string_replace_at(str, model->cursor_pos, 0, "|"); + char* move = str + cursor_pos; + memmove(move + 1, move, strlen(move) + 1); + str[cursor_pos] = '|'; } - if(model->cursor_pos > 0 && canvas_string_width(canvas, cstr) > needed_string_width) { + if(cursor_pos > 0 && canvas_string_width(canvas, str) > needed_string_width) { canvas_draw_str(canvas, start_pos, 22, "..."); start_pos += 6; needed_string_width -= 8; for(uint32_t off = 0; - !furi_string_empty(str) && canvas_string_width(canvas, cstr) > needed_string_width && - off < model->cursor_pos; + strlen(str) && canvas_string_width(canvas, str) > needed_string_width && + off < cursor_pos; off++) { - furi_string_right(str, 1); + str++; } } - if(canvas_string_width(canvas, cstr) > needed_string_width) { + if(canvas_string_width(canvas, str) > needed_string_width) { needed_string_width -= 4; - while(!furi_string_empty(str) && canvas_string_width(canvas, cstr) > needed_string_width) { - furi_string_left(str, furi_string_size(str) - 1); + size_t len = strlen(str); + while(len && canvas_string_width(canvas, str) > needed_string_width) { + str[len--] = '\0'; } - furi_string_cat_str(str, "..."); + strcat(str, "..."); } - canvas_draw_str(canvas, start_pos, 22, cstr); + canvas_draw_str(canvas, start_pos, 22, str); canvas_set_font(canvas, FontKeyboard); @@ -507,7 +508,6 @@ TextInput* text_input_alloc() { TextInputModel * model, { model->validator_text = furi_string_alloc(); - model->temp_str = furi_string_alloc(); model->minimum_length = 1; model->cursor_pos = 0; model->cursor_select = false; @@ -524,10 +524,7 @@ void text_input_free(TextInput* text_input) { with_view_model( text_input->view, TextInputModel * model, - { - furi_string_free(model->validator_text); - furi_string_free(model->temp_str); - }, + { furi_string_free(model->validator_text); }, false); // Send stop command From 9dedcd07b604762a5e8d880567fe9147b2fb2f60 Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 15 May 2023 15:55:22 +0400 Subject: [PATCH 31/71] api: added lib/nfc/protocols/nfc_util.h (#2674) --- firmware/targets/f18/api_symbols.csv | 2 +- firmware/targets/f7/api_symbols.csv | 10 ++++++++-- lib/nfc/SConscript | 1 + lib/nfc/protocols/nfc_util.h | 8 ++++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 828c77166..ee1ae1154 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,26.2,, +Version,+,26.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d73a1c7bc..e2bedffb7 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,26.2,, +Version,+,26.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -144,10 +144,12 @@ Header,+,lib/mlib/m-rbtree.h,, Header,+,lib/mlib/m-tuple.h,, Header,+,lib/mlib/m-variant.h,, Header,+,lib/nfc/nfc_device.h,, +Header,+,lib/nfc/protocols/nfc_util.h,, Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_slave.h,, Header,+,lib/print/wrappers.h,, +Header,+,lib/pulse_reader/pulse_reader.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_adc.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_bus.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_comp.h,, @@ -175,7 +177,6 @@ Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_tim.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_usart.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,, -Header,+,lib/pulse_reader/pulse_reader.h,, Header,+,lib/subghz/blocks/const.h,, Header,+,lib/subghz/blocks/decoder.h,, Header,+,lib/subghz/blocks/encoder.h,, @@ -2064,6 +2065,11 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* +Function,+,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" +Function,+,nfc_util_even_parity32,uint8_t,uint32_t +Function,+,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" +Function,+,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" +Function,+,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index b086298de..b8551db84 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -6,6 +6,7 @@ env.Append( ], SDK_HEADERS=[ File("nfc_device.h"), + File("protocols/nfc_util.h"), ], ) diff --git a/lib/nfc/protocols/nfc_util.h b/lib/nfc/protocols/nfc_util.h index 04fa7622b..a9d5a3f8a 100644 --- a/lib/nfc/protocols/nfc_util.h +++ b/lib/nfc/protocols/nfc_util.h @@ -2,6 +2,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest); uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len); @@ -11,3 +15,7 @@ uint8_t nfc_util_even_parity32(uint32_t data); uint8_t nfc_util_odd_parity8(uint8_t data); void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len); + +#ifdef __cplusplus +} +#endif From c10cef3336cc0ae03eee506177f5d8a771f9bcc8 Mon Sep 17 00:00:00 2001 From: clipboard1 Date: Tue, 16 May 2023 19:15:50 +0500 Subject: [PATCH 32/71] Moved pmkid on aps and channel to one section I think this solution looks better than two buttons for pmkid --- .../scenes/wifi_marauder_scene_start.c | 17 +++++------------ .../wifi_marauder_app_i.h | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) 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) From 7fc8f6b3b86bf92ecb6d11dfba12d2371bac40c9 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 01:27:24 +0300 Subject: [PATCH 33/71] Fix vscode example config --- .vscode/example/tasks.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json index 28e67d456..16437cb10 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)", From 26a113292b2b4431ccabe317a38342795efc51e5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 13:15:59 +0300 Subject: [PATCH 34/71] CAME Atomo - Add manually support --- .../main/subghz/helpers/subghz_custom_event.h | 2 + .../helpers/subghz_txrx_create_protocol_key.c | 28 +++++++++++++ .../helpers/subghz_txrx_create_protocol_key.h | 7 ++++ .../subghz/scenes/subghz_scene_set_type.c | 20 ++++++++++ firmware/targets/f7/api_symbols.csv | 1 + lib/subghz/protocols/came_atomo.c | 40 +++++++++++++++++++ lib/subghz/protocols/came_atomo.h | 16 ++++++++ 7 files changed, 114 insertions(+) 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_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/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 7d77d7de8..9016f9274 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2757,6 +2757,7 @@ Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" +Function,-,subghz_protocol_came_atomo_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint16_t, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_alutech_at_4n_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_alutech_at_4n_feed,void,"void*, _Bool, uint32_t" diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 870afede3..18e0214bb 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -120,6 +120,46 @@ 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 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 From 174c00a8e7d64277454ef521244e0fe5297a5efb Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 14:45:48 +0300 Subject: [PATCH 35/71] Atomo Custom buttons --- lib/subghz/protocols/came_atomo.c | 122 +++++++++++++++++++++++++++--- 1 file changed, 110 insertions(+), 12 deletions(-) diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 18e0214bb..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)); @@ -164,8 +173,9 @@ bool subghz_protocol_came_atomo_create_data( * 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; @@ -185,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); @@ -199,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; @@ -251,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]; @@ -280,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"); @@ -527,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) { @@ -584,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; @@ -621,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", From e0cb823ed084fa098c53bb6840fc53da4daab457 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 14:46:41 +0300 Subject: [PATCH 36/71] what happened here, ctrl+c / ctrl+v? --- lib/subghz/protocols/dooya.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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, From 9069bb6f6e83046e6731fc83a952c1f6fadc2c8f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 14:59:51 +0300 Subject: [PATCH 37/71] Update changelog and readme --- CHANGELOG.md | 5 ++++- ReadMe.md | 19 ++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85d918843..ce55ad74b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ * Plugins: WiFi Marauder -> Added sniff pmkid on selected aps from 0.10.4 update (by @clipboard1) * Plugins: SubGHz Bruteforcer -> Increase delay just a little bit to fix some cases when receiver will not get codes and decrease manual transmit delay * Plugins: UART Terminal -> Fix crashes on plugin load with RX connected -* SubGHz: App refactoring (OFW code ported + our code was refactored/cleaned up too) (by @gid9798 and @xMasterX | PR #461) +* SubGHz: **CAME Atomo - Add manually support and custom buttons support** +* SubGHz: **App refactoring** (OFW code ported + our code was refactored/cleaned up too) (by @gid9798 and @xMasterX | PR #461) * SubGHz: Using scene manager functions in DecodeRAW (by @gid9798 | PR #462) * SubGHz: Protocols and custom buttons refactoring (by @gid9798 | PR #465) * SubGHz: Move `counter increase` setting out of debug, change max value @@ -14,6 +15,8 @@ * BT API: Functions that allow to change bt mac address and device broadcasted name (by @Willy-JL and XFW contributors) * Infrared: `External output` move out of debug and add power option for external modules * Extra pack: Some app fixes +* FBT: Fix vscode example config for debug builds - please run `./fbt vscode_dist` again if you had issues with debug builds size +* OFW: api: added lib/nfc/protocols/nfc_util.h * OFW: fix PIN retry count reset on reboot * OFW: fbt: allow strings for fap_version field in app manifests * OFW: Rpc: add desktop service. Desktop: refactor locking routine. **Now PIN lock is actually cannot be bypassed by reboot!** / **Desktop settings will be reset, please set your PIN and favourite apps again!** diff --git a/ReadMe.md b/ReadMe.md index 350db11d6..596891d6e 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -59,8 +59,8 @@ Our Discord Community: * SubGHz -> Read mode UI improvements (scrolling text, + shows time when signal was received) (by @wosk) * Sub-GHz -> External CC1101 module support (Hardware SPI used) * SubGHz -> **Hold right in received signal list to delete selected signal** -* SubGHz -> **Custom buttons for Keeloq / Alutech AT4N / Nice Flor S / Somfy Telis / Security+ 2.0** - now you can use arrow buttons to send signal with different button code -* SubGHz -> BFT Mitto / Somfy Telis / Nice Flor S manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis) +* SubGHz -> **Custom buttons for Keeloq / Alutech AT4N / Nice Flor S / Somfy Telis / Security+ 2.0 / CAME Atomo** - now you can use arrow buttons to send signal with different button code +* SubGHz -> BFT Mitto / Somfy Telis / Nice Flor S / CAME Atomo, etc.. manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis) * SubGHz -> Debug mode counter increase settings (+1 -> +5, +10, default: +1) * SubGHz -> Debug PIN output settings for protocol development * Infrared -> Debug TX PIN output settings @@ -76,6 +76,7 @@ Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported man Encoders or sending made by @xMasterX: - Nero Radio 57bit (+ 56bit encoder improvements) +- CAME 12bit/24bit encoder fixes (already merged in OFW) - Keeloq: HCS101 - Keeloq: AN-Motors - Keeloq: JCM Tech @@ -90,20 +91,20 @@ Encoders or sending made by @xMasterX: - Keeloq: CAME Space - Keeloq: Aprimatic (model TR and similar) -Encoders/sending made by @Eng1n33r & @xMasterX: -- CAME Atomo -- Nice Flor S +Encoders or sending made by @Eng1n33r(first implementation in Q2 2022) & @xMasterX (current version): +- CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) +- Nice Flor S -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)] - Keeloq: BFT Mitto [External seed calculation required (For info contact me in Discord: Nano#8998)] -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - Star Line -- Security+ v1 & v2 +- Security+ v1 & v2 (encoders was made in OFW) Encoders made by @assasinfil & @xMasterX: -- Somfy Telis +- Somfy Telis -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - Somfy Keytis - KingGates Stylo 4k -- Alutech AT-4N -- Nice ON2E (Nice One) +- Alutech AT-4N -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) +- Nice ON2E (Nice One) -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) ## Please support development of the project The majority of this project is developed and maintained by me, @xMasterX. From 90f18075cb28b486ea6f1d53c967963709a840cd Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 15:41:51 +0300 Subject: [PATCH 38/71] Restore Rx indication after deletion after Memory is FULL by @wosk / PR #464 --- applications/main/subghz/scenes/subghz_scene_receiver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 9987449cd..273105bfe 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -227,6 +227,7 @@ 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_history_delete_item(subghz->history, subghz->idx_menu_chosen); From 88e47e9251c0c8e801e3e7858ebd3fce47138a00 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 17:24:14 +0300 Subject: [PATCH 39/71] Fix crashes when deleting signals in Read mode --- .../subghz/scenes/subghz_scene_receiver.c | 4 + applications/main/subghz/views/receiver.c | 122 +++++++++++------- applications/main/subghz/views/receiver.h | 4 + 3 files changed, 81 insertions(+), 49 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 273105bfe..943d47943 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -230,9 +230,13 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { 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); consumed = true; break; diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 6f272b6a6..53921f866 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -71,6 +71,7 @@ typedef struct { SubGhzViewReceiverMode mode; uint8_t u_rssi; size_t scroll_counter; + bool nodraw; } SubGhzViewReceiverModel; void subghz_view_receiver_set_mode( @@ -241,39 +242,47 @@ 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)); + 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)); - furi_string_reset(str_buff); - } - if(scrollbar) { - elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item); } furi_string_free(str_buff); @@ -464,29 +473,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, @@ -537,6 +529,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); @@ -571,6 +564,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); }, @@ -628,6 +622,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; @@ -640,7 +651,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); From e011c844de13b50aabf9fe2c3891b1492a9acade Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 17:29:08 +0300 Subject: [PATCH 40/71] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce55ad74b..a2f1a2a28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * Plugins: SubGHz Bruteforcer -> Increase delay just a little bit to fix some cases when receiver will not get codes and decrease manual transmit delay * Plugins: UART Terminal -> Fix crashes on plugin load with RX connected * SubGHz: **CAME Atomo - Add manually support and custom buttons support** +* SubGHz: Fix crashes when deleting signals using right arrow button in `Read` mode +* SubGHz: Restore Rx indication after deletion after Memory is FULL (by @wosk | PR #464) * SubGHz: **App refactoring** (OFW code ported + our code was refactored/cleaned up too) (by @gid9798 and @xMasterX | PR #461) * SubGHz: Using scene manager functions in DecodeRAW (by @gid9798 | PR #462) * SubGHz: Protocols and custom buttons refactoring (by @gid9798 | PR #465) From 45ae0c097b5dfeaf5b77a24863c556fc39af1cbd Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 17:51:25 +0300 Subject: [PATCH 41/71] Fix need saving message when all signals was removed --- applications/main/subghz/scenes/subghz_scene_receiver.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 943d47943..26e92e983 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -238,6 +238,9 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { 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: From c760bca4a9508f86dcf5855499572807faa685c9 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 19:45:30 +0300 Subject: [PATCH 42/71] OFW PR 2316: NFC V support https://github.com/g3gg0/flipperzero-firmware/tree/ISO15693 --- .../main/nfc/helpers/nfc_custom_event.h | 2 + applications/main/nfc/nfc.c | 3 + .../main/nfc/scenes/nfc_scene_config.h | 6 + .../main/nfc/scenes/nfc_scene_delete.c | 7 +- .../main/nfc/scenes/nfc_scene_extra_actions.c | 20 + .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 175 ++- .../main/nfc/scenes/nfc_scene_nfcv_emulate.c | 164 ++ .../nfc/scenes/nfc_scene_nfcv_key_input.c | 48 + .../main/nfc/scenes/nfc_scene_nfcv_menu.c | 63 + .../main/nfc/scenes/nfc_scene_nfcv_sniff.c | 155 ++ .../main/nfc/scenes/nfc_scene_nfcv_unlock.c | 154 ++ .../nfc/scenes/nfc_scene_nfcv_unlock_menu.c | 60 + applications/main/nfc/scenes/nfc_scene_read.c | 5 + applications/main/nfc/scenes/nfc_scene_rpc.c | 7 + .../main/nfc/scenes/nfc_scene_saved_menu.c | 3 + firmware/targets/f7/api_symbols.csv | 8 + lib/nfc/nfc_device.c | 356 ++++- lib/nfc/nfc_device.h | 4 + lib/nfc/nfc_worker.c | 248 ++- lib/nfc/nfc_worker.h | 11 + lib/nfc/nfc_worker_i.h | 2 + lib/nfc/protocols/nfcv.c | 1393 +++++++++++++++++ lib/nfc/protocols/nfcv.h | 291 ++++ lib/nfc/protocols/slix.c | 412 +++++ lib/nfc/protocols/slix.h | 46 + 25 files changed, 3613 insertions(+), 30 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_menu.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c create mode 100644 lib/nfc/protocols/nfcv.c create mode 100644 lib/nfc/protocols/nfcv.h create mode 100644 lib/nfc/protocols/slix.c create mode 100644 lib/nfc/protocols/slix.h diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index 4227a5b14..aa932a3d8 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -12,4 +12,6 @@ enum NfcCustomEvent { NfcCustomEventDictAttackSkip, NfcCustomEventRpcLoad, NfcCustomEventRpcSessionClose, + NfcCustomEventUpdateLog, + NfcCustomEventSaveShadow, }; diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 16d5837ea..59110940c 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -290,6 +290,9 @@ int32_t nfc_app(void* p) { } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); DOLPHIN_DEED(DolphinDeedNfcEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); DOLPHIN_DEED(DolphinDeedNfcEmulate); diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a9da07dfd..d6edebe73 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -14,6 +14,12 @@ ADD_SCENE(nfc, file_select, FileSelect) ADD_SCENE(nfc, emulate_uid, EmulateUid) ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) ADD_SCENE(nfc, nfca_menu, NfcaMenu) +ADD_SCENE(nfc, nfcv_menu, NfcVMenu) +ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu) +ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) +ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock) +ADD_SCENE(nfc, nfcv_emulate, NfcVEmulate) +ADD_SCENE(nfc, nfcv_sniff, NfcVSniff) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_delete.c b/applications/main/nfc/scenes/nfc_scene_delete.c index cbb52bfd0..0808db45a 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete.c +++ b/applications/main/nfc/scenes/nfc_scene_delete.c @@ -31,6 +31,8 @@ void nfc_scene_delete_on_enter(void* context) { nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); NfcProtocol protocol = nfc->dev->dev_data.protocol; + const char* nfc_type = "NFC-A"; + if(protocol == NfcDeviceProtocolEMV) { furi_string_set(temp_str, "EMV bank card"); } else if(protocol == NfcDeviceProtocolMifareUl) { @@ -39,12 +41,15 @@ void nfc_scene_delete_on_enter(void* context) { furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_set(temp_str, "MIFARE DESFire"); + } else if(protocol == NfcDeviceProtocolNfcV) { + furi_string_set(temp_str, "ISO15693 tag"); + nfc_type = "NFC-V"; } else { furi_string_set(temp_str, "Unknown ISO tag"); } widget_add_string_element( nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); - widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A"); + widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, nfc_type); furi_string_free(temp_str); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 66aaf5a26..7f5bc7e75 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -4,6 +4,8 @@ enum SubmenuIndex { SubmenuIndexReadCardType, SubmenuIndexMfClassicKeys, SubmenuIndexMfUltralightUnlock, + SubmenuIndexNfcVUnlock, + SubmenuIndexNfcVSniff, }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -34,6 +36,18 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexMfUltralightUnlock, nfc_scene_extra_actions_submenu_callback, nfc); + submenu_add_item( + submenu, + "Unlock SLIX-L", + SubmenuIndexNfcVUnlock, + nfc_scene_extra_actions_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Listen NfcV Reader", + SubmenuIndexNfcVSniff, + nfc_scene_extra_actions_submenu_callback, + nfc); submenu_set_selected_item( submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); @@ -58,6 +72,12 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + consumed = true; + } else if(event.event == SubmenuIndexNfcVSniff) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVSniff); + consumed = true; } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); } 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 92ad7b56e..38f3e75c7 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -7,6 +7,17 @@ void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType typ } } +uint32_t nfc_scene_nfc_data_info_get_key(uint8_t* data) { + uint32_t value = 0; + + for(uint32_t pos = 0; pos < 4; pos++) { + value <<= 8; + value |= data[pos]; + } + + return value; +} + void nfc_scene_nfc_data_info_on_enter(void* context) { Nfc* nfc = context; Widget* widget = nfc->widget; @@ -15,7 +26,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) || - (protocol == NfcDeviceProtocolMifareClassic)) { + (protocol == NfcDeviceProtocolMifareClassic) || (protocol == NfcDeviceProtocolNfcV)) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); text_scroll_height = 52; @@ -41,19 +52,165 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n"); + } else if(protocol == NfcDeviceProtocolNfcV) { + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "\e#ISO15693\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n"); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n"); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n"); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } } else { furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); } // Set tag iso data - char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; - furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); - furi_string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + if(protocol == NfcDeviceProtocolNfcV) { + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + + furi_string_cat_printf(temp_str, "UID:\n"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf(temp_str, "\n"); + + furi_string_cat_printf( + temp_str, + "DSFID: %02X %s\n", + nfcv_data->dsfid, + (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "AFI: %02X %s\n", + nfcv_data->afi, + (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); + furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref); + furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); + furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + + furi_string_cat_printf( + temp_str, "Data (%d byte)\n", nfcv_data->block_num * nfcv_data->block_size); + + int maxBlocks = nfcv_data->block_num; + if(maxBlocks > 32) { + maxBlocks = 32; + furi_string_cat_printf(temp_str, "(truncated to %d blocks)\n", maxBlocks); + } + + for(int block = 0; block < maxBlocks; block++) { + const char* status = (nfcv_data->security_status[block] & 0x01) ? "(lck)" : ""; + for(int pos = 0; pos < nfcv_data->block_size; pos++) { + furi_string_cat_printf( + temp_str, " %02X", nfcv_data->data[block * nfcv_data->block_size + pos]); + } + furi_string_cat_printf(temp_str, " %s\n", status); + } + furi_string_cat_printf(temp_str, "\n"); + + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "Type: Plain\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "Type: SLIX\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " EAS %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas)); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "Type: SLIX-S\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Read %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_read)); + furi_string_cat_printf( + temp_str, + " Write %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_write)); + furi_string_cat_printf( + temp_str, + " Privacy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy)); + furi_string_cat_printf( + temp_str, + " Destroy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy)); + furi_string_cat_printf( + temp_str, + " EAS %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas)); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "Type: SLIX-L\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Privacy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy)); + furi_string_cat_printf( + temp_str, + " Destroy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy)); + furi_string_cat_printf( + temp_str, + " EAS %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas)); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "Type: SLIX2\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Read %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_read)); + furi_string_cat_printf( + temp_str, + " Write %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_write)); + furi_string_cat_printf( + temp_str, + " Privacy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy)); + furi_string_cat_printf( + temp_str, + " Destroy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy)); + furi_string_cat_printf( + temp_str, + " EAS %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas)); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } + } else { + char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; + furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf( + temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); + furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); } - furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); - furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); // Set application specific data if(protocol == NfcDeviceProtocolMifareDesfire) { @@ -139,6 +296,8 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(protocol == NfcDeviceProtocolMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); + } else if(protocol == NfcDeviceProtocolNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c new file mode 100644 index 000000000..ca10f5d6e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c @@ -0,0 +1,164 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200) + +enum { + NfcSceneNfcVEmulateStateWidget, + NfcSceneNfcVEmulateStateTextBox, +}; + +bool nfc_scene_nfcv_emulate_worker_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + furi_assert(context); + Nfc* nfc = context; + + switch(event) { + case NfcWorkerEventNfcVCommandExecuted: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); + break; + case NfcWorkerEventNfcVContentChanged: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); + break; + default: + break; + } + return true; +} + +void nfc_scene_nfcv_emulate_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_emulate_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) { + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); + widget_add_string_element( + widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating NfcV"); + if(strcmp(nfc->dev->dev_name, "")) { + furi_string_printf(info_str, "%s", nfc->dev->dev_name); + } else { + for(uint8_t i = 0; i < data->uid_len; i++) { + furi_string_cat_printf(info_str, "%02X ", data->uid[i]); + } + } + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_emulate_widget_callback, nfc); + } +} + +void nfc_scene_nfcv_emulate_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_nfcv_emulate_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + text_box_set_text(text_box, ""); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_scene_nfcv_emulate_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_nfcv_emulate_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVEmulate); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventUpdateLog) { + // Add data button to widget if data is received for the first time + if(strlen(nfcv_data->last_command) > 0) { + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_nfcv_emulate_widget_config(nfc, true); + } + /* use the last n bytes from the log so there's enough space for the new log entry */ + size_t maxSize = + NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); + if(furi_string_size(nfc->text_box_store) >= maxSize) { + furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); + } + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + + /* clear previously logged command */ + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == NfcCustomEventSaveShadow) { + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVEmulateStateWidget) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateTextBox); + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVEmulateStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneNfcVEmulateStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_nfcv_emulate_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c new file mode 100644 index 000000000..cc53c4dcb --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c @@ -0,0 +1,48 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_nfcv_key_input_byte_input_callback(void* context) { + Nfc* nfc = context; + NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; + + memcpy(data->key_privacy, nfc->byte_input_store, 4); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_nfcv_key_input_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter The Password In Hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_nfcv_key_input_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 4); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_nfcv_key_input_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c new file mode 100644 index 000000000..44d677513 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -0,0 +1,63 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexSave, + SubmenuIndexEmulate, +}; + +void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_nfcv_menu_submenu_callback, nfc); + submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); + + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSave) { + nfc->dev->format = NfcDeviceSaveFormatNfcV; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_nfcv_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c b/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c new file mode 100644 index 000000000..2c0f17981 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c @@ -0,0 +1,155 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (800) + +enum { + NfcSceneNfcVSniffStateWidget, + NfcSceneNfcVSniffStateTextBox, +}; + +bool nfc_scene_nfcv_sniff_worker_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + furi_assert(context); + Nfc* nfc = context; + + switch(event) { + case NfcWorkerEventNfcVCommandExecuted: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); + break; + case NfcWorkerEventNfcVContentChanged: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); + break; + default: + break; + } + return true; +} + +void nfc_scene_nfcv_sniff_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_sniff_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +static void nfc_scene_nfcv_sniff_widget_config(Nfc* nfc, bool data_received) { + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); + widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Listen NfcV"); + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_sniff_widget_callback, nfc); + } +} + +void nfc_scene_nfcv_sniff_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_nfcv_sniff_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + text_box_set_text(text_box, ""); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVSniff, + &nfc->dev->dev_data, + nfc_scene_nfcv_sniff_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_nfcv_sniff_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVSniff); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventUpdateLog) { + // Add data button to widget if data is received for the first time + if(strlen(nfcv_data->last_command) > 0) { + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_nfcv_sniff_widget_config(nfc, true); + } + /* use the last n bytes from the log so there's enough space for the new log entry */ + size_t maxSize = + NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); + if(furi_string_size(nfc->text_box_store) >= maxSize) { + furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); + } + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + + /* clear previously logged command */ + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == NfcCustomEventSaveShadow) { + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVSniffStateWidget) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateTextBox); + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVSniffStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneNfcVSniffStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_nfcv_sniff_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c new file mode 100644 index 000000000..26de304de --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -0,0 +1,154 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneNfcVUnlockStateIdle, + NfcSceneNfcVUnlockStateDetecting, + NfcSceneNfcVUnlockStateUnlocked, + NfcSceneNfcVUnlockStateAlreadyUnlocked, + NfcSceneNfcVUnlockStateNotSupportedCard, +} NfcSceneNfcVUnlockState; + +static bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; + + if(event == NfcWorkerEventNfcVPassKey) { + memcpy(data->key_privacy, nfc->byte_input_store, 4); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + } + return true; +} + +void nfc_scene_nfcv_unlock_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { + FuriHalNfcDevData* nfc_data = &(nfc->dev->dev_data.nfc_data); + NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data); + + uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock); + if(curr_state != state) { + Popup* popup = nfc->popup; + if(state == NfcSceneNfcVUnlockStateDetecting) { + popup_reset(popup); + popup_set_text( + popup, "Put figurine on\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50); + } else if(state == NfcSceneNfcVUnlockStateUnlocked) { + popup_reset(popup); + + if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { + snprintf( + nfc->dev->dev_name, + sizeof(nfc->dev->dev_name), + "SLIX_%02X%02X%02X%02X%02X%02X%02X%02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + + nfc->dev->format = NfcDeviceSaveFormatNfcV; + + if(nfc_save_file(nfc)) { + popup_set_header(popup, "Successfully\nsaved", 94, 3, AlignCenter, AlignTop); + } else { + popup_set_header( + popup, "Unlocked but\nsave failed!", 94, 3, AlignCenter, AlignTop); + } + } else { + popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop); + } + + notification_message(nfc->notifications, &sequence_single_vibro); + //notification_message(nfc->notifications, &sequence_success); + + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + } else if(state == NfcSceneNfcVUnlockStateAlreadyUnlocked) { + popup_reset(popup); + + popup_set_header(popup, "Already\nUnlocked!", 94, 3, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + } else if(state == NfcSceneNfcVUnlockStateNotSupportedCard) { + popup_reset(popup); + popup_set_header(popup, "Wrong Type Of Card!", 64, 3, AlignCenter, AlignTop); + popup_set_text(popup, nfcv_data->error, 4, 22, AlignLeft, AlignTop); + popup_set_icon(popup, 73, 20, &I_DolphinCommon_56x48); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock, state); + } +} + +void nfc_scene_nfcv_unlock_on_enter(void* context) { + Nfc* nfc = context; + + nfc_device_clear(nfc->dev); + // Setup view + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVUnlockAndSave, + &nfc->dev->dev_data, + nfc_scene_nfcv_unlock_worker_callback, + nfc); + + nfc_blink_read_start(nfc); +} + +bool nfc_scene_nfcv_unlock_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventAborted) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateAlreadyUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateNotSupportedCard); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneNfcVUnlockMenu); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + nfc_blink_stop(nfc); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVUnlock, NfcSceneNfcVUnlockStateIdle); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c new file mode 100644 index 000000000..9c4c81fbd --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c @@ -0,0 +1,60 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexNfcVUnlockMenuManual, + SubmenuIndexNfcVUnlockMenuTonieBox, +}; + +void nfc_scene_nfcv_unlock_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_unlock_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + submenu_add_item( + submenu, + "Enter PWD Manually", + SubmenuIndexNfcVUnlockMenuManual, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As TonieBox", + SubmenuIndexNfcVUnlockMenuTonieBox, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNfcVUnlockMenuManual) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodManual; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu, event.event); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_menu_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 4252883b2..d30706c5b 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -68,6 +68,11 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; + } else if(event.event == NfcWorkerEventReadNfcV) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); // Set unlock password input to 0xFFFFFFFF only on fresh read diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc/scenes/nfc_scene_rpc.c index 60d01a30d..d06ee7564 100644 --- a/applications/main/nfc/scenes/nfc_scene_rpc.c +++ b/applications/main/nfc/scenes/nfc_scene_rpc.c @@ -55,6 +55,13 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { &nfc->dev->dev_data, nfc_scene_rpc_emulate_callback, nfc); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_scene_rpc_emulate_callback, + nfc); } else { nfc_worker_start( nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index e52493f2a..15771bcc3 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -44,6 +44,7 @@ void nfc_scene_saved_menu_on_enter(void* context) { } } else if( nfc->dev->format == NfcDeviceSaveFormatMifareUl || + nfc->dev->format == NfcDeviceSaveFormatNfcV || nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); @@ -117,6 +118,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 9016f9274..84d0beda8 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2139,6 +2139,14 @@ Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" Function,-,nfca_signal_alloc,NfcaSignal*, Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" Function,-,nfca_signal_free,void,NfcaSignal* +Function,+,nfcv_emu_deinit,void,NfcVData* +Function,+,nfcv_emu_init,void,"FuriHalNfcDevData*, NfcVData*" +Function,+,nfcv_emu_loop,_Bool,"FuriHalNfcTxRxContext*, FuriHalNfcDevData*, NfcVData*, uint32_t" +Function,+,nfcv_emu_send,void,"FuriHalNfcTxRxContext*, NfcVData*, uint8_t*, uint8_t, NfcVSendFlags, uint32_t" +Function,-,nfcv_inventory,ReturnCode,uint8_t* +Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*" +Function,-,nfcv_read_card,_Bool,"NfcVReader*, FuriHalNfcDevData*, NfcVData*" +Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*" Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 3c9cf408a..bdbc62e50 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -58,6 +58,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_ furi_string_set(format_string, "Mifare Classic"); } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { furi_string_set(format_string, "Mifare DESFire"); + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + furi_string_set(format_string, "ISO15693"); } else { furi_string_set(format_string, "Unknown"); } @@ -93,6 +95,11 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_st dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire; return true; } + if(furi_string_start_with_str(format_string, "ISO15693")) { + dev->format = NfcDeviceSaveFormatNfcV; + dev->dev_data.protocol = NfcDeviceProtocolNfcV; + return true; + } return false; } @@ -650,6 +657,298 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { return parsed; } +static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVData* data = &dev->dev_data.nfcv_data; + + do { + uint32_t temp_uint32 = 0; + uint8_t temp_uint8 = 0; + + if(!flipper_format_write_comment_cstr(file, "Data Storage Format Identifier")) break; + if(!flipper_format_write_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_write_comment_cstr(file, "Application Family Identifier")) break; + if(!flipper_format_write_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_write_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + temp_uint32 = data->block_num; + if(!flipper_format_write_comment_cstr(file, "Number of memory blocks, usually 0 to 256")) + break; + if(!flipper_format_write_uint32(file, "Block Count", &temp_uint32, 1)) break; + if(!flipper_format_write_comment_cstr(file, "Size of a single memory block, usually 4")) + break; + if(!flipper_format_write_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_write_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) + break; + if(!flipper_format_write_comment_cstr( + file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info")) + break; + if(!flipper_format_write_hex( + file, "Security Status", data->security_status, 1 + data->block_num)) + break; + if(!flipper_format_write_comment_cstr( + file, + "Subtype of this card (0 = ISO15693, 1 = SLIX, 2 = SLIX-S, 3 = SLIX-L, 4 = SLIX2)")) + break; + temp_uint8 = (uint8_t)data->sub_type; + if(!flipper_format_write_hex(file, "Subtype", &temp_uint8, 1)) break; + + switch(data->sub_type) { + case NfcVTypePlain: + if(!flipper_format_write_comment_cstr(file, "End of ISO15693 parameters")) break; + saved = true; + break; + case NfcVTypeSlix: + saved = nfc_device_save_slix_data(file, dev); + break; + case NfcVTypeSlixS: + saved = nfc_device_save_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + saved = nfc_device_save_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + saved = nfc_device_save_slix2_data(file, dev); + break; + default: + break; + } + } while(false); + + return saved; +} + +bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVData* data = &dev->dev_data.nfcv_data; + + memset(data, 0x00, sizeof(NfcVData)); + + do { + uint32_t temp_uint32 = 0; + uint8_t temp_value = 0; + + if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break; + data->block_num = temp_uint32; + if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_read_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) + break; + + /* optional, as added later */ + if(flipper_format_key_exist(file, "Security Status")) { + if(!flipper_format_read_hex( + file, "Security Status", data->security_status, 1 + data->block_num)) + break; + } + if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; + data->sub_type = temp_value; + + switch(data->sub_type) { + case NfcVTypePlain: + parsed = true; + break; + case NfcVTypeSlix: + parsed = nfc_device_load_slix_data(file, dev); + break; + case NfcVTypeSlixS: + parsed = nfc_device_load_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + parsed = nfc_device_load_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + parsed = nfc_device_load_slix2_data(file, dev); + break; + default: + break; + } + } while(false); + + return parsed; +} + static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { bool saved = false; EmvData* data = &dev->dev_data.emv_data; @@ -692,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] = {}; @@ -1097,23 +1402,30 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; // Write nfc device type if(!flipper_format_write_comment_cstr( - file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card")) + file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693")) 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; - // Save ATQA in MSB order for correct companion apps display - uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; - if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; - if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + + if(dev->format != NfcDeviceSaveFormatNfcV) { + // Write ATQA, SAK + if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break; + // Save ATQA in MSB order for correct companion apps display + uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; + if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; + if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + } + // Save more data if necessary if(dev->format == NfcDeviceSaveFormatMifareUl) { if(!nfc_device_save_mifare_ul_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_save_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + if(!nfc_device_save_nfcv_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatBankCard) { if(!nfc_device_save_bank_card_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { @@ -1190,18 +1502,20 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_parse_format_string(dev, temp_str)) break; // Read and parse UID, ATQA and SAK if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break; - if(!(data_cnt == 4 || data_cnt == 7)) break; + if(!(data_cnt == 4 || data_cnt == 7 || data_cnt == 8)) break; data->uid_len = data_cnt; if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; - if(version == version_with_lsb_atqa) { - if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; - } else { - uint8_t atqa[2] = {}; - if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; - data->atqa[0] = atqa[1]; - data->atqa[1] = atqa[0]; + if(dev->format != NfcDeviceSaveFormatNfcV) { + if(version == version_with_lsb_atqa) { + if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; + } else { + uint8_t atqa[2] = {}; + if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; + data->atqa[0] = atqa[1]; + data->atqa[1] = atqa[0]; + } + if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; } - if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; // Load CUID uint8_t* cuid_start = data->uid; if(data->uid_len == 7) { @@ -1216,6 +1530,8 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_load_mifare_classic_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_load_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + if(!nfc_device_load_nfcv_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatBankCard) { if(!nfc_device_load_bank_card_data(file, dev)) break; } diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 8b2e6e5ba..d5a9e57fb 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -31,6 +32,7 @@ typedef enum { NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, + NfcDeviceProtocolNfcV } NfcProtocol; typedef enum { @@ -39,6 +41,7 @@ typedef enum { NfcDeviceSaveFormatMifareUl, NfcDeviceSaveFormatMifareClassic, NfcDeviceSaveFormatMifareDesfire, + NfcDeviceSaveFormatNfcV, } NfcDeviceSaveFormat; typedef struct { @@ -74,6 +77,7 @@ typedef struct { MfUltralightData mf_ul_data; MfClassicData mf_classic_data; MifareDesfireData mf_df_data; + NfcVData nfcv_data; }; FuriString* parsed_data; } NfcDeviceData; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index ae6c504b0..197b6f58a 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -111,6 +111,14 @@ int32_t nfc_worker_task(void* context) { nfc_worker_mf_classic_dict_attack(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { nfc_worker_analyze_reader(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + nfc_worker_nfcv_emulate(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVSniff) { + nfc_worker_nfcv_sniff(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlock) { + nfc_worker_nfcv_unlock(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + nfc_worker_nfcv_unlock(nfc_worker); } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); @@ -118,6 +126,239 @@ int32_t nfc_worker_task(void* context) { return 0; } +static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + NfcVReader reader = {}; + + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + furi_hal_nfc_sleep(); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + do { + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; + if(!nfcv_read_card(&reader, nfc_data, nfcv_data)) break; + + read_success = true; + } while(false); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + + return read_success; +} + +void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + nfcv_emu_init(nfc_data, nfcv_data); + while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); + if(nfcv_data->modified) { + nfc_worker->callback(NfcWorkerEventNfcVContentChanged, nfc_worker->context); + nfcv_data->modified = false; + } + } + } + furi_delay_ms(10); + } + nfcv_emu_deinit(nfcv_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + +void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + nfcv_data->sub_type = NfcVTypeSniff; + nfcv_emu_init(nfc_data, nfcv_data); + + while(nfc_worker->state == NfcWorkerStateNfcVSniff) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); + } + } + furi_delay_ms(10); + } + nfcv_emu_deinit(nfcv_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + FuriHalNfcTxRxContext tx_rx = {}; + uint8_t* key_data = nfcv_data->sub_data.slix.key_privacy; + uint32_t key = 0; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + furi_hal_nfc_sleep(); + + while((nfc_worker->state == NfcWorkerStateNfcVUnlock) || + (nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) { + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_ll_txrx_on(); + furi_hal_nfc_ll_poll(); + if(furi_hal_nfc_ll_set_mode( + FuriHalNfcModePollNfcv, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48) != + FuriHalNfcReturnOk) { + break; + } + + furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER); + furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCV_POLLER); + furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); + furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCV); + + furi_hal_console_printf("Detect presence\r\n"); + ReturnCode ret = slix_get_random(nfcv_data); + + if(ret == ERR_NONE) { + /* there is some chip, responding with a RAND */ + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; + furi_hal_console_printf(" Chip detected. In privacy?\r\n"); + ret = nfcv_inventory(NULL); + + if(ret == ERR_NONE) { + /* chip is also visible, so no action required, just save */ + if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + NfcVReader reader = {}; + + if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { + furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); + snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + } else { + furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + } else { + furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + + furi_hal_console_printf( + " => chip is already visible, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + + key_data[0] = 0; + key_data[1] = 0; + key_data[2] = 0; + key_data[3] = 0; + + } else { + /* chip is invisible, try to unlock */ + furi_hal_console_printf(" chip is invisible, unlocking\r\n"); + + if(nfcv_data->auth_method == NfcVAuthMethodManual) { + key |= key_data[0] << 24; + key |= key_data[1] << 16; + key |= key_data[2] << 8; + key |= key_data[3] << 0; + + ret = slix_unlock(nfcv_data, 4); + } else { + key = 0x7FFD6E5B; + key_data[0] = key >> 24; + key_data[1] = key >> 16; + key_data[2] = key >> 8; + key_data[3] = key >> 0; + ret = slix_unlock(nfcv_data, 4); + + if(ret != ERR_NONE) { + /* main key failed, trying second one */ + furi_hal_console_printf(" trying second key after resetting\r\n"); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + if(slix_get_random(nfcv_data) != ERR_NONE) { + furi_hal_console_printf(" reset failed\r\n"); + } + + key = 0x0F0F0F0F; + key_data[0] = key >> 24; + key_data[1] = key >> 16; + key_data[2] = key >> 8; + key_data[3] = key >> 0; + ret = slix_unlock(nfcv_data, 4); + } + } + if(ret != ERR_NONE) { + /* unlock failed */ + furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); + snprintf( + nfcv_data->error, sizeof(nfcv_data->error), "Passwords not\naccepted"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + /* wait for disappearing */ + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + } + } + } else { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + } + + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_sleep(); + furi_delay_ms(100); + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MfUltralightReader reader = {}; @@ -378,7 +619,12 @@ void nfc_worker_read(NfcWorker* nfc_worker) { event = NfcWorkerEventReadUidNfcF; break; } else if(nfc_data->type == FuriHalNfcTypeV) { - event = NfcWorkerEventReadUidNfcV; + FURI_LOG_I(TAG, "NfcV detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; + if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) { + FURI_LOG_I(TAG, "nfc_worker_read_nfcv success"); + } + event = NfcWorkerEventReadNfcV; break; } } else { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index ce542828a..8918d8e8b 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -18,6 +18,10 @@ typedef enum { NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, + NfcWorkerStateNfcVEmulate, + NfcWorkerStateNfcVUnlock, + NfcWorkerStateNfcVUnlockAndSave, + NfcWorkerStateNfcVSniff, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -40,6 +44,7 @@ typedef enum { NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, NfcWorkerEventReadBankCard, + NfcWorkerEventReadNfcV, // Nfc worker common events NfcWorkerEventSuccess, @@ -70,6 +75,9 @@ typedef enum { // Mifare Ultralight events NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command + NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key + NfcWorkerEventNfcVCommandExecuted, + NfcWorkerEventNfcVContentChanged, } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); @@ -88,3 +96,6 @@ void nfc_worker_start( void* context); void nfc_worker_stop(NfcWorker* nfc_worker); +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); +void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker); +void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker); \ No newline at end of file diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 9733426ab..d13d3c5c8 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include struct NfcWorker { diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c new file mode 100644 index 000000000..471993b63 --- /dev/null +++ b/lib/nfc/protocols/nfcv.c @@ -0,0 +1,1393 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nfcv.h" +#include "nfc_util.h" +#include "slix.h" + +#define TAG "NfcV" + +/* macros to map "modulate field" flag to GPIO level */ +#define GPIO_LEVEL_MODULATED NFCV_LOAD_MODULATION_POLARITY +#define GPIO_LEVEL_UNMODULATED (!GPIO_LEVEL_MODULATED) + +/* timing macros */ +#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) +#define DIGITAL_SIGNAL_UNIT_US (100000.0f) + +ReturnCode nfcv_inventory(uint8_t* uid) { + uint16_t received = 0; + rfalNfcvInventoryRes res; + ReturnCode ret = ERR_NONE; + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret == ERR_NONE) { + if(uid != NULL) { + memcpy(uid, res.UID, NFCV_UID_LENGTH); + } + } + + return ret; +} + +ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* nfcv_data) { + UNUSED(reader); + + uint16_t received = 0; + for(size_t block = 0; block < nfcv_data->block_num; block++) { + uint8_t rxBuf[32]; + FURI_LOG_D(TAG, "Reading block %d/%d", block, (nfcv_data->block_num - 1)); + + ReturnCode ret = ERR_NONE; + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + ret = rfalNfcvPollerReadSingleBlock( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block, rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + break; + } + } + if(ret != ERR_NONE) { + FURI_LOG_D(TAG, "failed to read: %d", ret); + return ret; + } + memcpy( + &(nfcv_data->data[block * nfcv_data->block_size]), &rxBuf[1], nfcv_data->block_size); + FURI_LOG_D( + TAG, + " %02X %02X %02X %02X", + nfcv_data->data[block * nfcv_data->block_size + 0], + nfcv_data->data[block * nfcv_data->block_size + 1], + nfcv_data->data[block * nfcv_data->block_size + 2], + nfcv_data->data[block * nfcv_data->block_size + 3]); + } + + return ERR_NONE; +} + +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + uint8_t rxBuf[32]; + uint16_t received = 0; + ReturnCode ret = ERR_NONE; + + FURI_LOG_D(TAG, "Read SYSTEM INFORMATION..."); + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerGetSystemInformation( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret == ERR_NONE) { + nfc_data->type = FuriHalNfcTypeV; + nfc_data->uid_len = NFCV_UID_LENGTH; + /* UID is stored reversed in this response */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + nfc_data->uid[pos] = rxBuf[2 + (NFCV_UID_LENGTH - 1 - pos)]; + } + nfcv_data->dsfid = rxBuf[NFCV_UID_LENGTH + 2]; + nfcv_data->afi = rxBuf[NFCV_UID_LENGTH + 3]; + nfcv_data->block_num = rxBuf[NFCV_UID_LENGTH + 4] + 1; + nfcv_data->block_size = rxBuf[NFCV_UID_LENGTH + 5] + 1; + nfcv_data->ic_ref = rxBuf[NFCV_UID_LENGTH + 6]; + FURI_LOG_D( + TAG, + " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + FURI_LOG_D( + TAG, + " DSFID %d, AFI %d, Blocks %d, Size %d, IC Ref %d", + nfcv_data->dsfid, + nfcv_data->afi, + nfcv_data->block_num, + nfcv_data->block_size, + nfcv_data->ic_ref); + return ret; + } + FURI_LOG_D(TAG, "Failed: %d", ret); + + return ret; +} + +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(reader); + furi_assert(nfc_data); + furi_assert(nfcv_data); + + if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) { + return false; + } + + if(nfcv_read_blocks(reader, nfcv_data) != ERR_NONE) { + return false; + } + + if(slix_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX detected"); + nfcv_data->sub_type = NfcVTypeSlix; + } else if(slix2_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX2 detected"); + nfcv_data->sub_type = NfcVTypeSlix2; + } else if(slix_s_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-S detected"); + nfcv_data->sub_type = NfcVTypeSlixS; + } else if(slix_l_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-L detected"); + nfcv_data->sub_type = NfcVTypeSlixL; + } else { + nfcv_data->sub_type = NfcVTypePlain; + } + + return true; +} + +void nfcv_crc(uint8_t* data, uint32_t length) { + uint32_t reg = 0xFFFF; + + for(size_t i = 0; i < length; i++) { + reg = reg ^ ((uint32_t)data[i]); + for(size_t j = 0; j < 8; j++) { + if(reg & 0x0001) { + reg = (reg >> 1) ^ 0x8408; + } else { + reg = (reg >> 1); + } + } + } + + uint16_t crc = ~(uint16_t)(reg & 0xffff); + + data[length + 0] = crc & 0xFF; + data[length + 1] = crc >> 8; +} + +void nfcv_emu_free_signals(NfcVEmuAirSignals* signals) { + furi_assert(signals); + + if(signals->nfcv_resp_one) { + digital_signal_free(signals->nfcv_resp_one); + } + if(signals->nfcv_resp_zero) { + digital_signal_free(signals->nfcv_resp_zero); + } + if(signals->nfcv_resp_sof) { + digital_signal_free(signals->nfcv_resp_sof); + } + if(signals->nfcv_resp_eof) { + digital_signal_free(signals->nfcv_resp_eof); + } + signals->nfcv_resp_one = NULL; + signals->nfcv_resp_zero = NULL; + signals->nfcv_resp_sof = NULL; + signals->nfcv_resp_eof = NULL; +} + +bool nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_t slowdown) { + furi_assert(air); + furi_assert(signals); + + bool success = true; + + if(!signals->nfcv_resp_one) { + /* logical one: unmodulated then 8 pulses */ + signals->nfcv_resp_one = digital_signal_alloc( + slowdown * (air->nfcv_resp_unmod->edge_cnt + 8 * air->nfcv_resp_pulse->edge_cnt)); + if(!signals->nfcv_resp_one) { + return false; + } + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_unmod); + } + for(size_t i = 0; i < slowdown * 8; i++) { + success &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_pulse); + } + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_zero) { + /* logical zero: 8 pulses then unmodulated */ + signals->nfcv_resp_zero = digital_signal_alloc( + slowdown * (8 * air->nfcv_resp_pulse->edge_cnt + air->nfcv_resp_unmod->edge_cnt)); + if(!signals->nfcv_resp_zero) { + return false; + } + for(size_t i = 0; i < slowdown * 8; i++) { + success &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_pulse); + } + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_unmod); + } + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_sof) { + /* SOF: unmodulated, 24 pulses, logic 1 */ + signals->nfcv_resp_sof = digital_signal_alloc( + slowdown * (3 * air->nfcv_resp_unmod->edge_cnt + 24 * air->nfcv_resp_pulse->edge_cnt) + + signals->nfcv_resp_one->edge_cnt); + if(!signals->nfcv_resp_sof) { + return false; + } + for(size_t i = 0; i < slowdown * 3; i++) { + success &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_unmod); + } + for(size_t i = 0; i < slowdown * 24; i++) { + success &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_pulse); + } + success &= digital_signal_append(signals->nfcv_resp_sof, signals->nfcv_resp_one); + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_eof) { + /* EOF: logic 0, 24 pulses, unmodulated */ + signals->nfcv_resp_eof = digital_signal_alloc( + signals->nfcv_resp_zero->edge_cnt + + slowdown * (24 * air->nfcv_resp_pulse->edge_cnt + 3 * air->nfcv_resp_unmod->edge_cnt) + + air->nfcv_resp_unmod->edge_cnt); + if(!signals->nfcv_resp_eof) { + return false; + } + success &= digital_signal_append(signals->nfcv_resp_eof, signals->nfcv_resp_zero); + for(size_t i = 0; i < slowdown * 23; i++) { + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_pulse); + } + /* we don't want to add the last level as we just want a transition to "unmodulated" again */ + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_half_pulse); + } + } + return success; +} + +bool nfcv_emu_alloc(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + if(!nfcv_data->frame) { + nfcv_data->frame = malloc(NFCV_FRAMESIZE_MAX); + if(!nfcv_data->frame) { + return false; + } + } + + if(!nfcv_data->emu_air.nfcv_signal) { + /* assuming max frame length is 255 bytes */ + nfcv_data->emu_air.nfcv_signal = digital_sequence_alloc(8 * 255 + 2, &gpio_spi_r_mosi); + if(!nfcv_data->emu_air.nfcv_signal) { + return false; + } + } + if(!nfcv_data->emu_air.nfcv_resp_unmod) { + /* unmodulated 256/fc or 1024/fc signal as building block */ + nfcv_data->emu_air.nfcv_resp_unmod = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_unmod) { + return false; + } + nfcv_data->emu_air.nfcv_resp_unmod->start_level = GPIO_LEVEL_UNMODULATED; + nfcv_data->emu_air.nfcv_resp_unmod->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_unmod->edge_cnt = 1; + } + if(!nfcv_data->emu_air.nfcv_resp_pulse) { + /* modulated fc/32 or fc/8 pulse as building block */ + nfcv_data->emu_air.nfcv_resp_pulse = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_pulse) { + return false; + } + nfcv_data->emu_air.nfcv_resp_pulse->start_level = GPIO_LEVEL_MODULATED; + nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[1] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_pulse->edge_cnt = 2; + } + + if(!nfcv_data->emu_air.nfcv_resp_half_pulse) { + /* modulated fc/32 or fc/8 pulse as building block */ + nfcv_data->emu_air.nfcv_resp_half_pulse = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_half_pulse) { + return false; + } + nfcv_data->emu_air.nfcv_resp_half_pulse->start_level = GPIO_LEVEL_MODULATED; + nfcv_data->emu_air.nfcv_resp_half_pulse->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_half_pulse->edge_cnt = 1; + } + + bool success = true; + success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_high, 1); + success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_low, 4); + + if(!success) { + FURI_LOG_E(TAG, "Failed to allocate signals"); + return false; + } + + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_SOF, + nfcv_data->emu_air.signals_high.nfcv_resp_sof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_BIT0, + nfcv_data->emu_air.signals_high.nfcv_resp_zero); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_BIT1, + nfcv_data->emu_air.signals_high.nfcv_resp_one); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_EOF, + nfcv_data->emu_air.signals_high.nfcv_resp_eof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_SOF, + nfcv_data->emu_air.signals_low.nfcv_resp_sof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_BIT0, + nfcv_data->emu_air.signals_low.nfcv_resp_zero); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_BIT1, + nfcv_data->emu_air.signals_low.nfcv_resp_one); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_EOF, + nfcv_data->emu_air.signals_low.nfcv_resp_eof); + + return true; +} + +void nfcv_emu_free(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + if(nfcv_data->frame) { + free(nfcv_data->frame); + } + if(nfcv_data->emu_protocol_ctx) { + free(nfcv_data->emu_protocol_ctx); + } + if(nfcv_data->emu_air.nfcv_resp_unmod) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_unmod); + } + if(nfcv_data->emu_air.nfcv_resp_pulse) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse); + } + if(nfcv_data->emu_air.nfcv_resp_half_pulse) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_half_pulse); + } + if(nfcv_data->emu_air.nfcv_signal) { + digital_sequence_free(nfcv_data->emu_air.nfcv_signal); + } + if(nfcv_data->emu_air.reader_signal) { + pulse_reader_free(nfcv_data->emu_air.reader_signal); + } + + nfcv_data->frame = NULL; + nfcv_data->emu_air.nfcv_resp_unmod = NULL; + nfcv_data->emu_air.nfcv_resp_pulse = NULL; + nfcv_data->emu_air.nfcv_resp_half_pulse = NULL; + nfcv_data->emu_air.nfcv_signal = NULL; + nfcv_data->emu_air.reader_signal = NULL; + + nfcv_emu_free_signals(&nfcv_data->emu_air.signals_high); + nfcv_emu_free_signals(&nfcv_data->emu_air.signals_low); +} + +void nfcv_emu_send( + FuriHalNfcTxRxContext* tx_rx, + NfcVData* nfcv, + uint8_t* data, + uint8_t length, + NfcVSendFlags flags, + uint32_t send_time) { + furi_assert(tx_rx); + furi_assert(nfcv); + + /* picked default value (0) to match the most common format */ + if(!flags) { + flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof | + NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate; + } + + if(flags & NfcVSendFlagsCrc) { + nfcv_crc(data, length); + length += 2; + } + + /* depending on the request flags, send with high or low rate */ + uint32_t bit0 = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_BIT0 : NFCV_SIG_LOW_BIT0; + uint32_t bit1 = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_BIT1 : NFCV_SIG_LOW_BIT1; + uint32_t sof = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_SOF : NFCV_SIG_LOW_SOF; + uint32_t eof = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_EOF : NFCV_SIG_LOW_EOF; + + digital_sequence_clear(nfcv->emu_air.nfcv_signal); + + if(flags & NfcVSendFlagsSof) { + digital_sequence_add(nfcv->emu_air.nfcv_signal, sof); + } + + for(int bit_total = 0; bit_total < length * 8; bit_total++) { + uint32_t byte_pos = bit_total / 8; + uint32_t bit_pos = bit_total % 8; + uint8_t bit_val = 0x01 << bit_pos; + + digital_sequence_add(nfcv->emu_air.nfcv_signal, (data[byte_pos] & bit_val) ? bit1 : bit0); + } + + if(flags & NfcVSendFlagsEof) { + digital_sequence_add(nfcv->emu_air.nfcv_signal, eof); + } + + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + digital_sequence_set_sendtime(nfcv->emu_air.nfcv_signal, send_time); + digital_sequence_send(nfcv->emu_air.nfcv_signal); + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + + if(tx_rx->sniff_tx) { + tx_rx->sniff_tx(data, length * 8, false, tx_rx->sniff_context); + } +} + +static void nfcv_revuidcpy(uint8_t* dst, uint8_t* src) { + for(int pos = 0; pos < NFCV_UID_LENGTH; pos++) { + dst[pos] = src[NFCV_UID_LENGTH - 1 - pos]; + } +} + +static int nfcv_revuidcmp(uint8_t* dst, uint8_t* src) { + for(int pos = 0; pos < NFCV_UID_LENGTH; pos++) { + if(dst[pos] != src[NFCV_UID_LENGTH - 1 - pos]) { + return 1; + } + } + return 0; +} + +void nfcv_emu_handle_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + + if(nfcv_data->frame_length < 2) { + return; + } + + if(nfcv_data->echo_mode) { + nfcv_emu_send( + tx_rx, + nfcv_data, + nfcv_data->frame, + nfcv_data->frame_length, + NfcVSendFlagsSof | NfcVSendFlagsHighRate | NfcVSendFlagsEof, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO data"); + return; + } + + /* parse the frame data for the upcoming part 3 handling */ + ctx->flags = nfcv_data->frame[0]; + ctx->command = nfcv_data->frame[1]; + ctx->selected = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && (ctx->flags & NFCV_REQ_FLAG_SELECT); + ctx->addressed = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= NFCV_CMD_ADVANCED); + ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? NFCV_UID_LENGTH : 0); + ctx->response_flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof; + ctx->send_time = nfcv_data->eof_timestamp + NFCV_FDT_FC(4380); + + if(ctx->flags & NFCV_REQ_FLAG_DATA_RATE) { + ctx->response_flags |= NfcVSendFlagsHighRate; + } + if(ctx->flags & NFCV_REQ_FLAG_SUB_CARRIER) { + ctx->response_flags |= NfcVSendFlagsTwoSubcarrier; + } + + if(ctx->payload_offset + 2 > nfcv_data->frame_length) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "command 0x%02X, but packet is too short", ctx->command); +#endif + return; + } + + /* standard behavior is implemented */ + if(ctx->addressed) { + uint8_t* address = &nfcv_data->frame[ctx->address_offset]; + if(nfcv_revuidcmp(address, nfc_data->uid)) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "addressed command 0x%02X, but not for us:", ctx->command); + FURI_LOG_D( + TAG, + " dest: %02X%02X%02X%02X%02X%02X%02X%02X", + address[7], + address[6], + address[5], + address[4], + address[3], + address[2], + address[1], + address[0]); + FURI_LOG_D( + TAG, + " our UID: %02X%02X%02X%02X%02X%02X%02X%02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); +#endif + return; + } + } + + if(ctx->selected && !nfcv_data->selected) { +#ifdef NFCV_VERBOSE + FURI_LOG_D( + TAG, + "selected card shall execute command 0x%02X, but we were not selected", + ctx->command); +#endif + return; + } + + /* then give control to the card subtype specific protocol filter */ + if(ctx->emu_protocol_filter != NULL) { + if(ctx->emu_protocol_filter(tx_rx, nfc_data, nfcv_data)) { + if(strlen(nfcv_data->last_command) > 0) { +#ifdef NFCV_VERBOSE + FURI_LOG_D( + TAG, "Received command %s (handled by filter)", nfcv_data->last_command); +#endif + } + return; + } + } + + switch(ctx->command) { + case NFCV_CMD_INVENTORY: { + bool respond = false; + + if(ctx->flags & NFCV_REQ_FLAG_AFI) { + uint8_t afi = nfcv_data->frame[ctx->payload_offset]; + if(afi == nfcv_data->afi) { + respond = true; + } + } else { + respond = true; + } + + if(!nfcv_data->quiet && respond) { + int buffer_pos = 0; + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + ctx->response_buffer[buffer_pos++] = nfcv_data->dsfid; + nfcv_revuidcpy(&ctx->response_buffer[buffer_pos], nfc_data->uid); + buffer_pos += NFCV_UID_LENGTH; + + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + } else { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY (quiet)"); + } + break; + } + + case NFCV_CMD_STAY_QUIET: { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "STAYQUIET"); + nfcv_data->quiet = true; + break; + } + + case NFCV_CMD_LOCK_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + nfcv_data->security_status[block] |= 0x01; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK BLOCK %d", block); + break; + } + + case NFCV_CMD_WRITE_DSFID: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + + if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { + nfcv_data->dsfid = id; + nfcv_data->modified = true; + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE DSFID %02X", id); + break; + } + + case NFCV_CMD_WRITE_AFI: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + + if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { + nfcv_data->afi = id; + nfcv_data->modified = true; + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE AFI %02X", id); + break; + } + + case NFCV_CMD_LOCK_DSFID: { + if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { + nfcv_data->security_status[0] |= NfcVLockBitDsfid; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK DSFID"); + break; + } + + case NFCV_CMD_LOCK_AFI: { + if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { + nfcv_data->security_status[0] |= NfcVLockBitAfi; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK AFI"); + break; + } + + case NFCV_CMD_SELECT: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->selected = true; + nfcv_data->quiet = false; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SELECT"); + break; + } + + case NFCV_CMD_RESET_TO_READY: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->quiet = false; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "RESET_TO_READY"); + break; + } + + case NFCV_CMD_READ_MULTI_BLOCK: + case NFCV_CMD_READ_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + + if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + } + + if(block + blocks <= nfcv_data->block_num) { + uint8_t buffer_pos = 0; + + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + + for(int block_index = 0; block_index < blocks; block_index++) { + int block_current = block + block_index; + /* prepend security status */ + if(ctx->flags & NFCV_REQ_FLAG_OPTION) { + ctx->response_buffer[buffer_pos++] = + nfcv_data->security_status[1 + block_current]; + } + /* then the data block */ + memcpy( + &ctx->response_buffer[buffer_pos], + &nfcv_data->data[nfcv_data->block_size * block_current], + nfcv_data->block_size); + buffer_pos += nfcv_data->block_size; + } + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + } + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); + + break; + } + + case NFCV_CMD_WRITE_MULTI_BLOCK: + case NFCV_CMD_WRITE_BLOCK: { + uint8_t blocks = 1; + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t data_pos = ctx->payload_offset + 1; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[data_pos] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[data_pos]; + uint32_t data_len = nfcv_data->block_size * blocks; + + if((block + blocks) <= nfcv_data->block_num && + (data_pos + data_len + 2) == nfcv_data->frame_length) { + ctx->response_buffer[0] = NFCV_NOERROR; + memcpy( + &nfcv_data->data[nfcv_data->block_size * block], + &nfcv_data->frame[data_pos], + data_len); + nfcv_data->modified = true; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + } + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE MULTI BLOCK %d, %d blocks", + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE BLOCK %d <- %02X %02X %02X %02X", + block, + data[0], + data[1], + data[2], + data[3]); + } + break; + } + + case NFCV_CMD_GET_SYSTEM_INFO: { + int buffer_pos = 0; + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + ctx->response_buffer[buffer_pos++] = NFCV_SYSINFO_FLAG_DSFID | NFCV_SYSINFO_FLAG_AFI | + NFCV_SYSINFO_FLAG_MEMSIZE | NFCV_SYSINFO_FLAG_ICREF; + nfcv_revuidcpy(&ctx->response_buffer[buffer_pos], nfc_data->uid); + buffer_pos += NFCV_UID_LENGTH; + ctx->response_buffer[buffer_pos++] = nfcv_data->dsfid; /* DSFID */ + ctx->response_buffer[buffer_pos++] = nfcv_data->afi; /* AFI */ + ctx->response_buffer[buffer_pos++] = nfcv_data->block_num - 1; /* number of blocks */ + ctx->response_buffer[buffer_pos++] = nfcv_data->block_size - 1; /* block size */ + ctx->response_buffer[buffer_pos++] = nfcv_data->ic_ref; /* IC reference */ + + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO"); + break; + } + + case NFCV_CMD_CUST_ECHO_MODE: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->echo_mode = true; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO mode"); + break; + } + + case NFCV_CMD_CUST_ECHO_DATA: { + nfcv_emu_send( + tx_rx, + nfcv_data, + &nfcv_data->frame[ctx->payload_offset], + nfcv_data->frame_length - ctx->payload_offset - 2, + NfcVSendFlagsSof | NfcVSendFlagsHighRate | NfcVSendFlagsEof, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO data"); + break; + } + + default: + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "unsupported: %02X", + ctx->command); + break; + } + + if(strlen(nfcv_data->last_command) > 0) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); +#endif + } +} + +void nfcv_emu_sniff_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + + if(nfcv_data->frame_length < 2) { + return; + } + + /* parse the frame data for the upcoming part 3 handling */ + ctx->flags = nfcv_data->frame[0]; + ctx->command = nfcv_data->frame[1]; + ctx->selected = (ctx->flags & NFCV_REQ_FLAG_SELECT); + ctx->addressed = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= NFCV_CMD_ADVANCED); + ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? NFCV_UID_LENGTH : 0); + + char flags_string[5]; + + snprintf( + flags_string, + 5, + "%c%c%c%d", + (ctx->flags & NFCV_REQ_FLAG_INVENTORY) ? + 'I' : + (ctx->addressed ? 'A' : (ctx->selected ? 'S' : '*')), + ctx->advanced ? 'X' : ' ', + (ctx->flags & NFCV_REQ_FLAG_DATA_RATE) ? 'h' : 'l', + (ctx->flags & NFCV_REQ_FLAG_SUB_CARRIER) ? 2 : 1); + + switch(ctx->command) { + case NFCV_CMD_INVENTORY: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s INVENTORY", flags_string); + break; + } + + case NFCV_CMD_STAY_QUIET: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s STAYQUIET", flags_string); + nfcv_data->quiet = true; + break; + } + + case NFCV_CMD_LOCK_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s LOCK %d", + flags_string, + block); + break; + } + + case NFCV_CMD_WRITE_DSFID: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WR DSFID %d", + flags_string, + id); + break; + } + + case NFCV_CMD_WRITE_AFI: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WR AFI %d", + flags_string, + id); + break; + } + + case NFCV_CMD_LOCK_DSFID: { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s LOCK DSFID", + flags_string); + break; + } + + case NFCV_CMD_LOCK_AFI: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s LOCK AFI", flags_string); + break; + } + + case NFCV_CMD_SELECT: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s SELECT", flags_string); + break; + } + + case NFCV_CMD_RESET_TO_READY: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s RESET", flags_string); + break; + } + + case NFCV_CMD_READ_MULTI_BLOCK: + case NFCV_CMD_READ_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + + if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + } + + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s READ %d cnt: %d", + flags_string, + block, + blocks); + + break; + } + + case NFCV_CMD_WRITE_MULTI_BLOCK: + case NFCV_CMD_WRITE_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + uint8_t data_pos = 1; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[ctx->payload_offset + data_pos]; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WRITE %d, cnd %d", + flags_string, + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WRITE %d %02X %02X %02X %02X", + flags_string, + block, + data[0], + data[1], + data[2], + data[3]); + } + break; + } + + case NFCV_CMD_GET_SYSTEM_INFO: { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s SYSTEMINFO", + flags_string); + break; + } + + default: + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s unsupported: %02X", + flags_string, + ctx->command); + break; + } + + if(strlen(nfcv_data->last_command) > 0) { + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); + } +} + +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(nfc_data); + furi_assert(nfcv_data); + + if(!nfcv_emu_alloc(nfcv_data)) { + FURI_LOG_E(TAG, "Failed to allocate structures"); + nfcv_data->ready = false; + return; + } + + strcpy(nfcv_data->last_command, ""); + nfcv_data->quiet = false; + nfcv_data->selected = false; + nfcv_data->modified = false; + + /* everything is initialized */ + nfcv_data->ready = true; + + /* ensure the GPIO is already in unmodulated state */ + furi_hal_gpio_init(&gpio_spi_r_mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + + rfal_platform_spi_acquire(); + /* stop operation to configure for transparent and passive mode */ + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); + /* set enable, rx_enable and field detector enable */ + st25r3916WriteRegister( + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + /* explicitely set the modulation resistor in case system config changes for some reason */ + st25r3916WriteRegister( + ST25R3916_REG_PT_MOD, + (0 << ST25R3916_REG_PT_MOD_ptm_res_shift) | (15 << ST25R3916_REG_PT_MOD_pt_res_shift)); + /* target mode: target, other fields do not have any effect as we use transparent mode */ + st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ); + /* let us modulate the field using MOSI, read ASK modulation using IRQ */ + st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + + /* if not set already, initialize the default protocol handler */ + if(!nfcv_data->emu_protocol_ctx) { + nfcv_data->emu_protocol_ctx = malloc(sizeof(NfcVEmuProtocolCtx)); + if(nfcv_data->sub_type == NfcVTypeSniff) { + nfcv_data->emu_protocol_handler = &nfcv_emu_sniff_packet; + } else { + nfcv_data->emu_protocol_handler = &nfcv_emu_handle_packet; + } + } + + FURI_LOG_D(TAG, "Starting NfcV emulation"); + FURI_LOG_D( + TAG, + " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + + switch(nfcv_data->sub_type) { + case NfcVTypeSlixL: + FURI_LOG_D(TAG, " Card type: SLIX-L"); + slix_l_prepare(nfcv_data); + break; + case NfcVTypeSlixS: + FURI_LOG_D(TAG, " Card type: SLIX-S"); + slix_s_prepare(nfcv_data); + break; + case NfcVTypeSlix2: + FURI_LOG_D(TAG, " Card type: SLIX2"); + slix2_prepare(nfcv_data); + break; + case NfcVTypeSlix: + FURI_LOG_D(TAG, " Card type: SLIX"); + slix_prepare(nfcv_data); + break; + case NfcVTypePlain: + FURI_LOG_D(TAG, " Card type: Plain"); + break; + case NfcVTypeSniff: + FURI_LOG_D(TAG, " Card type: Sniffing"); + break; + } + + /* allocate a 512 edge buffer, more than enough */ + nfcv_data->emu_air.reader_signal = + pulse_reader_alloc(&gpio_nfc_irq_rfid_pull, NFCV_PULSE_BUFFER); + /* timebase shall be 1 ns */ + pulse_reader_set_timebase(nfcv_data->emu_air.reader_signal, PulseReaderUnitNanosecond); + /* and configure to already calculate the number of bits */ + pulse_reader_set_bittime(nfcv_data->emu_air.reader_signal, NFCV_PULSE_DURATION_NS); + /* this IO is fed into the µC via a diode, so we need a pulldown */ + pulse_reader_set_pull(nfcv_data->emu_air.reader_signal, GpioPullDown); + + /* start sampling */ + pulse_reader_start(nfcv_data->emu_air.reader_signal); +} + +void nfcv_emu_deinit(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + nfcv_emu_free(nfcv_data); + + if(nfcv_data->emu_protocol_ctx) { + free(nfcv_data->emu_protocol_ctx); + nfcv_data->emu_protocol_ctx = NULL; + } + + /* set registers back to how we found them */ + st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0x00); + st25r3916WriteRegister(ST25R3916_REG_MODE, 0x08); + rfal_platform_spi_release(); +} + +bool nfcv_emu_loop( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data, + uint32_t timeout_ms) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data); + + bool ret = false; + uint32_t frame_state = NFCV_FRAME_STATE_SOF1; + uint32_t periods_previous = 0; + uint32_t frame_pos = 0; + uint32_t byte_value = 0; + uint32_t bits_received = 0; + uint32_t timeout = timeout_ms * 1000; + bool wait_for_pulse = false; + + if(!nfcv_data->ready) { + return false; + } + +#ifdef NFCV_DIAGNOSTIC_DUMPS + uint8_t period_buffer[NFCV_DIAGNOSTIC_DUMP_SIZE]; + uint32_t period_buffer_pos = 0; +#endif + + while(true) { + uint32_t periods = pulse_reader_receive(nfcv_data->emu_air.reader_signal, timeout); + uint32_t timestamp = DWT->CYCCNT; + + /* when timed out, reset to SOF state */ + if(periods == PULSE_READER_NO_EDGE || periods == PULSE_READER_LOST_EDGE) { + break; + } + +#ifdef NFCV_DIAGNOSTIC_DUMPS + if(period_buffer_pos < sizeof(period_buffer)) { + period_buffer[period_buffer_pos++] = periods; + } +#endif + + /* short helper for detecting a pulse position */ + if(wait_for_pulse) { + wait_for_pulse = false; + if(periods != 1) { + frame_state = NFCV_FRAME_STATE_RESET; + } + continue; + } + + switch(frame_state) { + case NFCV_FRAME_STATE_SOF1: + if(periods == 1) { + frame_state = NFCV_FRAME_STATE_SOF2; + } else { + frame_state = NFCV_FRAME_STATE_SOF1; + break; + } + break; + + case NFCV_FRAME_STATE_SOF2: + /* waiting for the second low period, telling us about coding */ + if(periods == 6) { + frame_state = NFCV_FRAME_STATE_CODING_256; + periods_previous = 0; + wait_for_pulse = true; + } else if(periods == 4) { + frame_state = NFCV_FRAME_STATE_CODING_4; + periods_previous = 2; + wait_for_pulse = true; + } else { + frame_state = NFCV_FRAME_STATE_RESET; + } + break; + + case NFCV_FRAME_STATE_CODING_256: + if(periods_previous > periods) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + + if(periods > 512) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } + + periods_previous = 512 - (periods + 1); + byte_value = (periods - 1) / 2; + if(frame_pos < NFCV_FRAMESIZE_MAX) { + nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; + } + + wait_for_pulse = true; + + break; + + case NFCV_FRAME_STATE_CODING_4: + if(periods_previous > periods) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + periods_previous = 0; + + byte_value >>= 2; + bits_received += 2; + + if(periods == 1) { + byte_value |= 0x00 << 6; + periods_previous = 6; + } else if(periods == 3) { + byte_value |= 0x01 << 6; + periods_previous = 4; + } else if(periods == 5) { + byte_value |= 0x02 << 6; + periods_previous = 2; + } else if(periods == 7) { + byte_value |= 0x03 << 6; + periods_previous = 0; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } else { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(bits_received >= 8) { + if(frame_pos < NFCV_FRAMESIZE_MAX) { + nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; + } + bits_received = 0; + } + wait_for_pulse = true; + break; + } + + /* post-state-machine cleanup and reset */ + if(frame_state == NFCV_FRAME_STATE_RESET) { + frame_state = NFCV_FRAME_STATE_SOF1; + } else if(frame_state == NFCV_FRAME_STATE_EOF) { + nfcv_data->frame_length = frame_pos; + nfcv_data->eof_timestamp = timestamp; + break; + } + } + + if(frame_state == NFCV_FRAME_STATE_EOF) { + /* we know that this code uses TIM2, so stop pulse reader */ + pulse_reader_stop(nfcv_data->emu_air.reader_signal); + if(tx_rx->sniff_rx) { + tx_rx->sniff_rx(nfcv_data->frame, frame_pos * 8, false, tx_rx->sniff_context); + } + nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data); + + pulse_reader_start(nfcv_data->emu_air.reader_signal); + ret = true; + } else { + if(frame_state != NFCV_FRAME_STATE_SOF1) { +#ifdef NFCV_VERBOSE + FURI_LOG_T(TAG, "leaving while in state: %lu", frame_state); +#endif + } + } + +#ifdef NFCV_DIAGNOSTIC_DUMPS + if(period_buffer_pos) { + FURI_LOG_T(TAG, "pulses:"); + for(uint32_t pos = 0; pos < period_buffer_pos; pos++) { + FURI_LOG_T(TAG, " #%lu: %u", pos, period_buffer[pos]); + } + } +#endif + + return ret; +} diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h new file mode 100644 index 000000000..8ca6955d1 --- /dev/null +++ b/lib/nfc/protocols/nfcv.h @@ -0,0 +1,291 @@ +#pragma once + +#include +#include + +#include +#include +#include "nfc_util.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* true: modulating releases load, false: modulating adds load resistor to field coil */ +#define NFCV_LOAD_MODULATION_POLARITY (false) + +#define NFCV_FC (13560000.0f / 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 NFCV_PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) + +/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum number of blocks is defined as 256 */ +#define NFCV_BLOCKS_MAX 256 +/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum size of blocks is defined as 32 */ +#define NFCV_BLOCKSIZE_MAX 32 +/* the resulting memory size a card can have */ +#define NFCV_MEMSIZE_MAX (NFCV_BLOCKS_MAX * NFCV_BLOCKSIZE_MAX) +/* ISO/IEC 15693-3:2019(E) 7.1b: standard allows up to 8192, the maxium frame length that we are expected to receive/send is less */ +#define NFCV_FRAMESIZE_MAX (1 + NFCV_MEMSIZE_MAX + NFCV_BLOCKS_MAX) + +/* maximum string length for log messages */ +#define NFCV_LOG_STR_LEN 128 +/* maximum of pulses to be buffered by pulse reader */ +#define NFCV_PULSE_BUFFER 512 + +//#define NFCV_DIAGNOSTIC_DUMPS +//#define NFCV_DIAGNOSTIC_DUMP_SIZE 256 +//#define NFCV_VERBOSE + +/* helpers to calculate the send time based on DWT->CYCCNT */ +#define NFCV_FDT_USEC(usec) ((usec)*64) +#define NFCV_FDT_FC(ticks) ((ticks)*6400 / 1356) + +/* state machine when receiving frame bits */ +#define NFCV_FRAME_STATE_SOF1 0 +#define NFCV_FRAME_STATE_SOF2 1 +#define NFCV_FRAME_STATE_CODING_4 2 +#define NFCV_FRAME_STATE_CODING_256 3 +#define NFCV_FRAME_STATE_EOF 4 +#define NFCV_FRAME_STATE_RESET 5 + +/* sequences for every section of a frame */ +#define NFCV_SIG_SOF 0 +#define NFCV_SIG_BIT0 1 +#define NFCV_SIG_BIT1 2 +#define NFCV_SIG_EOF 3 +#define NFCV_SIG_LOW_SOF 4 +#define NFCV_SIG_LOW_BIT0 5 +#define NFCV_SIG_LOW_BIT1 6 +#define NFCV_SIG_LOW_EOF 7 + +/* various constants */ +#define NFCV_COMMAND_RETRIES 5 +#define NFCV_UID_LENGTH 8 + +/* ISO15693 protocol flags */ +typedef enum { + /* ISO15693 protocol flags when INVENTORY is NOT set */ + NFCV_REQ_FLAG_SUB_CARRIER = (1 << 0), + NFCV_REQ_FLAG_DATA_RATE = (1 << 1), + NFCV_REQ_FLAG_INVENTORY = (1 << 2), + NFCV_REQ_FLAG_PROTOCOL_EXT = (1 << 3), + NFCV_REQ_FLAG_SELECT = (1 << 4), + NFCV_REQ_FLAG_ADDRESS = (1 << 5), + NFCV_REQ_FLAG_OPTION = (1 << 6), + /* ISO15693 protocol flags when INVENTORY flag is set */ + NFCV_REQ_FLAG_AFI = (1 << 4), + NFCV_REQ_FLAG_NB_SLOTS = (1 << 5) +} NfcVRequestFlags; + +/* ISO15693 protocol flags */ +typedef enum { + NFCV_RES_FLAG_ERROR = (1 << 0), + NFCV_RES_FLAG_VALIDITY = (1 << 1), + NFCV_RES_FLAG_FINAL = (1 << 2), + NFCV_RES_FLAG_PROTOCOL_EXT = (1 << 3), + NFCV_RES_FLAG_SEC_LEN1 = (1 << 4), + NFCV_RES_FLAG_SEC_LEN2 = (1 << 5), + NFCV_RES_FLAG_WAIT_EXT = (1 << 6), +} NfcVRsponseFlags; + +/* flags for SYSINFO response */ +typedef enum { + NFCV_SYSINFO_FLAG_DSFID = (1 << 0), + NFCV_SYSINFO_FLAG_AFI = (1 << 1), + NFCV_SYSINFO_FLAG_MEMSIZE = (1 << 2), + NFCV_SYSINFO_FLAG_ICREF = (1 << 3) +} NfcVSysinfoFlags; + +/* ISO15693 command codes */ +typedef enum { + /* mandatory command codes */ + NFCV_CMD_INVENTORY = 0x01, + NFCV_CMD_STAY_QUIET = 0x02, + /* optional command codes */ + NFCV_CMD_READ_BLOCK = 0x20, + NFCV_CMD_WRITE_BLOCK = 0x21, + NFCV_CMD_LOCK_BLOCK = 0x22, + NFCV_CMD_READ_MULTI_BLOCK = 0x23, + NFCV_CMD_WRITE_MULTI_BLOCK = 0x24, + NFCV_CMD_SELECT = 0x25, + NFCV_CMD_RESET_TO_READY = 0x26, + NFCV_CMD_WRITE_AFI = 0x27, + NFCV_CMD_LOCK_AFI = 0x28, + NFCV_CMD_WRITE_DSFID = 0x29, + NFCV_CMD_LOCK_DSFID = 0x2A, + NFCV_CMD_GET_SYSTEM_INFO = 0x2B, + NFCV_CMD_READ_MULTI_SECSTATUS = 0x2C, + /* advanced command codes */ + NFCV_CMD_ADVANCED = 0xA0, + /* flipper zero custom command codes */ + NFCV_CMD_CUST_ECHO_MODE = 0xDE, + NFCV_CMD_CUST_ECHO_DATA = 0xDF +} NfcVCommands; + +/* ISO15693 Response error codes */ +typedef enum { + NFCV_NOERROR = 0x00, + NFCV_ERROR_CMD_NOT_SUP = 0x01, // Command not supported + NFCV_ERROR_CMD_NOT_REC = 0x02, // Command not recognized (eg. parameter error) + NFCV_ERROR_CMD_OPTION = 0x03, // Command option not supported + NFCV_ERROR_GENERIC = 0x0F, // No additional Info about this error + NFCV_ERROR_BLOCK_UNAVAILABLE = 0x10, + NFCV_ERROR_BLOCK_LOCKED_ALREADY = 0x11, // cannot lock again + NFCV_ERROR_BLOCK_LOCKED = 0x12, // cannot be changed + NFCV_ERROR_BLOCK_WRITE = 0x13, // Writing was unsuccessful + NFCV_ERROR_BLOCL_WRITELOCK = 0x14 // Locking was unsuccessful +} NfcVErrorcodes; + +typedef enum { + NfcVLockBitDsfid = 1, + NfcVLockBitAfi = 2, +} NfcVLockBits; + +typedef enum { + NfcVAuthMethodManual, + NfcVAuthMethodTonieBox, +} NfcVAuthMethod; + +typedef enum { + NfcVTypePlain = 0, + NfcVTypeSlix = 1, + NfcVTypeSlixS = 2, + NfcVTypeSlixL = 3, + NfcVTypeSlix2 = 4, + NfcVTypeSniff = 255, +} NfcVSubtype; + +typedef enum { + NfcVSendFlagsNormal = 0, + NfcVSendFlagsSof = 1 << 0, + NfcVSendFlagsCrc = 1 << 1, + NfcVSendFlagsEof = 1 << 2, + NfcVSendFlagsOneSubcarrier = 0, + NfcVSendFlagsTwoSubcarrier = 1 << 3, + NfcVSendFlagsLowRate = 0, + NfcVSendFlagsHighRate = 1 << 4 +} NfcVSendFlags; + +typedef struct { + uint8_t key_read[4]; + uint8_t key_write[4]; + uint8_t key_privacy[4]; + uint8_t key_destroy[4]; + uint8_t key_eas[4]; + uint8_t rand[2]; + bool privacy; +} NfcVSlixData; + +typedef union { + NfcVSlixData slix; +} NfcVSubtypeData; + +typedef struct { + DigitalSignal* nfcv_resp_sof; + DigitalSignal* nfcv_resp_one; + DigitalSignal* nfcv_resp_zero; + DigitalSignal* nfcv_resp_eof; +} NfcVEmuAirSignals; + +typedef struct { + PulseReader* reader_signal; + DigitalSignal* nfcv_resp_pulse; /* pulse length, fc/32 */ + DigitalSignal* nfcv_resp_half_pulse; /* half pulse length, fc/32 */ + DigitalSignal* nfcv_resp_unmod; /* unmodulated length 256/fc */ + NfcVEmuAirSignals signals_high; + NfcVEmuAirSignals signals_low; + DigitalSequence* nfcv_signal; +} NfcVEmuAir; + +typedef void (*NfcVEmuProtocolHandler)( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data); +typedef bool (*NfcVEmuProtocolFilter)( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data); + +/* the default ISO15693 handler context */ +typedef struct { + uint8_t flags; /* ISO15693-3 flags of the header as specified */ + uint8_t command; /* ISO15693-3 command at offset 1 as specified */ + bool selected; /* ISO15693-3 flags: selected frame */ + bool addressed; /* ISO15693-3 flags: addressed frame */ + bool advanced; /* ISO15693-3 command: advanced command */ + uint8_t address_offset; /* ISO15693-3 offset of the address in frame, if addressed is set */ + uint8_t payload_offset; /* ISO15693-3 offset of the payload in frame */ + + uint8_t response_buffer[NFCV_FRAMESIZE_MAX]; /* pre-allocated response buffer */ + NfcVSendFlags response_flags; /* flags to use when sending response */ + uint32_t send_time; /* timestamp when to send the response */ + + NfcVEmuProtocolFilter emu_protocol_filter; +} NfcVEmuProtocolCtx; + +typedef struct { + /* common ISO15693 fields, being specified in ISO15693-3 */ + uint8_t dsfid; + uint8_t afi; + uint8_t ic_ref; + uint16_t block_num; + uint8_t block_size; + uint8_t data[NFCV_MEMSIZE_MAX]; + uint8_t security_status[1 + NFCV_BLOCKS_MAX]; + bool selected; + bool quiet; + + bool modified; + bool ready; + bool echo_mode; + + /* specfic variant infos */ + NfcVSubtype sub_type; + NfcVSubtypeData sub_data; + NfcVAuthMethod auth_method; + + /* precalced air level data */ + NfcVEmuAir emu_air; + + uint8_t* frame; /* [NFCV_FRAMESIZE_MAX] ISO15693-2 incoming raw data from air layer */ + uint8_t frame_length; /* ISO15693-2 length of incoming data */ + uint32_t eof_timestamp; /* ISO15693-2 EOF timestamp, read from DWT->CYCCNT */ + + /* handler for the protocol layer as specified in ISO15693-3 */ + NfcVEmuProtocolHandler emu_protocol_handler; + void* emu_protocol_ctx; + /* runtime data */ + char last_command[NFCV_LOG_STR_LEN]; + char error[NFCV_LOG_STR_LEN]; +} NfcVData; + +typedef struct { + uint16_t blocks_to_read; + int16_t blocks_read; +} NfcVReader; + +ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data); +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data); +ReturnCode nfcv_inventory(uint8_t* uid); +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data); + +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); +void nfcv_emu_deinit(NfcVData* nfcv_data); +bool nfcv_emu_loop( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data, + uint32_t timeout_ms); +void nfcv_emu_send( + FuriHalNfcTxRxContext* tx_rx, + NfcVData* nfcv, + uint8_t* data, + uint8_t length, + NfcVSendFlags flags, + uint32_t send_time); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c new file mode 100644 index 000000000..ec3afc248 --- /dev/null +++ b/lib/nfc/protocols/slix.c @@ -0,0 +1,412 @@ + +#include +#include "nfcv.h" +#include "slix.h" +#include "nfc_util.h" +#include +#include "furi_hal_nfc.h" +#include + +#define TAG "SLIX" + +static uint32_t slix_read_be(uint8_t* data, uint32_t length) { + uint32_t value = 0; + + for(uint32_t pos = 0; pos < length; pos++) { + value <<= 8; + value |= data[pos]; + } + + return value; +} + +uint8_t slix_get_ti(FuriHalNfcDevData* nfc_data) { + return (nfc_data->uid[3] >> 3) & 3; +} + +bool slix_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) && + slix_get_ti(nfc_data) == 2) { + return true; + } + return false; +} + +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) && + slix_get_ti(nfc_data) == 1) { + return true; + } + return false; +} + +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x02)) { + return true; + } + return false; +} + +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x03)) { + return true; + } + return false; +} + +ReturnCode slix_get_random(NfcVData* data) { + uint16_t received = 0; + uint8_t rxBuf[32]; + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_GET_RANDOM_NUMBER, + RFAL_NFCV_REQ_FLAG_DEFAULT, + NFCV_MANUFACTURER_NXP, + NULL, + NULL, + 0, + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + if(received != 3) { + return ERR_PROTO; + } + if(data != NULL) { + data->sub_data.slix.rand[0] = rxBuf[2]; + data->sub_data.slix.rand[1] = rxBuf[1]; + } + } + + return ret; +} + +ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) { + furi_assert(rand); + + uint16_t received = 0; + uint8_t rxBuf[32]; + uint8_t cmd_set_pass[] = { + password_id, + data->sub_data.slix.rand[1], + data->sub_data.slix.rand[0], + data->sub_data.slix.rand[1], + data->sub_data.slix.rand[0]}; + uint8_t* password = NULL; + + switch(password_id) { + case SLIX_PASS_READ: + password = data->sub_data.slix.key_read; + break; + case SLIX_PASS_WRITE: + password = data->sub_data.slix.key_write; + break; + case SLIX_PASS_PRIVACY: + password = data->sub_data.slix.key_privacy; + break; + case SLIX_PASS_DESTROY: + password = data->sub_data.slix.key_destroy; + break; + case SLIX_PASS_EASAFI: + password = data->sub_data.slix.key_eas; + break; + default: + break; + } + + if(!password) { + return ERR_NOTSUPP; + } + + for(int pos = 0; pos < 4; pos++) { + cmd_set_pass[1 + pos] ^= password[3 - pos]; + } + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_SET_PASSWORD, + RFAL_NFCV_REQ_FLAG_DATA_RATE, + NFCV_MANUFACTURER_NXP, + NULL, + cmd_set_pass, + sizeof(cmd_set_pass), + rxBuf, + sizeof(rxBuf), + &received); + + return ret; +} + +bool slix_generic_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in, + uint32_t password_supported) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + + if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && + ctx->command != NFCV_CMD_NXP_SET_PASSWORD) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "command 0x%02X ignored, privacy mode", + ctx->command); + FURI_LOG_D(TAG, "%s", nfcv_data->last_command); + return true; + } + + bool handled = false; + + switch(ctx->command) { + case NFCV_CMD_NXP_GET_RANDOM_NUMBER: { + slix->rand[0] = furi_hal_random_get(); + slix->rand[1] = furi_hal_random_get(); + + ctx->response_buffer[0] = NFCV_NOERROR; + ctx->response_buffer[1] = slix->rand[1]; + ctx->response_buffer[2] = slix->rand[0]; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 3, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "GET_RANDOM_NUMBER -> 0x%02X%02X", + slix->rand[0], + slix->rand[1]); + + handled = true; + break; + } + + case NFCV_CMD_NXP_SET_PASSWORD: { + uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; + + if(!(password_id & password_supported)) { + break; + } + + uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; + uint8_t* rand = slix->rand; + uint8_t* password = NULL; + uint8_t password_rcv[4]; + + switch(password_id) { + case SLIX_PASS_READ: + password = slix->key_read; + break; + case SLIX_PASS_WRITE: + password = slix->key_write; + break; + case SLIX_PASS_PRIVACY: + password = slix->key_privacy; + break; + case SLIX_PASS_DESTROY: + password = slix->key_destroy; + break; + case SLIX_PASS_EASAFI: + password = slix->key_eas; + break; + default: + break; + } + + if(!password) { + break; + } + + for(int pos = 0; pos < 4; pos++) { + password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; + } + uint32_t pass_expect = slix_read_be(password, 4); + uint32_t pass_received = slix_read_be(password_rcv, 4); + + /* if the password is all-zeroes, just accept any password*/ + if(!pass_expect || pass_expect == pass_received) { + switch(password_id) { + case SLIX_PASS_READ: + break; + case SLIX_PASS_WRITE: + break; + case SLIX_PASS_PRIVACY: + slix->privacy = false; + nfcv_data->modified = true; + break; + case SLIX_PASS_DESTROY: + FURI_LOG_D(TAG, "Pooof! Got destroyed"); + break; + case SLIX_PASS_EASAFI: + break; + default: + break; + } + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "SET_PASSWORD #%02X 0x%08lX OK", + password_id, + pass_received); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "SET_PASSWORD #%02X 0x%08lX/%08lX FAIL", + password_id, + pass_received, + pass_expect); + } + handled = true; + break; + } + + case NFCV_CMD_NXP_ENABLE_PRIVACY: { + ctx->response_buffer[0] = NFCV_NOERROR; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "NFCV_CMD_NXP_ENABLE_PRIVACY"); + + slix->privacy = true; + handled = true; + break; + } + } + + return handled; +} + +bool slix_l_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter( + tx_rx, + nfc_data, + nfcv_data_in, + SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI)) { + return true; + } + + return handled; +} + +void slix_l_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_l_protocol_filter; +} + +bool slix_s_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) { + return true; + } + + return handled; +} + +void slix_s_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_s_protocol_filter; +} + +bool slix_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_EASAFI)) { + return true; + } + + return handled; +} + +void slix_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_protocol_filter; +} + +bool slix2_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) { + return true; + } + + return handled; +} + +void slix2_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix2_protocol_filter; +} diff --git a/lib/nfc/protocols/slix.h b/lib/nfc/protocols/slix.h new file mode 100644 index 000000000..701fa2f82 --- /dev/null +++ b/lib/nfc/protocols/slix.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include "nfc_util.h" +#include + +#define NFCV_MANUFACTURER_NXP 0x04 + +/* ISO15693-3 CUSTOM NXP COMMANDS */ +#define NFCV_CMD_NXP_SET_EAS 0xA2 +#define NFCV_CMD_NXP_RESET_EAS 0xA3 +#define NFCV_CMD_NXP_LOCK_EAS 0xA4 +#define NFCV_CMD_NXP_EAS_ALARM 0xA5 +#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 +#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7 +#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0 +#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 +#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2 +#define NFCV_CMD_NXP_SET_PASSWORD 0xB3 +#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4 +#define NFCV_CMD_NXP_DESTROY 0xB9 +#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA + +/* available passwords */ +#define SLIX_PASS_READ 0x01 +#define SLIX_PASS_WRITE 0x02 +#define SLIX_PASS_PRIVACY 0x04 +#define SLIX_PASS_DESTROY 0x08 +#define SLIX_PASS_EASAFI 0x10 + +#define SLIX_PASS_ALL \ + (SLIX_PASS_READ | SLIX_PASS_WRITE | SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI) + +bool slix_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); + +ReturnCode slix_get_random(NfcVData* data); +ReturnCode slix_unlock(NfcVData* data, uint32_t password_id); + +void slix_prepare(NfcVData* nfcv_data); +void slix_s_prepare(NfcVData* nfcv_data); +void slix_l_prepare(NfcVData* nfcv_data); +void slix2_prepare(NfcVData* nfcv_data); From d620285e0ae51f1b2d414643d19d01e02fde92f2 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 19:52:01 +0300 Subject: [PATCH 43/71] OFW PR 2669: nfc: Fix MFUL tearing flags read by GMMan --- lib/nfc/protocols/mifare_ultralight.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c index 0e28c0074..6960bd2a1 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -702,7 +702,7 @@ bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralight FURI_LOG_D(TAG, "Reading tearing flags"); for(size_t i = 0; i < 3; i++) { tx_rx->tx_data[0] = MF_UL_CHECK_TEARING; - tx_rx->rx_data[1] = i; + tx_rx->tx_data[1] = i; tx_rx->tx_bits = 16; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { From c9ddecff87419e5cf7aec4e29404c1cafcf46943 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 21:18:05 +0300 Subject: [PATCH 44/71] OFW PR 2658: Hitag by blackvault88 --- applications/main/lfrfid/lfrfid.c | 15 +- applications/main/lfrfid/lfrfid_i.h | 2 + .../scenes/lfrfid_scene_extra_actions.c | 12 + .../main/lfrfid/scenes/lfrfid_scene_read.c | 18 + .../lfrfid/scenes/lfrfid_scene_save_type.c | 3 + .../lfrfid/scenes/lfrfid_scene_saved_info.c | 12 +- .../main/lfrfid/views/lfrfid_view_read.c | 75 +- .../main/lfrfid/views/lfrfid_view_read.h | 11 +- .../services/gui/modules/byte_input.c | 14 +- .../services/gui/modules/byte_input.h | 2 +- firmware/targets/f7/api_symbols.csv | 2 +- lib/lfrfid/lfrfid_dict_file.c | 82 +- lib/lfrfid/lfrfid_hitag_worker.c | 2370 +++++++++++++++++ lib/lfrfid/lfrfid_hitag_worker.h | 86 + lib/lfrfid/lfrfid_worker.h | 3 + lib/lfrfid/lfrfid_worker_i.h | 1 + lib/lfrfid/lfrfid_worker_modes.c | 120 +- lib/lfrfid/protocols/lfrfid_protocols.c | 2 + lib/lfrfid/protocols/lfrfid_protocols.h | 2 + lib/lfrfid/protocols/protocol_hitag1.c | 104 + lib/lfrfid/protocols/protocol_hitag1.h | 4 + 21 files changed, 2893 insertions(+), 47 deletions(-) create mode 100644 lib/lfrfid/lfrfid_hitag_worker.c create mode 100644 lib/lfrfid/lfrfid_hitag_worker.h create mode 100644 lib/lfrfid/protocols/protocol_hitag1.c create mode 100644 lib/lfrfid/protocols/protocol_hitag1.h diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index a2bcdcf52..a82d2dd03 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -186,11 +186,16 @@ int32_t lfrfid_app(void* p) { 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/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index 86929a14a..a883d1926 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -56,6 +56,8 @@ enum LfRfidCustomEvent { LfRfidEventReadSenseCardEnd, LfRfidEventReadStartASK, LfRfidEventReadStartPSK, + LfRfidEventReadStartRTF, + LfRfidEventReadSenseHitag, //TODO combine with sensecardstart? LfRfidEventReadDone, LfRfidEventReadOverrun, LfRfidEventReadError, diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index d2bf20680..53bf32bbd 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -4,6 +4,7 @@ typedef enum { SubmenuIndexASK, SubmenuIndexPSK, + SubmenuIndexHitag, SubmenuIndexClearT5577, SubmenuIndexRAW, SubmenuIndexRAWEmulate, @@ -31,6 +32,12 @@ void lfrfid_scene_extra_actions_on_enter(void* context) { SubmenuIndexPSK, lfrfid_scene_extra_actions_submenu_callback, app); + submenu_add_item( + submenu, + "Read RTF (Reader Talks First)", + SubmenuIndexHitag, + lfrfid_scene_extra_actions_submenu_callback, + app); submenu_add_item( submenu, "Clear T5577 Password", @@ -79,6 +86,11 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; + } else if(event.event == SubmenuIndexHitag) { + app->read_type = LFRFIDWorkerReadTypeRTFOnly; + scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + DOLPHIN_DEED(DolphinDeedRfidRead); + consumed = true; } else if(event.event == SubmenuIndexClearT5577) { scene_manager_next_scene(app->scene_manager, LfRfidSceneClearT5577Confirm); consumed = true; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_read.c index 5f1959728..e9168c818 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read.c @@ -36,6 +36,10 @@ static void event = LfRfidEventReadStartASK; } else if(result == LFRFIDWorkerReadStartPSK) { event = LfRfidEventReadStartPSK; + } else if(result == LFRFIDWorkerReadStartRTF) { + event = LfRfidEventReadStartRTF; + } else if(result == LFRFIDWorkerReadSenseHitag) { //TODO combine with sensecardstart? + event = LfRfidEventReadSenseHitag; } else { return; } @@ -50,6 +54,9 @@ void lfrfid_scene_read_on_enter(void* context) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPskOnly); } else if(app->read_type == LFRFIDWorkerReadTypeASKOnly) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAskOnly); + } else if(app->read_type == LFRFIDWorkerReadTypeRTFOnly) { + lfrfid_view_read_set_read_state(app->read_view, LfRfidReadScanning); + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadRtfOnly); } lfrfid_worker_start_thread(app->lfworker); @@ -93,6 +100,17 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAsk); } consumed = true; + } else if(event.event == LfRfidEventReadStartRTF) { + if(app->read_type == LFRFIDWorkerReadTypeAuto) { + lfrfid_view_read_set_read_state(app->read_view, LfRfidReadScanning); + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadHitag); + } + consumed = true; + } else if(event.event == LfRfidEventReadSenseHitag) { //TODO combine with sensecardstart? + if(app->read_type == LFRFIDWorkerReadTypeAuto || + app->read_type == LFRFIDWorkerReadTypeRTFOnly) { + lfrfid_view_read_set_read_state(app->read_view, LfRfidReadTagDetected); + } } } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c index eb73b1123..f7bd6c6e5 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c @@ -17,6 +17,9 @@ void lfrfid_scene_save_type_on_enter(void* context) { SaveTypeCtx* state = malloc(sizeof(SaveTypeCtx)); FuriString* protocol_string = furi_string_alloc(); for(uint8_t i = 0; i < LFRFIDProtocolMax; i++) { + if(i == LFRFIDProtocolHitag1) { + continue; + } if((strcmp( protocol_dict_get_manufacturer(app->dict, i), protocol_dict_get_name(app->dict, i)) != 0) && diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c index c901629c3..812450314 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c @@ -17,11 +17,15 @@ void lfrfid_scene_saved_info_on_enter(void* context) { uint8_t* data = (uint8_t*)malloc(size); protocol_dict_get_data(app->dict, app->protocol_id, data, size); for(uint8_t i = 0; i < size; i++) { - if(i != 0) { - furi_string_cat_printf(tmp_string, ":"); + if(i >= 18) { + furi_string_cat_printf(tmp_string, ".."); + break; + } else { + if(i != 0) { + furi_string_cat_printf(tmp_string, ":"); + } + furi_string_cat_printf(tmp_string, "%02X", data[i]); } - - furi_string_cat_printf(tmp_string, "%02X", data[i]); } free(data); diff --git a/applications/main/lfrfid/views/lfrfid_view_read.c b/applications/main/lfrfid/views/lfrfid_view_read.c index 094afb617..c0dda4178 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.c +++ b/applications/main/lfrfid/views/lfrfid_view_read.c @@ -11,6 +11,7 @@ struct LfRfidReadView { typedef struct { IconAnimation* icon; LfRfidReadViewMode read_mode; + LfRfidReadViewState read_state; } LfRfidReadViewModel; static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { @@ -22,36 +23,69 @@ static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontPrimary); if(model->read_mode == LfRfidReadAsk) { - canvas_draw_str(canvas, 70, 16, "Reading 1/2"); + canvas_draw_str(canvas, 70, 8, "Reading 1/3"); - canvas_draw_str(canvas, 77, 29, "ASK"); - canvas_draw_icon(canvas, 70, 22, &I_ButtonRight_4x7); - canvas_draw_icon_animation(canvas, 102, 21, model->icon); + canvas_draw_str(canvas, 77, 20, "ASK"); + canvas_draw_icon(canvas, 70, 13, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 112, 12, model->icon); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 77, 43, "PSK"); + canvas_draw_str(canvas, 77, 33, "PSK"); + canvas_draw_str(canvas, 77, 46, "RTF"); } else if(model->read_mode == LfRfidReadPsk) { - canvas_draw_str(canvas, 70, 16, "Reading 2/2"); + canvas_draw_str(canvas, 70, 8, "Reading 2/3"); - canvas_draw_str(canvas, 77, 43, "PSK"); - canvas_draw_icon(canvas, 70, 36, &I_ButtonRight_4x7); - canvas_draw_icon_animation(canvas, 102, 35, model->icon); + canvas_draw_str(canvas, 77, 33, "PSK"); + canvas_draw_icon(canvas, 70, 26, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 112, 25, model->icon); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 77, 29, "ASK"); - } else { - canvas_draw_str(canvas, 72, 16, "Reading"); + canvas_draw_str(canvas, 77, 20, "ASK"); + canvas_draw_str(canvas, 77, 46, "RTF"); + } else if(model->read_mode == LfRfidReadHitag) { + if(model->read_state == LfRfidReadScanning) { + canvas_draw_str(canvas, 70, 8, "Reading 3/3"); - if(model->read_mode == LfRfidReadAskOnly) { - canvas_draw_str(canvas, 77, 35, "ASK"); - } else { - canvas_draw_str(canvas, 77, 35, "PSK"); + canvas_draw_str(canvas, 77, 46, "RTF"); + canvas_draw_icon(canvas, 70, 39, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 112, 38, model->icon); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 77, 20, "ASK"); + canvas_draw_str(canvas, 77, 33, "PSK"); + } else if(model->read_state == LfRfidReadTagDetected) { //TODO switch to other scene? + canvas_draw_str(canvas, 65, 8, "Hitag1 found"); + + canvas_set_font(canvas, FontSecondary); + //canvas_draw_str(canvas, 70, 20, "## ## ## ##"); //TODO get tag SN from hitag worker + canvas_draw_str(canvas, 70, 33, "Reading data"); + //canvas_draw_str(canvas, 70, 46, "Page: X/64"); //TODO get current page index from hitag worker + } + } else if(model->read_mode == LfRfidReadAskOnly) { + canvas_draw_str(canvas, 72, 16, "Reading"); + canvas_draw_str(canvas, 77, 35, "ASK"); + canvas_draw_icon_animation(canvas, 112, 27, model->icon); + } else if(model->read_mode == LfRfidReadPskOnly) { + canvas_draw_str(canvas, 72, 16, "Reading"); + canvas_draw_str(canvas, 77, 35, "PSK"); + canvas_draw_icon_animation(canvas, 112, 27, model->icon); + } else if(model->read_mode == LfRfidReadRtfOnly) { + if(model->read_state == LfRfidReadScanning) { + canvas_draw_str(canvas, 72, 16, "Reading"); + canvas_draw_str(canvas, 77, 35, "RTF"); + canvas_draw_icon_animation(canvas, 112, 27, model->icon); + } else if(model->read_state == LfRfidReadTagDetected) { //TODO switch to other scene? + canvas_draw_str(canvas, 65, 8, "Hitag1 found"); + + canvas_set_font(canvas, FontSecondary); + //canvas_draw_str(canvas, 70, 20, "## ## ## ##"); //TODO get tag SN from hitag worker + canvas_draw_str(canvas, 70, 33, "Reading data"); + //canvas_draw_str(canvas, 70, 46, "Page: X/64"); //TODO get current page index from hitag worker } - canvas_draw_icon_animation(canvas, 102, 27, model->icon); } canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 61, 56, "Don't move card"); + canvas_draw_str(canvas, 61, 60, "Don't move card"); } void lfrfid_view_read_enter(void* context) { @@ -111,3 +145,8 @@ void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMod }, true); } + +void lfrfid_view_read_set_read_state(LfRfidReadView* read_view, LfRfidReadViewState state) { + with_view_model( + read_view->view, LfRfidReadViewModel * model, { model->read_state = state; }, true); +} \ No newline at end of file diff --git a/applications/main/lfrfid/views/lfrfid_view_read.h b/applications/main/lfrfid/views/lfrfid_view_read.h index 55bb1f230..7d6d8ead4 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.h +++ b/applications/main/lfrfid/views/lfrfid_view_read.h @@ -4,10 +4,17 @@ typedef enum { LfRfidReadAsk, LfRfidReadPsk, + LfRfidReadHitag, LfRfidReadAskOnly, - LfRfidReadPskOnly + LfRfidReadPskOnly, + LfRfidReadRtfOnly, } LfRfidReadViewMode; +typedef enum { + LfRfidReadScanning, + LfRfidReadTagDetected, +} LfRfidReadViewState; + typedef struct LfRfidReadView LfRfidReadView; LfRfidReadView* lfrfid_view_read_alloc(); @@ -17,3 +24,5 @@ void lfrfid_view_read_free(LfRfidReadView* read_view); View* lfrfid_view_read_get_view(LfRfidReadView* read_view); void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode); + +void lfrfid_view_read_set_read_state(LfRfidReadView* read_view, LfRfidReadViewState state); \ No newline at end of file diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index b2d21f7ae..f1edf9de8 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -17,17 +17,17 @@ typedef struct { typedef struct { const char* header; uint8_t* bytes; - uint8_t bytes_count; + uint16_t bytes_count; ByteInputCallback input_callback; ByteChangedCallback changed_callback; void* callback_context; bool selected_high_nibble; - uint8_t selected_byte; + uint16_t selected_byte; int8_t selected_row; // row -1 - input, row 0 & 1 - keyboard uint8_t selected_column; - uint8_t first_visible_byte; + uint16_t first_visible_byte; } ByteInputModel; static const uint8_t keyboard_origin_x = 7; @@ -164,7 +164,7 @@ static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5); canvas_draw_icon(canvas, 123, 19, &I_ButtonRightSmall_3x5); - for(uint8_t i = model->first_visible_byte; + for(uint16_t i = model->first_visible_byte; i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); i++) { uint8_t byte_position = i - model->first_visible_byte; @@ -253,7 +253,7 @@ static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5); canvas_draw_icon(canvas, 122, 19, &I_ButtonRightSmall_3x5); - for(uint8_t i = model->first_visible_byte; + for(uint16_t i = model->first_visible_byte; i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); i++) { uint8_t byte_position = i - model->first_visible_byte; @@ -305,7 +305,7 @@ static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model * @param value char value * @param high_nibble set high nibble */ -static void byte_input_set_nibble(uint8_t* data, uint8_t position, char value, bool high_nibble) { +static void byte_input_set_nibble(uint8_t* data, uint16_t position, char value, bool high_nibble) { switch(value) { case '0': case '1': @@ -750,7 +750,7 @@ void byte_input_set_result_callback( ByteChangedCallback changed_callback, void* callback_context, uint8_t* bytes, - uint8_t bytes_count) { + uint16_t bytes_count) { with_view_model( byte_input->view, ByteInputModel * model, diff --git a/applications/services/gui/modules/byte_input.h b/applications/services/gui/modules/byte_input.h index 42c4b5d65..b8a4d4455 100644 --- a/applications/services/gui/modules/byte_input.h +++ b/applications/services/gui/modules/byte_input.h @@ -55,7 +55,7 @@ void byte_input_set_result_callback( ByteChangedCallback changed_callback, void* callback_context, uint8_t* bytes, - uint8_t bytes_count); + uint16_t bytes_count); /** Set byte input header text * diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 84d0beda8..4ea51493c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -626,7 +626,7 @@ Function,+,byte_input_alloc,ByteInput*, Function,+,byte_input_free,void,ByteInput* Function,+,byte_input_get_view,View*,ByteInput* Function,+,byte_input_set_header_text,void,"ByteInput*, const char*" -Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t" +Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint16_t" Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* diff --git a/lib/lfrfid/lfrfid_dict_file.c b/lib/lfrfid/lfrfid_dict_file.c index 7ae84f8b6..f8c3d5ef7 100644 --- a/lib/lfrfid/lfrfid_dict_file.c +++ b/lib/lfrfid/lfrfid_dict_file.c @@ -5,6 +5,42 @@ #define LFRFID_DICT_FILETYPE "Flipper RFID key" +bool lfrfid_dict_file_save_hitag1_data(FlipperFormat* file, uint8_t* data) { + FuriString* string = furi_string_alloc(); + bool result = false; + uint8_t pageSize = 4; + + do { + //write shortened data (tag ID) + if(!flipper_format_write_hex(file, "Data", data, pageSize)) break; + + if(!flipper_format_write_comment_cstr(file, "Hitag1 specific data")) break; + + //write pages + for(uint8_t p = 0; p < 64; p++) { + furi_string_printf(string, "Page %2u", p); + if(data[64 * pageSize + p]) { + //write page data + if(!flipper_format_write_hex( + file, furi_string_get_cstr(string), data + p * pageSize, pageSize)) + break; + } else { + //write ?? ?? ?? ?? + if(!flipper_format_write_string_cstr( + file, furi_string_get_cstr(string), "?? ?? ?? ??")) + break; + } + if(p == 64 - 1) { + result = true; + } + } + } while(false); + + furi_string_free(string); + + return result; +} + bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename) { furi_check(protocol != PROTOCOL_NO); Storage* storage = furi_record_open(RECORD_STORAGE); @@ -26,8 +62,12 @@ bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* // TODO: write comment about protocol sizes into file protocol_dict_get_data(dict, protocol, data, data_size); + if(protocol == LFRFIDProtocolHitag1) { + if(!lfrfid_dict_file_save_hitag1_data(file, data)) break; + } else { + if(!flipper_format_write_hex(file, "Data", data, data_size)) break; + } - if(!flipper_format_write_hex(file, "Data", data, data_size)) break; result = true; } while(false); @@ -138,6 +178,41 @@ static ProtocolId lfrfid_dict_protocol_fallback( return result; } +bool lfrfid_dict_file_load_hitag1_data(FlipperFormat* file, uint8_t* data) { + FuriString* string = furi_string_alloc(); + bool result = false; + uint8_t tagID[4]; + uint8_t pageSize = 4; + + do { + //read shortened data (tag ID) + if(!flipper_format_read_hex(file, "Data", tagID, 4)) break; + + //read pages + for(uint8_t p = 0; p < 64; p++) { + furi_string_printf(string, "Page %2u", p); + if(flipper_format_read_hex( + file, furi_string_get_cstr(string), data + p * pageSize, pageSize)) { + data[64 * pageSize + p] = 1; + } else { + data[64 * pageSize + p] = 0; + } + } + + //check data consistency + if(memcmp(tagID, data, pageSize) != 0) break; + + //check if tag ID & config page are succesfully read + if(data[64 * pageSize + 0] && data[64 * pageSize + 1]) { + result = true; + } + } while(false); + + furi_string_free(string); + + return result; +} + ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -163,6 +238,11 @@ ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) { if(protocol == PROTOCOL_NO) { protocol = lfrfid_dict_protocol_fallback(dict, furi_string_get_cstr(str_result), file); if(protocol == PROTOCOL_NO) break; + } else if(protocol == LFRFIDProtocolHitag1) { + // Hitag1 data + size_t data_size = protocol_dict_get_data_size(dict, protocol); + if(!lfrfid_dict_file_load_hitag1_data(file, data)) break; + protocol_dict_set_data(dict, protocol, data, data_size); } else { // data size_t data_size = protocol_dict_get_data_size(dict, protocol); diff --git a/lib/lfrfid/lfrfid_hitag_worker.c b/lib/lfrfid/lfrfid_hitag_worker.c new file mode 100644 index 000000000..02cd14ca0 --- /dev/null +++ b/lib/lfrfid/lfrfid_hitag_worker.c @@ -0,0 +1,2370 @@ +#include "lfrfid_hitag_worker.h" + +#define DMA_BUFFER_SIZE \ + 4000 //I need to keep this big enough? to have enough time inbetween HT & TC events perhaps? +#define HITAG_CMD_BUFFER_SIZE 100 //this was originally 1000, but can be lowered + +#define READ_BUFFER_SIZE 4000 +#define READ_BUFFER_COUNT 4 + +#define EMULATE_BUFFER_SIZE 50 +#define EMULATE_BUFFER_COUNT 50 + +//bitlength/ID for read cmd structure +#define HITAG_STOP 102 +#define HITAG_ON 101 +#define HITAG_OFF 100 +#define HITAG_SET 5 +#define HITAG_SELECT 5 +#define HITAG_CMD 4 +#define HITAG_BYTE 8 +#define HITAG_CRC 8 + +//carrier periods for read cmd structure +#define HITAG_LOW 7 +#define HITAG_STOP_HIGH 40 +#define HITAG_1_HIGH 21 +#define HITAG_0_HIGH 15 + +//bitlength for emulate reply cmd structure +#define HITAG_PAGE 32 +#define HITAG_STARTBIT 1 +#define HITAG_STARTBIT3 3 +#define HITAG_STARTBIT6 6 +#define HITAG_ACK 3 +#define HITAG_ACK_ADV 8 + +#define HITAG_BASEPERIOD 8 + +//carrier periods for read reply decoding +#define HITAG_DURATION_S 256 +#define HITAG_DURATION_M 384 +#define HITAG_DURATION_L 512 +#define HITAG_DURATION_ERROR_MARGIN 0.15 + +#define HITAG_BITPERIODS_AC 64 +#define HITAG_BITPERIODS_MC 32 + +//MISC +#define READ 1 +#define WRITE 2 + +#define CRC_PRESET 0xFF +#define CRC_POLYNOM 0x1D + +#define TAG "LFRFIDHitagWorker" + +// TIMER definitions +#define CARRIER_OUT_TIMER TIM1 +//#define CARRIER_OUT_TIMER_IRQ FuriHalInterruptIdTIM1 //TODO am i using this? +#define CARRIER_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH1 // or LL_TIM_CHANNEL_CH1N + +#define CARRIER_IN_TIMER TIM2 +#define CARRIER_IN_TIMER_IND_CH LL_TIM_CHANNEL_CH2 //no longer used via ETR setup +#define CARRIER_IN_TIMER_DIR_CH LL_TIM_CHANNEL_CH1 //no longer used via ETR setup + +#define CARRIER_IN_REFERENCE_TIMER TIM1 + +#define PULL_OUT_TIMER TIM2 +#define PULL_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 + +// DMA Channels definition +#define PULL_OUT_DMA DMA2 +#define PULL_OUT_DMA_CH1 LL_DMA_CHANNEL_1 +#define PULL_OUT_DMA_CH2 LL_DMA_CHANNEL_2 +#define PULL_OUT_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define PULL_OUT_DMA_CH1_DEF PULL_OUT_DMA, PULL_OUT_DMA_CH1 +#define PULL_OUT_DMA_CH2_DEF PULL_OUT_DMA, PULL_OUT_DMA_CH2 + +typedef enum { + HitagProtocolAntiCollision, + HitagProtocolManchesterCoding, +} LfRfidHitagProtocol; + +typedef enum { + LFRFIDHitagWorkerSignalHalfTransfer, + LFRFIDHitagWorkerSignalTransferComplete, + LFRFIDHitagWorkerSignalStop, +} LFRFIDHitagWorkerSignal; + +typedef enum { + HitagStateIdle, + HitagStateSelected, + HitagStateQuiet, +} LfRfidHitagState; + +typedef enum { + HitagModeBasic, + HitagModeAdvanced, +} LfRfidHitagMode; + +typedef struct { + uint32_t timer_buffer_arr[DMA_BUFFER_SIZE]; + uint32_t timer_buffer_ccr[DMA_BUFFER_SIZE]; +} TimerDMAData; + +typedef struct { + uint32_t CMDvalue[HITAG_CMD_BUFFER_SIZE]; //this can likely be a uint16_t instead of uint32_t + uint8_t CMDtype[HITAG_CMD_BUFFER_SIZE]; + uint16_t CMDlength; + uint16_t CMDposition; + uint16_t CMDsubposition; + bool CMDloop; + bool CMDactive; + uint32_t CMDcount; +} LfRfidHitagCMDData; + +typedef struct { + uint32_t replyvalue[HITAG_CMD_BUFFER_SIZE]; //this can likely be a uint8_t instead of uint32_t + uint8_t replytype[HITAG_CMD_BUFFER_SIZE]; + uint16_t replylength; +} LfRfidHitagReplyData; + +typedef struct { + BufferStream* stream; + VarintPair* pair; + uint32_t capCounter; + uint32_t prevTIMval; +} LfRfidHitagCaptureData; + +typedef struct { + uint8_t pageData[HITAG_PAGES * HITAG_PAGEBYTES]; + uint8_t pageKnown[HITAG_PAGES]; + uint8_t tagData[HITAG_PAGES * HITAG_PAGEBYTES + HITAG_PAGES]; + uint8_t pageRW[HITAG_PAGES]; + uint8_t pagePublic[HITAG_PAGES]; +} LFRFIDHitag; + +// main worker +struct LFRFIDHitagWorker { + FuriThread* thread; + FuriEventFlag* events; + uint8_t DMAeventCount; + + uint8_t carrierPrescaler; + + LFRFIDHitag* tag; + + LFRFIDHitagStatus workerstatus; + + ProtocolDict* dict; +}; + +//------------------------------------------------------------------ forward function definitions ------------------------------------------------------------------ + +static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context); +static int32_t lfrfid_hitag_worker_read_thread(void* thread_context); + +static void lfrfid_hitag_worker_carrier_in_IC_mode(void* capture_context); + +//------------------------------------------------------------------ shared functions ------------------------------------------------------------------ + +LFRFIDHitagWorker* lfrfid_hitag_worker_alloc(ProtocolDict* dict) { + furi_assert(dict); + + LFRFIDHitagWorker* worker = malloc(sizeof(LFRFIDHitagWorker)); + worker->dict = dict; + + worker->thread = furi_thread_alloc_ex("LFRFIDHitagWorker", 2048, NULL, worker); + + worker->events = furi_event_flag_alloc(NULL); + worker->DMAeventCount = 0; + + worker->carrierPrescaler = 2; + + worker->workerstatus = LFRFIDHitagStatusScanning; + + return worker; +} + +void lfrfid_hitag_worker_free(LFRFIDHitagWorker* worker) { + furi_thread_free(worker->thread); + furi_event_flag_free(worker->events); + free(worker); +} + +LFRFIDHitagStatus lfrfid_hitag_worker_get_status(LFRFIDHitagWorker* worker) { + return worker->workerstatus; +} + +void lfrfid_hitag_worker_start(LFRFIDHitagWorker* worker, LFRFIDHitagWorkerSetting setting) { + if(furi_thread_get_state(worker->thread) == FuriThreadStateStopped) { + switch(setting) { + case(LFRFIDHitagWorkerSettingRead): + DOLPHIN_DEED(DolphinDeedRfidRead); + furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_read_thread); + break; + case(LFRFIDHitagWorkerSettingEmulate): + DOLPHIN_DEED(DolphinDeedRfidEmulate); + furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_emulate_thread); + break; + default: //safety meassure, not to start a thread without a callback function + furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_read_thread); + break; + } + furi_thread_start(worker->thread); + } +} + +void lfrfid_hitag_worker_stop(LFRFIDHitagWorker* worker) { + furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalStop); + furi_thread_join(worker->thread); +} + +//------------------------------------------------------------------ READ TAG: capture input interrupt functions ------------------------------------------------------------------ + +static void lfrfid_hitag_worker_capture_in_cc_isr(bool level, uint32_t duration, void* context) { + LfRfidHitagCaptureData* capData = context; + + //check if there is a new pair available: pulse + period + bool need_to_send = varint_pair_pack(capData->pair, level, duration); + + //if so put it on buffer stream so it can be processed outside this interrupt routine + if(need_to_send) { + buffer_stream_send_from_isr( + capData->stream, + varint_pair_get_data(capData->pair), + varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); + } +} + +//------------------------------------------------------------------ READ TAG: carrier out TIMER, DMA & interrupts functions ------------------------------------------------------------------ + +static void lfrfid_hitag_worker_carrier_out_dma_isr(void* dma_context) { + LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)dma_context; + worker->DMAeventCount++; + + if(LL_DMA_IsActiveFlag_HT1(DMA1)) { + LL_DMA_ClearFlag_HT1(DMA1); + //halfway through DMA buffer --> first half can be repopulated + //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff + furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalHalfTransfer); + } + + if(LL_DMA_IsActiveFlag_TC1(DMA1)) { + LL_DMA_ClearFlag_TC1(DMA1); + //fully through DMA buffer --> second half can be repopulated + //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff + furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalTransferComplete); + } +} + +void lfrfid_hitag_worker_carrier_out_start( + uint32_t* duration, + uint32_t* pulse, + size_t length, + void* context) { + // configure timer + FURI_CRITICAL_ENTER(); + LL_TIM_DeInit(CARRIER_OUT_TIMER); + FURI_CRITICAL_EXIT(); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = SystemCoreClock / (125000 * HITAG_BASEPERIOD) - + 1; // sets the basis TIMER frequency to 8*freq (1MHz) for the timer + TIM_InitStruct.Autoreload = + HITAG_BASEPERIOD - + 1; //initial PWM period =125kHz (ARR will be handled via DMA further on) + LL_TIM_Init(CARRIER_OUT_TIMER, &TIM_InitStruct); + LL_TIM_DisableARRPreload(CARRIER_OUT_TIMER); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = + HITAG_BASEPERIOD / + 2; //initial pulse duration of half period ((CCR will be handled via DMA further on) + LL_TIM_OC_Init(CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_OC_SetPolarity(CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_EnableDMAReq_UPDATE(CARRIER_OUT_TIMER); + + // configure DMA "mem -> ARR" channel + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (CARRIER_OUT_TIMER->ARR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + + // configure DMA "mem -> CCR1" channel + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (CARRIER_OUT_TIMER->CCR1); + dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + + // attach interrupt to one of DMA channels + furi_hal_interrupt_set_isr( + FuriHalInterruptIdDma1Ch1, lfrfid_hitag_worker_carrier_out_dma_isr, context); + LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); + + // start + LL_TIM_EnableAllOutputs(CARRIER_OUT_TIMER); + + LL_TIM_SetCounter(CARRIER_OUT_TIMER, 0); + LL_TIM_EnableCounter(CARRIER_OUT_TIMER); +} + +void lfrfid_hitag_worker_carrier_out_stop() { + LL_TIM_DisableCounter(CARRIER_OUT_TIMER); + LL_TIM_DisableAllOutputs(CARRIER_OUT_TIMER); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); + LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); + + FURI_CRITICAL_ENTER(); + + LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); + LL_TIM_DeInit(CARRIER_OUT_TIMER); + + FURI_CRITICAL_EXIT(); +} + +//------------------------------------------------------------------ EMULATE TAG: pull out TIMER, DMA & interrupts functions ------------------------------------------------------------------ + +static void lfrfid_hitag_worker_pull_out_dma_stop(void* capture_context) { + //reconfigure pull_out pin to fixed low state + //via forced OC INACTIVE MODE + LL_TIM_OC_SetMode(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, LL_TIM_OCMODE_FORCED_INACTIVE); + + //disable DMA interrupts + //furi_hal_interrupt_set_isr(PULL_OUT_DMA_CH1_IRQ, NULL, NULL); + //LL_DMA_DisableIT_HT(PULL_OUT_DMA_CH1_DEF); + //LL_DMA_DisableIT_TC(PULL_OUT_DMA_CH1_DEF); + + //disable DMA channels & requests + LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER); + LL_DMA_DisableChannel(PULL_OUT_DMA_CH1_DEF); //need to disable when using normal mode? + LL_DMA_DisableChannel(PULL_OUT_DMA_CH2_DEF); //need to disable when using normal mode? + //LL_TIM_DisableAllOutputs(PULL_OUT_TIMER); //no need to disable when reconfiguring pin? + + //for logging both ccr & arr times during emulate (iso only arr) + LL_TIM_DisableIT_CC3(PULL_OUT_TIMER); + + //switch carrier detection back to input capture for stable readings (required for command detection) + lfrfid_hitag_worker_carrier_in_IC_mode(capture_context); +} + +static void lfrfid_hitag_worker_pull_out_dma_isr(void* capture_context) { + /* currently no HT interrupt enabled, only TC + if(LL_DMA_IsActiveFlag_HT1(PULL_OUT_DMA)) { + LL_DMA_ClearFlag_HT1(PULL_OUT_DMA); + //lfrfid_hitag_worker_pull_out_dma_stop(); + } + */ + + if(LL_DMA_IsActiveFlag_TC1(PULL_OUT_DMA)) { + LL_DMA_ClearFlag_TC1(PULL_OUT_DMA); + lfrfid_hitag_worker_pull_out_dma_stop(capture_context); + } +} + +static void lfrfid_hitag_worker_pull_out_dma_start(size_t length) { + //array pointers remain the same, but length changes so + //reset DMA length (only possible when DMA channel is disabled + //this also resets the DMA counter (the DMA length is the 'remaining counter' + LL_DMA_SetDataLength(PULL_OUT_DMA_CH1_DEF, length); + LL_DMA_SetDataLength(PULL_OUT_DMA_CH2_DEF, length); + + //enable DMA + LL_DMA_EnableChannel(PULL_OUT_DMA_CH1_DEF); + LL_DMA_EnableChannel(PULL_OUT_DMA_CH2_DEF); + + //set OC to PWM1 mode + LL_TIM_OC_SetMode( + PULL_OUT_TIMER, + PULL_OUT_TIMER_CHANNEL, + LL_TIM_OCMODE_PWM1); //during emulate (dma controlled) mode + + //only enable DMA requests on timer update after enabling both channels (otherwise you risk that first DMA request only triggers one of the channels) + LL_TIM_EnableDMAReq_UPDATE(PULL_OUT_TIMER); + + //for logging both ccr & arr times during emulate (iso only arr) + LL_TIM_EnableIT_CC3(PULL_OUT_TIMER); +} + +static void lfrfid_hitag_worker_pull_out_dma_setup( + uint32_t* duration, + uint32_t* pulse, + size_t length, + void* capture_context) { + //DMA setup + LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER); //start with DMA requests disabled + + // configure DMA "mem -> ARR" channel + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (PULL_OUT_TIMER->ARR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array + dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(PULL_OUT_DMA_CH1_DEF, &dma_config); + + // configure DMA "mem -> CCR3" channel + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (PULL_OUT_TIMER->CCR3); + dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array + dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(PULL_OUT_DMA_CH2_DEF, &dma_config); + + // enable DMA interrupts + furi_hal_interrupt_set_isr( + PULL_OUT_DMA_CH1_IRQ, lfrfid_hitag_worker_pull_out_dma_isr, capture_context); + //LL_DMA_EnableIT_HT(PULL_OUT_DMA_CH1_DEF); //let's try normal DMA mode and only transfer complete interrupt + LL_DMA_EnableIT_TC(PULL_OUT_DMA_CH1_DEF); +} + +//------------------------------------------------------------------ EMULATE TAG: carrier input TIMER & interrupt functions (switching input capture & ETR)------------------------------------------------------------------ + +static void lfrfid_hitag_worker_carrier_in_isr(void* capture_context) { + if(LL_TIM_IsActiveFlag_CC1(CARRIER_IN_TIMER)) { + uint32_t newTIMval = LL_TIM_IC_GetCaptureCH1(CARRIER_IN_TIMER); + LL_TIM_ClearFlag_CC1(CARRIER_IN_TIMER); + + //ARR for TIM2 in input capture mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both + //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values + //LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); + + LfRfidHitagCaptureData* capData = capture_context; + capData->capCounter++; + //pack varint (as part of a pair) + //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint + varint_pair_pack(capData->pair, true, newTIMval - capData->prevTIMval); + //but send anyhow, doesn't matter that it's not a pair + buffer_stream_send_from_isr( + capData->stream, + varint_pair_get_data(capData->pair), + varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); + + capData->prevTIMval = newTIMval; + } else if(LL_TIM_IsActiveFlag_UPDATE(CARRIER_IN_TIMER)) { + uint32_t newTIMval = LL_TIM_GetCounter(CARRIER_IN_REFERENCE_TIMER); + LL_TIM_ClearFlag_UPDATE(CARRIER_IN_TIMER); + + //ARR for ref timer in ETR mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both + //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values + LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + + LfRfidHitagCaptureData* capData = capture_context; + capData->capCounter++; + //pack varint (as part of a pair) + //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint + varint_pair_pack(capData->pair, true, newTIMval); // - capData->prevTIMval); + //but send anyhow, doesn't matter that it's not a pair + buffer_stream_send_from_isr( + capData->stream, + varint_pair_get_data(capData->pair), + varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); + + //capData->prevTIMval = newTIMval; + } else if(LL_TIM_IsActiveFlag_CC3(CARRIER_IN_TIMER)) { + uint32_t newTIMval = LL_TIM_GetCounter(CARRIER_IN_REFERENCE_TIMER); + LL_TIM_ClearFlag_CC3(CARRIER_IN_TIMER); + + //don't reset timer for the ccr (HIGH) timing, this way the arr (duration) timing is still full duraton timing (iso only LOW timing) + //LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + + LfRfidHitagCaptureData* capData = capture_context; + capData->capCounter++; + //pack varint (as part of a pair) + //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint + varint_pair_pack(capData->pair, true, newTIMval); // - capData->prevTIMval); + //but send anyhow, doesn't matter that it's not a pair + buffer_stream_send_from_isr( + capData->stream, + varint_pair_get_data(capData->pair), + varint_pair_get_size(capData->pair)); + varint_pair_reset(capData->pair); + + //capData->prevTIMval = newTIMval; + } +} + +static void lfrfid_hitag_worker_carrier_in_ETR_mode(void* capture_context, uint8_t ext_prescaler) { + //disable counters temporarily + LL_TIM_DisableCounter(CARRIER_IN_TIMER); + LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); + + //disable Input capture & related interrupt + LL_TIM_DisableIT_CC1(CARRIER_IN_TIMER); + LL_TIM_CC_DisableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); + + //reset capdata prev value + LfRfidHitagCaptureData* capData = capture_context; + capData->prevTIMval = 0; + + //switch clocksource to ETR with external prescaling via ARR + LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 1 - 1); //prescaler is only applied at next update event + LL_TIM_GenerateEvent_UPDATE( + CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry + LL_TIM_SetAutoReload(CARRIER_IN_TIMER, ext_prescaler - 1); + LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); + + //reconfigure carrier_in pin to TIM2 ETR + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); + + //reset timer counter & capture context for period calculation + LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); + LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + + //enable interrupt via update + LL_TIM_EnableIT_UPDATE(CARRIER_IN_TIMER); + + //re-enable counters + LL_TIM_EnableCounter(CARRIER_IN_TIMER); + LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); +} + +static void lfrfid_hitag_worker_carrier_in_IC_mode(void* capture_context) { + //disable counters temporarily + LL_TIM_DisableCounter(CARRIER_IN_TIMER); + LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); + + //disable interrupt via update + LL_TIM_DisableIT_UPDATE(CARRIER_IN_TIMER); + + //reset capdata prev value + LfRfidHitagCaptureData* capData = capture_context; + capData->prevTIMval = 0; + + //switch clocksource to system/64, external prescaling is handled in IC prescaler + LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 64 - 1); //prescaler is only applied at next update event + LL_TIM_GenerateEvent_UPDATE( + CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry + LL_TIM_SetAutoReload(CARRIER_IN_TIMER, UINT32_MAX); + + //reconfigure carrier_in pin to TIM2 CH1 for input capture + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + + //reset timer counter & capture context for period calculation + LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); + LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + + //enable Input capture & related interrupt + LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); + LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); + + //re-enable counters + LL_TIM_EnableCounter(CARRIER_IN_TIMER); + LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); +} + +static void lfrfid_hitag_worker_carrier_in_start( + void* capture_context, + uint8_t ext_prescaler, + uint32_t* duration, + uint32_t* pulse, + size_t length) { + FURI_CRITICAL_ENTER(); + LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); //required? + LL_DMA_DeInit(PULL_OUT_DMA_CH2_DEF); //required? + LL_TIM_DeInit(CARRIER_IN_TIMER); + LL_TIM_DeInit(CARRIER_IN_REFERENCE_TIMER); + FURI_CRITICAL_EXIT(); + + //setup reference timer: simple setup with base freq of 1MHz and max autoreload + LL_TIM_InitTypeDef TIM_InitStruct_Ref = {0}; + TIM_InitStruct_Ref.Prescaler = + 64 - + 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) + TIM_InitStruct_Ref.CounterMode = LL_TIM_COUNTERMODE_UP; + TIM_InitStruct_Ref.Autoreload = UINT32_MAX; + TIM_InitStruct_Ref.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(CARRIER_IN_REFERENCE_TIMER, &TIM_InitStruct_Ref); + + //setup carrier in timer for input capture + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = + 64 - + 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + TIM_InitStruct.Autoreload = UINT32_MAX; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(CARRIER_IN_TIMER, &TIM_InitStruct); + + LL_TIM_DisableARRPreload(CARRIER_IN_TIMER); + LL_TIM_SetClockSource( + CARRIER_IN_TIMER, + LL_TIM_CLOCKSOURCE_INTERNAL); //default is internal, so likely not required + LL_TIM_DisableDMAReq_TRIG(CARRIER_IN_TIMER); + LL_TIM_DisableIT_TRIG(CARRIER_IN_TIMER); + + //meanwhile already prepre the ETR + LL_TIM_ConfigETR( + CARRIER_IN_TIMER, + LL_TIM_ETR_POLARITY_INVERTED, + LL_TIM_ETR_PRESCALER_DIV1, + LL_TIM_ETR_FILTER_FDIV1); + + //INPUT CAPTURE SETUP + // Timer: channel 1 direct (from GPIO) + LL_TIM_IC_SetActiveInput( + CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI); + //prescaling direct channel seems to be working fine (and is necessary since otherwise sd write cannot keep up) + if(ext_prescaler == 4) { + LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV4); + } else if(ext_prescaler == 2) { + LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV2); + } else { + LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV1); + } + LL_TIM_IC_SetPolarity(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetFilter(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_FILTER_FDIV1); + /* Timer: channel 2 indirect (from channel 1) + // only measure & write to file the period (not the on & off cycle), this reduced CPU load on SD write to (hopefully) keep up with period measurements without prescaler + LL_TIM_IC_SetActiveInput(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_ACTIVEINPUT_INDIRECTTI); + //presaling indirect channel doesn't really work well yet :/ + LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_ICPSC_DIV2); + LL_TIM_IC_SetPolarity(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_IC_POLARITY_FALLING); + LL_TIM_IC_SetFilter(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_IC_FILTER_FDIV1); + */ + + //set interrupt callback for capturing period + LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); + //LL_TIM_EnableIT_CC2(CARRIER_IN_TIMER); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdTIM2, lfrfid_hitag_worker_carrier_in_isr, capture_context); + + //OUTPUT COMPARE SETUP + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + //TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; //during emulate (dma controlled) mode + TIM_OC_InitStruct.OCMode = + LL_TIM_OCMODE_FORCED_INACTIVE; //during carrier in put output to forced low state + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = + 0; //0% this should have almost similar effect as keeping output forced inactive (there's still some micropulse emited, going high at ARR and immediately down again at CCR value) + LL_TIM_OC_Init(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); + LL_TIM_OC_SetPolarity( + PULL_OUT_TIMER, + PULL_OUT_TIMER_CHANNEL, + LL_TIM_OCPOLARITY_HIGH); //active high (gpio goes high when pulse is high) + + //INIT DMA (do not start it yet) + lfrfid_hitag_worker_pull_out_dma_setup(duration, pulse, length, capture_context); + + LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); + //LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH); + LL_TIM_CC_EnableChannel(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL); + LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); + LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); + LL_TIM_EnableCounter(CARRIER_IN_TIMER); + LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); +} + +void lfrfid_hitag_worker_carrier_in_stop() { + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + + LL_TIM_DisableCounter(CARRIER_IN_TIMER); + LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); + LL_TIM_DisableAllOutputs(CARRIER_IN_TIMER); //used for pull pin OC + + FURI_CRITICAL_ENTER(); + LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); + LL_DMA_DeInit(PULL_OUT_DMA_CH2_DEF); + LL_TIM_DeInit(CARRIER_IN_TIMER); + LL_TIM_DeInit(CARRIER_IN_REFERENCE_TIMER); + FURI_CRITICAL_EXIT(); +} + +//------------------------------------------------------------------ hitag data parsing functions ------------------------------------------------------------------ + +static void lfrfid_hitag_worker_yield_dma_buffer_BPLM( + LfRfidHitagCMDData* dataCMD, + LfRfidHitagCMDData* backupCMD, + TimerDMAData* dataDMA, + size_t start, + size_t len) { + //based on interal timer with freq of 1MHz (period of 1us) + //use active command + LfRfidHitagCMDData* data = dataCMD; + if(!data->CMDactive) { + data = backupCMD; + } + uint16_t lastarrposition = 0; + size_t toskip = 0; + bool itemProcessed = true; + size_t s = start; + //for ( ; data->CMDloop || data->CMDposition < data->CMDlength ; data->CMDposition++){ + while(1) { + //at the end of current command (real or backup), check if there's unprocessed CMD available, if not switch to backup + if(data->CMDposition == data->CMDlength) { + data->CMDcount++; + if(dataCMD->CMDposition < + dataCMD + ->CMDlength) { //currently backup & new unprocessed command available --> switch over to real CMD + data->CMDactive = false; + data = dataCMD; + data->CMDactive = true; + data->CMDcount = 0; + } else if(dataCMD + ->CMDloop) { //if looping (real or backup) --> reset real (current) CMD to startposition + data->CMDposition = 0; + data->CMDsubposition = 0; + } else if(dataCMD + ->CMDactive) { //real command and not looping --> switch to backup CMD and reset backup CMD to startposition + data->CMDactive = false; + data = backupCMD; + data->CMDactive = true; + data->CMDposition = 0; + data->CMDsubposition = 0; + data->CMDcount = 0; + } else { //backupCMD but set to not loop --> loop anyway: reset to startposition + data->CMDposition = 0; + data->CMDsubposition = 0; + } + } + //start processing the next CMD item + lastarrposition = s; + if(s == start) { + toskip = data->CMDsubposition; + } else { + toskip = 0; + data->CMDsubposition = 0; + } + switch(data->CMDtype[data->CMDposition]) { + case HITAG_STOP: //just 1 STOP cycle (HITAG_LOW lows + HITAG_STOP_HIGH highs) + if(s > start) { + dataDMA->timer_buffer_arr[s - 1] += HITAG_LOW * HITAG_BASEPERIOD; + } + for(size_t t = 0; t < HITAG_STOP_HIGH - toskip; t++) { + if(s == len + start) { + //arrays are filled, do not continue out + itemProcessed = false; + break; + } + dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; + dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; + s++; + } + break; + case HITAG_ON: //x ON cycles --> update next X arr/ccr values + for(size_t x = 0; x < data->CMDvalue[data->CMDposition] - toskip; x++) { + if(s == len + start) { + //arrays are filled, do not continue out + itemProcessed = false; + break; + } + dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; + dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; + s++; + } + break; + case HITAG_OFF: //x OFF cycles (update previous arr value) + if(s > start) { + dataDMA->timer_buffer_arr[s - 1] += + data->CMDvalue[data->CMDposition] * HITAG_BASEPERIOD; + } + break; + default: // <=HITAG_BYTE: //bits to be sent + //get each bit & update arr/ccr accordingly (7 lows + HITAG_0_HIGH/HITAG_1_HIGH highs) + for(size_t b = 8 - data->CMDtype[data->CMDposition]; b < 8; b++) { + if(s > start) { + dataDMA->timer_buffer_arr[s - 1] += HITAG_LOW * HITAG_BASEPERIOD; + } + if(0x80 & (data->CMDvalue[data->CMDposition] << b)) { + for(size_t t = 0; t < HITAG_1_HIGH; t++) { + if(s == len + start) { + //arrays are filled, do not continue out + itemProcessed = false; + break; + } + dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; + dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; + s++; + if(toskip > 0) { + toskip--; + s--; + } + } + } else { + for(size_t t = 0; t < HITAG_0_HIGH; t++) { + if(s == len + start) { + //arrays are filled, do not continue out + itemProcessed = false; + break; + } + dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; + dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; + s++; + if(toskip > 0) { + toskip--; + s--; + } + } + } + if(!itemProcessed) { + break; + } + } + break; + } + if(!itemProcessed) { + break; + } + data->CMDposition++; + } + + /*if buffer is not full, but command is fully processed: fill remainder of buffer with normal on cycles + if (stimer_buffer_ccr[s]=HITAG_BASEPERIOD/4; //25%duty cycle + dataDMA->timer_buffer_arr[s]=1*HITAG_BASEPERIOD-1; + } else { + dataDMA->timer_buffer_ccr[s]=HITAG_BASEPERIOD/2; + dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; + } + } + //other stuff to do? + } + */ + + //if buffer was full, but command not yet fully processed: set starting position for next time: + if(!itemProcessed) { + data->CMDsubposition += (len + start) - lastarrposition; + } else { + data->CMDsubposition = 0; + } +} + +static uint16_t lfrfid_hitag_worker_yield_dma_buffer_MC( + LfRfidHitagReplyData* dataReply, + TimerDMAData* dataDMA, + uint8_t carrierPrescaler) { + //based on external carrier with freq of 125kHz (period of 8us) + + uint16_t dma_len = 0; + + bool bit = false; + bool prevBit = true; //must be true to initialze MC yield for first bit + + //process reply bits and add HIGH-LOW for 1 or LOW-HIGH for 0 + for(uint8_t i = 0; i < dataReply->replylength; i++) { + for(int8_t b = dataReply->replytype[i] - 1; b >= 0; b--) { + bit = (dataReply->replyvalue[i] >> b & 0b00000001); + if(bit) { //HIGH-LOW + if(prevBit) { //if first bit (initial value of prevBit=true) or if prev bit was 1: start of new default cycle + dataDMA->timer_buffer_arr[dma_len] = 32 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 16; + dma_len++; + } else { //previous bit was 0, so extend the ccr and arr with 16 + dataDMA->timer_buffer_arr[dma_len - 1] += 16; + dataDMA->timer_buffer_ccr[dma_len - 1] += 16; + } + } else { //LOW-HIGH + if(prevBit) { //previous bit was 1 so extend the LOW (arr only) of prev cycle with 16 and add new default cycle + dataDMA->timer_buffer_arr[dma_len - 1] += 16; + + dataDMA->timer_buffer_arr[dma_len] = 32 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 16; + dma_len++; + } else { //previous bit was 0 so just add new default cycle + dataDMA->timer_buffer_arr[dma_len] = 32 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 16; + dma_len++; + } + } + prevBit = bit; + } + } + + //add extra period, to reset timer back to normal carrier_in detection after reply has finished + //also required to make sure the last real dma values are fully executed before dma signals transfer complete, which resets worker into command detection + dataDMA->timer_buffer_arr[dma_len] = carrierPrescaler - 1; + dataDMA->timer_buffer_ccr[dma_len] = 0; + dma_len++; + + return dma_len; +} + +static uint16_t lfrfid_hitag_worker_yield_dma_buffer_AC( + LfRfidHitagReplyData* dataReply, + TimerDMAData* dataDMA, + uint8_t carrierPrescaler) { + //based on external carrier with freq of 125kHz (period of 8us) + + uint16_t dma_len = 0; + + //process reply bits and add 2 short pulses for 1 or 1 long pulse for 0 + for(uint8_t i = 0; i < dataReply->replylength; i++) { + for(int8_t b = dataReply->replytype[i] - 1; b >= 0; b--) { + if(dataReply->replyvalue[i] >> b & 0b00000001) { + dataDMA->timer_buffer_arr[dma_len] = 32 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 16; //16 + dma_len++; + dataDMA->timer_buffer_arr[dma_len] = 32 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 16; //16 + dma_len++; + } else { + dataDMA->timer_buffer_arr[dma_len] = 64 - 1; + dataDMA->timer_buffer_ccr[dma_len] = 32; //32 + dma_len++; + } + } + } + + //add extra period, to reset timer back to normal carrier_in detection after reply has finished + //also required to make sure the last real dma values are fully executed before dma signals transfer complete, which resets worker into command detection + dataDMA->timer_buffer_arr[dma_len] = carrierPrescaler - 1; + dataDMA->timer_buffer_ccr[dma_len] = 0; + dma_len++; + + return dma_len; +} + +static void lfrfid_hitag_worker_decoder_feed( + uint32_t pulse, + uint32_t duration, + uint32_t* memBlock, + uint8_t* bits, + bool* half, + bool* finished, + LfRfidHitagProtocol protocol, + uint8_t startBits) { + uint32_t bit = 1 << (32 - 1); + if(*bits == 0) { + *half = false; + } + if(protocol == HitagProtocolManchesterCoding) { + if(HITAG_DURATION_S * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && + duration <= HITAG_DURATION_S * (1 + HITAG_DURATION_ERROR_MARGIN)) { + //short period (HL) detected: same bit as previous + if(*bits < startBits) { + //don't store startbits in memory, just increase bitcounter + (*bits)++; + } else if(*bits == startBits) { + //same as previous bit (last startbit it 1, so add 1 to the memBlock) + memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + } else { + //add same as previous bit + if(memBlock[(*bits - startBits - 1) / 32] & + (bit >> (*bits - startBits - 1) % 32)) { + memBlock[(*bits - startBits) / 32] |= + (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + } else { + (*bits)++; //no mem action for 0, just increase bitcounter + } + } + } else if( + HITAG_DURATION_M * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && + duration <= HITAG_DURATION_M * (1 + HITAG_DURATION_ERROR_MARGIN)) { + //medium period (H|HL or HL|L) detected: half 0 + full 1 or full 1 + half 0 detected + if(*bits < startBits) { + //assuming all startbits are 1's, this can only happen on last startbit followed by 0 + //don't store startbits, no mem action for 0, just increase bitcounter twice + (*bits)++; + (*bits)++; + *half = true; + } else if(*bits == startBits) { + //assuming all startbits were 1's, this is a data 10 detection + memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + (*bits)++; //no mem action for 0, just increase bitcounter + *half = true; + } else { + //if we're in half of a bit, add 1, else add 10 and set half flag + if(*half) { + memBlock[(*bits - startBits) / 32] |= + (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + *half = false; + } else { + memBlock[(*bits - startBits) / 32] |= + (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + (*bits)++; //no mem action for 0, just increase bitcounter + *half = true; + } + } + } else if( + HITAG_DURATION_L * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && + duration <= HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN)) { + //long period (H|HL|L) detected: half 0 + full 1 + half 0 detected + if(*bits <= startBits) { + //TODO: throw error since this can't happen till after first real bit, since all startbits should be 1's + } else { + //TODO check that prev bit is indeed 0 + //add 10 to the memBlock + memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + (*bits)++; //no mem action for 0, just increase bitcounter + } + } else if(HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN) < duration) { + //end of modulation by tag, check if this pulse duration includes a bit + if(*bits > 0) { + if(*half && pulse < HITAG_DURATION_S) { + //second half of 0 bit, no action + } else if(*half && pulse < HITAG_DURATION_M) { + //second half or 0 bit + another 1 bit + memBlock[(*bits - startBits) / 32] |= + (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + } else if(!(*half) && pulse < HITAG_DURATION_S) { + //another 1 bit + memBlock[(*bits - startBits) / 32] |= + (bit >> (*bits - startBits) % 32); //add 1 + (*bits)++; + } else { + //invalid pulse duration --> reset? + } + *finished = true; + } + } else { + //invalid pulse duration: reset tempData if necessary + if(*bits > 0) { + memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); + *bits = 0; + *finished = false; + } + } + } else if(protocol == HitagProtocolAntiCollision) { + if(HITAG_DURATION_S * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && + duration <= HITAG_DURATION_S * (1 + HITAG_DURATION_ERROR_MARGIN)) { + //short period (HL): first or second half of a 1 detected + //if first short: add 1 to memBlock and increase half counter + if(*half == false) { + if(*bits >= startBits) { + memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); + } + (*bits)++; + *half = true; + } else { + //if second small: reset half counter + *half = false; + } + } else if( + HITAG_DURATION_L * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && + duration <= HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN)) { + //long period (HHLL): 0 detected + //should only happen after full detection of 1 or after startBits + if(*bits < startBits || *half) { + //reset + memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); + *bits = 0; + *finished = false; + } else { + (*bits)++; //add 0 to memBlock + } + } else if(HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN) < duration) { + //end of modulation by tag, check if this pulse duration includes a bit + if(*bits > 0) { + if(!(*half) && pulse > HITAG_DURATION_S) { + (*bits)++; //add 0 to memBlock + } else { + //invalid pulse duration: reset? + } + *finished = true; + } + } else { + //invalid pulse duration: reset tempData if necessary + if(*bits > 0) { + memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); + *bits = 0; + *finished = false; + } + } + } +} + +static void lfrfid_hitag_worker_calc_crc(uint8_t* crc, uint8_t data, uint8_t bitcount) { + *crc ^= data; // crc = crc (exor) data + do { + if(*crc & 0x80) // if (MSB-CRC == 1) + { + *crc <<= 1; // CRC = CRC bit-shift left + *crc ^= CRC_POLYNOM; // CRC = CRC (exor) CRC_POLYNOM + } else { + *crc <<= 1; // CRC = CRC bit-shift left + } + } while(--bitcount); +} + +static bool lfrfid_hitag_worker_validate_command(uint32_t* bits, uint8_t len) { + if(len == 5 && (bits[0] >> (32 - 5)) == 0b11001) { + //SET_CCNEW + return true; + } else if(len == 5 && (bits[0] >> (32 - 5)) == 0b00110) { + //SET_CC + return true; + } else if(len == 45 && (bits[0] >> (32 - 5)) == 0b00000) { + //SELECT + + //calculate CRC for first 37 bits (4bytes + 5 bits) + uint8_t byte1 = bits[0] >> 24 & 0xff; + uint8_t byte2 = bits[0] >> 16 & 0xff; + uint8_t byte3 = bits[0] >> 8 & 0xff; + uint8_t byte4 = bits[0] & 0xff; + uint8_t byte5 = bits[1] >> 24 & 0b11111000; + uint8_t crcRead = bits[1] >> 19 & 0xff; + + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, byte1, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte2, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte3, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte4, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte5, 5); + + //and compare result with received crc + return (crcRead == crc); + } + //else if (len == 20 && (bits[0]>>31 || (bits[0]>>(32-4)) == 0b0111)){ + else if(len == 20 && bits[0] > 0x70000000) { + //R/W page/block (cmd starting with 1) or HALT (0111) --> so bit0 should be >= 0b01110000 00000000 00000000 00000000 (0x70000000) + //calculate CRC for first 12 bits (1byte + 4 bits) + uint8_t byte1 = bits[0] >> 24 & 0xff; + uint8_t byte2 = bits[0] >> 16 & 0b11110000; + uint8_t crcRead = bits[0] >> 12 & 0xff; + + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, byte1, 8); + lfrfid_hitag_worker_calc_crc(&crc, byte2, 4); + + //and compare result with received crc + return (crcRead == crc); + } + return false; +} + +static bool lfrfid_hitag_worker_validate_bits(uint32_t* bits, uint8_t bitcounter) { + if(47 >= bitcounter && bitcounter >= 45) { + for(uint8_t i = 0; i < bitcounter - 44; i++) { + if(i > 0) { + bits[0] = bits[0] << 1 | bits[1] >> (32 - 1); + bits[1] = bits[1] << 1; + } + if(lfrfid_hitag_worker_validate_command(bits, 45)) return true; + } + } else if(22 >= bitcounter && bitcounter >= 20) { + for(uint8_t i = 0; i < bitcounter - 19; i++) { + if(i > 0) { + bits[0] = bits[0] << 1; // | bits[1]>>(32-1); + //bits[1] = bits[1]<<1; + } + if(lfrfid_hitag_worker_validate_command(bits, 20)) return true; + } + } else if(7 >= bitcounter && bitcounter >= 5) { + for(uint8_t i = 0; i < bitcounter - 4; i++) { + if(i > 0) { + bits[0] = bits[0] << 1; // | bits[1]>>(32-1); + //bits[1] = bits[1]<<1; + } + if(lfrfid_hitag_worker_validate_command(bits, 5)) return true; + } + } + return false; +} + +static uint8_t lfrfid_hitag_worker_validate_ripple_delta(uint32_t delta) { + if(18 <= delta && delta <= 22) return 1; + if(26 <= delta && delta <= 32) return 1; + return 0; +} + +void lfrfid_hitag_worker_initialize_tag_data(LFRFIDHitag* tag) { + //initialize a tag datastructure + //set all pages to 0x00000000 + memset(tag->pageData, 0, sizeof(tag->pageData)); + + //set all pages to unknown + for(uint8_t i = 0; i < HITAG_PAGES; i++) { + tag->pageKnown[i] = 0; + } + + //default RW settings: + memset(tag->pageRW, READ, HITAG_PAGES / 2 * sizeof(tag->pageRW[0])); + memset(tag->pageRW + HITAG_PAGES / 2, READ | WRITE, HITAG_PAGES / 2 * sizeof(tag->pageRW[0])); + tag->pageRW[2] = 0; + tag->pageRW[3] = 0; + + //default public/secret settings: + for(uint8_t i = 0; i < HITAG_PAGES; i++) { + tag->pagePublic[i] = 1; + } + for(uint8_t i = 2; i <= 31; i++) { + tag->pagePublic[i] = 0; + } +} + +static void lfrfid_hitag_worker_combine_tag_data(LFRFIDHitag* tag) { + memcpy(tag->tagData, tag->pageData, HITAG_PAGES * HITAG_PAGEBYTES); + memcpy(tag->tagData + HITAG_PAGES * HITAG_PAGEBYTES, tag->pageKnown, HITAG_PAGES); +} + +static void lfrfid_hitag_worker_split_tag_data(LFRFIDHitag* tag) { + memcpy(tag->pageData, tag->tagData, HITAG_PAGES * HITAG_PAGEBYTES); + memcpy(tag->pageKnown, tag->tagData + HITAG_PAGES * HITAG_PAGEBYTES, HITAG_PAGES); +} + +void lfrfid_hitag_worker_process_config_page(LFRFIDHitag* tag) { + //RW settings via byte 0 + if(tag->pageData[4 + 0] & 0b10000000) { //block 1 (logData) RW or 0 + memset(tag->pageRW + 1 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 1 * 4, 0, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b01000000) { //key A&B (page 2&3) W or 0 + memset(tag->pageRW + 2, WRITE, 2 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 2, 0, 2 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00100000) { //block 2 RW or RO + memset(tag->pageRW + 2 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 2 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00010000) { //block 3 RW or RO + memset(tag->pageRW + 3 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 3 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00001000) { //block 4 RW or RO + memset(tag->pageRW + 4 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 4 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00000100) { //block 5 RW or RO + memset(tag->pageRW + 5 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 5 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00000010) { //block 6 RW or RO + memset(tag->pageRW + 6 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 6 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + if(tag->pageData[4 + 0] & 0b00000001) { //block 7 RW or RO + memset(tag->pageRW + 7 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); + } else { + memset(tag->pageRW + 7 * 4, READ, 4 * sizeof(tag->pageRW[0])); + } + //RW settings via byte 1 + if(tag->pageData[4 + 1] & 0b00010000) { //config page (1) RW or RO + tag->pageRW[1] = READ / WRITE; + } else { + tag->pageRW[1] = READ; + } + + //public/secret settings via byte 1 + if(tag->pageData[4 + 1] & 0b00000001) { //block 4-7 public or secret + for(uint8_t i = 4 * 4; i < 8 * 4; i++) { + tag->pagePublic[i] = 1; + } + } else { + for(uint8_t i = 4 * 4; i < 8 * 4; i++) { + tag->pagePublic[i] = 0; + } + } +} + +//------------------------------------------------------------------ worker threads ------------------------------------------------------------------ + +static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { + LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)thread_context; + + //load tag + worker->tag = malloc(sizeof(LFRFIDHitag)); + lfrfid_hitag_worker_initialize_tag_data(worker->tag); + + size_t data_size = protocol_dict_get_data_size(worker->dict, LFRFIDProtocolHitag1); + protocol_dict_get_data(worker->dict, LFRFIDProtocolHitag1, worker->tag->tagData, data_size); + + lfrfid_hitag_worker_split_tag_data(worker->tag); + //TODO: check if a tag has been properly loaded with known serial nr & config page + lfrfid_hitag_worker_process_config_page(worker->tag); + + //init capData for carrier_in + LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); + capData->stream = buffer_stream_alloc(EMULATE_BUFFER_SIZE, EMULATE_BUFFER_COUNT); + capData->pair = varint_pair_alloc(); + capData->capCounter = 0; + capData->prevTIMval = 0; + + //init dmaData for emulate mode: + uint16_t dmaLen = 0; + TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); + + LfRfidHitagReplyData* dataReply = malloc(sizeof(LfRfidHitagReplyData)); + + //set pins to read setup + furi_hal_rfid_pins_read(); + //reconfigure carrier_out to fixed low state instead + furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_rfid_carrier_out, false); + //reconfigure PA15 carrier_in to alt ftn 1 (TIM2 CH1) to use it for input capture + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + //reconfigure pull to alternate function 1 (TIM2 CH3) to drive low (for read carrier) or pull antenna via DMA (for emulation) + furi_hal_gpio_init_ex( + &gpio_nfc_irq_rfid_pull, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedLow, + GpioAltFn1TIM2); + + //start capture via carrier in instead of rfid in + lfrfid_hitag_worker_carrier_in_start( + capData, + worker->carrierPrescaler, + dataDMA->timer_buffer_arr, + dataDMA->timer_buffer_ccr, + dmaLen); + + uint8_t prescaler = worker->carrierPrescaler; + uint8_t period = 8 * prescaler - 1; + + bool carrier_on = false; + int8_t carrier_count = 0; + uint8_t carrier_threshold_on = 5; + int8_t carrier_threshold_off = -5; + + uint32_t periodcounter = 0; + + uint8_t localMax = 0; + uint8_t localMin = 255; + uint32_t localMaxIndex = 0; + uint32_t localMinIndex = 0; + uint32_t lastMaxIndex = 0; + uint32_t lastMinIndex = 0; + uint8_t maxBitCounter = 0; + uint8_t minBitCounter = 0; + uint32_t maxBits[5]; + uint32_t minBits[5]; + int8_t cmd = 0; + uint32_t one = 1 << (32 - 1); + uint32_t cmdIndex = 0; + + uint32_t miniMaxDelta = 0; + uint32_t miniMaxIndex = 0; + uint32_t prevMaxDelta = 0; + uint32_t maxDelta = 0; + + uint32_t miniMinDelta = 0; + uint32_t miniMinIndex = 0; + uint32_t prevMinDelta = 0; + uint32_t minDelta = 0; + + uint32_t prevduration = 0; + uint32_t duration = 0; + uint8_t durationcounter = 0; + uint8_t durationthreshold = 5; + + uint8_t tagState = HitagStateIdle; + uint8_t tagMode = HitagModeBasic; + + while(1) { + Buffer* buffer = buffer_stream_receive(capData->stream, 1); + + if(buffer != NULL) { + size_t buffsize = buffer_get_size(buffer); + uint8_t* buffdata = buffer_get_data(buffer); + + //parse carrier_in duration & scan for cmds + size_t index = 0; + while(index < buffsize) { + prevduration = duration; + size_t tmp_size = + varint_uint32_unpack(&duration, &buffdata[index], buffsize - index); + + if(tmp_size == 0) { + FURI_LOG_E(TAG, "can't unpack varint pair"); + break; + } else { + index += tmp_size; + + periodcounter++; + + if(!carrier_on) { //detect if carrier is available + if(0.75 * period <= duration && duration <= 1.25 * period) { + carrier_count++; + if(carrier_count >= carrier_threshold_on) { + carrier_on = true; + localMax = 0; + localMin = 255; + minBitCounter = 0; + maxBitCounter = 0; + minBits[0] = 0; + minBits[1] = 0; + maxBits[0] = 0; + maxBits[1] = 0; + + carrier_count = 0; + //TODO reset tagstate to Idle & tagMode to basic after power_on_reset (no carrier for 10ms) + } + } else { + carrier_count = 0; + } + } else { + //detect if carrier is still available + if(duration <= 0.75 * period || 1.25 * period <= duration) { + carrier_count--; + if(carrier_count <= carrier_threshold_off) { + carrier_on = false; + carrier_count = 0; + } + } else { + carrier_count = 0; + } + + if(carrier_on) { //if carrier is still available + //finetune the average period for reference + if(prevduration == duration) { + durationcounter++; + if(durationcounter == durationthreshold && period != duration) { + period = duration; + } + } else { + durationcounter = 0; + } + + //detect local MAX, and update maxCmd detection + if(duration > prevduration && duration > period) { + //rising edge above the normal periodduration: update localMax + localMax = duration; + localMaxIndex = periodcounter; + } else if( + duration < prevduration && localMax > period && + periodcounter <= localMaxIndex + 2) { + //we passed a local maximum, derive length between current and prev max index + if(miniMaxDelta == 0) { + prevMaxDelta = maxDelta; + } + maxDelta = (localMaxIndex - lastMaxIndex) * prescaler; + + //check if there's a miniDelta and if it should be added to previous or next delta + if(miniMaxDelta > 0) { + //by default minidelta is added to next + //in case doing this does not result in 2 consequtive valid deltas, but adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta + if((lfrfid_hitag_worker_validate_ripple_delta( + prevMaxDelta + miniMaxDelta) + + lfrfid_hitag_worker_validate_ripple_delta( + maxDelta - miniMaxDelta)) > + (lfrfid_hitag_worker_validate_ripple_delta(prevMaxDelta) + + lfrfid_hitag_worker_validate_ripple_delta(maxDelta))) { + //update the previous (last) bit from 0 to 1 if a command was ongoing + if(maxBitCounter > 0) { + maxBits[(maxBitCounter - 1) / 32] |= + (one >> (maxBitCounter - 1) % 32); + } + maxDelta -= miniMaxDelta; + lastMaxIndex = miniMaxIndex; + } + miniMaxDelta = 0; + } + + //now process the (possibly updated) delta + if(maxDelta >= 18) { + if(18 <= maxDelta && maxDelta <= 22) { + maxBitCounter++; + } else if(26 <= maxDelta && maxDelta <= 32) { + maxBits[(maxBitCounter) / 32] |= + (one >> (maxBitCounter) % 32); + maxBitCounter++; + } else if(maxBitCounter > 0) { + //end of bitstring caused by invalid delta + + if(lfrfid_hitag_worker_validate_bits( + maxBits, maxBitCounter)) { + cmd = 1; + cmdIndex = localMaxIndex; + + //CMD found, stop searching for minCMD: reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } else { + //reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } + } + lastMaxIndex = localMaxIndex; + } else { + miniMaxDelta = maxDelta; + miniMaxIndex = localMaxIndex; + } + localMax = 0; + } + + //terminate bitstring ico absence of next delta (a true end) + if(periodcounter == lastMaxIndex + 40 / prescaler && + maxBitCounter > 0) { + if(lfrfid_hitag_worker_validate_bits(maxBits, maxBitCounter)) { + cmd = 1; + cmdIndex = localMaxIndex; + + //CMD found, stop searching for minCMD: reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } else { + //reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } + } + + //detect local MIN, and update minCmd detection + if(duration < prevduration && duration < period) { + //rising edge above the normal periodduration: update localMax + localMin = duration; + localMinIndex = periodcounter; + } else if( + duration > prevduration && localMin < period && + periodcounter <= localMinIndex + 2) { + //we passed a local minimum, derive length between current and prev max index + if(miniMinDelta == 0) { + prevMinDelta = minDelta; + } + minDelta = (localMinIndex - lastMinIndex) * prescaler; + + //check if there's a miniDelta and if it should be added to previous or next delta + if(miniMinDelta > 0) { + //by default minidelta is added to next + //in case doing this does not result in 2 consequtive valid deltas, but adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta + if((lfrfid_hitag_worker_validate_ripple_delta( + prevMinDelta + miniMinDelta) + + lfrfid_hitag_worker_validate_ripple_delta( + minDelta - miniMinDelta)) > + (lfrfid_hitag_worker_validate_ripple_delta(prevMinDelta) + + lfrfid_hitag_worker_validate_ripple_delta(minDelta))) { + //update the previous (last) bit from 0 to 1 if a command was ongoing + if(minBitCounter > 0) { + minBits[(minBitCounter - 1) / 32] |= + (one >> (minBitCounter - 1) % 32); + } + minDelta -= miniMinDelta; + lastMinIndex = miniMinIndex; + } + miniMinDelta = 0; + } + + //now process the (possibly updated) delta + if(minDelta >= 18) { + if(18 <= minDelta && minDelta <= 22) { + minBitCounter++; + } else if(26 <= minDelta && minDelta <= 32) { + minBits[(minBitCounter) / 32] |= + (one >> (minBitCounter) % 32); + minBitCounter++; + } else if(minBitCounter > 0) { + //end of bitstring caused by invalid delta + if(lfrfid_hitag_worker_validate_bits( + minBits, minBitCounter)) { + cmd = -1; + cmdIndex = localMinIndex; + + //CMD found, stop searching for maxCMD: reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } else { + //reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } + } + lastMinIndex = localMinIndex; + } else { + miniMinDelta = minDelta; + miniMinIndex = localMinIndex; + } + localMin = 255; + } + + //terminate of bitstring caused by absence of next delta (a true end) + if(periodcounter == lastMinIndex + 40 / prescaler && + minBitCounter > 0) { + if(lfrfid_hitag_worker_validate_bits(minBits, minBitCounter)) { + cmd = -1; + cmdIndex = localMinIndex; + + //CMD found, stop searching for maxCMD: reset the maxCmd detection + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } else { + //reset the minCmd detection + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } + } + + //if a valid cmd has been detected: process it & reply + if(cmd != 0) { + uint32_t bits[5]; + uint8_t size = 0; + //select the cmd bit source (max or min detection) + if(cmd == 1) { + bits[0] = maxBits[0]; + bits[1] = maxBits[1]; + size = maxBitCounter; + + maxBits[0] = 0; + maxBits[1] = 0; + maxBitCounter = 0; + } else { + bits[0] = minBits[0]; + bits[1] = minBits[1]; + size = minBitCounter; + + minBits[0] = 0; + minBits[1] = 0; + minBitCounter = 0; + } + cmd = 0; + + dmaLen = 0; + if(size == 5 && (bits[0] >> (32 - 5)) == 0b11001) { //SET_CCNEW + if(tagState != HitagStateQuiet) { + //reset selected state + tagState = HitagStateIdle; + + //set tag in advanced mode + tagMode = HitagModeAdvanced; + + //prepare emulate DMA buffer with advanced SN reply (page 0) + dataReply->replyvalue[0] = 0b00000111; + dataReply->replytype[0] = HITAG_STARTBIT3; + dataReply->replyvalue[1] = worker->tag->pageData[0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = worker->tag->pageData[1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = worker->tag->pageData[2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = worker->tag->pageData[3]; + dataReply->replytype[4] = HITAG_BYTE; + + dataReply->replylength = 5; + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC( + dataReply, dataDMA, worker->carrierPrescaler); + } + } else if(size == 5 && (bits[0] >> (32 - 5)) == 0b00110) { //SET_CC + if(tagState != HitagStateQuiet) { + //reset selected state + tagState = HitagStateIdle; + + //do not (re)set tag in basic mode, this only happens during power on reset + + //prepare emulate DMA buffer with basic SN reply (page 0) + dataReply->replyvalue[0] = 0b00000001; + dataReply->replytype[0] = HITAG_STARTBIT; + dataReply->replyvalue[1] = worker->tag->pageData[0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = worker->tag->pageData[1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = worker->tag->pageData[2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = worker->tag->pageData[3]; + dataReply->replytype[4] = HITAG_BYTE; + + dataReply->replylength = 5; + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC( + dataReply, dataDMA, worker->carrierPrescaler); + } + } else if(size == 45 && (bits[0] >> (32 - 5)) == 0b00000) { //SELECT + //check if SN is matching + uint32_t SN_cmd = bits[0] << 5 | bits[1] >> (32 - 5); + uint32_t SN_tag = worker->tag->pageData[0] << 24 | + worker->tag->pageData[1] << 16 | + worker->tag->pageData[2] << 8 | + worker->tag->pageData[3]; + if(SN_cmd == SN_tag) { + //reply with config (page 1 from tag memory) + tagState = HitagStateSelected; + + //prepare emulate DMA buffer with basic/advanced config page reply (page 1) + if(tagMode == HitagModeBasic) { + dataReply->replyvalue[0] = 0b00000001; + dataReply->replytype[0] = HITAG_STARTBIT; + + dataReply->replyvalue[1] = + worker->tag->pageData[4 + 0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = + worker->tag->pageData[4 + 1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = + worker->tag->pageData[4 + 2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = + worker->tag->pageData[4 + 3]; + dataReply->replytype[4] = HITAG_BYTE; + + dataReply->replylength = 5; + } else if(tagMode == HitagModeAdvanced) { + dataReply->replyvalue[0] = 0b00111111; + dataReply->replytype[0] = HITAG_STARTBIT6; + + dataReply->replyvalue[1] = + worker->tag->pageData[4 + 0]; + dataReply->replytype[1] = HITAG_BYTE; + dataReply->replyvalue[2] = + worker->tag->pageData[4 + 1]; + dataReply->replytype[2] = HITAG_BYTE; + dataReply->replyvalue[3] = + worker->tag->pageData[4 + 2]; + dataReply->replytype[3] = HITAG_BYTE; + dataReply->replyvalue[4] = + worker->tag->pageData[4 + 3]; + dataReply->replytype[4] = HITAG_BYTE; + + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc( + &crc, dataReply->replyvalue[1], 8); + lfrfid_hitag_worker_calc_crc( + &crc, dataReply->replyvalue[2], 8); + lfrfid_hitag_worker_calc_crc( + &crc, dataReply->replyvalue[3], 8); + lfrfid_hitag_worker_calc_crc( + &crc, dataReply->replyvalue[4], 8); + + dataReply->replyvalue[5] = crc; + dataReply->replytype[5] = HITAG_CRC; + + dataReply->replylength = 6; + } + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC( + dataReply, dataDMA, worker->carrierPrescaler); + } + } else if(size == 20 && (bits[0] >> (31))) { //R/W + if(tagState == HitagStateSelected) { + //reply with requested pages in case of read public (ignore secret block/pages & ignore write cmd) + uint8_t rwCmd = bits[0] >> (32 - 4); + uint8_t addr = bits[0] >> 20 & 0xff; + uint8_t pages = 0; + switch(rwCmd) { + case(0b1100): + //read public page + if(worker->tag->pageKnown[addr] && + worker->tag->pagePublic[addr]) { + pages = 1; + } + break; + case(0b1101): + //read public block + pages = (64 - addr) % 4; + if(pages == 0) { + pages = 4; + } + for(uint8_t p = addr; p < addr + pages; p++) { + if(!worker->tag->pageKnown[p] || + !worker->tag->pagePublic[p]) { + pages = 0; + break; + } + } + break; + } + if(pages > 0) { + //pages + for(uint8_t i = 0; i < pages; i++) { + dataReply->replyvalue[1 + i * 4 + 0] = + worker->tag->pageData[(addr + i) * 4 + 0]; + dataReply->replytype[1 + i * 4 + 0] = HITAG_BYTE; + dataReply->replyvalue[1 + i * 4 + 1] = + worker->tag->pageData[(addr + i) * 4 + 1]; + dataReply->replytype[1 + i * 4 + 1] = HITAG_BYTE; + dataReply->replyvalue[1 + i * 4 + 2] = + worker->tag->pageData[(addr + i) * 4 + 2]; + dataReply->replytype[1 + i * 4 + 2] = HITAG_BYTE; + dataReply->replyvalue[1 + i * 4 + 3] = + worker->tag->pageData[(addr + i) * 4 + 3]; + dataReply->replytype[1 + i * 4 + 3] = HITAG_BYTE; + } + //startsequence & CRC + if(tagMode == HitagModeBasic) { + dataReply->replyvalue[0] = 0b00000001; + dataReply->replytype[0] = HITAG_STARTBIT; + + dataReply->replylength = 1 + pages * 4; + } else if(tagMode == HitagModeAdvanced) { + dataReply->replyvalue[0] = 0b00111111; + dataReply->replytype[0] = HITAG_STARTBIT6; + + uint8_t crc = CRC_PRESET; + + for(uint8_t i = 0; i < pages; i++) { + lfrfid_hitag_worker_calc_crc( + &crc, + dataReply->replyvalue[1 + i * 4 + 0], + 8); + lfrfid_hitag_worker_calc_crc( + &crc, + dataReply->replyvalue[1 + i * 4 + 1], + 8); + lfrfid_hitag_worker_calc_crc( + &crc, + dataReply->replyvalue[1 + i * 4 + 2], + 8); + lfrfid_hitag_worker_calc_crc( + &crc, + dataReply->replyvalue[1 + i * 4 + 3], + 8); + } + + dataReply->replyvalue[1 + pages * 4] = crc; + dataReply->replytype[1 + pages * 4] = HITAG_CRC; + + dataReply->replylength = 2 + pages * 4; + } + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC( + dataReply, dataDMA, worker->carrierPrescaler); + } + } + } else if(size == 20 && (bits[0] >> (32 - 4)) == 0b0111) { //HALT + //TODO validate if dummy address is a valid one (one of the default public pages (32-63)) + + //acknowledge & go silent untill next reset + if(tagState == HitagStateSelected) { + tagState = HitagStateQuiet; + + if(tagMode == HitagModeBasic) { + dataReply->replyvalue[0] = 0b00000101; + dataReply->replytype[0] = HITAG_ACK; + } else if(tagMode == HitagModeAdvanced) { + dataReply->replyvalue[0] = 0b11111101; + dataReply->replytype[0] = HITAG_ACK_ADV; + } + + dataReply->replylength = 1; + + dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC( + dataReply, dataDMA, worker->carrierPrescaler); + } + } + + if(dmaLen > 0) { //switch to reply mode + //switch carrier mode to ETR required for emulation + lfrfid_hitag_worker_carrier_in_ETR_mode(capData, prescaler); + + //respect hitag WAIT1 time + uint32_t wait1_periods = 180 / prescaler; + while(capData->capCounter - cmdIndex < wait1_periods) { + furi_delay_us(40); + } + + //just start the DMA, stopping is handled in DMA transfer complete interrupt + lfrfid_hitag_worker_pull_out_dma_start((size_t)dmaLen); + } + } + } + } + } + } + + //reset buffer + buffer_reset(buffer); + } + + if(buffer_stream_get_overrun_count(capData->stream) > 0) { + FURI_LOG_E(TAG, "Read overrun, recovering"); + buffer_stream_reset(capData->stream); + } + + uint32_t flags = furi_event_flag_get(worker->events); + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { + break; + } + } + + //stop carrier capture in + lfrfid_hitag_worker_carrier_in_stop(); + + free(worker->tag); + free(dataDMA); + free(dataReply); + varint_pair_free(capData->pair); + buffer_stream_free(capData->stream); + free(capData); + + //clear the stop flag on workder so that i can use the worker to launch a new read routine without having to restart the app + furi_event_flag_clear(worker->events, 1 << LFRFIDHitagWorkerSignalStop); + + return 0; +} + +static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { + LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)thread_context; + + //initialize a tag datastructure + worker->tag = malloc(sizeof(LFRFIDHitag)); + lfrfid_hitag_worker_initialize_tag_data(worker->tag); + + //prep variables for decoder + LfRfidHitagProtocol currentProtocol = HitagProtocolAntiCollision; + uint8_t startBits = 1; + uint32_t memBlock[HITAG_BLOCKPAGES] = {0}; + uint8_t bits = 0; + bool halfBit = false; + bool finished = false; + + //prep variables for worker + uint8_t expectedBits = 33; + uint8_t readings = 0; + uint8_t currPage = 0; + bool readSucces = false; + uint8_t readThreshold = 3; + + // setup pins for reading + furi_hal_rfid_pins_read(); + + //and setup TIM1 CH1N with DMA + //fill the DMA arr & ccr buffers + /*start with looping SETCC command*/ + LfRfidHitagCMDData* dataCMD = malloc(sizeof(LfRfidHitagCMDData)); + dataCMD->CMDlength = 0; + dataCMD->CMDposition = 0; + dataCMD->CMDsubposition = 0; + dataCMD->CMDactive = false; + + LfRfidHitagCMDData* backupCMD = malloc(sizeof(LfRfidHitagCMDData)); + uint32_t value[] = { + 128, + 0b00110, + 1, + 213, + (uint32_t)(1.05 * 33 * HITAG_BITPERIODS_AC)}; //always take 5% reading time buffer + uint8_t type[] = {HITAG_ON, HITAG_SET, HITAG_STOP, HITAG_ON, HITAG_ON}; + for(size_t i = 0; i < 5; i++) { + backupCMD->CMDvalue[i] = value[i]; + backupCMD->CMDtype[i] = type[i]; + } + backupCMD->CMDlength = 5; + backupCMD->CMDposition = 0; + backupCMD->CMDsubposition = 0; + backupCMD->CMDloop = + true; //not really required since by definition the backup cmd is looping already in yield code + backupCMD->CMDactive = true; + + TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); + lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE / 2); + lfrfid_hitag_worker_yield_dma_buffer_BPLM( + dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE / 2, DMA_BUFFER_SIZE / 2); + + //start TIM1 with DMA function + lfrfid_hitag_worker_carrier_out_start( + dataDMA->timer_buffer_arr, dataDMA->timer_buffer_ccr, DMA_BUFFER_SIZE, worker); + + // start capture + LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); + capData->stream = buffer_stream_alloc(READ_BUFFER_SIZE, READ_BUFFER_COUNT); + capData->pair = varint_pair_alloc(); + furi_hal_rfid_tim_read_capture_start(lfrfid_hitag_worker_capture_in_cc_isr, capData); + + while(1) { + Buffer* buffer = buffer_stream_receive(capData->stream, 1); + + //process data if available + if(buffer != NULL) { + size_t buffsize = buffer_get_size(buffer); + uint8_t* buffdata = buffer_get_data(buffer); + + //if scanning for tagdata, then also parse buffer data + size_t index = 0; + while(expectedBits > 0 && index < buffsize) { + uint32_t duration; + uint32_t pulse; + size_t tmp_size; + + if(!varint_pair_unpack( + &buffdata[index], buffsize - index, &pulse, &duration, &tmp_size)) { + FURI_LOG_E(TAG, "can't unpack varint pair"); + break; + } else { + index += tmp_size; + + //feed pulse duration to decoder + lfrfid_hitag_worker_decoder_feed( + pulse, + duration, + memBlock, + &bits, + &halfBit, + &finished, + currentProtocol, + startBits); + + //see if i have a valid bit sequence + if(finished) { + //bitsequence ready for validation + if(bits == + expectedBits) { //eligable bitsequence detected, accept it only after x consequtive readings + //reset the backupCMD count to ensure some pause between bitsequence detection & the CMD injection + backupCMD->CMDcount = 0; + + if(readings == 0) { + for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { + worker->tag->pageData[(currPage + p) * 4 + 0] = + memBlock[p] >> 24 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 1] = + memBlock[p] >> 16 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 2] = + memBlock[p] >> 8 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 3] = memBlock[p] & + 0xff; + worker->tag->pageKnown[currPage + p] = 0; + } + readings = 1; + } else if(readings < readThreshold) { + bool match = true; + for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { + if(worker->tag->pageData[(currPage + p) * 4 + 0] != + (memBlock[p] >> 24 & 0xff) || + worker->tag->pageData[(currPage + p) * 4 + 1] != + (memBlock[p] >> 16 & 0xff) || + worker->tag->pageData[(currPage + p) * 4 + 2] != + (memBlock[p] >> 8 & 0xff) || + worker->tag->pageData[(currPage + p) * 4 + 3] != + (memBlock[p] & 0xff)) { + match = false; + break; + } + } + if(match) { + readings++; + if(readings == readThreshold) { + //succesful page(s) reading + readSucces = true; + + for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { + worker->tag->pageKnown[currPage + p] = 1; + } + + readings = 0; + expectedBits = 0; //to prevent further updates to tag data + } + } else { + for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { + worker->tag->pageData[(currPage + p) * 4 + 0] = + memBlock[p] >> 24 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 1] = + memBlock[p] >> 16 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 2] = + memBlock[p] >> 8 & 0xff; + worker->tag->pageData[(currPage + p) * 4 + 3] = + memBlock[p] & 0xff; + worker->tag->pageKnown[currPage + p] = 0; + } + readings = 1; + } + } + } + //reset tempdata for next bitsequence readout + memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); + bits = 0; + finished = false; + } + } + } + + //reset buffer + buffer_reset(buffer); + } + + //if bitsequence found & after pause, prep & launch the command for reading out next bit of data + if(readSucces && backupCMD->CMDcount >= 7) { + readSucces = false; + backupCMD->CMDcount = + 0; //required since otherwise the 'relaunch' of cmd is triggered straightaway + + //update protocolData + size_t data_size = protocol_dict_get_data_size(worker->dict, LFRFIDProtocolHitag1); + lfrfid_hitag_worker_combine_tag_data(worker->tag); + protocol_dict_set_data( + worker->dict, LFRFIDProtocolHitag1, worker->tag->tagData, data_size); + + if(currPage == 0) { //read config page next via SELECT cmd + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] found", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3]); + DOLPHIN_DEED(DolphinDeedRfidReadSuccess); + worker->workerstatus = LFRFIDHitagStatusDetected; + + currentProtocol = HitagProtocolManchesterCoding; + expectedBits = 33; + currPage = 1; + } else if(currPage == 1) { //process config page and start reading public blocks + lfrfid_hitag_worker_process_config_page(worker->tag); + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] config page read", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3]); + //find next public readible block + for(currPage = 4; currPage <= HITAG_PAGES - 4; currPage += 4) { + if(worker->tag->pagePublic[currPage] && + (worker->tag->pageRW[currPage] & READ)) { //public readible page/block found + expectedBits = 129; + break; + } else { + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] block %u not public/readible", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3], + currPage / 4); + } + } + } else if(currPage < HITAG_PAGES - 4) { //scan for next public readible page + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] block %u read", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3], + currPage / 4); + for(currPage += 4; currPage <= HITAG_PAGES - 4; currPage += 4) { + if(worker->tag->pagePublic[currPage] && + (worker->tag->pageRW[currPage] & READ)) { //public readible page/block found + expectedBits = 129; + break; + } else { + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] block %u not public/readible", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3], + currPage / 4); + } + } + } else if( + currPage == + HITAG_PAGES - + 4) { //all public readible pages read: signal view & break out of while loop (stops the worker) + FURI_LOG_D( + TAG, + "Hitag1, [%02X %02X %02X %02X] block %u read", + worker->tag->pageData[0], + worker->tag->pageData[1], + worker->tag->pageData[2], + worker->tag->pageData[3], + currPage / 4); + + worker->workerstatus = LFRFIDHitagStatusRead; + break; + } + + if(expectedBits > 0) { //inject CMD + //prep SELECT cmd bytes + uint8_t SN0 = worker->tag->pageData[0]; + uint8_t SN1 = worker->tag->pageData[1]; + uint8_t SN2 = worker->tag->pageData[2]; + uint8_t SN3 = worker->tag->pageData[3]; + uint8_t crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, (0b00000 << (8 - 5)), 5); + lfrfid_hitag_worker_calc_crc(&crc, SN0, 8); + lfrfid_hitag_worker_calc_crc(&crc, SN1, 8); + lfrfid_hitag_worker_calc_crc(&crc, SN2, 8); + lfrfid_hitag_worker_calc_crc(&crc, SN3, 8); + + //prep SELECT cmd array + uint32_t value[] = { + 128, + 0b00000, + SN0, + SN1, + SN2, + SN3, + crc, + 1, + 213, + (uint32_t)(1.05 * 33 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + uint8_t type[] = { + HITAG_ON, + HITAG_SELECT, + HITAG_BYTE, + HITAG_BYTE, + HITAG_BYTE, + HITAG_BYTE, + HITAG_CRC, + HITAG_STOP, + HITAG_ON, + HITAG_ON}; + uint16_t len = 10; + uint16_t cnt = 1; + for(size_t i = 0; i < cnt * len; i++) { + dataCMD->CMDvalue[i] = value[i % len]; + dataCMD->CMDtype[i] = type[i % len]; + } + dataCMD->CMDlength = cnt * len; + dataCMD->CMDposition = 0; + dataCMD->CMDsubposition = 0; + dataCMD->CMDloop = false; + + if(currPage < 4 * 4) { //add READPAGE cmd to array + crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, (0b1100 << (8 - 4)), 4); + lfrfid_hitag_worker_calc_crc(&crc, currPage, 8); + uint32_t value2[] = { + 128, + 0b1100, + currPage, + crc, + 1, + 213, + (uint32_t)(1.05 * 33 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + uint8_t type2[] = { + HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; + uint16_t len2 = 7; + uint16_t cnt2 = 5; + for(size_t i = 0; i < cnt2 * len2; i++) { + dataCMD->CMDvalue[cnt * len + i] = value2[i % len2]; + dataCMD->CMDtype[cnt * len + i] = type2[i % len2]; + } + dataCMD->CMDlength += cnt2 * len2; + } else { //add READBLOCK cmd to array + crc = CRC_PRESET; + lfrfid_hitag_worker_calc_crc(&crc, (0b1101 << (8 - 4)), 4); + lfrfid_hitag_worker_calc_crc(&crc, currPage, 8); + uint32_t value2[] = { + 128, + 0b1101, + currPage, + crc, + 1, + 213, + (uint32_t)(1.05 * 129 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer + uint8_t type2[] = { + HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; + uint16_t len2 = 7; + uint16_t cnt2 = 5; + for(size_t i = 0; i < cnt2 * len2; i++) { + dataCMD->CMDvalue[cnt * len + i] = value2[i % len2]; + dataCMD->CMDtype[cnt * len + i] = type2[i % len2]; + } + dataCMD->CMDlength += cnt2 * len2; + } + } + } + + //while not having x consequtive reads, relaunch the (already prepared) command + if(!readSucces && backupCMD->CMDcount >= 7 && currPage > 0 && expectedBits > 0) { + backupCMD->CMDcount = 0; + dataCMD->CMDposition = 0; + dataCMD->CMDsubposition = 0; + } + + if(buffer_stream_get_overrun_count(capData->stream) > 0) { + FURI_LOG_E(TAG, "Read overrun, recovering"); + buffer_stream_reset(capData->stream); + } + + //check for stop events + uint32_t flags = furi_event_flag_get(worker->events); + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { + break; + } + + //check for DMA HT/TC signals to repopulate the DMA buffer + if(worker->DMAeventCount > 1) { + FURI_LOG_E(TAG, "DMA refilling can't keep up %u", worker->DMAeventCount); + } + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalHalfTransfer)) { + //refill first half of dma buffer + lfrfid_hitag_worker_yield_dma_buffer_BPLM( + dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE / 2); + } + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalTransferComplete)) { + //refill second half of dma buffer + lfrfid_hitag_worker_yield_dma_buffer_BPLM( + dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE / 2, DMA_BUFFER_SIZE / 2); + } + worker->DMAeventCount = 0; + //clear all flags (i have processed all known/expected events) + furi_event_flag_clear(worker->events, flags); + } + + //stop timers + furi_hal_rfid_tim_read_capture_stop(); + lfrfid_hitag_worker_carrier_out_stop(); + + //free memory + free(dataCMD); + free(backupCMD); + free(dataDMA); + varint_pair_free(capData->pair); + buffer_stream_free(capData->stream); + free(capData); + + //allow the view that started the thread to signal the stop, which will properly merge the threads & de-init the worker + if(currPage == HITAG_PAGES - 4) { + const uint32_t available_flags = (1 << LFRFIDHitagWorkerSignalStop); + while(true) { + uint32_t flags = furi_event_flag_wait( + worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever); + + if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { + break; + } + } + } + + free(worker->tag); + + //clear the stop flag on workder so that i can use the same worker to launch a new routine + furi_event_flag_clear(worker->events, 1 << LFRFIDHitagWorkerSignalStop); + + return 0; +} diff --git a/lib/lfrfid/lfrfid_hitag_worker.h b/lib/lfrfid/lfrfid_hitag_worker.h new file mode 100644 index 000000000..9817991b8 --- /dev/null +++ b/lib/lfrfid/lfrfid_hitag_worker.h @@ -0,0 +1,86 @@ +#pragma once +#include +#include +#include +#include +#include +#include +//#include +#include "protocols/lfrfid_protocols.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#define HITAG_BLOCKS 16 +#define HITAG_BLOCKPAGES 4 +#define HITAG_PAGES 64 +#define HITAG_PAGEBYTES 4 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + LFRFIDHitagWorkerSettingRead, + LFRFIDHitagWorkerSettingEmulate, +} LFRFIDHitagWorkerSetting; + +typedef enum { + LFRFIDHitagStatusScanning, + LFRFIDHitagStatusDetected, + LFRFIDHitagStatusRead, +} LFRFIDHitagStatus; + +typedef struct LFRFIDHitagWorker LFRFIDHitagWorker; + +/** + * @brief Get the tag read status + * + * @return tag read status + */ +LFRFIDHitagStatus lfrfid_hitag_worker_get_status(LFRFIDHitagWorker* worker); + +/** + * @brief Allocate a new LFRFIDHitagWorker instance + * + * @return LFRFIDHitagWorker* + */ +LFRFIDHitagWorker* lfrfid_hitag_worker_alloc(ProtocolDict* dict); + +/** + * @brief Free a LFRFIDHitagWorker instance + * + * @param worker LFRFIDHitagWorker instance + */ +void lfrfid_hitag_worker_free(LFRFIDHitagWorker* worker); + +/** + * @brief Start hitag worker from own generated field + * + * @param worker LFRFIDHitagWorker instance + * @param setting read/emulate + */ +void lfrfid_hitag_worker_start(LFRFIDHitagWorker* worker, LFRFIDHitagWorkerSetting setting); + +/** + * @brief Stop worker + * + * @param worker + */ +void lfrfid_hitag_worker_stop(LFRFIDHitagWorker* worker); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h index def9f89a4..682b077a0 100644 --- a/lib/lfrfid/lfrfid_worker.h +++ b/lib/lfrfid/lfrfid_worker.h @@ -23,6 +23,7 @@ typedef enum { LFRFIDWorkerReadTypeAuto, LFRFIDWorkerReadTypeASKOnly, LFRFIDWorkerReadTypePSKOnly, + LFRFIDWorkerReadTypeRTFOnly, } LFRFIDWorkerReadType; typedef enum { @@ -32,6 +33,8 @@ typedef enum { LFRFIDWorkerReadSenseCardEnd, LFRFIDWorkerReadStartASK, LFRFIDWorkerReadStartPSK, + LFRFIDWorkerReadStartRTF, + LFRFIDWorkerReadSenseHitag, //TODO combine with sense carstart? LFRFIDWorkerReadDone, } LFRFIDWorkerReadResult; diff --git a/lib/lfrfid/lfrfid_worker_i.h b/lib/lfrfid/lfrfid_worker_i.h index 33c0bff08..d46ef2bba 100644 --- a/lib/lfrfid/lfrfid_worker_i.h +++ b/lib/lfrfid/lfrfid_worker_i.h @@ -8,6 +8,7 @@ #include #include "lfrfid_worker.h" #include "lfrfid_raw_worker.h" +#include "lfrfid_hitag_worker.h" #include "protocols/lfrfid_protocols.h" #ifdef __cplusplus diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index 9b6f16eb1..c716c0d09 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -94,7 +94,7 @@ typedef enum { LFRFIDWorkerReadTimeout, } LFRFIDWorkerReadState; -static LFRFIDWorkerReadState lfrfid_worker_read_internal( +static LFRFIDWorkerReadState lfrfid_worker_read_ttf( //tag talks first LFRFIDWorker* worker, LFRFIDFeature feature, uint32_t timeout, @@ -334,6 +334,63 @@ static LFRFIDWorkerReadState lfrfid_worker_read_internal( return state; } +static LFRFIDWorkerReadState lfrfid_worker_read_rtf( //reader talks first + LFRFIDWorker* worker, + LFRFIDFeature feature, + uint32_t timeout, + ProtocolId* result_protocol) { + UNUSED(feature); + LFRFIDWorkerReadState state = LFRFIDWorkerReadTimeout; + + FURI_LOG_D(TAG, "Start RTF"); + if(worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadStartRTF, PROTOCOL_NO, worker->cb_ctx); + } + LFRFIDHitagWorker* hitag_worker = lfrfid_hitag_worker_alloc(worker->protocols); + + lfrfid_hitag_worker_start(hitag_worker, LFRFIDHitagWorkerSettingRead); + + FURI_LOG_D(TAG, "Read started"); + + //scan for hitag for a while and stay in hitag mode if card was detected + uint8_t delays = 0; + uint8_t delay_ms = 100; + bool notified = false; + while(1) { + furi_delay_ms(delay_ms); + + if(lfrfid_worker_check_for_stop(worker)) { + state = LFRFIDWorkerReadExit; + *result_protocol = PROTOCOL_NO; + break; + } + + if(lfrfid_hitag_worker_get_status(hitag_worker) == LFRFIDHitagStatusDetected) { + *result_protocol = + LFRFIDProtocolHitag1; //TODO get protocol ID from hitag_worker when expanding the worker to include other hitag protocols + if(!notified && worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadSenseHitag, *result_protocol, worker->cb_ctx); + notified = true; + } + } else if(lfrfid_hitag_worker_get_status(hitag_worker) == LFRFIDHitagStatusRead) { + state = LFRFIDWorkerReadOK; + *result_protocol = + LFRFIDProtocolHitag1; //TODO get protocol ID from hitag_worker when expanding the worker to include other hitag protocols + break; + } else if(++delays >= timeout / delay_ms) { + state = LFRFIDWorkerReadTimeout; + break; + } + } + + lfrfid_hitag_worker_stop(hitag_worker); + lfrfid_hitag_worker_free(hitag_worker); + + FURI_LOG_D(TAG, "Read stopped"); + + return state; +} + static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { ProtocolId read_result = PROTOCOL_NO; LFRFIDWorkerReadState state; @@ -341,6 +398,8 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) { feature = LFRFIDFeaturePSK; + } else if(worker->read_type == LFRFIDWorkerReadTypeRTFOnly) { + feature = LFRFIDFeatureRTF; } else { feature = LFRFIDFeatureASK; } @@ -348,8 +407,14 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { if(worker->read_type == LFRFIDWorkerReadTypeAuto) { while(1) { // read for a while - state = lfrfid_worker_read_internal( - worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + + if(feature == LFRFIDFeatureASK || feature == LFRFIDFeaturePSK) { + state = lfrfid_worker_read_ttf( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + } else if(feature == LFRFIDFeatureRTF) { + state = lfrfid_worker_read_rtf( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + } if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { break; @@ -358,7 +423,9 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { // switch to next feature if(feature == LFRFIDFeatureASK) { feature = LFRFIDFeaturePSK; - } else { + } else if(feature == LFRFIDFeaturePSK) { + feature = LFRFIDFeatureRTF; + } else if(feature == LFRFIDFeatureRTF) { feature = LFRFIDFeatureASK; } @@ -367,10 +434,12 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { } else { while(1) { if(worker->read_type == LFRFIDWorkerReadTypeASKOnly) { - state = lfrfid_worker_read_internal(worker, feature, UINT32_MAX, &read_result); - } else { - state = lfrfid_worker_read_internal( + state = lfrfid_worker_read_ttf(worker, feature, UINT32_MAX, &read_result); + } else if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) { + state = lfrfid_worker_read_ttf( worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + } else { + state = lfrfid_worker_read_rtf(worker, feature, UINT32_MAX, &read_result); } if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { @@ -406,7 +475,7 @@ static void lfrfid_worker_emulate_dma_isr(bool half, void* context) { furi_stream_buffer_send(stream, &flag, sizeof(uint32_t), 0); } -static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { +static void lfrfid_worker_emulate_ttf(LFRFIDWorker* worker) { LFRFIDWorkerEmulateBuffer* buffer = malloc(sizeof(LFRFIDWorkerEmulateBuffer)); FuriStreamBuffer* stream = furi_stream_buffer_alloc(sizeof(uint32_t), sizeof(uint32_t)); LFRFIDProtocol protocol = worker->protocol; @@ -495,6 +564,39 @@ static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { pulse_glue_free(pulse_glue); } +static void lfrfid_worker_emulate_rtf(LFRFIDWorker* worker) { + LFRFIDHitagWorker* hitag_worker = lfrfid_hitag_worker_alloc( + worker->protocols); //todo, pass protocols & protocol id when expanding the worker to include other hitag protocols + + lfrfid_hitag_worker_start(hitag_worker, LFRFIDHitagWorkerSettingEmulate); + uint8_t delay_ms = 100; + while(1) { + furi_delay_ms(delay_ms); + + if(lfrfid_worker_check_for_stop(worker)) { + break; + } + } + + lfrfid_hitag_worker_stop(hitag_worker); + lfrfid_hitag_worker_free(hitag_worker); +} + +static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { + if(worker != NULL) { + if(worker->protocols != NULL) { + LFRFIDFeature feature = + protocol_dict_get_features(worker->protocols, worker->protocol); + + if(feature == LFRFIDFeatureRTF) { + lfrfid_worker_emulate_rtf(worker); + } else { + lfrfid_worker_emulate_ttf(worker); + } + } + } +} + /**************************************************************************************************/ /********************************************* WRITE **********************************************/ /**************************************************************************************************/ @@ -521,7 +623,7 @@ static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) { t5577_write(&request->t5577); ProtocolId read_result = PROTOCOL_NO; - LFRFIDWorkerReadState state = lfrfid_worker_read_internal( + LFRFIDWorkerReadState state = lfrfid_worker_read_ttf( worker, protocol_dict_get_features(worker->protocols, protocol), LFRFID_WORKER_WRITE_VERIFY_TIME_MS, diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index 2c1f0ad97..43d59e123 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -16,6 +16,7 @@ #include "protocol_pac_stanley.h" #include "protocol_keri.h" #include "protocol_gallagher.h" +#include "protocol_hitag1.h" const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, @@ -35,4 +36,5 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolPACStanley] = &protocol_pac_stanley, [LFRFIDProtocolKeri] = &protocol_keri, [LFRFIDProtocolGallagher] = &protocol_gallagher, + [LFRFIDProtocolHitag1] = &protocol_hitag1, }; \ No newline at end of file diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 848f003a3..6d0e3fa4e 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -5,6 +5,7 @@ typedef enum { LFRFIDFeatureASK = 1 << 0, /** ASK Demodulation */ LFRFIDFeaturePSK = 1 << 1, /** PSK Demodulation */ + LFRFIDFeatureRTF = 1 << 2, /** Reader Talks First: ASK Demodulation with 2 way communication */ } LFRFIDFeature; typedef enum { @@ -25,6 +26,7 @@ typedef enum { LFRFIDProtocolPACStanley, LFRFIDProtocolKeri, LFRFIDProtocolGallagher, + LFRFIDProtocolHitag1, LFRFIDProtocolMax, } LFRFIDProtocol; diff --git a/lib/lfrfid/protocols/protocol_hitag1.c b/lib/lfrfid/protocols/protocol_hitag1.c new file mode 100644 index 000000000..1890ea1ad --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hitag1.c @@ -0,0 +1,104 @@ +#include +#include +#include "lfrfid_protocols.h" + +#define HITAG1_PAGES 64 +#define HITAG1_DATA_SIZE HITAG1_PAGES * 4 + HITAG1_PAGES + +typedef struct { + uint8_t tagData[HITAG1_DATA_SIZE]; +} ProtocolHitag1; + +ProtocolHitag1* protocol_hitag1_alloc(void) { + ProtocolHitag1* protocol = malloc(sizeof(ProtocolHitag1)); + + return protocol; +}; + +void protocol_hitag1_free(ProtocolHitag1* protocol) { + free(protocol); +}; + +uint8_t* protocol_hitag1_get_data(ProtocolHitag1* protocol) { + return protocol->tagData; +}; + +void protocol_hitag1_decoder_start(ProtocolHitag1* protocol) { + UNUSED(protocol); + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... +}; + +bool protocol_hitag1_decoder_feed(ProtocolHitag1* protocol, bool level, uint32_t duration) { + UNUSED(protocol); + UNUSED(level); + UNUSED(duration); + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... + + bool result = false; + return result; +}; + +bool protocol_hitag1_encoder_start(ProtocolHitag1* protocol) { + UNUSED(protocol); + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... + + return false; +}; + +LevelDuration protocol_hitag1_encoder_yield(ProtocolHitag1* protocol) { + UNUSED(protocol); + // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... + + bool level = 0; + uint32_t duration = 0; + return level_duration_make(level, duration); +}; + +bool protocol_hitag1_write_data(ProtocolHitag1* protocol, void* data) { + UNUSED(protocol); + UNUSED(data); + + //this protocol cannot be simply written to card --> don't do anything, just return false + + return false; +}; + +void protocol_hitag1_render_data(ProtocolHitag1* protocol, FuriString* result) { + uint8_t pages = 0; + for(uint8_t p = 0; p < HITAG1_PAGES; p++) { + pages += protocol->tagData[HITAG1_PAGES * 4 + p]; + } + furi_string_printf( + result, + "SN: %02X %02X %02X %02X\r\n" + "Pages read: %u / 64", + protocol->tagData[0], + protocol->tagData[1], + protocol->tagData[2], + protocol->tagData[3], + pages); +}; + +const ProtocolBase protocol_hitag1 = { + .name = "Hitag1", + .manufacturer = "Philips", + .data_size = HITAG1_DATA_SIZE, + .features = LFRFIDFeatureRTF, + .validate_count = 1, + .alloc = (ProtocolAlloc)protocol_hitag1_alloc, + .free = (ProtocolFree)protocol_hitag1_free, + .get_data = (ProtocolGetData)protocol_hitag1_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_hitag1_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_hitag1_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_hitag1_encoder_start, + .yield = (ProtocolEncoderYield)protocol_hitag1_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_hitag1_render_data, + .render_brief_data = (ProtocolRenderData)protocol_hitag1_render_data, + .write_data = (ProtocolWriteData)protocol_hitag1_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_hitag1.h b/lib/lfrfid/protocols/protocol_hitag1.h new file mode 100644 index 000000000..517eeb5ef --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hitag1.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_hitag1; \ No newline at end of file From fc18cea875a8c8b5483bfd06c4fb0fba6131da01 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 21:22:22 +0300 Subject: [PATCH 45/71] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2f1a2a28..d15721ed9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ * Infrared: `External output` move out of debug and add power option for external modules * Extra pack: Some app fixes * FBT: Fix vscode example config for debug builds - please run `./fbt vscode_dist` again if you had issues with debug builds size +* OFW PR 2658: Hitag (by @blackvault88) (with minor fixes, hitag excluded from add manually temporarily, + fixed lfrfid favourites start when key cannot be parsed) +* OFW PR 2316: NFC V support (by @g3gg0 & @nvx) +* OFW PR 2669: nfc: Fix MFUL tearing flags read (by @GMMan) * OFW: api: added lib/nfc/protocols/nfc_util.h * OFW: fix PIN retry count reset on reboot * OFW: fbt: allow strings for fap_version field in app manifests From 95d6465dacf1b0c54bd544701c58c68c97d1e60b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 21:30:36 +0300 Subject: [PATCH 46/71] OFW PR 2666: Add fr-FR-mac key layout by FelixLgr --- CHANGELOG.md | 1 + .../resources/badusb/assets/layouts/fr-FR-mac.kl | Bin 0 -> 256 bytes 2 files changed, 1 insertion(+) create mode 100644 assets/resources/badusb/assets/layouts/fr-FR-mac.kl diff --git a/CHANGELOG.md b/CHANGELOG.md index d15721ed9..5cface529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * OFW PR 2658: Hitag (by @blackvault88) (with minor fixes, hitag excluded from add manually temporarily, + fixed lfrfid favourites start when key cannot be parsed) * OFW PR 2316: NFC V support (by @g3gg0 & @nvx) * OFW PR 2669: nfc: Fix MFUL tearing flags read (by @GMMan) +* OFW PR 2666: BadUSB: Add fr-FR-mac key layout (by @FelixLgr) * OFW: api: added lib/nfc/protocols/nfc_util.h * OFW: fix PIN retry count reset on reboot * OFW: fbt: allow strings for fap_version field in app manifests diff --git a/assets/resources/badusb/assets/layouts/fr-FR-mac.kl b/assets/resources/badusb/assets/layouts/fr-FR-mac.kl new file mode 100644 index 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` Date: Wed, 17 May 2023 22:37:24 +0300 Subject: [PATCH 47/71] remove region_data from int --- .../storage_move_to_sd/storage_move_to_sd.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/applications/system/storage_move_to_sd/storage_move_to_sd.c b/applications/system/storage_move_to_sd/storage_move_to_sd.c index 9c91b9266..2f33eec33 100644 --- a/applications/system/storage_move_to_sd/storage_move_to_sd.c +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.c @@ -5,6 +5,7 @@ #include #include #include +#include #define TAG "MoveToSd" @@ -20,6 +21,17 @@ static bool storage_move_to_sd_check_entry(const char* name, FileInfo* fileinfo, return (name && (*name != '.')); } +static void storage_move_to_sd_remove_region() { + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) return; + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(storage_common_exists(storage, INT_PATH(".region_data"))) { + storage_common_remove(storage, INT_PATH(".region_data")); + } + + furi_record_close(RECORD_STORAGE); +} + bool storage_move_to_sd_perform(void) { Storage* storage = furi_record_open(RECORD_STORAGE); @@ -162,6 +174,9 @@ int32_t storage_move_to_sd_app(void* p) { FURI_LOG_I(TAG, "Nothing to move"); } + // Remove unused region file from int memory + storage_move_to_sd_remove_region(); + return 0; } From 51ba8b66b699a85fd39b5c8b804a57ca2b0c38f8 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 23:05:44 +0300 Subject: [PATCH 48/71] Save a little bit (~2k) of space --- applications/main/nfc/scenes/nfc_scene_config.h | 4 ++++ applications/main/nfc/scenes/nfc_scene_debug.c | 2 ++ .../main/nfc/scenes/nfc_scene_emulate_apdu_sequence.c | 2 ++ applications/main/nfc/scenes/nfc_scene_field.c | 2 ++ applications/main/nfc/scenes/nfc_scene_start.c | 11 ++++++++--- lib/nfc/nfc_worker.c | 11 ++++++++--- lib/nfc/protocols/emv.c | 6 ++++-- 7 files changed, 30 insertions(+), 8 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index d6edebe73..445c4436a 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -52,14 +52,18 @@ ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess) ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) ADD_SCENE(nfc, emv_menu, EmvMenu) +#if FURI_DEBUG ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) +#endif ADD_SCENE(nfc, device_info, DeviceInfo) ADD_SCENE(nfc, delete, Delete) ADD_SCENE(nfc, delete_success, DeleteSuccess) ADD_SCENE(nfc, restore_original_confirm, RestoreOriginalConfirm) ADD_SCENE(nfc, restore_original, RestoreOriginal) +#if FURI_DEBUG ADD_SCENE(nfc, debug, Debug) ADD_SCENE(nfc, field, Field) +#endif ADD_SCENE(nfc, dict_not_found, DictNotFound) ADD_SCENE(nfc, rpc, Rpc) ADD_SCENE(nfc, exit_confirm, ExitConfirm) diff --git a/applications/main/nfc/scenes/nfc_scene_debug.c b/applications/main/nfc/scenes/nfc_scene_debug.c index ed079c2ed..5dc29c96a 100644 --- a/applications/main/nfc/scenes/nfc_scene_debug.c +++ b/applications/main/nfc/scenes/nfc_scene_debug.c @@ -1,3 +1,4 @@ +#if FURI_DEBUG #include "../nfc_i.h" enum SubmenuDebugIndex { @@ -52,3 +53,4 @@ void nfc_scene_debug_on_exit(void* context) { submenu_reset(nfc->submenu); } +#endif \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_apdu_sequence.c b/applications/main/nfc/scenes/nfc_scene_emulate_apdu_sequence.c index 358ad2ab6..bc3f0f42d 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_apdu_sequence.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate_apdu_sequence.c @@ -1,3 +1,4 @@ +#if FURI_DEBUG #include "../nfc_i.h" #include @@ -32,3 +33,4 @@ void nfc_scene_emulate_apdu_sequence_on_exit(void* context) { nfc_blink_stop(nfc); } +#endif \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_field.c b/applications/main/nfc/scenes/nfc_scene_field.c index e3eb6a708..f287e8796 100644 --- a/applications/main/nfc/scenes/nfc_scene_field.c +++ b/applications/main/nfc/scenes/nfc_scene_field.c @@ -1,3 +1,4 @@ +#if FURI_DEBUG #include "../nfc_i.h" void nfc_scene_field_on_enter(void* context) { @@ -31,3 +32,4 @@ void nfc_scene_field_on_exit(void* context) { notification_internal_message(nfc->notifications, &sequence_reset_blue); popup_reset(nfc->popup); } +#endif \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index a01f871ab..858d519fc 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -8,7 +8,9 @@ enum SubmenuIndex { SubmenuIndexSaved, SubmenuIndexExtraAction, SubmenuIndexAddManually, +#if FURI_DEBUG SubmenuIndexDebug, +#endif }; void nfc_scene_start_submenu_callback(void* context, uint32_t index) { @@ -29,12 +31,12 @@ void nfc_scene_start_on_enter(void* context) { submenu, "Extra Actions", SubmenuIndexExtraAction, nfc_scene_start_submenu_callback, nfc); submenu_add_item( submenu, "Add Manually", SubmenuIndexAddManually, nfc_scene_start_submenu_callback, nfc); - +#if FURI_DEBUG if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { submenu_add_item( submenu, "Debug", SubmenuIndexDebug, nfc_scene_start_submenu_callback, nfc); } - +#endif submenu_set_selected_item( submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart)); @@ -82,11 +84,14 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually); scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); consumed = true; - } else if(event.event == SubmenuIndexDebug) { + } +#if FURI_DEBUG + else if(event.event == SubmenuIndexDebug) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug); scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); consumed = true; } +#endif } return consumed; } diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 197b6f58a..8f7a1b5e4 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -95,9 +95,13 @@ int32_t nfc_worker_task(void* context) { } } else if(nfc_worker->state == NfcWorkerStateUidEmulate) { nfc_worker_emulate_uid(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { + } +#if FURI_DEBUG + else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { nfc_worker_emulate_apdu(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { + } +#endif + else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { nfc_worker_emulate_mf_ultralight(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { nfc_worker_emulate_mf_classic(nfc_worker); @@ -741,7 +745,7 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { } } } - +#if FURI_DEBUG void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; FuriHalNfcDevData params = { @@ -774,6 +778,7 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { reader_analyzer_stop(nfc_worker->reader_analyzer); } } +#endif void nfc_worker_mf_ultralight_auth_received_callback(MfUltralightAuth auth, void* context) { furi_assert(context); diff --git a/lib/nfc/protocols/emv.c b/lib/nfc/protocols/emv.c index 4c4ac856b..8cc5fc44e 100644 --- a/lib/nfc/protocols/emv.c +++ b/lib/nfc/protocols/emv.c @@ -44,7 +44,7 @@ const PDOLValue* const pdol_values[] = { &pdol_transaction_cert, &pdol_unpredict_number, }; - +#if FURI_DEBUG static const uint8_t select_ppse_ans[] = {0x6F, 0x29, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31, 0xA5, 0x17, 0xBF, 0x0C, 0x14, 0x61, 0x12, 0x4F, 0x07, @@ -61,7 +61,7 @@ static const uint8_t pdol_ans[] = {0x77, 0x40, 0x82, 0x02, 0x20, 0x00, 0x57, 0x1 0x00, 0x9F, 0x26, 0x08, 0x7A, 0x65, 0x7F, 0xD3, 0x52, 0x96, 0xC9, 0x85, 0x9F, 0x27, 0x01, 0x00, 0x9F, 0x36, 0x02, 0x06, 0x0C, 0x9F, 0x6C, 0x02, 0x10, 0x00, 0x90, 0x00}; - +#endif static void emv_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) { if(furi_log_get_level() == FuriLogLevelTrace) { FURI_LOG_T(TAG, "%s", message); @@ -409,6 +409,7 @@ bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) { return card_num_read; } +#if FURI_DEBUG bool emv_card_emulation(FuriHalNfcTxRxContext* tx_rx) { furi_assert(tx_rx); bool emulation_complete = false; @@ -442,3 +443,4 @@ bool emv_card_emulation(FuriHalNfcTxRxContext* tx_rx) { return emulation_complete; } +#endif \ No newline at end of file From d539b948a00a9d846fe1f828dc9b9738c3fa0ebe Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 23:06:41 +0300 Subject: [PATCH 49/71] Revert hitag for now, need to cleanup more space --- applications/main/lfrfid/lfrfid_i.h | 2 - .../scenes/lfrfid_scene_extra_actions.c | 12 - .../main/lfrfid/scenes/lfrfid_scene_read.c | 18 - .../lfrfid/scenes/lfrfid_scene_save_type.c | 3 - .../lfrfid/scenes/lfrfid_scene_saved_info.c | 12 +- .../main/lfrfid/views/lfrfid_view_read.c | 73 +- .../main/lfrfid/views/lfrfid_view_read.h | 11 +- .../services/gui/modules/byte_input.c | 14 +- .../services/gui/modules/byte_input.h | 2 +- firmware/targets/f7/api_symbols.csv | 2 +- lib/lfrfid/lfrfid_dict_file.c | 82 +- lib/lfrfid/lfrfid_hitag_worker.c | 2370 ----------------- lib/lfrfid/lfrfid_hitag_worker.h | 86 - lib/lfrfid/lfrfid_worker.h | 3 - lib/lfrfid/lfrfid_worker_i.h | 1 - lib/lfrfid/lfrfid_worker_modes.c | 120 +- lib/lfrfid/protocols/lfrfid_protocols.c | 2 - lib/lfrfid/protocols/lfrfid_protocols.h | 2 - lib/lfrfid/protocols/protocol_hitag1.c | 104 - lib/lfrfid/protocols/protocol_hitag1.h | 4 - 20 files changed, 41 insertions(+), 2882 deletions(-) delete mode 100644 lib/lfrfid/lfrfid_hitag_worker.c delete mode 100644 lib/lfrfid/lfrfid_hitag_worker.h delete mode 100644 lib/lfrfid/protocols/protocol_hitag1.c delete mode 100644 lib/lfrfid/protocols/protocol_hitag1.h diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index a883d1926..86929a14a 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -56,8 +56,6 @@ enum LfRfidCustomEvent { LfRfidEventReadSenseCardEnd, LfRfidEventReadStartASK, LfRfidEventReadStartPSK, - LfRfidEventReadStartRTF, - LfRfidEventReadSenseHitag, //TODO combine with sensecardstart? LfRfidEventReadDone, LfRfidEventReadOverrun, LfRfidEventReadError, diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index 53bf32bbd..d2bf20680 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -4,7 +4,6 @@ typedef enum { SubmenuIndexASK, SubmenuIndexPSK, - SubmenuIndexHitag, SubmenuIndexClearT5577, SubmenuIndexRAW, SubmenuIndexRAWEmulate, @@ -32,12 +31,6 @@ void lfrfid_scene_extra_actions_on_enter(void* context) { SubmenuIndexPSK, lfrfid_scene_extra_actions_submenu_callback, app); - submenu_add_item( - submenu, - "Read RTF (Reader Talks First)", - SubmenuIndexHitag, - lfrfid_scene_extra_actions_submenu_callback, - app); submenu_add_item( submenu, "Clear T5577 Password", @@ -86,11 +79,6 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; - } else if(event.event == SubmenuIndexHitag) { - app->read_type = LFRFIDWorkerReadTypeRTFOnly; - scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); - DOLPHIN_DEED(DolphinDeedRfidRead); - consumed = true; } else if(event.event == SubmenuIndexClearT5577) { scene_manager_next_scene(app->scene_manager, LfRfidSceneClearT5577Confirm); consumed = true; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_read.c index e9168c818..5f1959728 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read.c @@ -36,10 +36,6 @@ static void event = LfRfidEventReadStartASK; } else if(result == LFRFIDWorkerReadStartPSK) { event = LfRfidEventReadStartPSK; - } else if(result == LFRFIDWorkerReadStartRTF) { - event = LfRfidEventReadStartRTF; - } else if(result == LFRFIDWorkerReadSenseHitag) { //TODO combine with sensecardstart? - event = LfRfidEventReadSenseHitag; } else { return; } @@ -54,9 +50,6 @@ void lfrfid_scene_read_on_enter(void* context) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPskOnly); } else if(app->read_type == LFRFIDWorkerReadTypeASKOnly) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAskOnly); - } else if(app->read_type == LFRFIDWorkerReadTypeRTFOnly) { - lfrfid_view_read_set_read_state(app->read_view, LfRfidReadScanning); - lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadRtfOnly); } lfrfid_worker_start_thread(app->lfworker); @@ -100,17 +93,6 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAsk); } consumed = true; - } else if(event.event == LfRfidEventReadStartRTF) { - if(app->read_type == LFRFIDWorkerReadTypeAuto) { - lfrfid_view_read_set_read_state(app->read_view, LfRfidReadScanning); - lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadHitag); - } - consumed = true; - } else if(event.event == LfRfidEventReadSenseHitag) { //TODO combine with sensecardstart? - if(app->read_type == LFRFIDWorkerReadTypeAuto || - app->read_type == LFRFIDWorkerReadTypeRTFOnly) { - lfrfid_view_read_set_read_state(app->read_view, LfRfidReadTagDetected); - } } } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c index f7bd6c6e5..eb73b1123 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c @@ -17,9 +17,6 @@ void lfrfid_scene_save_type_on_enter(void* context) { SaveTypeCtx* state = malloc(sizeof(SaveTypeCtx)); FuriString* protocol_string = furi_string_alloc(); for(uint8_t i = 0; i < LFRFIDProtocolMax; i++) { - if(i == LFRFIDProtocolHitag1) { - continue; - } if((strcmp( protocol_dict_get_manufacturer(app->dict, i), protocol_dict_get_name(app->dict, i)) != 0) && diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c index 812450314..c901629c3 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c @@ -17,15 +17,11 @@ void lfrfid_scene_saved_info_on_enter(void* context) { uint8_t* data = (uint8_t*)malloc(size); protocol_dict_get_data(app->dict, app->protocol_id, data, size); for(uint8_t i = 0; i < size; i++) { - if(i >= 18) { - furi_string_cat_printf(tmp_string, ".."); - break; - } else { - if(i != 0) { - furi_string_cat_printf(tmp_string, ":"); - } - furi_string_cat_printf(tmp_string, "%02X", data[i]); + if(i != 0) { + furi_string_cat_printf(tmp_string, ":"); } + + furi_string_cat_printf(tmp_string, "%02X", data[i]); } free(data); diff --git a/applications/main/lfrfid/views/lfrfid_view_read.c b/applications/main/lfrfid/views/lfrfid_view_read.c index c0dda4178..094afb617 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.c +++ b/applications/main/lfrfid/views/lfrfid_view_read.c @@ -11,7 +11,6 @@ struct LfRfidReadView { typedef struct { IconAnimation* icon; LfRfidReadViewMode read_mode; - LfRfidReadViewState read_state; } LfRfidReadViewModel; static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { @@ -23,69 +22,36 @@ static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontPrimary); if(model->read_mode == LfRfidReadAsk) { - canvas_draw_str(canvas, 70, 8, "Reading 1/3"); + canvas_draw_str(canvas, 70, 16, "Reading 1/2"); - canvas_draw_str(canvas, 77, 20, "ASK"); - canvas_draw_icon(canvas, 70, 13, &I_ButtonRight_4x7); - canvas_draw_icon_animation(canvas, 112, 12, model->icon); + canvas_draw_str(canvas, 77, 29, "ASK"); + canvas_draw_icon(canvas, 70, 22, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 102, 21, model->icon); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 77, 33, "PSK"); - canvas_draw_str(canvas, 77, 46, "RTF"); + canvas_draw_str(canvas, 77, 43, "PSK"); } else if(model->read_mode == LfRfidReadPsk) { - canvas_draw_str(canvas, 70, 8, "Reading 2/3"); + canvas_draw_str(canvas, 70, 16, "Reading 2/2"); - canvas_draw_str(canvas, 77, 33, "PSK"); - canvas_draw_icon(canvas, 70, 26, &I_ButtonRight_4x7); - canvas_draw_icon_animation(canvas, 112, 25, model->icon); + canvas_draw_str(canvas, 77, 43, "PSK"); + canvas_draw_icon(canvas, 70, 36, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 102, 35, model->icon); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 77, 20, "ASK"); - canvas_draw_str(canvas, 77, 46, "RTF"); - } else if(model->read_mode == LfRfidReadHitag) { - if(model->read_state == LfRfidReadScanning) { - canvas_draw_str(canvas, 70, 8, "Reading 3/3"); - - canvas_draw_str(canvas, 77, 46, "RTF"); - canvas_draw_icon(canvas, 70, 39, &I_ButtonRight_4x7); - canvas_draw_icon_animation(canvas, 112, 38, model->icon); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 77, 20, "ASK"); - canvas_draw_str(canvas, 77, 33, "PSK"); - } else if(model->read_state == LfRfidReadTagDetected) { //TODO switch to other scene? - canvas_draw_str(canvas, 65, 8, "Hitag1 found"); - - canvas_set_font(canvas, FontSecondary); - //canvas_draw_str(canvas, 70, 20, "## ## ## ##"); //TODO get tag SN from hitag worker - canvas_draw_str(canvas, 70, 33, "Reading data"); - //canvas_draw_str(canvas, 70, 46, "Page: X/64"); //TODO get current page index from hitag worker - } - } else if(model->read_mode == LfRfidReadAskOnly) { + canvas_draw_str(canvas, 77, 29, "ASK"); + } else { canvas_draw_str(canvas, 72, 16, "Reading"); - canvas_draw_str(canvas, 77, 35, "ASK"); - canvas_draw_icon_animation(canvas, 112, 27, model->icon); - } else if(model->read_mode == LfRfidReadPskOnly) { - canvas_draw_str(canvas, 72, 16, "Reading"); - canvas_draw_str(canvas, 77, 35, "PSK"); - canvas_draw_icon_animation(canvas, 112, 27, model->icon); - } else if(model->read_mode == LfRfidReadRtfOnly) { - if(model->read_state == LfRfidReadScanning) { - canvas_draw_str(canvas, 72, 16, "Reading"); - canvas_draw_str(canvas, 77, 35, "RTF"); - canvas_draw_icon_animation(canvas, 112, 27, model->icon); - } else if(model->read_state == LfRfidReadTagDetected) { //TODO switch to other scene? - canvas_draw_str(canvas, 65, 8, "Hitag1 found"); - canvas_set_font(canvas, FontSecondary); - //canvas_draw_str(canvas, 70, 20, "## ## ## ##"); //TODO get tag SN from hitag worker - canvas_draw_str(canvas, 70, 33, "Reading data"); - //canvas_draw_str(canvas, 70, 46, "Page: X/64"); //TODO get current page index from hitag worker + if(model->read_mode == LfRfidReadAskOnly) { + canvas_draw_str(canvas, 77, 35, "ASK"); + } else { + canvas_draw_str(canvas, 77, 35, "PSK"); } + canvas_draw_icon_animation(canvas, 102, 27, model->icon); } canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 61, 60, "Don't move card"); + canvas_draw_str(canvas, 61, 56, "Don't move card"); } void lfrfid_view_read_enter(void* context) { @@ -145,8 +111,3 @@ void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMod }, true); } - -void lfrfid_view_read_set_read_state(LfRfidReadView* read_view, LfRfidReadViewState state) { - with_view_model( - read_view->view, LfRfidReadViewModel * model, { model->read_state = state; }, true); -} \ No newline at end of file diff --git a/applications/main/lfrfid/views/lfrfid_view_read.h b/applications/main/lfrfid/views/lfrfid_view_read.h index 7d6d8ead4..55bb1f230 100644 --- a/applications/main/lfrfid/views/lfrfid_view_read.h +++ b/applications/main/lfrfid/views/lfrfid_view_read.h @@ -4,17 +4,10 @@ typedef enum { LfRfidReadAsk, LfRfidReadPsk, - LfRfidReadHitag, LfRfidReadAskOnly, - LfRfidReadPskOnly, - LfRfidReadRtfOnly, + LfRfidReadPskOnly } LfRfidReadViewMode; -typedef enum { - LfRfidReadScanning, - LfRfidReadTagDetected, -} LfRfidReadViewState; - typedef struct LfRfidReadView LfRfidReadView; LfRfidReadView* lfrfid_view_read_alloc(); @@ -24,5 +17,3 @@ void lfrfid_view_read_free(LfRfidReadView* read_view); View* lfrfid_view_read_get_view(LfRfidReadView* read_view); void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode); - -void lfrfid_view_read_set_read_state(LfRfidReadView* read_view, LfRfidReadViewState state); \ No newline at end of file diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index f1edf9de8..b2d21f7ae 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -17,17 +17,17 @@ typedef struct { typedef struct { const char* header; uint8_t* bytes; - uint16_t bytes_count; + uint8_t bytes_count; ByteInputCallback input_callback; ByteChangedCallback changed_callback; void* callback_context; bool selected_high_nibble; - uint16_t selected_byte; + uint8_t selected_byte; int8_t selected_row; // row -1 - input, row 0 & 1 - keyboard uint8_t selected_column; - uint16_t first_visible_byte; + uint8_t first_visible_byte; } ByteInputModel; static const uint8_t keyboard_origin_x = 7; @@ -164,7 +164,7 @@ static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5); canvas_draw_icon(canvas, 123, 19, &I_ButtonRightSmall_3x5); - for(uint16_t i = model->first_visible_byte; + for(uint8_t i = model->first_visible_byte; i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); i++) { uint8_t byte_position = i - model->first_visible_byte; @@ -253,7 +253,7 @@ static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5); canvas_draw_icon(canvas, 122, 19, &I_ButtonRightSmall_3x5); - for(uint16_t i = model->first_visible_byte; + for(uint8_t i = model->first_visible_byte; i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); i++) { uint8_t byte_position = i - model->first_visible_byte; @@ -305,7 +305,7 @@ static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model * @param value char value * @param high_nibble set high nibble */ -static void byte_input_set_nibble(uint8_t* data, uint16_t position, char value, bool high_nibble) { +static void byte_input_set_nibble(uint8_t* data, uint8_t position, char value, bool high_nibble) { switch(value) { case '0': case '1': @@ -750,7 +750,7 @@ void byte_input_set_result_callback( ByteChangedCallback changed_callback, void* callback_context, uint8_t* bytes, - uint16_t bytes_count) { + uint8_t bytes_count) { with_view_model( byte_input->view, ByteInputModel * model, diff --git a/applications/services/gui/modules/byte_input.h b/applications/services/gui/modules/byte_input.h index b8a4d4455..42c4b5d65 100644 --- a/applications/services/gui/modules/byte_input.h +++ b/applications/services/gui/modules/byte_input.h @@ -55,7 +55,7 @@ void byte_input_set_result_callback( ByteChangedCallback changed_callback, void* callback_context, uint8_t* bytes, - uint16_t bytes_count); + uint8_t bytes_count); /** Set byte input header text * diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 4ea51493c..84d0beda8 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -626,7 +626,7 @@ Function,+,byte_input_alloc,ByteInput*, Function,+,byte_input_free,void,ByteInput* Function,+,byte_input_get_view,View*,ByteInput* Function,+,byte_input_set_header_text,void,"ByteInput*, const char*" -Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint16_t" +Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t" Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* diff --git a/lib/lfrfid/lfrfid_dict_file.c b/lib/lfrfid/lfrfid_dict_file.c index f8c3d5ef7..7ae84f8b6 100644 --- a/lib/lfrfid/lfrfid_dict_file.c +++ b/lib/lfrfid/lfrfid_dict_file.c @@ -5,42 +5,6 @@ #define LFRFID_DICT_FILETYPE "Flipper RFID key" -bool lfrfid_dict_file_save_hitag1_data(FlipperFormat* file, uint8_t* data) { - FuriString* string = furi_string_alloc(); - bool result = false; - uint8_t pageSize = 4; - - do { - //write shortened data (tag ID) - if(!flipper_format_write_hex(file, "Data", data, pageSize)) break; - - if(!flipper_format_write_comment_cstr(file, "Hitag1 specific data")) break; - - //write pages - for(uint8_t p = 0; p < 64; p++) { - furi_string_printf(string, "Page %2u", p); - if(data[64 * pageSize + p]) { - //write page data - if(!flipper_format_write_hex( - file, furi_string_get_cstr(string), data + p * pageSize, pageSize)) - break; - } else { - //write ?? ?? ?? ?? - if(!flipper_format_write_string_cstr( - file, furi_string_get_cstr(string), "?? ?? ?? ??")) - break; - } - if(p == 64 - 1) { - result = true; - } - } - } while(false); - - furi_string_free(string); - - return result; -} - bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename) { furi_check(protocol != PROTOCOL_NO); Storage* storage = furi_record_open(RECORD_STORAGE); @@ -62,12 +26,8 @@ bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* // TODO: write comment about protocol sizes into file protocol_dict_get_data(dict, protocol, data, data_size); - if(protocol == LFRFIDProtocolHitag1) { - if(!lfrfid_dict_file_save_hitag1_data(file, data)) break; - } else { - if(!flipper_format_write_hex(file, "Data", data, data_size)) break; - } + if(!flipper_format_write_hex(file, "Data", data, data_size)) break; result = true; } while(false); @@ -178,41 +138,6 @@ static ProtocolId lfrfid_dict_protocol_fallback( return result; } -bool lfrfid_dict_file_load_hitag1_data(FlipperFormat* file, uint8_t* data) { - FuriString* string = furi_string_alloc(); - bool result = false; - uint8_t tagID[4]; - uint8_t pageSize = 4; - - do { - //read shortened data (tag ID) - if(!flipper_format_read_hex(file, "Data", tagID, 4)) break; - - //read pages - for(uint8_t p = 0; p < 64; p++) { - furi_string_printf(string, "Page %2u", p); - if(flipper_format_read_hex( - file, furi_string_get_cstr(string), data + p * pageSize, pageSize)) { - data[64 * pageSize + p] = 1; - } else { - data[64 * pageSize + p] = 0; - } - } - - //check data consistency - if(memcmp(tagID, data, pageSize) != 0) break; - - //check if tag ID & config page are succesfully read - if(data[64 * pageSize + 0] && data[64 * pageSize + 1]) { - result = true; - } - } while(false); - - furi_string_free(string); - - return result; -} - ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -238,11 +163,6 @@ ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) { if(protocol == PROTOCOL_NO) { protocol = lfrfid_dict_protocol_fallback(dict, furi_string_get_cstr(str_result), file); if(protocol == PROTOCOL_NO) break; - } else if(protocol == LFRFIDProtocolHitag1) { - // Hitag1 data - size_t data_size = protocol_dict_get_data_size(dict, protocol); - if(!lfrfid_dict_file_load_hitag1_data(file, data)) break; - protocol_dict_set_data(dict, protocol, data, data_size); } else { // data size_t data_size = protocol_dict_get_data_size(dict, protocol); diff --git a/lib/lfrfid/lfrfid_hitag_worker.c b/lib/lfrfid/lfrfid_hitag_worker.c deleted file mode 100644 index 02cd14ca0..000000000 --- a/lib/lfrfid/lfrfid_hitag_worker.c +++ /dev/null @@ -1,2370 +0,0 @@ -#include "lfrfid_hitag_worker.h" - -#define DMA_BUFFER_SIZE \ - 4000 //I need to keep this big enough? to have enough time inbetween HT & TC events perhaps? -#define HITAG_CMD_BUFFER_SIZE 100 //this was originally 1000, but can be lowered - -#define READ_BUFFER_SIZE 4000 -#define READ_BUFFER_COUNT 4 - -#define EMULATE_BUFFER_SIZE 50 -#define EMULATE_BUFFER_COUNT 50 - -//bitlength/ID for read cmd structure -#define HITAG_STOP 102 -#define HITAG_ON 101 -#define HITAG_OFF 100 -#define HITAG_SET 5 -#define HITAG_SELECT 5 -#define HITAG_CMD 4 -#define HITAG_BYTE 8 -#define HITAG_CRC 8 - -//carrier periods for read cmd structure -#define HITAG_LOW 7 -#define HITAG_STOP_HIGH 40 -#define HITAG_1_HIGH 21 -#define HITAG_0_HIGH 15 - -//bitlength for emulate reply cmd structure -#define HITAG_PAGE 32 -#define HITAG_STARTBIT 1 -#define HITAG_STARTBIT3 3 -#define HITAG_STARTBIT6 6 -#define HITAG_ACK 3 -#define HITAG_ACK_ADV 8 - -#define HITAG_BASEPERIOD 8 - -//carrier periods for read reply decoding -#define HITAG_DURATION_S 256 -#define HITAG_DURATION_M 384 -#define HITAG_DURATION_L 512 -#define HITAG_DURATION_ERROR_MARGIN 0.15 - -#define HITAG_BITPERIODS_AC 64 -#define HITAG_BITPERIODS_MC 32 - -//MISC -#define READ 1 -#define WRITE 2 - -#define CRC_PRESET 0xFF -#define CRC_POLYNOM 0x1D - -#define TAG "LFRFIDHitagWorker" - -// TIMER definitions -#define CARRIER_OUT_TIMER TIM1 -//#define CARRIER_OUT_TIMER_IRQ FuriHalInterruptIdTIM1 //TODO am i using this? -#define CARRIER_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH1 // or LL_TIM_CHANNEL_CH1N - -#define CARRIER_IN_TIMER TIM2 -#define CARRIER_IN_TIMER_IND_CH LL_TIM_CHANNEL_CH2 //no longer used via ETR setup -#define CARRIER_IN_TIMER_DIR_CH LL_TIM_CHANNEL_CH1 //no longer used via ETR setup - -#define CARRIER_IN_REFERENCE_TIMER TIM1 - -#define PULL_OUT_TIMER TIM2 -#define PULL_OUT_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 - -// DMA Channels definition -#define PULL_OUT_DMA DMA2 -#define PULL_OUT_DMA_CH1 LL_DMA_CHANNEL_1 -#define PULL_OUT_DMA_CH2 LL_DMA_CHANNEL_2 -#define PULL_OUT_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 -#define PULL_OUT_DMA_CH1_DEF PULL_OUT_DMA, PULL_OUT_DMA_CH1 -#define PULL_OUT_DMA_CH2_DEF PULL_OUT_DMA, PULL_OUT_DMA_CH2 - -typedef enum { - HitagProtocolAntiCollision, - HitagProtocolManchesterCoding, -} LfRfidHitagProtocol; - -typedef enum { - LFRFIDHitagWorkerSignalHalfTransfer, - LFRFIDHitagWorkerSignalTransferComplete, - LFRFIDHitagWorkerSignalStop, -} LFRFIDHitagWorkerSignal; - -typedef enum { - HitagStateIdle, - HitagStateSelected, - HitagStateQuiet, -} LfRfidHitagState; - -typedef enum { - HitagModeBasic, - HitagModeAdvanced, -} LfRfidHitagMode; - -typedef struct { - uint32_t timer_buffer_arr[DMA_BUFFER_SIZE]; - uint32_t timer_buffer_ccr[DMA_BUFFER_SIZE]; -} TimerDMAData; - -typedef struct { - uint32_t CMDvalue[HITAG_CMD_BUFFER_SIZE]; //this can likely be a uint16_t instead of uint32_t - uint8_t CMDtype[HITAG_CMD_BUFFER_SIZE]; - uint16_t CMDlength; - uint16_t CMDposition; - uint16_t CMDsubposition; - bool CMDloop; - bool CMDactive; - uint32_t CMDcount; -} LfRfidHitagCMDData; - -typedef struct { - uint32_t replyvalue[HITAG_CMD_BUFFER_SIZE]; //this can likely be a uint8_t instead of uint32_t - uint8_t replytype[HITAG_CMD_BUFFER_SIZE]; - uint16_t replylength; -} LfRfidHitagReplyData; - -typedef struct { - BufferStream* stream; - VarintPair* pair; - uint32_t capCounter; - uint32_t prevTIMval; -} LfRfidHitagCaptureData; - -typedef struct { - uint8_t pageData[HITAG_PAGES * HITAG_PAGEBYTES]; - uint8_t pageKnown[HITAG_PAGES]; - uint8_t tagData[HITAG_PAGES * HITAG_PAGEBYTES + HITAG_PAGES]; - uint8_t pageRW[HITAG_PAGES]; - uint8_t pagePublic[HITAG_PAGES]; -} LFRFIDHitag; - -// main worker -struct LFRFIDHitagWorker { - FuriThread* thread; - FuriEventFlag* events; - uint8_t DMAeventCount; - - uint8_t carrierPrescaler; - - LFRFIDHitag* tag; - - LFRFIDHitagStatus workerstatus; - - ProtocolDict* dict; -}; - -//------------------------------------------------------------------ forward function definitions ------------------------------------------------------------------ - -static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context); -static int32_t lfrfid_hitag_worker_read_thread(void* thread_context); - -static void lfrfid_hitag_worker_carrier_in_IC_mode(void* capture_context); - -//------------------------------------------------------------------ shared functions ------------------------------------------------------------------ - -LFRFIDHitagWorker* lfrfid_hitag_worker_alloc(ProtocolDict* dict) { - furi_assert(dict); - - LFRFIDHitagWorker* worker = malloc(sizeof(LFRFIDHitagWorker)); - worker->dict = dict; - - worker->thread = furi_thread_alloc_ex("LFRFIDHitagWorker", 2048, NULL, worker); - - worker->events = furi_event_flag_alloc(NULL); - worker->DMAeventCount = 0; - - worker->carrierPrescaler = 2; - - worker->workerstatus = LFRFIDHitagStatusScanning; - - return worker; -} - -void lfrfid_hitag_worker_free(LFRFIDHitagWorker* worker) { - furi_thread_free(worker->thread); - furi_event_flag_free(worker->events); - free(worker); -} - -LFRFIDHitagStatus lfrfid_hitag_worker_get_status(LFRFIDHitagWorker* worker) { - return worker->workerstatus; -} - -void lfrfid_hitag_worker_start(LFRFIDHitagWorker* worker, LFRFIDHitagWorkerSetting setting) { - if(furi_thread_get_state(worker->thread) == FuriThreadStateStopped) { - switch(setting) { - case(LFRFIDHitagWorkerSettingRead): - DOLPHIN_DEED(DolphinDeedRfidRead); - furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_read_thread); - break; - case(LFRFIDHitagWorkerSettingEmulate): - DOLPHIN_DEED(DolphinDeedRfidEmulate); - furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_emulate_thread); - break; - default: //safety meassure, not to start a thread without a callback function - furi_thread_set_callback(worker->thread, lfrfid_hitag_worker_read_thread); - break; - } - furi_thread_start(worker->thread); - } -} - -void lfrfid_hitag_worker_stop(LFRFIDHitagWorker* worker) { - furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalStop); - furi_thread_join(worker->thread); -} - -//------------------------------------------------------------------ READ TAG: capture input interrupt functions ------------------------------------------------------------------ - -static void lfrfid_hitag_worker_capture_in_cc_isr(bool level, uint32_t duration, void* context) { - LfRfidHitagCaptureData* capData = context; - - //check if there is a new pair available: pulse + period - bool need_to_send = varint_pair_pack(capData->pair, level, duration); - - //if so put it on buffer stream so it can be processed outside this interrupt routine - if(need_to_send) { - buffer_stream_send_from_isr( - capData->stream, - varint_pair_get_data(capData->pair), - varint_pair_get_size(capData->pair)); - varint_pair_reset(capData->pair); - } -} - -//------------------------------------------------------------------ READ TAG: carrier out TIMER, DMA & interrupts functions ------------------------------------------------------------------ - -static void lfrfid_hitag_worker_carrier_out_dma_isr(void* dma_context) { - LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)dma_context; - worker->DMAeventCount++; - - if(LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); - //halfway through DMA buffer --> first half can be repopulated - //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff - furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalHalfTransfer); - } - - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); - //fully through DMA buffer --> second half can be repopulated - //just signal that it can be repopulated, not execute the repopulating itself, since this is an interrupt, holding up all other stuff - furi_event_flag_set(worker->events, 1 << LFRFIDHitagWorkerSignalTransferComplete); - } -} - -void lfrfid_hitag_worker_carrier_out_start( - uint32_t* duration, - uint32_t* pulse, - size_t length, - void* context) { - // configure timer - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(CARRIER_OUT_TIMER); - FURI_CRITICAL_EXIT(); - - LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = SystemCoreClock / (125000 * HITAG_BASEPERIOD) - - 1; // sets the basis TIMER frequency to 8*freq (1MHz) for the timer - TIM_InitStruct.Autoreload = - HITAG_BASEPERIOD - - 1; //initial PWM period =125kHz (ARR will be handled via DMA further on) - LL_TIM_Init(CARRIER_OUT_TIMER, &TIM_InitStruct); - LL_TIM_DisableARRPreload(CARRIER_OUT_TIMER); - - LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; - TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; - TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE; - TIM_OC_InitStruct.CompareValue = - HITAG_BASEPERIOD / - 2; //initial pulse duration of half period ((CCR will be handled via DMA further on) - LL_TIM_OC_Init(CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); - - LL_TIM_OC_SetPolarity(CARRIER_OUT_TIMER, CARRIER_OUT_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); - LL_TIM_EnableDMAReq_UPDATE(CARRIER_OUT_TIMER); - - // configure DMA "mem -> ARR" channel - LL_DMA_InitTypeDef dma_config = {0}; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (CARRIER_OUT_TIMER->ARR); - dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = length; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - - // configure DMA "mem -> CCR1" channel - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (CARRIER_OUT_TIMER->CCR1); - dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = length; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - - // attach interrupt to one of DMA channels - furi_hal_interrupt_set_isr( - FuriHalInterruptIdDma1Ch1, lfrfid_hitag_worker_carrier_out_dma_isr, context); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); - - // start - LL_TIM_EnableAllOutputs(CARRIER_OUT_TIMER); - - LL_TIM_SetCounter(CARRIER_OUT_TIMER, 0); - LL_TIM_EnableCounter(CARRIER_OUT_TIMER); -} - -void lfrfid_hitag_worker_carrier_out_stop() { - LL_TIM_DisableCounter(CARRIER_OUT_TIMER); - LL_TIM_DisableAllOutputs(CARRIER_OUT_TIMER); - - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); - - FURI_CRITICAL_ENTER(); - - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); - LL_TIM_DeInit(CARRIER_OUT_TIMER); - - FURI_CRITICAL_EXIT(); -} - -//------------------------------------------------------------------ EMULATE TAG: pull out TIMER, DMA & interrupts functions ------------------------------------------------------------------ - -static void lfrfid_hitag_worker_pull_out_dma_stop(void* capture_context) { - //reconfigure pull_out pin to fixed low state - //via forced OC INACTIVE MODE - LL_TIM_OC_SetMode(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, LL_TIM_OCMODE_FORCED_INACTIVE); - - //disable DMA interrupts - //furi_hal_interrupt_set_isr(PULL_OUT_DMA_CH1_IRQ, NULL, NULL); - //LL_DMA_DisableIT_HT(PULL_OUT_DMA_CH1_DEF); - //LL_DMA_DisableIT_TC(PULL_OUT_DMA_CH1_DEF); - - //disable DMA channels & requests - LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER); - LL_DMA_DisableChannel(PULL_OUT_DMA_CH1_DEF); //need to disable when using normal mode? - LL_DMA_DisableChannel(PULL_OUT_DMA_CH2_DEF); //need to disable when using normal mode? - //LL_TIM_DisableAllOutputs(PULL_OUT_TIMER); //no need to disable when reconfiguring pin? - - //for logging both ccr & arr times during emulate (iso only arr) - LL_TIM_DisableIT_CC3(PULL_OUT_TIMER); - - //switch carrier detection back to input capture for stable readings (required for command detection) - lfrfid_hitag_worker_carrier_in_IC_mode(capture_context); -} - -static void lfrfid_hitag_worker_pull_out_dma_isr(void* capture_context) { - /* currently no HT interrupt enabled, only TC - if(LL_DMA_IsActiveFlag_HT1(PULL_OUT_DMA)) { - LL_DMA_ClearFlag_HT1(PULL_OUT_DMA); - //lfrfid_hitag_worker_pull_out_dma_stop(); - } - */ - - if(LL_DMA_IsActiveFlag_TC1(PULL_OUT_DMA)) { - LL_DMA_ClearFlag_TC1(PULL_OUT_DMA); - lfrfid_hitag_worker_pull_out_dma_stop(capture_context); - } -} - -static void lfrfid_hitag_worker_pull_out_dma_start(size_t length) { - //array pointers remain the same, but length changes so - //reset DMA length (only possible when DMA channel is disabled - //this also resets the DMA counter (the DMA length is the 'remaining counter' - LL_DMA_SetDataLength(PULL_OUT_DMA_CH1_DEF, length); - LL_DMA_SetDataLength(PULL_OUT_DMA_CH2_DEF, length); - - //enable DMA - LL_DMA_EnableChannel(PULL_OUT_DMA_CH1_DEF); - LL_DMA_EnableChannel(PULL_OUT_DMA_CH2_DEF); - - //set OC to PWM1 mode - LL_TIM_OC_SetMode( - PULL_OUT_TIMER, - PULL_OUT_TIMER_CHANNEL, - LL_TIM_OCMODE_PWM1); //during emulate (dma controlled) mode - - //only enable DMA requests on timer update after enabling both channels (otherwise you risk that first DMA request only triggers one of the channels) - LL_TIM_EnableDMAReq_UPDATE(PULL_OUT_TIMER); - - //for logging both ccr & arr times during emulate (iso only arr) - LL_TIM_EnableIT_CC3(PULL_OUT_TIMER); -} - -static void lfrfid_hitag_worker_pull_out_dma_setup( - uint32_t* duration, - uint32_t* pulse, - size_t length, - void* capture_context) { - //DMA setup - LL_TIM_DisableDMAReq_UPDATE(PULL_OUT_TIMER); //start with DMA requests disabled - - // configure DMA "mem -> ARR" channel - LL_DMA_InitTypeDef dma_config = {0}; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (PULL_OUT_TIMER->ARR); - dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array - dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = length; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(PULL_OUT_DMA_CH1_DEF, &dma_config); - - // configure DMA "mem -> CCR3" channel - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (PULL_OUT_TIMER->CCR3); - dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - //dma_config.Mode = LL_DMA_MODE_CIRCULAR; //keep cycling through memory array - dma_config.Mode = LL_DMA_MODE_NORMAL; //cycle only once through memory array - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = length; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(PULL_OUT_DMA_CH2_DEF, &dma_config); - - // enable DMA interrupts - furi_hal_interrupt_set_isr( - PULL_OUT_DMA_CH1_IRQ, lfrfid_hitag_worker_pull_out_dma_isr, capture_context); - //LL_DMA_EnableIT_HT(PULL_OUT_DMA_CH1_DEF); //let's try normal DMA mode and only transfer complete interrupt - LL_DMA_EnableIT_TC(PULL_OUT_DMA_CH1_DEF); -} - -//------------------------------------------------------------------ EMULATE TAG: carrier input TIMER & interrupt functions (switching input capture & ETR)------------------------------------------------------------------ - -static void lfrfid_hitag_worker_carrier_in_isr(void* capture_context) { - if(LL_TIM_IsActiveFlag_CC1(CARRIER_IN_TIMER)) { - uint32_t newTIMval = LL_TIM_IC_GetCaptureCH1(CARRIER_IN_TIMER); - LL_TIM_ClearFlag_CC1(CARRIER_IN_TIMER); - - //ARR for TIM2 in input capture mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both - //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values - //LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - - LfRfidHitagCaptureData* capData = capture_context; - capData->capCounter++; - //pack varint (as part of a pair) - //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint - varint_pair_pack(capData->pair, true, newTIMval - capData->prevTIMval); - //but send anyhow, doesn't matter that it's not a pair - buffer_stream_send_from_isr( - capData->stream, - varint_pair_get_data(capData->pair), - varint_pair_get_size(capData->pair)); - varint_pair_reset(capData->pair); - - capData->prevTIMval = newTIMval; - } else if(LL_TIM_IsActiveFlag_UPDATE(CARRIER_IN_TIMER)) { - uint32_t newTIMval = LL_TIM_GetCounter(CARRIER_IN_REFERENCE_TIMER); - LL_TIM_ClearFlag_UPDATE(CARRIER_IN_TIMER); - - //ARR for ref timer in ETR mode is UINT32_MAX so at freq of 1MHz this is 70+ minutes, instead of resetting counter, just store prev value and take difference of both - //this will increase measurement accuracy, since both values are then purely hardware controlled, no dependency of interrupt timing to (re)set any timer values - LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - LfRfidHitagCaptureData* capData = capture_context; - capData->capCounter++; - //pack varint (as part of a pair) - //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint - varint_pair_pack(capData->pair, true, newTIMval); // - capData->prevTIMval); - //but send anyhow, doesn't matter that it's not a pair - buffer_stream_send_from_isr( - capData->stream, - varint_pair_get_data(capData->pair), - varint_pair_get_size(capData->pair)); - varint_pair_reset(capData->pair); - - //capData->prevTIMval = newTIMval; - } else if(LL_TIM_IsActiveFlag_CC3(CARRIER_IN_TIMER)) { - uint32_t newTIMval = LL_TIM_GetCounter(CARRIER_IN_REFERENCE_TIMER); - LL_TIM_ClearFlag_CC3(CARRIER_IN_TIMER); - - //don't reset timer for the ccr (HIGH) timing, this way the arr (duration) timing is still full duraton timing (iso only LOW timing) - //LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - LfRfidHitagCaptureData* capData = capture_context; - capData->capCounter++; - //pack varint (as part of a pair) - //cleaner would be to pack as a varint iso varint_pair, though i'm not sure if I can get the data & size of a packed varint - varint_pair_pack(capData->pair, true, newTIMval); // - capData->prevTIMval); - //but send anyhow, doesn't matter that it's not a pair - buffer_stream_send_from_isr( - capData->stream, - varint_pair_get_data(capData->pair), - varint_pair_get_size(capData->pair)); - varint_pair_reset(capData->pair); - - //capData->prevTIMval = newTIMval; - } -} - -static void lfrfid_hitag_worker_carrier_in_ETR_mode(void* capture_context, uint8_t ext_prescaler) { - //disable counters temporarily - LL_TIM_DisableCounter(CARRIER_IN_TIMER); - LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); - - //disable Input capture & related interrupt - LL_TIM_DisableIT_CC1(CARRIER_IN_TIMER); - LL_TIM_CC_DisableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); - - //reset capdata prev value - LfRfidHitagCaptureData* capData = capture_context; - capData->prevTIMval = 0; - - //switch clocksource to ETR with external prescaling via ARR - LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 1 - 1); //prescaler is only applied at next update event - LL_TIM_GenerateEvent_UPDATE( - CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry - LL_TIM_SetAutoReload(CARRIER_IN_TIMER, ext_prescaler - 1); - LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); - - //reconfigure carrier_in pin to TIM2 ETR - furi_hal_gpio_init_ex( - &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); - - //reset timer counter & capture context for period calculation - LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - //enable interrupt via update - LL_TIM_EnableIT_UPDATE(CARRIER_IN_TIMER); - - //re-enable counters - LL_TIM_EnableCounter(CARRIER_IN_TIMER); - LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); -} - -static void lfrfid_hitag_worker_carrier_in_IC_mode(void* capture_context) { - //disable counters temporarily - LL_TIM_DisableCounter(CARRIER_IN_TIMER); - LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); - - //disable interrupt via update - LL_TIM_DisableIT_UPDATE(CARRIER_IN_TIMER); - - //reset capdata prev value - LfRfidHitagCaptureData* capData = capture_context; - capData->prevTIMval = 0; - - //switch clocksource to system/64, external prescaling is handled in IC prescaler - LL_TIM_SetClockSource(CARRIER_IN_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_SetPrescaler(CARRIER_IN_TIMER, 64 - 1); //prescaler is only applied at next update event - LL_TIM_GenerateEvent_UPDATE( - CARRIER_IN_TIMER); //prescaler is only applied at next update event //TODO: how to prevent this from creating a capdata entry - LL_TIM_SetAutoReload(CARRIER_IN_TIMER, UINT32_MAX); - - //reconfigure carrier_in pin to TIM2 CH1 for input capture - furi_hal_gpio_init_ex( - &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); - - //reset timer counter & capture context for period calculation - LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - - //enable Input capture & related interrupt - LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); - LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); - - //re-enable counters - LL_TIM_EnableCounter(CARRIER_IN_TIMER); - LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); -} - -static void lfrfid_hitag_worker_carrier_in_start( - void* capture_context, - uint8_t ext_prescaler, - uint32_t* duration, - uint32_t* pulse, - size_t length) { - FURI_CRITICAL_ENTER(); - LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); //required? - LL_DMA_DeInit(PULL_OUT_DMA_CH2_DEF); //required? - LL_TIM_DeInit(CARRIER_IN_TIMER); - LL_TIM_DeInit(CARRIER_IN_REFERENCE_TIMER); - FURI_CRITICAL_EXIT(); - - //setup reference timer: simple setup with base freq of 1MHz and max autoreload - LL_TIM_InitTypeDef TIM_InitStruct_Ref = {0}; - TIM_InitStruct_Ref.Prescaler = - 64 - - 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) - TIM_InitStruct_Ref.CounterMode = LL_TIM_COUNTERMODE_UP; - TIM_InitStruct_Ref.Autoreload = UINT32_MAX; - TIM_InitStruct_Ref.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; - LL_TIM_Init(CARRIER_IN_REFERENCE_TIMER, &TIM_InitStruct_Ref); - - //setup carrier in timer for input capture - LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = - 64 - - 1; //system base freq is 64MHz, so this sets base freq for TIM to 1MHz (aka 1us period) - TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; - TIM_InitStruct.Autoreload = UINT32_MAX; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; - LL_TIM_Init(CARRIER_IN_TIMER, &TIM_InitStruct); - - LL_TIM_DisableARRPreload(CARRIER_IN_TIMER); - LL_TIM_SetClockSource( - CARRIER_IN_TIMER, - LL_TIM_CLOCKSOURCE_INTERNAL); //default is internal, so likely not required - LL_TIM_DisableDMAReq_TRIG(CARRIER_IN_TIMER); - LL_TIM_DisableIT_TRIG(CARRIER_IN_TIMER); - - //meanwhile already prepre the ETR - LL_TIM_ConfigETR( - CARRIER_IN_TIMER, - LL_TIM_ETR_POLARITY_INVERTED, - LL_TIM_ETR_PRESCALER_DIV1, - LL_TIM_ETR_FILTER_FDIV1); - - //INPUT CAPTURE SETUP - // Timer: channel 1 direct (from GPIO) - LL_TIM_IC_SetActiveInput( - CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI); - //prescaling direct channel seems to be working fine (and is necessary since otherwise sd write cannot keep up) - if(ext_prescaler == 4) { - LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV4); - } else if(ext_prescaler == 2) { - LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV2); - } else { - LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_ICPSC_DIV1); - } - LL_TIM_IC_SetPolarity(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetFilter(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH, LL_TIM_IC_FILTER_FDIV1); - /* Timer: channel 2 indirect (from channel 1) - // only measure & write to file the period (not the on & off cycle), this reduced CPU load on SD write to (hopefully) keep up with period measurements without prescaler - LL_TIM_IC_SetActiveInput(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_ACTIVEINPUT_INDIRECTTI); - //presaling indirect channel doesn't really work well yet :/ - LL_TIM_IC_SetPrescaler(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_ICPSC_DIV2); - LL_TIM_IC_SetPolarity(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_IC_POLARITY_FALLING); - LL_TIM_IC_SetFilter(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH, LL_TIM_IC_FILTER_FDIV1); - */ - - //set interrupt callback for capturing period - LL_TIM_EnableIT_CC1(CARRIER_IN_TIMER); - //LL_TIM_EnableIT_CC2(CARRIER_IN_TIMER); - furi_hal_interrupt_set_isr( - FuriHalInterruptIdTIM2, lfrfid_hitag_worker_carrier_in_isr, capture_context); - - //OUTPUT COMPARE SETUP - LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; - //TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; //during emulate (dma controlled) mode - TIM_OC_InitStruct.OCMode = - LL_TIM_OCMODE_FORCED_INACTIVE; //during carrier in put output to forced low state - TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; - TIM_OC_InitStruct.CompareValue = - 0; //0% this should have almost similar effect as keeping output forced inactive (there's still some micropulse emited, going high at ARR and immediately down again at CCR value) - LL_TIM_OC_Init(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL, &TIM_OC_InitStruct); - LL_TIM_OC_SetPolarity( - PULL_OUT_TIMER, - PULL_OUT_TIMER_CHANNEL, - LL_TIM_OCPOLARITY_HIGH); //active high (gpio goes high when pulse is high) - - //INIT DMA (do not start it yet) - lfrfid_hitag_worker_pull_out_dma_setup(duration, pulse, length, capture_context); - - LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_DIR_CH); - //LL_TIM_CC_EnableChannel(CARRIER_IN_TIMER, CARRIER_IN_TIMER_IND_CH); - LL_TIM_CC_EnableChannel(PULL_OUT_TIMER, PULL_OUT_TIMER_CHANNEL); - LL_TIM_SetCounter(CARRIER_IN_TIMER, 0); - LL_TIM_SetCounter(CARRIER_IN_REFERENCE_TIMER, 0); - LL_TIM_EnableCounter(CARRIER_IN_TIMER); - LL_TIM_EnableCounter(CARRIER_IN_REFERENCE_TIMER); -} - -void lfrfid_hitag_worker_carrier_in_stop() { - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); - - LL_TIM_DisableCounter(CARRIER_IN_TIMER); - LL_TIM_DisableCounter(CARRIER_IN_REFERENCE_TIMER); - LL_TIM_DisableAllOutputs(CARRIER_IN_TIMER); //used for pull pin OC - - FURI_CRITICAL_ENTER(); - LL_DMA_DeInit(PULL_OUT_DMA_CH1_DEF); - LL_DMA_DeInit(PULL_OUT_DMA_CH2_DEF); - LL_TIM_DeInit(CARRIER_IN_TIMER); - LL_TIM_DeInit(CARRIER_IN_REFERENCE_TIMER); - FURI_CRITICAL_EXIT(); -} - -//------------------------------------------------------------------ hitag data parsing functions ------------------------------------------------------------------ - -static void lfrfid_hitag_worker_yield_dma_buffer_BPLM( - LfRfidHitagCMDData* dataCMD, - LfRfidHitagCMDData* backupCMD, - TimerDMAData* dataDMA, - size_t start, - size_t len) { - //based on interal timer with freq of 1MHz (period of 1us) - //use active command - LfRfidHitagCMDData* data = dataCMD; - if(!data->CMDactive) { - data = backupCMD; - } - uint16_t lastarrposition = 0; - size_t toskip = 0; - bool itemProcessed = true; - size_t s = start; - //for ( ; data->CMDloop || data->CMDposition < data->CMDlength ; data->CMDposition++){ - while(1) { - //at the end of current command (real or backup), check if there's unprocessed CMD available, if not switch to backup - if(data->CMDposition == data->CMDlength) { - data->CMDcount++; - if(dataCMD->CMDposition < - dataCMD - ->CMDlength) { //currently backup & new unprocessed command available --> switch over to real CMD - data->CMDactive = false; - data = dataCMD; - data->CMDactive = true; - data->CMDcount = 0; - } else if(dataCMD - ->CMDloop) { //if looping (real or backup) --> reset real (current) CMD to startposition - data->CMDposition = 0; - data->CMDsubposition = 0; - } else if(dataCMD - ->CMDactive) { //real command and not looping --> switch to backup CMD and reset backup CMD to startposition - data->CMDactive = false; - data = backupCMD; - data->CMDactive = true; - data->CMDposition = 0; - data->CMDsubposition = 0; - data->CMDcount = 0; - } else { //backupCMD but set to not loop --> loop anyway: reset to startposition - data->CMDposition = 0; - data->CMDsubposition = 0; - } - } - //start processing the next CMD item - lastarrposition = s; - if(s == start) { - toskip = data->CMDsubposition; - } else { - toskip = 0; - data->CMDsubposition = 0; - } - switch(data->CMDtype[data->CMDposition]) { - case HITAG_STOP: //just 1 STOP cycle (HITAG_LOW lows + HITAG_STOP_HIGH highs) - if(s > start) { - dataDMA->timer_buffer_arr[s - 1] += HITAG_LOW * HITAG_BASEPERIOD; - } - for(size_t t = 0; t < HITAG_STOP_HIGH - toskip; t++) { - if(s == len + start) { - //arrays are filled, do not continue out - itemProcessed = false; - break; - } - dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; - dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; - s++; - } - break; - case HITAG_ON: //x ON cycles --> update next X arr/ccr values - for(size_t x = 0; x < data->CMDvalue[data->CMDposition] - toskip; x++) { - if(s == len + start) { - //arrays are filled, do not continue out - itemProcessed = false; - break; - } - dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; - dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; - s++; - } - break; - case HITAG_OFF: //x OFF cycles (update previous arr value) - if(s > start) { - dataDMA->timer_buffer_arr[s - 1] += - data->CMDvalue[data->CMDposition] * HITAG_BASEPERIOD; - } - break; - default: // <=HITAG_BYTE: //bits to be sent - //get each bit & update arr/ccr accordingly (7 lows + HITAG_0_HIGH/HITAG_1_HIGH highs) - for(size_t b = 8 - data->CMDtype[data->CMDposition]; b < 8; b++) { - if(s > start) { - dataDMA->timer_buffer_arr[s - 1] += HITAG_LOW * HITAG_BASEPERIOD; - } - if(0x80 & (data->CMDvalue[data->CMDposition] << b)) { - for(size_t t = 0; t < HITAG_1_HIGH; t++) { - if(s == len + start) { - //arrays are filled, do not continue out - itemProcessed = false; - break; - } - dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; - dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; - s++; - if(toskip > 0) { - toskip--; - s--; - } - } - } else { - for(size_t t = 0; t < HITAG_0_HIGH; t++) { - if(s == len + start) { - //arrays are filled, do not continue out - itemProcessed = false; - break; - } - dataDMA->timer_buffer_ccr[s] = HITAG_BASEPERIOD / 2; - dataDMA->timer_buffer_arr[s] = HITAG_BASEPERIOD - 1; - s++; - if(toskip > 0) { - toskip--; - s--; - } - } - } - if(!itemProcessed) { - break; - } - } - break; - } - if(!itemProcessed) { - break; - } - data->CMDposition++; - } - - /*if buffer is not full, but command is fully processed: fill remainder of buffer with normal on cycles - if (stimer_buffer_ccr[s]=HITAG_BASEPERIOD/4; //25%duty cycle - dataDMA->timer_buffer_arr[s]=1*HITAG_BASEPERIOD-1; - } else { - dataDMA->timer_buffer_ccr[s]=HITAG_BASEPERIOD/2; - dataDMA->timer_buffer_arr[s]=HITAG_BASEPERIOD-1; - } - } - //other stuff to do? - } - */ - - //if buffer was full, but command not yet fully processed: set starting position for next time: - if(!itemProcessed) { - data->CMDsubposition += (len + start) - lastarrposition; - } else { - data->CMDsubposition = 0; - } -} - -static uint16_t lfrfid_hitag_worker_yield_dma_buffer_MC( - LfRfidHitagReplyData* dataReply, - TimerDMAData* dataDMA, - uint8_t carrierPrescaler) { - //based on external carrier with freq of 125kHz (period of 8us) - - uint16_t dma_len = 0; - - bool bit = false; - bool prevBit = true; //must be true to initialze MC yield for first bit - - //process reply bits and add HIGH-LOW for 1 or LOW-HIGH for 0 - for(uint8_t i = 0; i < dataReply->replylength; i++) { - for(int8_t b = dataReply->replytype[i] - 1; b >= 0; b--) { - bit = (dataReply->replyvalue[i] >> b & 0b00000001); - if(bit) { //HIGH-LOW - if(prevBit) { //if first bit (initial value of prevBit=true) or if prev bit was 1: start of new default cycle - dataDMA->timer_buffer_arr[dma_len] = 32 - 1; - dataDMA->timer_buffer_ccr[dma_len] = 16; - dma_len++; - } else { //previous bit was 0, so extend the ccr and arr with 16 - dataDMA->timer_buffer_arr[dma_len - 1] += 16; - dataDMA->timer_buffer_ccr[dma_len - 1] += 16; - } - } else { //LOW-HIGH - if(prevBit) { //previous bit was 1 so extend the LOW (arr only) of prev cycle with 16 and add new default cycle - dataDMA->timer_buffer_arr[dma_len - 1] += 16; - - dataDMA->timer_buffer_arr[dma_len] = 32 - 1; - dataDMA->timer_buffer_ccr[dma_len] = 16; - dma_len++; - } else { //previous bit was 0 so just add new default cycle - dataDMA->timer_buffer_arr[dma_len] = 32 - 1; - dataDMA->timer_buffer_ccr[dma_len] = 16; - dma_len++; - } - } - prevBit = bit; - } - } - - //add extra period, to reset timer back to normal carrier_in detection after reply has finished - //also required to make sure the last real dma values are fully executed before dma signals transfer complete, which resets worker into command detection - dataDMA->timer_buffer_arr[dma_len] = carrierPrescaler - 1; - dataDMA->timer_buffer_ccr[dma_len] = 0; - dma_len++; - - return dma_len; -} - -static uint16_t lfrfid_hitag_worker_yield_dma_buffer_AC( - LfRfidHitagReplyData* dataReply, - TimerDMAData* dataDMA, - uint8_t carrierPrescaler) { - //based on external carrier with freq of 125kHz (period of 8us) - - uint16_t dma_len = 0; - - //process reply bits and add 2 short pulses for 1 or 1 long pulse for 0 - for(uint8_t i = 0; i < dataReply->replylength; i++) { - for(int8_t b = dataReply->replytype[i] - 1; b >= 0; b--) { - if(dataReply->replyvalue[i] >> b & 0b00000001) { - dataDMA->timer_buffer_arr[dma_len] = 32 - 1; - dataDMA->timer_buffer_ccr[dma_len] = 16; //16 - dma_len++; - dataDMA->timer_buffer_arr[dma_len] = 32 - 1; - dataDMA->timer_buffer_ccr[dma_len] = 16; //16 - dma_len++; - } else { - dataDMA->timer_buffer_arr[dma_len] = 64 - 1; - dataDMA->timer_buffer_ccr[dma_len] = 32; //32 - dma_len++; - } - } - } - - //add extra period, to reset timer back to normal carrier_in detection after reply has finished - //also required to make sure the last real dma values are fully executed before dma signals transfer complete, which resets worker into command detection - dataDMA->timer_buffer_arr[dma_len] = carrierPrescaler - 1; - dataDMA->timer_buffer_ccr[dma_len] = 0; - dma_len++; - - return dma_len; -} - -static void lfrfid_hitag_worker_decoder_feed( - uint32_t pulse, - uint32_t duration, - uint32_t* memBlock, - uint8_t* bits, - bool* half, - bool* finished, - LfRfidHitagProtocol protocol, - uint8_t startBits) { - uint32_t bit = 1 << (32 - 1); - if(*bits == 0) { - *half = false; - } - if(protocol == HitagProtocolManchesterCoding) { - if(HITAG_DURATION_S * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && - duration <= HITAG_DURATION_S * (1 + HITAG_DURATION_ERROR_MARGIN)) { - //short period (HL) detected: same bit as previous - if(*bits < startBits) { - //don't store startbits in memory, just increase bitcounter - (*bits)++; - } else if(*bits == startBits) { - //same as previous bit (last startbit it 1, so add 1 to the memBlock) - memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 - (*bits)++; - } else { - //add same as previous bit - if(memBlock[(*bits - startBits - 1) / 32] & - (bit >> (*bits - startBits - 1) % 32)) { - memBlock[(*bits - startBits) / 32] |= - (bit >> (*bits - startBits) % 32); //add 1 - (*bits)++; - } else { - (*bits)++; //no mem action for 0, just increase bitcounter - } - } - } else if( - HITAG_DURATION_M * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && - duration <= HITAG_DURATION_M * (1 + HITAG_DURATION_ERROR_MARGIN)) { - //medium period (H|HL or HL|L) detected: half 0 + full 1 or full 1 + half 0 detected - if(*bits < startBits) { - //assuming all startbits are 1's, this can only happen on last startbit followed by 0 - //don't store startbits, no mem action for 0, just increase bitcounter twice - (*bits)++; - (*bits)++; - *half = true; - } else if(*bits == startBits) { - //assuming all startbits were 1's, this is a data 10 detection - memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 - (*bits)++; - (*bits)++; //no mem action for 0, just increase bitcounter - *half = true; - } else { - //if we're in half of a bit, add 1, else add 10 and set half flag - if(*half) { - memBlock[(*bits - startBits) / 32] |= - (bit >> (*bits - startBits) % 32); //add 1 - (*bits)++; - *half = false; - } else { - memBlock[(*bits - startBits) / 32] |= - (bit >> (*bits - startBits) % 32); //add 1 - (*bits)++; - (*bits)++; //no mem action for 0, just increase bitcounter - *half = true; - } - } - } else if( - HITAG_DURATION_L * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && - duration <= HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN)) { - //long period (H|HL|L) detected: half 0 + full 1 + half 0 detected - if(*bits <= startBits) { - //TODO: throw error since this can't happen till after first real bit, since all startbits should be 1's - } else { - //TODO check that prev bit is indeed 0 - //add 10 to the memBlock - memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); //add 1 - (*bits)++; - (*bits)++; //no mem action for 0, just increase bitcounter - } - } else if(HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN) < duration) { - //end of modulation by tag, check if this pulse duration includes a bit - if(*bits > 0) { - if(*half && pulse < HITAG_DURATION_S) { - //second half of 0 bit, no action - } else if(*half && pulse < HITAG_DURATION_M) { - //second half or 0 bit + another 1 bit - memBlock[(*bits - startBits) / 32] |= - (bit >> (*bits - startBits) % 32); //add 1 - (*bits)++; - } else if(!(*half) && pulse < HITAG_DURATION_S) { - //another 1 bit - memBlock[(*bits - startBits) / 32] |= - (bit >> (*bits - startBits) % 32); //add 1 - (*bits)++; - } else { - //invalid pulse duration --> reset? - } - *finished = true; - } - } else { - //invalid pulse duration: reset tempData if necessary - if(*bits > 0) { - memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); - *bits = 0; - *finished = false; - } - } - } else if(protocol == HitagProtocolAntiCollision) { - if(HITAG_DURATION_S * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && - duration <= HITAG_DURATION_S * (1 + HITAG_DURATION_ERROR_MARGIN)) { - //short period (HL): first or second half of a 1 detected - //if first short: add 1 to memBlock and increase half counter - if(*half == false) { - if(*bits >= startBits) { - memBlock[(*bits - startBits) / 32] |= (bit >> (*bits - startBits) % 32); - } - (*bits)++; - *half = true; - } else { - //if second small: reset half counter - *half = false; - } - } else if( - HITAG_DURATION_L * (1 - HITAG_DURATION_ERROR_MARGIN) <= duration && - duration <= HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN)) { - //long period (HHLL): 0 detected - //should only happen after full detection of 1 or after startBits - if(*bits < startBits || *half) { - //reset - memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); - *bits = 0; - *finished = false; - } else { - (*bits)++; //add 0 to memBlock - } - } else if(HITAG_DURATION_L * (1 + HITAG_DURATION_ERROR_MARGIN) < duration) { - //end of modulation by tag, check if this pulse duration includes a bit - if(*bits > 0) { - if(!(*half) && pulse > HITAG_DURATION_S) { - (*bits)++; //add 0 to memBlock - } else { - //invalid pulse duration: reset? - } - *finished = true; - } - } else { - //invalid pulse duration: reset tempData if necessary - if(*bits > 0) { - memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); - *bits = 0; - *finished = false; - } - } - } -} - -static void lfrfid_hitag_worker_calc_crc(uint8_t* crc, uint8_t data, uint8_t bitcount) { - *crc ^= data; // crc = crc (exor) data - do { - if(*crc & 0x80) // if (MSB-CRC == 1) - { - *crc <<= 1; // CRC = CRC bit-shift left - *crc ^= CRC_POLYNOM; // CRC = CRC (exor) CRC_POLYNOM - } else { - *crc <<= 1; // CRC = CRC bit-shift left - } - } while(--bitcount); -} - -static bool lfrfid_hitag_worker_validate_command(uint32_t* bits, uint8_t len) { - if(len == 5 && (bits[0] >> (32 - 5)) == 0b11001) { - //SET_CCNEW - return true; - } else if(len == 5 && (bits[0] >> (32 - 5)) == 0b00110) { - //SET_CC - return true; - } else if(len == 45 && (bits[0] >> (32 - 5)) == 0b00000) { - //SELECT - - //calculate CRC for first 37 bits (4bytes + 5 bits) - uint8_t byte1 = bits[0] >> 24 & 0xff; - uint8_t byte2 = bits[0] >> 16 & 0xff; - uint8_t byte3 = bits[0] >> 8 & 0xff; - uint8_t byte4 = bits[0] & 0xff; - uint8_t byte5 = bits[1] >> 24 & 0b11111000; - uint8_t crcRead = bits[1] >> 19 & 0xff; - - uint8_t crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc(&crc, byte1, 8); - lfrfid_hitag_worker_calc_crc(&crc, byte2, 8); - lfrfid_hitag_worker_calc_crc(&crc, byte3, 8); - lfrfid_hitag_worker_calc_crc(&crc, byte4, 8); - lfrfid_hitag_worker_calc_crc(&crc, byte5, 5); - - //and compare result with received crc - return (crcRead == crc); - } - //else if (len == 20 && (bits[0]>>31 || (bits[0]>>(32-4)) == 0b0111)){ - else if(len == 20 && bits[0] > 0x70000000) { - //R/W page/block (cmd starting with 1) or HALT (0111) --> so bit0 should be >= 0b01110000 00000000 00000000 00000000 (0x70000000) - //calculate CRC for first 12 bits (1byte + 4 bits) - uint8_t byte1 = bits[0] >> 24 & 0xff; - uint8_t byte2 = bits[0] >> 16 & 0b11110000; - uint8_t crcRead = bits[0] >> 12 & 0xff; - - uint8_t crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc(&crc, byte1, 8); - lfrfid_hitag_worker_calc_crc(&crc, byte2, 4); - - //and compare result with received crc - return (crcRead == crc); - } - return false; -} - -static bool lfrfid_hitag_worker_validate_bits(uint32_t* bits, uint8_t bitcounter) { - if(47 >= bitcounter && bitcounter >= 45) { - for(uint8_t i = 0; i < bitcounter - 44; i++) { - if(i > 0) { - bits[0] = bits[0] << 1 | bits[1] >> (32 - 1); - bits[1] = bits[1] << 1; - } - if(lfrfid_hitag_worker_validate_command(bits, 45)) return true; - } - } else if(22 >= bitcounter && bitcounter >= 20) { - for(uint8_t i = 0; i < bitcounter - 19; i++) { - if(i > 0) { - bits[0] = bits[0] << 1; // | bits[1]>>(32-1); - //bits[1] = bits[1]<<1; - } - if(lfrfid_hitag_worker_validate_command(bits, 20)) return true; - } - } else if(7 >= bitcounter && bitcounter >= 5) { - for(uint8_t i = 0; i < bitcounter - 4; i++) { - if(i > 0) { - bits[0] = bits[0] << 1; // | bits[1]>>(32-1); - //bits[1] = bits[1]<<1; - } - if(lfrfid_hitag_worker_validate_command(bits, 5)) return true; - } - } - return false; -} - -static uint8_t lfrfid_hitag_worker_validate_ripple_delta(uint32_t delta) { - if(18 <= delta && delta <= 22) return 1; - if(26 <= delta && delta <= 32) return 1; - return 0; -} - -void lfrfid_hitag_worker_initialize_tag_data(LFRFIDHitag* tag) { - //initialize a tag datastructure - //set all pages to 0x00000000 - memset(tag->pageData, 0, sizeof(tag->pageData)); - - //set all pages to unknown - for(uint8_t i = 0; i < HITAG_PAGES; i++) { - tag->pageKnown[i] = 0; - } - - //default RW settings: - memset(tag->pageRW, READ, HITAG_PAGES / 2 * sizeof(tag->pageRW[0])); - memset(tag->pageRW + HITAG_PAGES / 2, READ | WRITE, HITAG_PAGES / 2 * sizeof(tag->pageRW[0])); - tag->pageRW[2] = 0; - tag->pageRW[3] = 0; - - //default public/secret settings: - for(uint8_t i = 0; i < HITAG_PAGES; i++) { - tag->pagePublic[i] = 1; - } - for(uint8_t i = 2; i <= 31; i++) { - tag->pagePublic[i] = 0; - } -} - -static void lfrfid_hitag_worker_combine_tag_data(LFRFIDHitag* tag) { - memcpy(tag->tagData, tag->pageData, HITAG_PAGES * HITAG_PAGEBYTES); - memcpy(tag->tagData + HITAG_PAGES * HITAG_PAGEBYTES, tag->pageKnown, HITAG_PAGES); -} - -static void lfrfid_hitag_worker_split_tag_data(LFRFIDHitag* tag) { - memcpy(tag->pageData, tag->tagData, HITAG_PAGES * HITAG_PAGEBYTES); - memcpy(tag->pageKnown, tag->tagData + HITAG_PAGES * HITAG_PAGEBYTES, HITAG_PAGES); -} - -void lfrfid_hitag_worker_process_config_page(LFRFIDHitag* tag) { - //RW settings via byte 0 - if(tag->pageData[4 + 0] & 0b10000000) { //block 1 (logData) RW or 0 - memset(tag->pageRW + 1 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW + 1 * 4, 0, 4 * sizeof(tag->pageRW[0])); - } - if(tag->pageData[4 + 0] & 0b01000000) { //key A&B (page 2&3) W or 0 - memset(tag->pageRW + 2, WRITE, 2 * sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW + 2, 0, 2 * sizeof(tag->pageRW[0])); - } - if(tag->pageData[4 + 0] & 0b00100000) { //block 2 RW or RO - memset(tag->pageRW + 2 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW + 2 * 4, READ, 4 * sizeof(tag->pageRW[0])); - } - if(tag->pageData[4 + 0] & 0b00010000) { //block 3 RW or RO - memset(tag->pageRW + 3 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW + 3 * 4, READ, 4 * sizeof(tag->pageRW[0])); - } - if(tag->pageData[4 + 0] & 0b00001000) { //block 4 RW or RO - memset(tag->pageRW + 4 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW + 4 * 4, READ, 4 * sizeof(tag->pageRW[0])); - } - if(tag->pageData[4 + 0] & 0b00000100) { //block 5 RW or RO - memset(tag->pageRW + 5 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW + 5 * 4, READ, 4 * sizeof(tag->pageRW[0])); - } - if(tag->pageData[4 + 0] & 0b00000010) { //block 6 RW or RO - memset(tag->pageRW + 6 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW + 6 * 4, READ, 4 * sizeof(tag->pageRW[0])); - } - if(tag->pageData[4 + 0] & 0b00000001) { //block 7 RW or RO - memset(tag->pageRW + 7 * 4, READ | WRITE, 4 * sizeof(tag->pageRW[0])); - } else { - memset(tag->pageRW + 7 * 4, READ, 4 * sizeof(tag->pageRW[0])); - } - //RW settings via byte 1 - if(tag->pageData[4 + 1] & 0b00010000) { //config page (1) RW or RO - tag->pageRW[1] = READ / WRITE; - } else { - tag->pageRW[1] = READ; - } - - //public/secret settings via byte 1 - if(tag->pageData[4 + 1] & 0b00000001) { //block 4-7 public or secret - for(uint8_t i = 4 * 4; i < 8 * 4; i++) { - tag->pagePublic[i] = 1; - } - } else { - for(uint8_t i = 4 * 4; i < 8 * 4; i++) { - tag->pagePublic[i] = 0; - } - } -} - -//------------------------------------------------------------------ worker threads ------------------------------------------------------------------ - -static int32_t lfrfid_hitag_worker_emulate_thread(void* thread_context) { - LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)thread_context; - - //load tag - worker->tag = malloc(sizeof(LFRFIDHitag)); - lfrfid_hitag_worker_initialize_tag_data(worker->tag); - - size_t data_size = protocol_dict_get_data_size(worker->dict, LFRFIDProtocolHitag1); - protocol_dict_get_data(worker->dict, LFRFIDProtocolHitag1, worker->tag->tagData, data_size); - - lfrfid_hitag_worker_split_tag_data(worker->tag); - //TODO: check if a tag has been properly loaded with known serial nr & config page - lfrfid_hitag_worker_process_config_page(worker->tag); - - //init capData for carrier_in - LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); - capData->stream = buffer_stream_alloc(EMULATE_BUFFER_SIZE, EMULATE_BUFFER_COUNT); - capData->pair = varint_pair_alloc(); - capData->capCounter = 0; - capData->prevTIMval = 0; - - //init dmaData for emulate mode: - uint16_t dmaLen = 0; - TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); - - LfRfidHitagReplyData* dataReply = malloc(sizeof(LfRfidHitagReplyData)); - - //set pins to read setup - furi_hal_rfid_pins_read(); - //reconfigure carrier_out to fixed low state instead - furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_rfid_carrier_out, false); - //reconfigure PA15 carrier_in to alt ftn 1 (TIM2 CH1) to use it for input capture - furi_hal_gpio_init_ex( - &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); - //reconfigure pull to alternate function 1 (TIM2 CH3) to drive low (for read carrier) or pull antenna via DMA (for emulation) - furi_hal_gpio_init_ex( - &gpio_nfc_irq_rfid_pull, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedLow, - GpioAltFn1TIM2); - - //start capture via carrier in instead of rfid in - lfrfid_hitag_worker_carrier_in_start( - capData, - worker->carrierPrescaler, - dataDMA->timer_buffer_arr, - dataDMA->timer_buffer_ccr, - dmaLen); - - uint8_t prescaler = worker->carrierPrescaler; - uint8_t period = 8 * prescaler - 1; - - bool carrier_on = false; - int8_t carrier_count = 0; - uint8_t carrier_threshold_on = 5; - int8_t carrier_threshold_off = -5; - - uint32_t periodcounter = 0; - - uint8_t localMax = 0; - uint8_t localMin = 255; - uint32_t localMaxIndex = 0; - uint32_t localMinIndex = 0; - uint32_t lastMaxIndex = 0; - uint32_t lastMinIndex = 0; - uint8_t maxBitCounter = 0; - uint8_t minBitCounter = 0; - uint32_t maxBits[5]; - uint32_t minBits[5]; - int8_t cmd = 0; - uint32_t one = 1 << (32 - 1); - uint32_t cmdIndex = 0; - - uint32_t miniMaxDelta = 0; - uint32_t miniMaxIndex = 0; - uint32_t prevMaxDelta = 0; - uint32_t maxDelta = 0; - - uint32_t miniMinDelta = 0; - uint32_t miniMinIndex = 0; - uint32_t prevMinDelta = 0; - uint32_t minDelta = 0; - - uint32_t prevduration = 0; - uint32_t duration = 0; - uint8_t durationcounter = 0; - uint8_t durationthreshold = 5; - - uint8_t tagState = HitagStateIdle; - uint8_t tagMode = HitagModeBasic; - - while(1) { - Buffer* buffer = buffer_stream_receive(capData->stream, 1); - - if(buffer != NULL) { - size_t buffsize = buffer_get_size(buffer); - uint8_t* buffdata = buffer_get_data(buffer); - - //parse carrier_in duration & scan for cmds - size_t index = 0; - while(index < buffsize) { - prevduration = duration; - size_t tmp_size = - varint_uint32_unpack(&duration, &buffdata[index], buffsize - index); - - if(tmp_size == 0) { - FURI_LOG_E(TAG, "can't unpack varint pair"); - break; - } else { - index += tmp_size; - - periodcounter++; - - if(!carrier_on) { //detect if carrier is available - if(0.75 * period <= duration && duration <= 1.25 * period) { - carrier_count++; - if(carrier_count >= carrier_threshold_on) { - carrier_on = true; - localMax = 0; - localMin = 255; - minBitCounter = 0; - maxBitCounter = 0; - minBits[0] = 0; - minBits[1] = 0; - maxBits[0] = 0; - maxBits[1] = 0; - - carrier_count = 0; - //TODO reset tagstate to Idle & tagMode to basic after power_on_reset (no carrier for 10ms) - } - } else { - carrier_count = 0; - } - } else { - //detect if carrier is still available - if(duration <= 0.75 * period || 1.25 * period <= duration) { - carrier_count--; - if(carrier_count <= carrier_threshold_off) { - carrier_on = false; - carrier_count = 0; - } - } else { - carrier_count = 0; - } - - if(carrier_on) { //if carrier is still available - //finetune the average period for reference - if(prevduration == duration) { - durationcounter++; - if(durationcounter == durationthreshold && period != duration) { - period = duration; - } - } else { - durationcounter = 0; - } - - //detect local MAX, and update maxCmd detection - if(duration > prevduration && duration > period) { - //rising edge above the normal periodduration: update localMax - localMax = duration; - localMaxIndex = periodcounter; - } else if( - duration < prevduration && localMax > period && - periodcounter <= localMaxIndex + 2) { - //we passed a local maximum, derive length between current and prev max index - if(miniMaxDelta == 0) { - prevMaxDelta = maxDelta; - } - maxDelta = (localMaxIndex - lastMaxIndex) * prescaler; - - //check if there's a miniDelta and if it should be added to previous or next delta - if(miniMaxDelta > 0) { - //by default minidelta is added to next - //in case doing this does not result in 2 consequtive valid deltas, but adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta - if((lfrfid_hitag_worker_validate_ripple_delta( - prevMaxDelta + miniMaxDelta) + - lfrfid_hitag_worker_validate_ripple_delta( - maxDelta - miniMaxDelta)) > - (lfrfid_hitag_worker_validate_ripple_delta(prevMaxDelta) + - lfrfid_hitag_worker_validate_ripple_delta(maxDelta))) { - //update the previous (last) bit from 0 to 1 if a command was ongoing - if(maxBitCounter > 0) { - maxBits[(maxBitCounter - 1) / 32] |= - (one >> (maxBitCounter - 1) % 32); - } - maxDelta -= miniMaxDelta; - lastMaxIndex = miniMaxIndex; - } - miniMaxDelta = 0; - } - - //now process the (possibly updated) delta - if(maxDelta >= 18) { - if(18 <= maxDelta && maxDelta <= 22) { - maxBitCounter++; - } else if(26 <= maxDelta && maxDelta <= 32) { - maxBits[(maxBitCounter) / 32] |= - (one >> (maxBitCounter) % 32); - maxBitCounter++; - } else if(maxBitCounter > 0) { - //end of bitstring caused by invalid delta - - if(lfrfid_hitag_worker_validate_bits( - maxBits, maxBitCounter)) { - cmd = 1; - cmdIndex = localMaxIndex; - - //CMD found, stop searching for minCMD: reset the minCmd detection - minBits[0] = 0; - minBits[1] = 0; - minBitCounter = 0; - } else { - //reset the maxCmd detection - maxBits[0] = 0; - maxBits[1] = 0; - maxBitCounter = 0; - } - } - lastMaxIndex = localMaxIndex; - } else { - miniMaxDelta = maxDelta; - miniMaxIndex = localMaxIndex; - } - localMax = 0; - } - - //terminate bitstring ico absence of next delta (a true end) - if(periodcounter == lastMaxIndex + 40 / prescaler && - maxBitCounter > 0) { - if(lfrfid_hitag_worker_validate_bits(maxBits, maxBitCounter)) { - cmd = 1; - cmdIndex = localMaxIndex; - - //CMD found, stop searching for minCMD: reset the minCmd detection - minBits[0] = 0; - minBits[1] = 0; - minBitCounter = 0; - } else { - //reset the maxCmd detection - maxBits[0] = 0; - maxBits[1] = 0; - maxBitCounter = 0; - } - } - - //detect local MIN, and update minCmd detection - if(duration < prevduration && duration < period) { - //rising edge above the normal periodduration: update localMax - localMin = duration; - localMinIndex = periodcounter; - } else if( - duration > prevduration && localMin < period && - periodcounter <= localMinIndex + 2) { - //we passed a local minimum, derive length between current and prev max index - if(miniMinDelta == 0) { - prevMinDelta = minDelta; - } - minDelta = (localMinIndex - lastMinIndex) * prescaler; - - //check if there's a miniDelta and if it should be added to previous or next delta - if(miniMinDelta > 0) { - //by default minidelta is added to next - //in case doing this does not result in 2 consequtive valid deltas, but adding it to previous does result in 2 consequtive valid deltas, then assign it to previous delta - if((lfrfid_hitag_worker_validate_ripple_delta( - prevMinDelta + miniMinDelta) + - lfrfid_hitag_worker_validate_ripple_delta( - minDelta - miniMinDelta)) > - (lfrfid_hitag_worker_validate_ripple_delta(prevMinDelta) + - lfrfid_hitag_worker_validate_ripple_delta(minDelta))) { - //update the previous (last) bit from 0 to 1 if a command was ongoing - if(minBitCounter > 0) { - minBits[(minBitCounter - 1) / 32] |= - (one >> (minBitCounter - 1) % 32); - } - minDelta -= miniMinDelta; - lastMinIndex = miniMinIndex; - } - miniMinDelta = 0; - } - - //now process the (possibly updated) delta - if(minDelta >= 18) { - if(18 <= minDelta && minDelta <= 22) { - minBitCounter++; - } else if(26 <= minDelta && minDelta <= 32) { - minBits[(minBitCounter) / 32] |= - (one >> (minBitCounter) % 32); - minBitCounter++; - } else if(minBitCounter > 0) { - //end of bitstring caused by invalid delta - if(lfrfid_hitag_worker_validate_bits( - minBits, minBitCounter)) { - cmd = -1; - cmdIndex = localMinIndex; - - //CMD found, stop searching for maxCMD: reset the maxCmd detection - maxBits[0] = 0; - maxBits[1] = 0; - maxBitCounter = 0; - } else { - //reset the minCmd detection - minBits[0] = 0; - minBits[1] = 0; - minBitCounter = 0; - } - } - lastMinIndex = localMinIndex; - } else { - miniMinDelta = minDelta; - miniMinIndex = localMinIndex; - } - localMin = 255; - } - - //terminate of bitstring caused by absence of next delta (a true end) - if(periodcounter == lastMinIndex + 40 / prescaler && - minBitCounter > 0) { - if(lfrfid_hitag_worker_validate_bits(minBits, minBitCounter)) { - cmd = -1; - cmdIndex = localMinIndex; - - //CMD found, stop searching for maxCMD: reset the maxCmd detection - maxBits[0] = 0; - maxBits[1] = 0; - maxBitCounter = 0; - } else { - //reset the minCmd detection - minBits[0] = 0; - minBits[1] = 0; - minBitCounter = 0; - } - } - - //if a valid cmd has been detected: process it & reply - if(cmd != 0) { - uint32_t bits[5]; - uint8_t size = 0; - //select the cmd bit source (max or min detection) - if(cmd == 1) { - bits[0] = maxBits[0]; - bits[1] = maxBits[1]; - size = maxBitCounter; - - maxBits[0] = 0; - maxBits[1] = 0; - maxBitCounter = 0; - } else { - bits[0] = minBits[0]; - bits[1] = minBits[1]; - size = minBitCounter; - - minBits[0] = 0; - minBits[1] = 0; - minBitCounter = 0; - } - cmd = 0; - - dmaLen = 0; - if(size == 5 && (bits[0] >> (32 - 5)) == 0b11001) { //SET_CCNEW - if(tagState != HitagStateQuiet) { - //reset selected state - tagState = HitagStateIdle; - - //set tag in advanced mode - tagMode = HitagModeAdvanced; - - //prepare emulate DMA buffer with advanced SN reply (page 0) - dataReply->replyvalue[0] = 0b00000111; - dataReply->replytype[0] = HITAG_STARTBIT3; - dataReply->replyvalue[1] = worker->tag->pageData[0]; - dataReply->replytype[1] = HITAG_BYTE; - dataReply->replyvalue[2] = worker->tag->pageData[1]; - dataReply->replytype[2] = HITAG_BYTE; - dataReply->replyvalue[3] = worker->tag->pageData[2]; - dataReply->replytype[3] = HITAG_BYTE; - dataReply->replyvalue[4] = worker->tag->pageData[3]; - dataReply->replytype[4] = HITAG_BYTE; - - dataReply->replylength = 5; - - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC( - dataReply, dataDMA, worker->carrierPrescaler); - } - } else if(size == 5 && (bits[0] >> (32 - 5)) == 0b00110) { //SET_CC - if(tagState != HitagStateQuiet) { - //reset selected state - tagState = HitagStateIdle; - - //do not (re)set tag in basic mode, this only happens during power on reset - - //prepare emulate DMA buffer with basic SN reply (page 0) - dataReply->replyvalue[0] = 0b00000001; - dataReply->replytype[0] = HITAG_STARTBIT; - dataReply->replyvalue[1] = worker->tag->pageData[0]; - dataReply->replytype[1] = HITAG_BYTE; - dataReply->replyvalue[2] = worker->tag->pageData[1]; - dataReply->replytype[2] = HITAG_BYTE; - dataReply->replyvalue[3] = worker->tag->pageData[2]; - dataReply->replytype[3] = HITAG_BYTE; - dataReply->replyvalue[4] = worker->tag->pageData[3]; - dataReply->replytype[4] = HITAG_BYTE; - - dataReply->replylength = 5; - - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_AC( - dataReply, dataDMA, worker->carrierPrescaler); - } - } else if(size == 45 && (bits[0] >> (32 - 5)) == 0b00000) { //SELECT - //check if SN is matching - uint32_t SN_cmd = bits[0] << 5 | bits[1] >> (32 - 5); - uint32_t SN_tag = worker->tag->pageData[0] << 24 | - worker->tag->pageData[1] << 16 | - worker->tag->pageData[2] << 8 | - worker->tag->pageData[3]; - if(SN_cmd == SN_tag) { - //reply with config (page 1 from tag memory) - tagState = HitagStateSelected; - - //prepare emulate DMA buffer with basic/advanced config page reply (page 1) - if(tagMode == HitagModeBasic) { - dataReply->replyvalue[0] = 0b00000001; - dataReply->replytype[0] = HITAG_STARTBIT; - - dataReply->replyvalue[1] = - worker->tag->pageData[4 + 0]; - dataReply->replytype[1] = HITAG_BYTE; - dataReply->replyvalue[2] = - worker->tag->pageData[4 + 1]; - dataReply->replytype[2] = HITAG_BYTE; - dataReply->replyvalue[3] = - worker->tag->pageData[4 + 2]; - dataReply->replytype[3] = HITAG_BYTE; - dataReply->replyvalue[4] = - worker->tag->pageData[4 + 3]; - dataReply->replytype[4] = HITAG_BYTE; - - dataReply->replylength = 5; - } else if(tagMode == HitagModeAdvanced) { - dataReply->replyvalue[0] = 0b00111111; - dataReply->replytype[0] = HITAG_STARTBIT6; - - dataReply->replyvalue[1] = - worker->tag->pageData[4 + 0]; - dataReply->replytype[1] = HITAG_BYTE; - dataReply->replyvalue[2] = - worker->tag->pageData[4 + 1]; - dataReply->replytype[2] = HITAG_BYTE; - dataReply->replyvalue[3] = - worker->tag->pageData[4 + 2]; - dataReply->replytype[3] = HITAG_BYTE; - dataReply->replyvalue[4] = - worker->tag->pageData[4 + 3]; - dataReply->replytype[4] = HITAG_BYTE; - - uint8_t crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc( - &crc, dataReply->replyvalue[1], 8); - lfrfid_hitag_worker_calc_crc( - &crc, dataReply->replyvalue[2], 8); - lfrfid_hitag_worker_calc_crc( - &crc, dataReply->replyvalue[3], 8); - lfrfid_hitag_worker_calc_crc( - &crc, dataReply->replyvalue[4], 8); - - dataReply->replyvalue[5] = crc; - dataReply->replytype[5] = HITAG_CRC; - - dataReply->replylength = 6; - } - - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC( - dataReply, dataDMA, worker->carrierPrescaler); - } - } else if(size == 20 && (bits[0] >> (31))) { //R/W - if(tagState == HitagStateSelected) { - //reply with requested pages in case of read public (ignore secret block/pages & ignore write cmd) - uint8_t rwCmd = bits[0] >> (32 - 4); - uint8_t addr = bits[0] >> 20 & 0xff; - uint8_t pages = 0; - switch(rwCmd) { - case(0b1100): - //read public page - if(worker->tag->pageKnown[addr] && - worker->tag->pagePublic[addr]) { - pages = 1; - } - break; - case(0b1101): - //read public block - pages = (64 - addr) % 4; - if(pages == 0) { - pages = 4; - } - for(uint8_t p = addr; p < addr + pages; p++) { - if(!worker->tag->pageKnown[p] || - !worker->tag->pagePublic[p]) { - pages = 0; - break; - } - } - break; - } - if(pages > 0) { - //pages - for(uint8_t i = 0; i < pages; i++) { - dataReply->replyvalue[1 + i * 4 + 0] = - worker->tag->pageData[(addr + i) * 4 + 0]; - dataReply->replytype[1 + i * 4 + 0] = HITAG_BYTE; - dataReply->replyvalue[1 + i * 4 + 1] = - worker->tag->pageData[(addr + i) * 4 + 1]; - dataReply->replytype[1 + i * 4 + 1] = HITAG_BYTE; - dataReply->replyvalue[1 + i * 4 + 2] = - worker->tag->pageData[(addr + i) * 4 + 2]; - dataReply->replytype[1 + i * 4 + 2] = HITAG_BYTE; - dataReply->replyvalue[1 + i * 4 + 3] = - worker->tag->pageData[(addr + i) * 4 + 3]; - dataReply->replytype[1 + i * 4 + 3] = HITAG_BYTE; - } - //startsequence & CRC - if(tagMode == HitagModeBasic) { - dataReply->replyvalue[0] = 0b00000001; - dataReply->replytype[0] = HITAG_STARTBIT; - - dataReply->replylength = 1 + pages * 4; - } else if(tagMode == HitagModeAdvanced) { - dataReply->replyvalue[0] = 0b00111111; - dataReply->replytype[0] = HITAG_STARTBIT6; - - uint8_t crc = CRC_PRESET; - - for(uint8_t i = 0; i < pages; i++) { - lfrfid_hitag_worker_calc_crc( - &crc, - dataReply->replyvalue[1 + i * 4 + 0], - 8); - lfrfid_hitag_worker_calc_crc( - &crc, - dataReply->replyvalue[1 + i * 4 + 1], - 8); - lfrfid_hitag_worker_calc_crc( - &crc, - dataReply->replyvalue[1 + i * 4 + 2], - 8); - lfrfid_hitag_worker_calc_crc( - &crc, - dataReply->replyvalue[1 + i * 4 + 3], - 8); - } - - dataReply->replyvalue[1 + pages * 4] = crc; - dataReply->replytype[1 + pages * 4] = HITAG_CRC; - - dataReply->replylength = 2 + pages * 4; - } - - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC( - dataReply, dataDMA, worker->carrierPrescaler); - } - } - } else if(size == 20 && (bits[0] >> (32 - 4)) == 0b0111) { //HALT - //TODO validate if dummy address is a valid one (one of the default public pages (32-63)) - - //acknowledge & go silent untill next reset - if(tagState == HitagStateSelected) { - tagState = HitagStateQuiet; - - if(tagMode == HitagModeBasic) { - dataReply->replyvalue[0] = 0b00000101; - dataReply->replytype[0] = HITAG_ACK; - } else if(tagMode == HitagModeAdvanced) { - dataReply->replyvalue[0] = 0b11111101; - dataReply->replytype[0] = HITAG_ACK_ADV; - } - - dataReply->replylength = 1; - - dmaLen = lfrfid_hitag_worker_yield_dma_buffer_MC( - dataReply, dataDMA, worker->carrierPrescaler); - } - } - - if(dmaLen > 0) { //switch to reply mode - //switch carrier mode to ETR required for emulation - lfrfid_hitag_worker_carrier_in_ETR_mode(capData, prescaler); - - //respect hitag WAIT1 time - uint32_t wait1_periods = 180 / prescaler; - while(capData->capCounter - cmdIndex < wait1_periods) { - furi_delay_us(40); - } - - //just start the DMA, stopping is handled in DMA transfer complete interrupt - lfrfid_hitag_worker_pull_out_dma_start((size_t)dmaLen); - } - } - } - } - } - } - - //reset buffer - buffer_reset(buffer); - } - - if(buffer_stream_get_overrun_count(capData->stream) > 0) { - FURI_LOG_E(TAG, "Read overrun, recovering"); - buffer_stream_reset(capData->stream); - } - - uint32_t flags = furi_event_flag_get(worker->events); - if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { - break; - } - } - - //stop carrier capture in - lfrfid_hitag_worker_carrier_in_stop(); - - free(worker->tag); - free(dataDMA); - free(dataReply); - varint_pair_free(capData->pair); - buffer_stream_free(capData->stream); - free(capData); - - //clear the stop flag on workder so that i can use the worker to launch a new read routine without having to restart the app - furi_event_flag_clear(worker->events, 1 << LFRFIDHitagWorkerSignalStop); - - return 0; -} - -static int32_t lfrfid_hitag_worker_read_thread(void* thread_context) { - LFRFIDHitagWorker* worker = (LFRFIDHitagWorker*)thread_context; - - //initialize a tag datastructure - worker->tag = malloc(sizeof(LFRFIDHitag)); - lfrfid_hitag_worker_initialize_tag_data(worker->tag); - - //prep variables for decoder - LfRfidHitagProtocol currentProtocol = HitagProtocolAntiCollision; - uint8_t startBits = 1; - uint32_t memBlock[HITAG_BLOCKPAGES] = {0}; - uint8_t bits = 0; - bool halfBit = false; - bool finished = false; - - //prep variables for worker - uint8_t expectedBits = 33; - uint8_t readings = 0; - uint8_t currPage = 0; - bool readSucces = false; - uint8_t readThreshold = 3; - - // setup pins for reading - furi_hal_rfid_pins_read(); - - //and setup TIM1 CH1N with DMA - //fill the DMA arr & ccr buffers - /*start with looping SETCC command*/ - LfRfidHitagCMDData* dataCMD = malloc(sizeof(LfRfidHitagCMDData)); - dataCMD->CMDlength = 0; - dataCMD->CMDposition = 0; - dataCMD->CMDsubposition = 0; - dataCMD->CMDactive = false; - - LfRfidHitagCMDData* backupCMD = malloc(sizeof(LfRfidHitagCMDData)); - uint32_t value[] = { - 128, - 0b00110, - 1, - 213, - (uint32_t)(1.05 * 33 * HITAG_BITPERIODS_AC)}; //always take 5% reading time buffer - uint8_t type[] = {HITAG_ON, HITAG_SET, HITAG_STOP, HITAG_ON, HITAG_ON}; - for(size_t i = 0; i < 5; i++) { - backupCMD->CMDvalue[i] = value[i]; - backupCMD->CMDtype[i] = type[i]; - } - backupCMD->CMDlength = 5; - backupCMD->CMDposition = 0; - backupCMD->CMDsubposition = 0; - backupCMD->CMDloop = - true; //not really required since by definition the backup cmd is looping already in yield code - backupCMD->CMDactive = true; - - TimerDMAData* dataDMA = malloc(sizeof(TimerDMAData)); - lfrfid_hitag_worker_yield_dma_buffer_BPLM(dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE / 2); - lfrfid_hitag_worker_yield_dma_buffer_BPLM( - dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE / 2, DMA_BUFFER_SIZE / 2); - - //start TIM1 with DMA function - lfrfid_hitag_worker_carrier_out_start( - dataDMA->timer_buffer_arr, dataDMA->timer_buffer_ccr, DMA_BUFFER_SIZE, worker); - - // start capture - LfRfidHitagCaptureData* capData = malloc(sizeof(LfRfidHitagCaptureData)); - capData->stream = buffer_stream_alloc(READ_BUFFER_SIZE, READ_BUFFER_COUNT); - capData->pair = varint_pair_alloc(); - furi_hal_rfid_tim_read_capture_start(lfrfid_hitag_worker_capture_in_cc_isr, capData); - - while(1) { - Buffer* buffer = buffer_stream_receive(capData->stream, 1); - - //process data if available - if(buffer != NULL) { - size_t buffsize = buffer_get_size(buffer); - uint8_t* buffdata = buffer_get_data(buffer); - - //if scanning for tagdata, then also parse buffer data - size_t index = 0; - while(expectedBits > 0 && index < buffsize) { - uint32_t duration; - uint32_t pulse; - size_t tmp_size; - - if(!varint_pair_unpack( - &buffdata[index], buffsize - index, &pulse, &duration, &tmp_size)) { - FURI_LOG_E(TAG, "can't unpack varint pair"); - break; - } else { - index += tmp_size; - - //feed pulse duration to decoder - lfrfid_hitag_worker_decoder_feed( - pulse, - duration, - memBlock, - &bits, - &halfBit, - &finished, - currentProtocol, - startBits); - - //see if i have a valid bit sequence - if(finished) { - //bitsequence ready for validation - if(bits == - expectedBits) { //eligable bitsequence detected, accept it only after x consequtive readings - //reset the backupCMD count to ensure some pause between bitsequence detection & the CMD injection - backupCMD->CMDcount = 0; - - if(readings == 0) { - for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { - worker->tag->pageData[(currPage + p) * 4 + 0] = - memBlock[p] >> 24 & 0xff; - worker->tag->pageData[(currPage + p) * 4 + 1] = - memBlock[p] >> 16 & 0xff; - worker->tag->pageData[(currPage + p) * 4 + 2] = - memBlock[p] >> 8 & 0xff; - worker->tag->pageData[(currPage + p) * 4 + 3] = memBlock[p] & - 0xff; - worker->tag->pageKnown[currPage + p] = 0; - } - readings = 1; - } else if(readings < readThreshold) { - bool match = true; - for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { - if(worker->tag->pageData[(currPage + p) * 4 + 0] != - (memBlock[p] >> 24 & 0xff) || - worker->tag->pageData[(currPage + p) * 4 + 1] != - (memBlock[p] >> 16 & 0xff) || - worker->tag->pageData[(currPage + p) * 4 + 2] != - (memBlock[p] >> 8 & 0xff) || - worker->tag->pageData[(currPage + p) * 4 + 3] != - (memBlock[p] & 0xff)) { - match = false; - break; - } - } - if(match) { - readings++; - if(readings == readThreshold) { - //succesful page(s) reading - readSucces = true; - - for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { - worker->tag->pageKnown[currPage + p] = 1; - } - - readings = 0; - expectedBits = 0; //to prevent further updates to tag data - } - } else { - for(uint8_t p = 0; p < (expectedBits - 1) / 32; p++) { - worker->tag->pageData[(currPage + p) * 4 + 0] = - memBlock[p] >> 24 & 0xff; - worker->tag->pageData[(currPage + p) * 4 + 1] = - memBlock[p] >> 16 & 0xff; - worker->tag->pageData[(currPage + p) * 4 + 2] = - memBlock[p] >> 8 & 0xff; - worker->tag->pageData[(currPage + p) * 4 + 3] = - memBlock[p] & 0xff; - worker->tag->pageKnown[currPage + p] = 0; - } - readings = 1; - } - } - } - //reset tempdata for next bitsequence readout - memset(memBlock, 0, HITAG_BLOCKPAGES * sizeof(memBlock[0])); - bits = 0; - finished = false; - } - } - } - - //reset buffer - buffer_reset(buffer); - } - - //if bitsequence found & after pause, prep & launch the command for reading out next bit of data - if(readSucces && backupCMD->CMDcount >= 7) { - readSucces = false; - backupCMD->CMDcount = - 0; //required since otherwise the 'relaunch' of cmd is triggered straightaway - - //update protocolData - size_t data_size = protocol_dict_get_data_size(worker->dict, LFRFIDProtocolHitag1); - lfrfid_hitag_worker_combine_tag_data(worker->tag); - protocol_dict_set_data( - worker->dict, LFRFIDProtocolHitag1, worker->tag->tagData, data_size); - - if(currPage == 0) { //read config page next via SELECT cmd - FURI_LOG_D( - TAG, - "Hitag1, [%02X %02X %02X %02X] found", - worker->tag->pageData[0], - worker->tag->pageData[1], - worker->tag->pageData[2], - worker->tag->pageData[3]); - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); - worker->workerstatus = LFRFIDHitagStatusDetected; - - currentProtocol = HitagProtocolManchesterCoding; - expectedBits = 33; - currPage = 1; - } else if(currPage == 1) { //process config page and start reading public blocks - lfrfid_hitag_worker_process_config_page(worker->tag); - FURI_LOG_D( - TAG, - "Hitag1, [%02X %02X %02X %02X] config page read", - worker->tag->pageData[0], - worker->tag->pageData[1], - worker->tag->pageData[2], - worker->tag->pageData[3]); - //find next public readible block - for(currPage = 4; currPage <= HITAG_PAGES - 4; currPage += 4) { - if(worker->tag->pagePublic[currPage] && - (worker->tag->pageRW[currPage] & READ)) { //public readible page/block found - expectedBits = 129; - break; - } else { - FURI_LOG_D( - TAG, - "Hitag1, [%02X %02X %02X %02X] block %u not public/readible", - worker->tag->pageData[0], - worker->tag->pageData[1], - worker->tag->pageData[2], - worker->tag->pageData[3], - currPage / 4); - } - } - } else if(currPage < HITAG_PAGES - 4) { //scan for next public readible page - FURI_LOG_D( - TAG, - "Hitag1, [%02X %02X %02X %02X] block %u read", - worker->tag->pageData[0], - worker->tag->pageData[1], - worker->tag->pageData[2], - worker->tag->pageData[3], - currPage / 4); - for(currPage += 4; currPage <= HITAG_PAGES - 4; currPage += 4) { - if(worker->tag->pagePublic[currPage] && - (worker->tag->pageRW[currPage] & READ)) { //public readible page/block found - expectedBits = 129; - break; - } else { - FURI_LOG_D( - TAG, - "Hitag1, [%02X %02X %02X %02X] block %u not public/readible", - worker->tag->pageData[0], - worker->tag->pageData[1], - worker->tag->pageData[2], - worker->tag->pageData[3], - currPage / 4); - } - } - } else if( - currPage == - HITAG_PAGES - - 4) { //all public readible pages read: signal view & break out of while loop (stops the worker) - FURI_LOG_D( - TAG, - "Hitag1, [%02X %02X %02X %02X] block %u read", - worker->tag->pageData[0], - worker->tag->pageData[1], - worker->tag->pageData[2], - worker->tag->pageData[3], - currPage / 4); - - worker->workerstatus = LFRFIDHitagStatusRead; - break; - } - - if(expectedBits > 0) { //inject CMD - //prep SELECT cmd bytes - uint8_t SN0 = worker->tag->pageData[0]; - uint8_t SN1 = worker->tag->pageData[1]; - uint8_t SN2 = worker->tag->pageData[2]; - uint8_t SN3 = worker->tag->pageData[3]; - uint8_t crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc(&crc, (0b00000 << (8 - 5)), 5); - lfrfid_hitag_worker_calc_crc(&crc, SN0, 8); - lfrfid_hitag_worker_calc_crc(&crc, SN1, 8); - lfrfid_hitag_worker_calc_crc(&crc, SN2, 8); - lfrfid_hitag_worker_calc_crc(&crc, SN3, 8); - - //prep SELECT cmd array - uint32_t value[] = { - 128, - 0b00000, - SN0, - SN1, - SN2, - SN3, - crc, - 1, - 213, - (uint32_t)(1.05 * 33 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer - uint8_t type[] = { - HITAG_ON, - HITAG_SELECT, - HITAG_BYTE, - HITAG_BYTE, - HITAG_BYTE, - HITAG_BYTE, - HITAG_CRC, - HITAG_STOP, - HITAG_ON, - HITAG_ON}; - uint16_t len = 10; - uint16_t cnt = 1; - for(size_t i = 0; i < cnt * len; i++) { - dataCMD->CMDvalue[i] = value[i % len]; - dataCMD->CMDtype[i] = type[i % len]; - } - dataCMD->CMDlength = cnt * len; - dataCMD->CMDposition = 0; - dataCMD->CMDsubposition = 0; - dataCMD->CMDloop = false; - - if(currPage < 4 * 4) { //add READPAGE cmd to array - crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc(&crc, (0b1100 << (8 - 4)), 4); - lfrfid_hitag_worker_calc_crc(&crc, currPage, 8); - uint32_t value2[] = { - 128, - 0b1100, - currPage, - crc, - 1, - 213, - (uint32_t)(1.05 * 33 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer - uint8_t type2[] = { - HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; - uint16_t len2 = 7; - uint16_t cnt2 = 5; - for(size_t i = 0; i < cnt2 * len2; i++) { - dataCMD->CMDvalue[cnt * len + i] = value2[i % len2]; - dataCMD->CMDtype[cnt * len + i] = type2[i % len2]; - } - dataCMD->CMDlength += cnt2 * len2; - } else { //add READBLOCK cmd to array - crc = CRC_PRESET; - lfrfid_hitag_worker_calc_crc(&crc, (0b1101 << (8 - 4)), 4); - lfrfid_hitag_worker_calc_crc(&crc, currPage, 8); - uint32_t value2[] = { - 128, - 0b1101, - currPage, - crc, - 1, - 213, - (uint32_t)(1.05 * 129 * HITAG_BITPERIODS_MC)}; //always take 5% reading time buffer - uint8_t type2[] = { - HITAG_ON, HITAG_CMD, HITAG_BYTE, HITAG_CRC, HITAG_STOP, HITAG_ON, HITAG_ON}; - uint16_t len2 = 7; - uint16_t cnt2 = 5; - for(size_t i = 0; i < cnt2 * len2; i++) { - dataCMD->CMDvalue[cnt * len + i] = value2[i % len2]; - dataCMD->CMDtype[cnt * len + i] = type2[i % len2]; - } - dataCMD->CMDlength += cnt2 * len2; - } - } - } - - //while not having x consequtive reads, relaunch the (already prepared) command - if(!readSucces && backupCMD->CMDcount >= 7 && currPage > 0 && expectedBits > 0) { - backupCMD->CMDcount = 0; - dataCMD->CMDposition = 0; - dataCMD->CMDsubposition = 0; - } - - if(buffer_stream_get_overrun_count(capData->stream) > 0) { - FURI_LOG_E(TAG, "Read overrun, recovering"); - buffer_stream_reset(capData->stream); - } - - //check for stop events - uint32_t flags = furi_event_flag_get(worker->events); - if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { - break; - } - - //check for DMA HT/TC signals to repopulate the DMA buffer - if(worker->DMAeventCount > 1) { - FURI_LOG_E(TAG, "DMA refilling can't keep up %u", worker->DMAeventCount); - } - if(FURI_BIT(flags, LFRFIDHitagWorkerSignalHalfTransfer)) { - //refill first half of dma buffer - lfrfid_hitag_worker_yield_dma_buffer_BPLM( - dataCMD, backupCMD, dataDMA, 0, DMA_BUFFER_SIZE / 2); - } - if(FURI_BIT(flags, LFRFIDHitagWorkerSignalTransferComplete)) { - //refill second half of dma buffer - lfrfid_hitag_worker_yield_dma_buffer_BPLM( - dataCMD, backupCMD, dataDMA, DMA_BUFFER_SIZE / 2, DMA_BUFFER_SIZE / 2); - } - worker->DMAeventCount = 0; - //clear all flags (i have processed all known/expected events) - furi_event_flag_clear(worker->events, flags); - } - - //stop timers - furi_hal_rfid_tim_read_capture_stop(); - lfrfid_hitag_worker_carrier_out_stop(); - - //free memory - free(dataCMD); - free(backupCMD); - free(dataDMA); - varint_pair_free(capData->pair); - buffer_stream_free(capData->stream); - free(capData); - - //allow the view that started the thread to signal the stop, which will properly merge the threads & de-init the worker - if(currPage == HITAG_PAGES - 4) { - const uint32_t available_flags = (1 << LFRFIDHitagWorkerSignalStop); - while(true) { - uint32_t flags = furi_event_flag_wait( - worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever); - - if(FURI_BIT(flags, LFRFIDHitagWorkerSignalStop)) { - break; - } - } - } - - free(worker->tag); - - //clear the stop flag on workder so that i can use the same worker to launch a new routine - furi_event_flag_clear(worker->events, 1 << LFRFIDHitagWorkerSignalStop); - - return 0; -} diff --git a/lib/lfrfid/lfrfid_hitag_worker.h b/lib/lfrfid/lfrfid_hitag_worker.h deleted file mode 100644 index 9817991b8..000000000 --- a/lib/lfrfid/lfrfid_hitag_worker.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -//#include -#include "protocols/lfrfid_protocols.h" - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include - -#include - -#define HITAG_BLOCKS 16 -#define HITAG_BLOCKPAGES 4 -#define HITAG_PAGES 64 -#define HITAG_PAGEBYTES 4 - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - LFRFIDHitagWorkerSettingRead, - LFRFIDHitagWorkerSettingEmulate, -} LFRFIDHitagWorkerSetting; - -typedef enum { - LFRFIDHitagStatusScanning, - LFRFIDHitagStatusDetected, - LFRFIDHitagStatusRead, -} LFRFIDHitagStatus; - -typedef struct LFRFIDHitagWorker LFRFIDHitagWorker; - -/** - * @brief Get the tag read status - * - * @return tag read status - */ -LFRFIDHitagStatus lfrfid_hitag_worker_get_status(LFRFIDHitagWorker* worker); - -/** - * @brief Allocate a new LFRFIDHitagWorker instance - * - * @return LFRFIDHitagWorker* - */ -LFRFIDHitagWorker* lfrfid_hitag_worker_alloc(ProtocolDict* dict); - -/** - * @brief Free a LFRFIDHitagWorker instance - * - * @param worker LFRFIDHitagWorker instance - */ -void lfrfid_hitag_worker_free(LFRFIDHitagWorker* worker); - -/** - * @brief Start hitag worker from own generated field - * - * @param worker LFRFIDHitagWorker instance - * @param setting read/emulate - */ -void lfrfid_hitag_worker_start(LFRFIDHitagWorker* worker, LFRFIDHitagWorkerSetting setting); - -/** - * @brief Stop worker - * - * @param worker - */ -void lfrfid_hitag_worker_stop(LFRFIDHitagWorker* worker); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h index 682b077a0..def9f89a4 100644 --- a/lib/lfrfid/lfrfid_worker.h +++ b/lib/lfrfid/lfrfid_worker.h @@ -23,7 +23,6 @@ typedef enum { LFRFIDWorkerReadTypeAuto, LFRFIDWorkerReadTypeASKOnly, LFRFIDWorkerReadTypePSKOnly, - LFRFIDWorkerReadTypeRTFOnly, } LFRFIDWorkerReadType; typedef enum { @@ -33,8 +32,6 @@ typedef enum { LFRFIDWorkerReadSenseCardEnd, LFRFIDWorkerReadStartASK, LFRFIDWorkerReadStartPSK, - LFRFIDWorkerReadStartRTF, - LFRFIDWorkerReadSenseHitag, //TODO combine with sense carstart? LFRFIDWorkerReadDone, } LFRFIDWorkerReadResult; diff --git a/lib/lfrfid/lfrfid_worker_i.h b/lib/lfrfid/lfrfid_worker_i.h index d46ef2bba..33c0bff08 100644 --- a/lib/lfrfid/lfrfid_worker_i.h +++ b/lib/lfrfid/lfrfid_worker_i.h @@ -8,7 +8,6 @@ #include #include "lfrfid_worker.h" #include "lfrfid_raw_worker.h" -#include "lfrfid_hitag_worker.h" #include "protocols/lfrfid_protocols.h" #ifdef __cplusplus diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c index c716c0d09..9b6f16eb1 100644 --- a/lib/lfrfid/lfrfid_worker_modes.c +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -94,7 +94,7 @@ typedef enum { LFRFIDWorkerReadTimeout, } LFRFIDWorkerReadState; -static LFRFIDWorkerReadState lfrfid_worker_read_ttf( //tag talks first +static LFRFIDWorkerReadState lfrfid_worker_read_internal( LFRFIDWorker* worker, LFRFIDFeature feature, uint32_t timeout, @@ -334,63 +334,6 @@ static LFRFIDWorkerReadState lfrfid_worker_read_ttf( //tag talks first return state; } -static LFRFIDWorkerReadState lfrfid_worker_read_rtf( //reader talks first - LFRFIDWorker* worker, - LFRFIDFeature feature, - uint32_t timeout, - ProtocolId* result_protocol) { - UNUSED(feature); - LFRFIDWorkerReadState state = LFRFIDWorkerReadTimeout; - - FURI_LOG_D(TAG, "Start RTF"); - if(worker->read_cb) { - worker->read_cb(LFRFIDWorkerReadStartRTF, PROTOCOL_NO, worker->cb_ctx); - } - LFRFIDHitagWorker* hitag_worker = lfrfid_hitag_worker_alloc(worker->protocols); - - lfrfid_hitag_worker_start(hitag_worker, LFRFIDHitagWorkerSettingRead); - - FURI_LOG_D(TAG, "Read started"); - - //scan for hitag for a while and stay in hitag mode if card was detected - uint8_t delays = 0; - uint8_t delay_ms = 100; - bool notified = false; - while(1) { - furi_delay_ms(delay_ms); - - if(lfrfid_worker_check_for_stop(worker)) { - state = LFRFIDWorkerReadExit; - *result_protocol = PROTOCOL_NO; - break; - } - - if(lfrfid_hitag_worker_get_status(hitag_worker) == LFRFIDHitagStatusDetected) { - *result_protocol = - LFRFIDProtocolHitag1; //TODO get protocol ID from hitag_worker when expanding the worker to include other hitag protocols - if(!notified && worker->read_cb) { - worker->read_cb(LFRFIDWorkerReadSenseHitag, *result_protocol, worker->cb_ctx); - notified = true; - } - } else if(lfrfid_hitag_worker_get_status(hitag_worker) == LFRFIDHitagStatusRead) { - state = LFRFIDWorkerReadOK; - *result_protocol = - LFRFIDProtocolHitag1; //TODO get protocol ID from hitag_worker when expanding the worker to include other hitag protocols - break; - } else if(++delays >= timeout / delay_ms) { - state = LFRFIDWorkerReadTimeout; - break; - } - } - - lfrfid_hitag_worker_stop(hitag_worker); - lfrfid_hitag_worker_free(hitag_worker); - - FURI_LOG_D(TAG, "Read stopped"); - - return state; -} - static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { ProtocolId read_result = PROTOCOL_NO; LFRFIDWorkerReadState state; @@ -398,8 +341,6 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) { feature = LFRFIDFeaturePSK; - } else if(worker->read_type == LFRFIDWorkerReadTypeRTFOnly) { - feature = LFRFIDFeatureRTF; } else { feature = LFRFIDFeatureASK; } @@ -407,14 +348,8 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { if(worker->read_type == LFRFIDWorkerReadTypeAuto) { while(1) { // read for a while - - if(feature == LFRFIDFeatureASK || feature == LFRFIDFeaturePSK) { - state = lfrfid_worker_read_ttf( - worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); - } else if(feature == LFRFIDFeatureRTF) { - state = lfrfid_worker_read_rtf( - worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); - } + state = lfrfid_worker_read_internal( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { break; @@ -423,9 +358,7 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { // switch to next feature if(feature == LFRFIDFeatureASK) { feature = LFRFIDFeaturePSK; - } else if(feature == LFRFIDFeaturePSK) { - feature = LFRFIDFeatureRTF; - } else if(feature == LFRFIDFeatureRTF) { + } else { feature = LFRFIDFeatureASK; } @@ -434,12 +367,10 @@ static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { } else { while(1) { if(worker->read_type == LFRFIDWorkerReadTypeASKOnly) { - state = lfrfid_worker_read_ttf(worker, feature, UINT32_MAX, &read_result); - } else if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) { - state = lfrfid_worker_read_ttf( - worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + state = lfrfid_worker_read_internal(worker, feature, UINT32_MAX, &read_result); } else { - state = lfrfid_worker_read_rtf(worker, feature, UINT32_MAX, &read_result); + state = lfrfid_worker_read_internal( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); } if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { @@ -475,7 +406,7 @@ static void lfrfid_worker_emulate_dma_isr(bool half, void* context) { furi_stream_buffer_send(stream, &flag, sizeof(uint32_t), 0); } -static void lfrfid_worker_emulate_ttf(LFRFIDWorker* worker) { +static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { LFRFIDWorkerEmulateBuffer* buffer = malloc(sizeof(LFRFIDWorkerEmulateBuffer)); FuriStreamBuffer* stream = furi_stream_buffer_alloc(sizeof(uint32_t), sizeof(uint32_t)); LFRFIDProtocol protocol = worker->protocol; @@ -564,39 +495,6 @@ static void lfrfid_worker_emulate_ttf(LFRFIDWorker* worker) { pulse_glue_free(pulse_glue); } -static void lfrfid_worker_emulate_rtf(LFRFIDWorker* worker) { - LFRFIDHitagWorker* hitag_worker = lfrfid_hitag_worker_alloc( - worker->protocols); //todo, pass protocols & protocol id when expanding the worker to include other hitag protocols - - lfrfid_hitag_worker_start(hitag_worker, LFRFIDHitagWorkerSettingEmulate); - uint8_t delay_ms = 100; - while(1) { - furi_delay_ms(delay_ms); - - if(lfrfid_worker_check_for_stop(worker)) { - break; - } - } - - lfrfid_hitag_worker_stop(hitag_worker); - lfrfid_hitag_worker_free(hitag_worker); -} - -static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { - if(worker != NULL) { - if(worker->protocols != NULL) { - LFRFIDFeature feature = - protocol_dict_get_features(worker->protocols, worker->protocol); - - if(feature == LFRFIDFeatureRTF) { - lfrfid_worker_emulate_rtf(worker); - } else { - lfrfid_worker_emulate_ttf(worker); - } - } - } -} - /**************************************************************************************************/ /********************************************* WRITE **********************************************/ /**************************************************************************************************/ @@ -623,7 +521,7 @@ static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) { t5577_write(&request->t5577); ProtocolId read_result = PROTOCOL_NO; - LFRFIDWorkerReadState state = lfrfid_worker_read_ttf( + LFRFIDWorkerReadState state = lfrfid_worker_read_internal( worker, protocol_dict_get_features(worker->protocols, protocol), LFRFID_WORKER_WRITE_VERIFY_TIME_MS, diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index 43d59e123..2c1f0ad97 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -16,7 +16,6 @@ #include "protocol_pac_stanley.h" #include "protocol_keri.h" #include "protocol_gallagher.h" -#include "protocol_hitag1.h" const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, @@ -36,5 +35,4 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolPACStanley] = &protocol_pac_stanley, [LFRFIDProtocolKeri] = &protocol_keri, [LFRFIDProtocolGallagher] = &protocol_gallagher, - [LFRFIDProtocolHitag1] = &protocol_hitag1, }; \ No newline at end of file diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 6d0e3fa4e..848f003a3 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -5,7 +5,6 @@ typedef enum { LFRFIDFeatureASK = 1 << 0, /** ASK Demodulation */ LFRFIDFeaturePSK = 1 << 1, /** PSK Demodulation */ - LFRFIDFeatureRTF = 1 << 2, /** Reader Talks First: ASK Demodulation with 2 way communication */ } LFRFIDFeature; typedef enum { @@ -26,7 +25,6 @@ typedef enum { LFRFIDProtocolPACStanley, LFRFIDProtocolKeri, LFRFIDProtocolGallagher, - LFRFIDProtocolHitag1, LFRFIDProtocolMax, } LFRFIDProtocol; diff --git a/lib/lfrfid/protocols/protocol_hitag1.c b/lib/lfrfid/protocols/protocol_hitag1.c deleted file mode 100644 index 1890ea1ad..000000000 --- a/lib/lfrfid/protocols/protocol_hitag1.c +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include -#include "lfrfid_protocols.h" - -#define HITAG1_PAGES 64 -#define HITAG1_DATA_SIZE HITAG1_PAGES * 4 + HITAG1_PAGES - -typedef struct { - uint8_t tagData[HITAG1_DATA_SIZE]; -} ProtocolHitag1; - -ProtocolHitag1* protocol_hitag1_alloc(void) { - ProtocolHitag1* protocol = malloc(sizeof(ProtocolHitag1)); - - return protocol; -}; - -void protocol_hitag1_free(ProtocolHitag1* protocol) { - free(protocol); -}; - -uint8_t* protocol_hitag1_get_data(ProtocolHitag1* protocol) { - return protocol->tagData; -}; - -void protocol_hitag1_decoder_start(ProtocolHitag1* protocol) { - UNUSED(protocol); - // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... -}; - -bool protocol_hitag1_decoder_feed(ProtocolHitag1* protocol, bool level, uint32_t duration) { - UNUSED(protocol); - UNUSED(level); - UNUSED(duration); - // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... - - bool result = false; - return result; -}; - -bool protocol_hitag1_encoder_start(ProtocolHitag1* protocol) { - UNUSED(protocol); - // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... - - return false; -}; - -LevelDuration protocol_hitag1_encoder_yield(ProtocolHitag1* protocol) { - UNUSED(protocol); - // Not applicalble, encoding & decoding is handled in lfrfid_hitag_worker... - - bool level = 0; - uint32_t duration = 0; - return level_duration_make(level, duration); -}; - -bool protocol_hitag1_write_data(ProtocolHitag1* protocol, void* data) { - UNUSED(protocol); - UNUSED(data); - - //this protocol cannot be simply written to card --> don't do anything, just return false - - return false; -}; - -void protocol_hitag1_render_data(ProtocolHitag1* protocol, FuriString* result) { - uint8_t pages = 0; - for(uint8_t p = 0; p < HITAG1_PAGES; p++) { - pages += protocol->tagData[HITAG1_PAGES * 4 + p]; - } - furi_string_printf( - result, - "SN: %02X %02X %02X %02X\r\n" - "Pages read: %u / 64", - protocol->tagData[0], - protocol->tagData[1], - protocol->tagData[2], - protocol->tagData[3], - pages); -}; - -const ProtocolBase protocol_hitag1 = { - .name = "Hitag1", - .manufacturer = "Philips", - .data_size = HITAG1_DATA_SIZE, - .features = LFRFIDFeatureRTF, - .validate_count = 1, - .alloc = (ProtocolAlloc)protocol_hitag1_alloc, - .free = (ProtocolFree)protocol_hitag1_free, - .get_data = (ProtocolGetData)protocol_hitag1_get_data, - .decoder = - { - .start = (ProtocolDecoderStart)protocol_hitag1_decoder_start, - .feed = (ProtocolDecoderFeed)protocol_hitag1_decoder_feed, - }, - .encoder = - { - .start = (ProtocolEncoderStart)protocol_hitag1_encoder_start, - .yield = (ProtocolEncoderYield)protocol_hitag1_encoder_yield, - }, - .render_data = (ProtocolRenderData)protocol_hitag1_render_data, - .render_brief_data = (ProtocolRenderData)protocol_hitag1_render_data, - .write_data = (ProtocolWriteData)protocol_hitag1_write_data, -}; diff --git a/lib/lfrfid/protocols/protocol_hitag1.h b/lib/lfrfid/protocols/protocol_hitag1.h deleted file mode 100644 index 517eeb5ef..000000000 --- a/lib/lfrfid/protocols/protocol_hitag1.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -extern const ProtocolBase protocol_hitag1; \ No newline at end of file From 27028471bb2a7b1a96f162c0ec37ed14cbeec292 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 23:16:25 +0300 Subject: [PATCH 50/71] Update changelog --- CHANGELOG.md | 1 - applications/main/application.fam | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cface529..2b016121a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,6 @@ * Infrared: `External output` move out of debug and add power option for external modules * Extra pack: Some app fixes * FBT: Fix vscode example config for debug builds - please run `./fbt vscode_dist` again if you had issues with debug builds size -* OFW PR 2658: Hitag (by @blackvault88) (with minor fixes, hitag excluded from add manually temporarily, + fixed lfrfid favourites start when key cannot be parsed) * OFW PR 2316: NFC V support (by @g3gg0 & @nvx) * OFW PR 2669: nfc: Fix MFUL tearing flags read (by @GMMan) * OFW PR 2666: BadUSB: Add fr-FR-mac key layout (by @FelixLgr) diff --git a/applications/main/application.fam b/applications/main/application.fam index 35a63ddab..4ac735e92 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -24,9 +24,9 @@ App( name="Basic applications for main menu", apptype=FlipperAppType.METAPACKAGE, provides=[ - "gpio", + #"gpio", #"ibutton", - "infrared", + #"infrared", "lfrfid", "nfc", "subghz", From ce7517d5ce090dc9d2ae8016e9b18c6799bcf112 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 17 May 2023 23:23:37 +0300 Subject: [PATCH 51/71] Update docs --- documentation/SubGHzRemoteProg.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/SubGHzRemoteProg.md b/documentation/SubGHzRemoteProg.md index a5357fe18..7cd8ea68a 100644 --- a/documentation/SubGHzRemoteProg.md +++ b/documentation/SubGHzRemoteProg.md @@ -68,6 +68,11 @@ Watch this videos to learn more (videos in Russian language): https://www.youtub 6. Long press (Right Arrow) - (0xF button - Btn:F) on Flipper for like 3-5 sec 7. Done? +## CAME Atomo + +1. Use google to find instructions - `how to program new CAME Atomo remote into receiver` +2. Watch this video to learn more (video in Russian language): https://www.youtube.com/watch?v=XeHUwfcSS30 + ## Nice Flor S - Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> Nice FloR-S 433Mhz From 15da25759e92b4252c4cddb0e3b3ef2686a9a82b Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 17 May 2023 23:30:22 +0100 Subject: [PATCH 52/71] Updated tv.ir Added Brandt TV Raw data Support (Thanks FAYVIXE for the codes and testing!) --- assets/resources/infrared/assets/tv.ir | 42 ++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 8c781eea9..ee20cd342 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 2nd May, 2023 -# Last Checked 2nd May, 2023 +# Last Updated 17th May, 2023 +# Last Checked 17th May, 2023 # name: POWER type: parsed @@ -1973,3 +1973,41 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 273 1816 272 767 277 789 255 785 249 791 253 787 246 1818 281 785 248 765 279 1812 276 789 255 759 275 791 253 1812 276 789 255 46372 281 1808 280 785 249 791 253 787 247 793 251 1814 274 791 253 1812 276 1814 274 791 253 1812 276 1814 274 1815 273 792 252 1813 275 42172 272 1819 280 785 249 765 279 761 273 768 276 764 280 1811 277 788 246 768 276 1815 273 792 252 788 246 794 250 1815 273 791 253 +# +# Brandt TV +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9346 4516 657 502 659 502 688 473 687 475 634 527 635 527 634 1662 633 528 662 1634 662 1658 635 1660 608 1687 609 1687 633 1662 634 527 634 1662 634 528 633 1663 632 529 632 530 631 1665 630 531 630 531 631 531 630 1666 630 531 630 1666 630 1666 630 532 630 1666 630 1666 630 1666 630 +# +name: VOL+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9348 4542 631 529 632 503 659 502 688 474 634 527 634 527 634 1661 635 528 661 1636 660 1659 634 1637 632 1687 608 1687 609 1687 609 552 632 1664 633 528 633 1663 633 529 632 1664 631 1665 631 531 630 531 631 531 631 1665 631 531 631 1666 630 531 631 531 630 1666 630 1666 630 1666 631 +# +name: VOL- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9376 4512 660 475 684 475 635 527 634 528 633 527 634 527 634 1662 662 525 636 1658 637 1658 637 1658 636 1659 636 1660 635 1660 635 526 635 1661 634 527 634 1662 633 1663 632 1664 631 1665 630 531 631 531 630 531 630 1666 630 531 630 531 631 531 630 531 631 1666 630 1666 630 1667 630 +# +name: CH+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9315 4513 657 501 660 501 659 500 661 502 688 499 661 474 634 1660 634 527 634 1660 634 1686 608 1687 607 1686 608 1686 608 1686 608 552 608 1686 608 1687 607 1687 632 530 631 1664 630 1665 630 531 630 531 630 531 630 531 630 531 630 1665 630 531 630 531 630 1665 630 1665 630 1665 630 +# +name: CH- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9345 4540 631 503 658 529 661 475 683 476 635 526 635 527 634 1661 634 527 662 1658 636 1659 634 1660 608 1686 609 1686 634 1661 634 527 634 1661 634 1662 633 1662 633 1663 632 1664 630 1665 630 531 630 531 630 531 630 531 631 531 630 531 630 531 630 532 629 1666 630 1666 629 1666 629 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9348 4516 657 501 660 502 659 502 659 502 688 474 634 527 634 1662 634 528 633 1687 634 1660 608 1664 631 1687 608 1687 609 1686 609 552 609 1687 608 553 632 529 633 529 632 530 631 1665 630 531 630 531 631 531 630 1666 630 1666 630 1666 630 1666 630 531 631 1666 630 1666 630 1666 630 From f8c4b6ba59943510d8af8ff9a3773dc61a2a6559 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 17 May 2023 23:30:43 +0100 Subject: [PATCH 53/71] Updated projectors.ir No new additions --- assets/resources/infrared/assets/projectors.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index 8e81a25bd..43c3723ef 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 2nd May, 2023 -# Last Checked 2nd May, 2023 +# Last Checked 17th May, 2023 # # ON name: POWER From f214f582695263e34596a58d3cd3de00983aec5e Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 17 May 2023 23:31:10 +0100 Subject: [PATCH 54/71] Updated fans.ir New additions --- assets/resources/infrared/assets/fans.ir | 34 ++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index e422bdce4..a741daf8e 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 2nd May, 2023 -# Last Checked 2nd May, 2023 +# Last Updated 17th May, 2023 +# Last Checked 17th May, 2023 # name: POWER type: raw @@ -1503,3 +1503,33 @@ type: parsed protocol: NEC address: 80 00 00 00 command: 1A 00 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1370 314 1375 320 519 1167 1370 322 1339 350 465 1221 467 1222 467 1222 466 1221 467 1221 467 1221 1317 7067 1317 372 1341 349 491 1198 1340 352 1337 353 486 1202 486 1226 462 1226 462 1227 461 1227 462 1227 1311 7073 1312 378 1311 378 462 1227 1311 378 1312 378 462 1227 462 1227 461 1227 462 1227 461 1227 461 1227 1311 7074 1311 378 1311 378 462 1227 1311 379 1310 379 461 1228 461 1228 460 1228 460 1228 460 1229 459 1228 1310 7076 1309 381 1309 380 460 1229 1310 381 1309 381 458 1230 458 1230 459 1230 459 1230 459 1230 458 1230 1309 7077 1309 380 1310 380 460 1229 1310 380 1310 380 459 1229 460 1229 459 1229 460 1229 459 1229 459 1229 1310 7075 1310 379 1310 380 459 1229 1310 380 1310 379 460 1229 460 1229 459 1229 460 1228 460 1229 459 1229 1310 7074 1310 380 1310 379 460 1229 1310 379 1310 379 460 1229 459 1229 459 1229 460 1229 460 1229 460 1228 1311 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1394 321 1369 321 520 1141 1397 321 1368 321 519 1142 495 1194 495 1194 494 1195 494 1195 1366 351 491 7891 1341 350 1337 353 487 1201 1337 354 1336 354 487 1202 487 1202 487 1202 487 1202 487 1202 1336 354 487 7900 1336 354 1337 354 486 1202 1337 354 1336 354 487 1202 487 1202 487 1203 486 1202 487 1202 1336 354 487 7901 1335 354 1336 354 487 1203 1335 354 1336 355 486 1203 486 1203 486 1203 486 1203 486 1203 1335 355 486 7900 1335 355 1335 355 486 1203 1335 355 1335 355 486 1203 486 1204 485 1204 485 1204 485 1203 1335 356 485 7900 1334 356 1334 356 485 1204 1334 356 1334 356 485 1204 485 1204 484 1204 485 1204 485 1204 1333 357 484 7901 1333 357 1333 380 460 1228 1310 380 1310 380 460 1228 460 1228 460 1228 460 1228 460 1228 1310 380 460 7924 1309 380 1310 380 460 1229 1309 380 1310 380 460 1229 459 1229 460 1229 459 1230 459 1230 1309 381 458 +# +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1343 372 1318 372 467 1194 1346 372 1318 372 467 1193 525 1164 1400 319 495 1166 523 1166 523 1167 521 7890 1344 347 1340 350 489 1200 1339 351 1339 351 488 1201 488 1201 1338 351 488 1201 488 1201 488 1201 488 7899 1338 352 1338 352 487 1201 1339 352 1338 352 488 1201 488 1201 1339 352 487 1202 487 1201 488 1201 488 7899 1338 352 1338 352 487 1201 1338 352 1338 352 487 1202 487 1202 1337 353 486 1202 487 1202 487 1202 487 7900 1337 353 1337 353 486 1202 1338 353 1337 353 486 1202 487 1202 1338 353 486 1202 487 1202 487 1202 487 7900 1337 353 1337 353 486 1203 1336 354 1336 354 485 1203 486 1204 1335 354 485 1203 485 1203 486 1203 486 7901 1336 378 1312 355 484 1205 1335 378 1312 378 461 1227 462 1227 1313 378 461 1228 461 1228 461 1228 461 7925 1312 378 1312 378 461 1228 1311 378 1312 378 461 1228 461 1228 1312 378 461 1228 461 1228 461 1228 461 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1370 319 1371 321 519 1167 1371 322 1366 324 488 1198 466 1222 466 1222 1317 375 464 1221 467 1220 468 7915 1317 372 1341 350 490 1197 1340 376 1313 377 462 1226 462 1226 462 1226 1312 377 462 1226 462 1226 462 7921 1312 377 1313 377 462 1226 1312 377 1312 377 462 1226 462 1226 462 1226 1312 378 462 1226 462 1226 462 7922 1312 377 1313 377 462 1226 1313 377 1312 377 462 1226 462 1226 462 1226 1313 377 462 1226 462 1226 462 7921 1312 377 1312 377 462 1226 1312 377 1313 377 462 1226 462 1226 462 1226 1312 377 462 1226 462 1226 462 7921 1312 377 1313 377 462 1226 1313 377 1312 354 485 1202 486 1202 486 1202 1337 352 487 1202 487 1201 487 7897 1337 352 1337 351 488 1200 1339 351 1339 352 487 1201 487 1201 488 1201 1338 352 487 1201 487 1201 488 7896 1338 352 1337 352 487 1201 1337 352 1337 352 487 1201 487 1201 487 1201 1338 352 487 1201 487 1201 487 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1369 349 1370 319 521 1167 1371 321 1340 349 466 1221 467 1222 467 1221 468 1221 1318 373 466 1220 468 7917 1344 347 1341 349 489 1198 1340 351 1338 352 487 1202 486 1202 486 1202 486 1202 1337 353 486 1202 486 7922 1312 377 1313 377 462 1227 1312 377 1313 378 461 1227 461 1227 462 1227 462 1227 1312 377 462 1227 461 7923 1312 378 1311 378 461 1227 1312 378 1311 377 462 1227 461 1227 461 1227 461 1227 1312 377 462 1227 462 7922 1312 378 1312 377 462 1227 1312 354 1336 354 485 1227 461 1227 461 1227 461 1203 1336 353 486 1203 486 7922 1312 353 1336 354 485 1203 1336 354 1336 354 485 1203 485 1227 461 1227 461 1227 1312 378 461 1227 461 7923 1312 378 1312 378 461 1227 1312 378 1312 378 461 1228 461 1228 461 1228 460 1228 1311 378 461 1227 461 7923 1311 378 1311 378 461 1227 1312 378 1312 378 461 1227 462 1227 461 1227 461 1227 1312 378 461 1228 461 6641 1312 378 1312 355 484 1228 1311 355 1335 378 461 1228 461 1228 460 1228 460 1228 1311 378 461 1228 460 7924 1311 379 1310 379 460 1228 1311 379 1311 379 460 1229 459 1229 459 1229 460 1229 1310 380 459 1229 459 7925 1310 380 1309 381 458 1230 1309 381 1309 381 458 1230 458 1231 457 1231 458 1231 1308 381 458 1231 457 7952 1283 407 1283 407 432 1256 1283 407 1283 407 432 1257 431 1257 432 1257 431 1257 1283 408 431 1257 431 From 9c3d2d736d47d3f8d5b9a1ed36bb80a06f4e4dcb Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 17 May 2023 23:31:39 +0100 Subject: [PATCH 55/71] Updated audio.ir No new additions --- assets/resources/infrared/assets/audio.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index ce6d0a7aa..14c18c04c 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 14th Apr, 2023 -# Last Checked 2nd May, 2023 +# Last Checked 17th May, 2023 # name: POWER type: parsed From 0164e061a44513354b0379497e87dabf175af3a6 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 17 May 2023 23:32:00 +0100 Subject: [PATCH 56/71] Updated ac.ir No new additions --- assets/resources/infrared/assets/ac.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 777d5399c..9a272af0e 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 2nd May, 2023 -# Last Checked 2nd May, 2023 +# Last Checked 17th May, 2023 # name: POWER type: raw From 5b4ec05dbe42dd073fdb0de3cf9dd1dcd57cbe38 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 17 May 2023 23:47:04 +0100 Subject: [PATCH 57/71] Updated tv.ir More additions --- assets/resources/infrared/assets/tv.ir | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index ee20cd342..a0fe73bd9 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -2011,3 +2011,39 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 9348 4516 657 501 660 502 659 502 659 502 688 474 634 527 634 1662 634 528 633 1687 634 1660 608 1664 631 1687 608 1687 609 1686 609 552 609 1687 608 553 632 529 633 529 632 530 631 1665 630 531 630 531 631 531 630 1666 630 1666 630 1666 630 1666 630 531 631 1666 630 1666 630 1666 630 +# +name: POWER +type: parsed +protocol: Samsung32 +address: 3E 00 00 00 +command: 0C 00 00 00 +# +name: VOL+ +type: parsed +protocol: Samsung32 +address: 3E 00 00 00 +command: 14 00 00 00 +# +name: VOL- +type: parsed +protocol: Samsung32 +address: 3E 00 00 00 +command: 15 00 00 00 +# +name: CH+ +type: parsed +protocol: Samsung32 +address: 3E 00 00 00 +command: 12 00 00 00 +# +name: CH- +type: parsed +protocol: Samsung32 +address: 3E 00 00 00 +command: 13 00 00 00 +# +name: MUTE +type: parsed +protocol: Samsung32 +address: 3E 00 00 00 +command: 0D 00 00 00 From 78a2b12be099868b3efff0ffa0f1af6c5662e7d6 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Wed, 17 May 2023 23:50:24 +0100 Subject: [PATCH 58/71] Updated ac.ir New additions --- assets/resources/infrared/assets/ac.ir | 50 +++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 9a272af0e..44f685bce 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,6 +1,6 @@ Filetype: IR library file Version: 1 -# Last Updated 2nd May, 2023 +# Last Updated 17th May, 2023 # Last Checked 17th May, 2023 # name: POWER @@ -1770,3 +1770,51 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 645 17766 3059 8884 533 461 557 1427 560 435 584 433 534 458 534 458 534 458 588 404 587 405 586 1399 586 407 584 408 558 1429 555 1457 502 491 525 1460 528 1458 554 1431 554 1431 555 1431 554 438 555 438 554 439 553 440 553 464 528 464 528 464 529 465 528 465 526 467 526 467 527 465 553 439 554 439 554 438 554 439 554 439 553 439 554 439 553 439 553 439 554 440 552 464 528 464 528 465 528 466 527 466 501 492 525 468 526 466 527 466 553 439 554 439 553 440 552 1433 553 1433 553 2936 3024 8893 552 1458 527 465 528 465 527 467 526 467 500 493 524 468 526 467 526 467 552 1434 552 441 552 441 552 1435 550 465 528 1458 527 1458 528 1459 527 1485 501 1485 500 1486 501 491 527 466 528 465 528 465 527 465 528 465 527 465 528 465 528 465 527 465 528 465 527 466 527 466 527 492 500 492 501 492 500 493 500 493 500 492 526 466 527 465 527 465 527 465 527 466 527 466 527 466 527 465 527 466 526 466 527 466 526 467 526 493 499 493 474 518 499 494 500 493 526 2936 3025 8918 526 1459 527 466 527 466 527 466 526 466 527 466 526 467 526 467 525 467 526 1460 525 493 499 493 498 495 498 495 499 493 500 493 525 1460 527 1460 525 1460 526 1460 525 1460 526 1460 526 468 525 1487 499 1487 497 495 498 495 499 493 500 1486 525 1460 525 1460 525 467 525 467 526 467 525 467 526 468 524 1462 524 468 524 494 498 1488 497 1488 499 494 499 493 525 468 525 1461 524 468 525 468 524 468 525 468 525 468 524 468 525 469 524 494 498 494 499 1488 473 1514 524 +# +name: POWER +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 46 00 00 00 +# +name: TEMP+ +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 07 00 00 00 +# +name: TEMP- +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 0C 00 00 00 +# +name: TIMER +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 42 00 00 00 +# +name: MODE +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 09 00 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9464 4442 637 583 601 584 601 584 601 584 600 582 603 580 604 580 630 555 629 1637 627 1665 598 1668 571 1696 569 1696 594 1672 594 1672 592 1674 593 1673 593 591 594 1673 593 1672 593 591 591 593 569 616 568 616 593 593 591 1674 593 592 593 593 592 1675 592 1675 592 1700 568 1700 568 39559 9409 2272 567 96244 9461 2246 594 +# +name: SWING +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9505 4415 688 531 602 584 602 585 601 584 601 581 604 580 605 580 605 581 628 1636 628 1665 599 1667 597 1670 595 1671 595 1671 596 1672 595 1673 595 1673 595 592 594 591 595 1672 595 590 595 590 595 591 594 591 594 590 595 1672 595 1672 595 591 594 1672 595 1672 595 1672 595 1672 595 39565 9410 2244 592 96225 9442 2244 592 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9515 4477 575 611 576 551 636 610 576 611 575 610 576 581 606 580 606 580 606 1661 631 1639 628 1666 600 1667 599 1671 596 1673 595 1673 595 1673 595 593 594 617 570 594 593 1674 594 617 570 617 570 617 570 617 570 1699 569 1699 569 1699 569 618 569 1699 569 1698 570 1698 570 1699 569 From 2b017664607bf00a4ab0d3682b956187ce35370d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 18 May 2023 02:38:45 +0300 Subject: [PATCH 59/71] Mifare mini with sak 89 support --- lib/nfc/protocols/mifare_classic.c | 5 +++-- lib/nfc/protocols/mifare_common.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index d2d7467dc..e9d43baf5 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -376,7 +376,8 @@ bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num) { bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { UNUSED(ATQA1); - if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { + 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 @@ -393,7 +394,7 @@ MfClassicType mf_classic_get_classic_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t 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 90b57e1f0..5277cc8cd 100644 --- a/lib/nfc/protocols/mifare_common.c +++ b/lib/nfc/protocols/mifare_common.c @@ -6,7 +6,8 @@ MifareType mifare_common_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { 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; From e3cf9a03068c374d93b765902b55a0c1ce03440d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 18 May 2023 02:40:10 +0300 Subject: [PATCH 60/71] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b016121a..a16fc8256 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Plugins: WiFi Marauder -> Added sniff pmkid on selected aps from 0.10.4 update (by @clipboard1) * Plugins: SubGHz Bruteforcer -> Increase delay just a little bit to fix some cases when receiver will not get codes and decrease manual transmit delay * Plugins: UART Terminal -> Fix crashes on plugin load with RX connected +* NFC: Mifare mini with SAK 0x89 support * SubGHz: **CAME Atomo - Add manually support and custom buttons support** * SubGHz: Fix crashes when deleting signals using right arrow button in `Read` mode * SubGHz: Restore Rx indication after deletion after Memory is FULL (by @wosk | PR #464) @@ -16,6 +17,7 @@ * GUI: Text Input improvements, added cursor and ability to set minimal length (by @Willy-JL) * BT API: Functions that allow to change bt mac address and device broadcasted name (by @Willy-JL and XFW contributors) * Infrared: `External output` move out of debug and add power option for external modules +* Infrared: Updated universal remote assets (by @amec0e | PR #474) * Extra pack: Some app fixes * FBT: Fix vscode example config for debug builds - please run `./fbt vscode_dist` again if you had issues with debug builds size * OFW PR 2316: NFC V support (by @g3gg0 & @nvx) From 11d4ad1e233b54ce1807416b8fbacb077a7999d3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 18 May 2023 03:16:51 +0300 Subject: [PATCH 61/71] fix builds --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 7776bce6b..a03063528 100644 --- a/.drone.yml +++ b/.drone.yml @@ -85,7 +85,7 @@ steps: - zip -r artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.zip artifacts-extra-apps/f7-update-${DRONE_TAG}e - zip -r artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.zip artifacts-ofw-anims/f7-update-${DRONE_TAG}n - zip -r artifacts-default/flipper-z-f7-update-${DRONE_TAG}.zip artifacts-default/f7-update-${DRONE_TAG} - - tar czpf artifacts-default/flipper-z-any-scripts-${DRONE_TAG}.tgz scripts debug + - tar czpf artifacts-default/flipper-z-any-scripts-${DRONE_TAG}.tgz scripts - rm -rf artifacts-extra-apps/f7-update-${DRONE_TAG} - rm -rf artifacts-ofw-anims/f7-update-${DRONE_TAG} - rm -rf artifacts-default/f7-update-${DRONE_TAG} From 64817ac2166b67282534546193020030e985aace Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Thu, 18 May 2023 10:59:08 +0300 Subject: [PATCH 62/71] Move dangerous_settings check --- applications/main/subghz/application.fam | 13 ++++++++- .../main/subghz/subghz_dangerous_freq.c | 23 ++++++++++++++++ .../targets/f7/furi_hal/furi_hal_subghz.c | 27 ++++++++----------- .../targets/f7/furi_hal/furi_hal_subghz.h | 5 ++-- .../targets/f7/furi_hal/furi_hal_subghz_i.h | 3 +++ 5 files changed, 52 insertions(+), 19 deletions(-) create mode 100644 applications/main/subghz/subghz_dangerous_freq.c create mode 100644 firmware/targets/f7/furi_hal/furi_hal_subghz_i.h diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index f0dc66e89..6cd28bd85 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_dangerous_settings", + ], icon="A_Sub1ghz_14", stack_size=3 * 1024, order=10, @@ -23,3 +26,11 @@ App( requires=["subghz"], order=40, ) + +App( + appid="subghz_load_dangerous_settings", + apptype=FlipperAppType.STARTUP, + entry_point="subghz_dangerous_freq", + requires=["subghz"], + order=41, +) diff --git a/applications/main/subghz/subghz_dangerous_freq.c b/applications/main/subghz/subghz_dangerous_freq.c new file mode 100644 index 000000000..69a54f04b --- /dev/null +++ b/applications/main/subghz/subghz_dangerous_freq.c @@ -0,0 +1,23 @@ +#include +#include + +#include + +#include + +void subghz_dangerous_freq() { + bool is_extended_i = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + + if(flipper_format_file_open_existing(fff_data_file, "/ext/subghz/assets/dangerous_settings")) { + flipper_format_read_bool( + fff_data_file, "yes_i_want_to_destroy_my_flipper", &is_extended_i, 1); + } + + furi_hal_subghz_set_dangerous_frequency(is_extended_i); + + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 6a566d471..06a7990fb 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -41,6 +41,7 @@ volatile FuriHalSubGhz furi_hal_subghz = { .rolling_counter_mult = 1, .ext_module_power_disabled = false, .timestamp_file_names = false, + .dangerous_frequency_i = false, }; void furi_hal_subghz_select_radio_type(SubGhzRadioType state) { @@ -96,6 +97,10 @@ bool furi_hal_subghz_get_timestamp_file_names(void) { return furi_hal_subghz.timestamp_file_names; } +void furi_hal_subghz_set_dangerous_frequency(bool state_i) { + furi_hal_subghz.dangerous_frequency_i = state_i; +} + void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin) { furi_hal_subghz.async_mirror_pin = pin; } @@ -448,29 +453,19 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { } bool furi_hal_subghz_is_tx_allowed(uint32_t value) { - bool is_extended = false; + bool allow_extended_for_int = furi_hal_subghz.dangerous_frequency_i; - // TODO: !!! Move file check to another place - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - - if(flipper_format_file_open_existing(fff_data_file, "/ext/subghz/assets/dangerous_settings")) { - flipper_format_read_bool( - fff_data_file, "yes_i_want_to_destroy_my_flipper", &is_extended, 1); - } - - flipper_format_free(fff_data_file); - furi_record_close(RECORD_STORAGE); - - if(!(value >= 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 dangerous 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 35eab4faf..0ef7bd90a 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 dangerous_frequency_i : 1; } FuriHalSubGhz; extern volatile FuriHalSubGhz furi_hal_subghz; 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..e7fe2602f --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz_i.h @@ -0,0 +1,3 @@ +#pragma once + +void furi_hal_subghz_set_dangerous_frequency(bool state_i); \ No newline at end of file From e8264dc6e73005a714145dfc667b28d1c57ba13b Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Thu, 18 May 2023 13:48:25 +0300 Subject: [PATCH 63/71] subghz_remote_app --- .../main/subghz_remote/application.fam | 7 +- .../helpers/subrem_custom_event.h | 18 + .../main/subghz_remote/helpers/subrem_types.h | 32 + .../main/subghz_remote/scenes/subrem_scene.c | 30 + .../main/subghz_remote/scenes/subrem_scene.h | 29 + .../scenes/subrem_scene_config.h | 3 + .../scenes/subrem_scene_openmapfile.c | 41 + .../scenes/subrem_scene_remote.c | 129 ++ .../subghz_remote/scenes/subrem_scene_start.c | 74 ++ .../main/subghz_remote/subghz_remote_app.c | 1151 ++--------------- .../main/subghz_remote/subghz_remote_app_i.c | 449 +++++++ .../main/subghz_remote/subghz_remote_app_i.h | 84 ++ .../main/subghz_remote/views/remote.c | 302 +++++ .../main/subghz_remote/views/remote.h | 38 + 14 files changed, 1338 insertions(+), 1049 deletions(-) create mode 100644 applications/main/subghz_remote/helpers/subrem_custom_event.h create mode 100644 applications/main/subghz_remote/helpers/subrem_types.h create mode 100644 applications/main/subghz_remote/scenes/subrem_scene.c create mode 100644 applications/main/subghz_remote/scenes/subrem_scene.h create mode 100644 applications/main/subghz_remote/scenes/subrem_scene_config.h create mode 100644 applications/main/subghz_remote/scenes/subrem_scene_openmapfile.c create mode 100644 applications/main/subghz_remote/scenes/subrem_scene_remote.c create mode 100644 applications/main/subghz_remote/scenes/subrem_scene_start.c create mode 100644 applications/main/subghz_remote/subghz_remote_app_i.c create mode 100644 applications/main/subghz_remote/subghz_remote_app_i.h create mode 100644 applications/main/subghz_remote/views/remote.c create mode 100644 applications/main/subghz_remote/views/remote.h diff --git a/applications/main/subghz_remote/application.fam b/applications/main/subghz_remote/application.fam index e09f8500f..e785043e6 100644 --- a/applications/main/subghz_remote/application.fam +++ b/applications/main/subghz_remote/application.fam @@ -3,7 +3,10 @@ App( name="Sub-GHz Remote", apptype=FlipperAppType.APP, entry_point="subghz_remote_app", - cdefines=["APP_SUBGHZREMOTE"], + cdefines=[ + "APP_SUBGHZREMOTE", + "SUBREM_LIGHT", + ], requires=[ "gui", "dialogs", @@ -11,4 +14,4 @@ App( icon="A_SubGHzRemote_14", stack_size=4 * 1024, order=11, -) +) \ No newline at end of file diff --git a/applications/main/subghz_remote/helpers/subrem_custom_event.h b/applications/main/subghz_remote/helpers/subrem_custom_event.h new file mode 100644 index 000000000..46ab8ad54 --- /dev/null +++ b/applications/main/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/main/subghz_remote/helpers/subrem_types.h b/applications/main/subghz_remote/helpers/subrem_types.h new file mode 100644 index 000000000..1b99aac6d --- /dev/null +++ b/applications/main/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/main/subghz_remote/scenes/subrem_scene.c b/applications/main/subghz_remote/scenes/subrem_scene.c new file mode 100644 index 000000000..c45285b96 --- /dev/null +++ b/applications/main/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/main/subghz_remote/scenes/subrem_scene.h b/applications/main/subghz_remote/scenes/subrem_scene.h new file mode 100644 index 000000000..5c01f8ca5 --- /dev/null +++ b/applications/main/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/main/subghz_remote/scenes/subrem_scene_config.h b/applications/main/subghz_remote/scenes/subrem_scene_config.h new file mode 100644 index 000000000..93d4de642 --- /dev/null +++ b/applications/main/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/main/subghz_remote/scenes/subrem_scene_openmapfile.c b/applications/main/subghz_remote/scenes/subrem_scene_openmapfile.c new file mode 100644 index 000000000..3391845e1 --- /dev/null +++ b/applications/main/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/main/subghz_remote/scenes/subrem_scene_remote.c b/applications/main/subghz_remote/scenes/subrem_scene_remote.c new file mode 100644 index 000000000..d543ca999 --- /dev/null +++ b/applications/main/subghz_remote/scenes/subrem_scene_remote.c @@ -0,0 +1,129 @@ +#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); + } + 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/main/subghz_remote/scenes/subrem_scene_start.c b/applications/main/subghz_remote/scenes/subrem_scene_start.c new file mode 100644 index 000000000..962eda54c --- /dev/null +++ b/applications/main/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/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index 8cef4da3a..de80c9cdd 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -1,766 +1,27 @@ -#include +#include "subghz_remote_app_i.h" -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include #include -#include -#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(); @@ -773,24 +34,77 @@ 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, STORAGE_APP_DATA_PATH_PREFIX); - 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; + + scene_manager_next_scene(app->scene_manager, SubRemSceneStart); 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 @@ -798,304 +112,47 @@ 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); } int32_t subghz_remote_app(void* p) { UNUSED(p); - 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); - storage_common_migrate(storage, EXT_PATH("unirf"), SUBREMOTEMAP_FOLDER); - - 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/main/subghz_remote/subghz_remote_app_i.c b/applications/main/subghz_remote/subghz_remote_app_i.c new file mode 100644 index 000000000..cde4292a4 --- /dev/null +++ b/applications/main/subghz_remote/subghz_remote_app_i.c @@ -0,0 +1,449 @@ +#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 + // TODO: Standart name or part of name + furi_string_set_str(sub_preset->label, "N/A"); + } 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/main/subghz_remote/subghz_remote_app_i.h b/applications/main/subghz_remote/subghz_remote_app_i.h new file mode 100644 index 000000000..98cb4ac48 --- /dev/null +++ b/applications/main/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 ANY_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); \ No newline at end of file diff --git a/applications/main/subghz_remote/views/remote.c b/applications/main/subghz_remote/views/remote.c new file mode 100644 index 000000000..85c63568e --- /dev/null +++ b/applications/main/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/main/subghz_remote/views/remote.h b/applications/main/subghz_remote/views/remote.h new file mode 100644 index 000000000..76121cf8a --- /dev/null +++ b/applications/main/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 From 6f9f3e949617d997492ba46c1c6f60a1cefaff30 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Thu, 18 May 2023 15:16:31 +0300 Subject: [PATCH 64/71] minor fixes --- applications/main/subghz_remote/scenes/subrem_scene_remote.c | 1 + applications/main/subghz_remote/subghz_remote_app_i.c | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz_remote/scenes/subrem_scene_remote.c b/applications/main/subghz_remote/scenes/subrem_scene_remote.c index d543ca999..c24583233 100644 --- a/applications/main/subghz_remote/scenes/subrem_scene_remote.c +++ b/applications/main/subghz_remote/scenes/subrem_scene_remote.c @@ -93,6 +93,7 @@ bool subrem_scene_remote_on_event(void* context, SceneManagerEvent event) { 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) { diff --git a/applications/main/subghz_remote/subghz_remote_app_i.c b/applications/main/subghz_remote/subghz_remote_app_i.c index cde4292a4..9b0f77d17 100644 --- a/applications/main/subghz_remote/subghz_remote_app_i.c +++ b/applications/main/subghz_remote/subghz_remote_app_i.c @@ -106,8 +106,7 @@ static bool subrem_map_preset_load(SubGhzRemoteApp* app, FlipperFormat* fff_data #if FURI_DEBUG FURI_LOG_W(TAG, "No Label for %s", map_file_labels[i][0]); #endif - // TODO: Standart name or part of name - furi_string_set_str(sub_preset->label, "N/A"); + path_extract_filename(sub_preset->file_path, sub_preset->label, true); } else { FURI_LOG_I( TAG, From 6f205edd9a6392b1a58155bc4fcab71eb4246076 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Thu, 18 May 2023 19:20:34 +0300 Subject: [PATCH 65/71] minor fixes --- applications/main/subghz_remote/subghz_remote_app.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index de80c9cdd..0b3f645ab 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -35,7 +35,7 @@ SubGhzRemoteApp* subghz_remote_app_alloc() { furi_hal_power_suppress_charge_enter(); app->file_path = furi_string_alloc(); - furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); + furi_string_set(app->file_path, SUBREM_APP_FOLDER); // GUI app->gui = furi_record_open(RECORD_GUI); @@ -97,7 +97,11 @@ SubGhzRemoteApp* subghz_remote_app_alloc() { 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; } From 99dde674cabb5fb7447e48579d680a02d47c667d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 May 2023 03:31:46 +0300 Subject: [PATCH 66/71] small changes --- applications/main/subghz/application.fam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index 6cd28bd85..60cf0c0b7 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -31,6 +31,6 @@ App( appid="subghz_load_dangerous_settings", apptype=FlipperAppType.STARTUP, entry_point="subghz_dangerous_freq", - requires=["subghz"], - order=41, + requires=["storage", "subghz"], + order=500, ) From a7ee5fea93ad424c159b9a9a242a13d6b79f5c8f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 May 2023 04:33:14 +0300 Subject: [PATCH 67/71] Name changer as service + extra checks --- applications/services/application.fam | 1 + applications/services/desktop/desktop.c | 23 ---------- .../services/namechanger/application.fam | 8 ++++ .../services/namechanger/namechange.c | 46 +++++++++++++++++++ lib/toolbox/namechanger.c | 14 ++++++ 5 files changed, 69 insertions(+), 23 deletions(-) create mode 100644 applications/services/namechanger/application.fam create mode 100644 applications/services/namechanger/namechange.c diff --git a/applications/services/application.fam b/applications/services/application.fam index aec49b231..afa69301d 100644 --- a/applications/services/application.fam +++ b/applications/services/application.fam @@ -9,5 +9,6 @@ App( "desktop", "loader", "power", + "namechange_srv", ], ) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 25e2017c2..77999dfcc 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -9,9 +9,6 @@ #include #include -#include -#include - #include "animations/animation_manager.h" #include "desktop/scenes/desktop_scene.h" #include "desktop/scenes/desktop_scene_i.h" @@ -429,26 +426,6 @@ int32_t desktop_srv(void* p) { scene_manager_next_scene(desktop->scene_manager, DesktopSceneFault); } - // I added some very bydlo kod here, and thrown some delays to make it worse, pls don't look at it, it will make you cry from laugh - if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { - if(NameChanger_Init()) { - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_close(cli); - furi_delay_ms(2); - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); - - furi_delay_ms(3); - Bt* bt = furi_record_open(RECORD_BT); - if(!bt_set_profile(bt, BtProfileSerial)) { - FURI_LOG_D(TAG, "Failed to touch bluetooth to name change"); - } - furi_record_close(RECORD_BT); - bt = NULL; - furi_delay_ms(3); - } - } - view_dispatcher_run(desktop->view_dispatcher); desktop_free(desktop); diff --git a/applications/services/namechanger/application.fam b/applications/services/namechanger/application.fam new file mode 100644 index 000000000..a4bc23938 --- /dev/null +++ b/applications/services/namechanger/application.fam @@ -0,0 +1,8 @@ +App( + appid="namechange_srv", + apptype=FlipperAppType.STARTUP, + entry_point="namechange_on_system_start", + requires=["storage", "cli", "bt"], + conflicts=["updater"], + order=600, +) \ No newline at end of file diff --git a/applications/services/namechanger/namechange.c b/applications/services/namechanger/namechange.c new file mode 100644 index 000000000..22cb0d9de --- /dev/null +++ b/applications/services/namechanger/namechange.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +#include +#include + +int32_t namechange_on_system_start(void* p) { + UNUSED(p); + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + return 0; + } + + // Wait for all required services to start and create their records + uint8_t timeout = 0; + while(!furi_record_exists(RECORD_CLI) || !furi_record_exists(RECORD_BT) || + !furi_record_exists(RECORD_STORAGE)) { + timeout++; + if(timeout > 250) { + return 0; + } + furi_delay_ms(5); + } + + // Hehe bad code now here, bad bad bad, very bad, bad example, dont take it, make it better + + if(NameChanger_Init()) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_close(cli); + furi_delay_ms(2); // why i added delays here + cli_session_open(cli, &cli_vcp); + furi_record_close(RECORD_CLI); + + furi_delay_ms(3); + Bt* bt = furi_record_open(RECORD_BT); + if(!bt_set_profile(bt, BtProfileSerial)) { + //FURI_LOG_D(TAG, "Failed to touch bluetooth to name change"); + } + furi_record_close(RECORD_BT); + bt = NULL; + furi_delay_ms(3); + } + + return 0; +} \ No newline at end of file diff --git a/lib/toolbox/namechanger.c b/lib/toolbox/namechanger.c index 33842bcd7..94e08ac13 100644 --- a/lib/toolbox/namechanger.c +++ b/lib/toolbox/namechanger.c @@ -6,6 +6,20 @@ bool NameChanger_Init() { Storage* storage = furi_record_open(RECORD_STORAGE); + + // Kostil + velosiped = top ficha + uint8_t timeout = 0; + while(timeout < 11) { + if(storage_sd_status(storage) == FSE_OK) break; + furi_delay_ms(250); + timeout++; + /*if(timeout == 10) { + // Failed to init namechanger, SD card not ready + furi_record_close(RECORD_STORAGE); + return false; + }*/ + } + FuriString* str = furi_string_alloc(); FlipperFormat* file = flipper_format_file_alloc(storage); From 324186dad2917372f520fd7e55d499b33d126e89 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 May 2023 04:49:29 +0300 Subject: [PATCH 68/71] Add back cnt in debug --- .../scenes/subghz_scene_radio_settings.c | 84 ++++++++++++++----- 1 file changed, 65 insertions(+), 19 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_radio_settings.c b/applications/main/subghz/scenes/subghz_scene_radio_settings.c index 8e8d05019..b60ea0b2d 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]); From 481a8e0f33bb32dfedd8d0650bef1b07189ccd80 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 May 2023 04:51:25 +0300 Subject: [PATCH 69/71] Move after nchgr service --- applications/main/subghz/application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index 60cf0c0b7..0b403feda 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -32,5 +32,5 @@ App( apptype=FlipperAppType.STARTUP, entry_point="subghz_dangerous_freq", requires=["storage", "subghz"], - order=500, + order=650, ) From 10d926de0df79fd590d044646a4009075a79bbb6 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 May 2023 05:09:14 +0300 Subject: [PATCH 70/71] EXT_PATH only --- applications/main/subghz_remote/subghz_remote_app_i.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/subghz_remote/subghz_remote_app_i.h b/applications/main/subghz_remote/subghz_remote_app_i.h index 98cb4ac48..1cbdbd5cd 100644 --- a/applications/main/subghz_remote/subghz_remote_app_i.h +++ b/applications/main/subghz_remote/subghz_remote_app_i.h @@ -26,7 +26,7 @@ #include #include -#define SUBREM_APP_FOLDER ANY_PATH("subghz_remote") +#define SUBREM_APP_FOLDER EXT_PATH("subghz_remote") #define SUBREM_MAX_LEN_NAME 64 typedef struct { From e7965c6d95ceb465296f53b3a49f146b719d11e5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 19 May 2023 06:33:15 +0300 Subject: [PATCH 71/71] De-libify name changer --- applications/services/application.fam | 2 +- .../services/namechanger/application.fam | 4 +- .../services/namechanger/namechange.c | 46 ------------------- .../services/namechanger}/namechanger.c | 46 ++++++++++++++++++- .../services/namechanger/namechanger.h | 5 ++ .../desktop_settings/desktop_settings_app.c | 2 +- lib/toolbox/namechanger.h | 18 -------- 7 files changed, 54 insertions(+), 69 deletions(-) delete mode 100644 applications/services/namechanger/namechange.c rename {lib/toolbox => applications/services/namechanger}/namechanger.c (60%) create mode 100644 applications/services/namechanger/namechanger.h delete mode 100644 lib/toolbox/namechanger.h diff --git a/applications/services/application.fam b/applications/services/application.fam index afa69301d..0b5009096 100644 --- a/applications/services/application.fam +++ b/applications/services/application.fam @@ -9,6 +9,6 @@ App( "desktop", "loader", "power", - "namechange_srv", + "namechanger_srv", ], ) diff --git a/applications/services/namechanger/application.fam b/applications/services/namechanger/application.fam index a4bc23938..0eaeab987 100644 --- a/applications/services/namechanger/application.fam +++ b/applications/services/namechanger/application.fam @@ -1,7 +1,7 @@ App( - appid="namechange_srv", + appid="namechanger_srv", apptype=FlipperAppType.STARTUP, - entry_point="namechange_on_system_start", + entry_point="namechanger_on_system_start", requires=["storage", "cli", "bt"], conflicts=["updater"], order=600, diff --git a/applications/services/namechanger/namechange.c b/applications/services/namechanger/namechange.c deleted file mode 100644 index 22cb0d9de..000000000 --- a/applications/services/namechanger/namechange.c +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -int32_t namechange_on_system_start(void* p) { - UNUSED(p); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { - return 0; - } - - // Wait for all required services to start and create their records - uint8_t timeout = 0; - while(!furi_record_exists(RECORD_CLI) || !furi_record_exists(RECORD_BT) || - !furi_record_exists(RECORD_STORAGE)) { - timeout++; - if(timeout > 250) { - return 0; - } - furi_delay_ms(5); - } - - // Hehe bad code now here, bad bad bad, very bad, bad example, dont take it, make it better - - if(NameChanger_Init()) { - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_close(cli); - furi_delay_ms(2); // why i added delays here - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); - - furi_delay_ms(3); - Bt* bt = furi_record_open(RECORD_BT); - if(!bt_set_profile(bt, BtProfileSerial)) { - //FURI_LOG_D(TAG, "Failed to touch bluetooth to name change"); - } - furi_record_close(RECORD_BT); - bt = NULL; - furi_delay_ms(3); - } - - return 0; -} \ No newline at end of file diff --git a/lib/toolbox/namechanger.c b/applications/services/namechanger/namechanger.c similarity index 60% rename from lib/toolbox/namechanger.c rename to applications/services/namechanger/namechanger.c index 94e08ac13..796dd2d3b 100644 --- a/lib/toolbox/namechanger.c +++ b/applications/services/namechanger/namechanger.c @@ -1,10 +1,15 @@ #include "namechanger.h" +#include #include +#include +#include +#include +#include #include #define TAG "NameChanger" -bool NameChanger_Init() { +static bool namechanger_init() { Storage* storage = furi_record_open(RECORD_STORAGE); // Kostil + velosiped = top ficha @@ -64,4 +69,43 @@ bool NameChanger_Init() { furi_string_free(str); return res; +} + +int32_t namechanger_on_system_start(void* p) { + UNUSED(p); + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + return 0; + } + + // Wait for all required services to start and create their records + uint8_t timeout = 0; + while(!furi_record_exists(RECORD_CLI) || !furi_record_exists(RECORD_BT) || + !furi_record_exists(RECORD_STORAGE)) { + timeout++; + if(timeout > 250) { + return 0; + } + furi_delay_ms(5); + } + + // Hehe bad code now here, bad bad bad, very bad, bad example, dont take it, make it better + + if(namechanger_init()) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_close(cli); + furi_delay_ms(2); // why i added delays here + cli_session_open(cli, &cli_vcp); + furi_record_close(RECORD_CLI); + + furi_delay_ms(3); + Bt* bt = furi_record_open(RECORD_BT); + if(!bt_set_profile(bt, BtProfileSerial)) { + //FURI_LOG_D(TAG, "Failed to touch bluetooth to name change"); + } + furi_record_close(RECORD_BT); + bt = NULL; + furi_delay_ms(3); + } + + return 0; } \ No newline at end of file diff --git a/applications/services/namechanger/namechanger.h b/applications/services/namechanger/namechanger.h new file mode 100644 index 000000000..dac1b7952 --- /dev/null +++ b/applications/services/namechanger/namechanger.h @@ -0,0 +1,5 @@ +#pragma once + +#define NAMECHANGER_HEADER "Flipper Name File" +#define NAMECHANGER_VERSION 1 +#define NAMECHANGER_PATH EXT_PATH("dolphin/name.settings") diff --git a/applications/settings/desktop_settings/desktop_settings_app.c b/applications/settings/desktop_settings/desktop_settings_app.c index 317d57654..7e04271f2 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include diff --git a/lib/toolbox/namechanger.h b/lib/toolbox/namechanger.h deleted file mode 100644 index bfaadec15..000000000 --- a/lib/toolbox/namechanger.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#define NAMECHANGER_HEADER "Flipper Name File" -#define NAMECHANGER_VERSION 1 -#define NAMECHANGER_PATH EXT_PATH("dolphin/name.settings") - -#include "stdbool.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Initializes the name changer. (Load name file, apply changes) -bool NameChanger_Init(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file