From fe31d0cc12264b750325a39023cf03d8d470080f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 6 Oct 2025 20:31:39 +0300 Subject: [PATCH 01/30] upd changelog add fix by noproto --- CHANGELOG.md | 1 + .../mf_ultralight/mf_ultralight.c | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 705689ae0..8a74d9b9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * OFW: **JS views finished** * OFW: BLE: improved pairing security * OFW: FeliCa Emulation: Handle certain Polling commands in firmware +* OFW PR 4287: Fix Ultralight EV1 regression (by @noproto) * OFW PR 4271: NFC: **Ultralight C NFC App Key Management, Dictionary Attack** (by @noproto) * OFW PR 4265: NFC: **Fix read crash** with unexpectedly large MFC AUTH(0) response (by @WillyJL) * OFW PR 4251: CLI: **Fix long delay** with quick connect/disconnect (by @WillyJL) diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index aa3cc2e75..5a0c88cf7 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -151,13 +151,16 @@ static NfcCommand if(!mf_ultralight_event->data->auth_context.skip_auth) { mf_ultralight_event->data->auth_context.password = instance->mf_ul_auth->password; - // Only set tdes_key for Manual/Reader auth types, not for dictionary attacks - if(instance->mf_ul_auth->type == MfUltralightAuthTypeManual || - instance->mf_ul_auth->type == MfUltralightAuthTypeReader) { - mf_ultralight_event->data->key_request_data.key = instance->mf_ul_auth->tdes_key; - mf_ultralight_event->data->key_request_data.key_provided = true; - } else { - mf_ultralight_event->data->key_request_data.key_provided = false; + if(data->type == MfUltralightTypeMfulC) { + // Only set tdes_key for Manual/Reader auth types, not for dictionary attacks + if(instance->mf_ul_auth->type == MfUltralightAuthTypeManual || + instance->mf_ul_auth->type == MfUltralightAuthTypeReader) { + mf_ultralight_event->data->key_request_data.key = + instance->mf_ul_auth->tdes_key; + mf_ultralight_event->data->key_request_data.key_provided = true; + } else { + mf_ultralight_event->data->key_request_data.key_provided = false; + } } } } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthSuccess) { From 4d2a08cc1126798356d343e18e2b595f19a81a20 Mon Sep 17 00:00:00 2001 From: Roman Belyakovsky Date: Sat, 11 Oct 2025 11:27:31 +0300 Subject: [PATCH 02/30] HID PTT: adding global zoom shortcuts for macos --- applications/system/hid_app/views/hid_ptt.c | 57 +++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/applications/system/hid_app/views/hid_ptt.c b/applications/system/hid_app/views/hid_ptt.c index 58599eb51..a0a313e75 100644 --- a/applications/system/hid_app/views/hid_ptt.c +++ b/applications/system/hid_app/views/hid_ptt.c @@ -54,6 +54,7 @@ enum HidPushToTalkAppIndex { HidPushToTalkAppIndexTeamSpeak, HidPushToTalkAppIndexWebex, HidPushToTalkAppIndexZoom, + HidPushToTalkAppIndexZoomGlobal, HidPushToTalkAppIndexSize, }; @@ -109,6 +110,40 @@ static void hid_ptt_trigger_hand_zoom(HidPushToTalk* hid_ptt) { hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_Y); } +// zoom global macos +static void hid_ptt_trigger_mute_macos_zoom_global(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_M); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_M); +} + +static void hid_ptt_trigger_camera_macos_zoom_global(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_U); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_U); +} + +static void hid_ptt_trigger_hand_zoom_global(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_Y); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | + HID_KEYBOARD_Y); +} + // this one is widely used across different apps static void hid_ptt_trigger_cmd_shift_m(HidPushToTalk* hid_ptt) { hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); @@ -457,6 +492,13 @@ static void hid_ptt_menu_callback( model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; break; + case HidPushToTalkAppIndexZoomGlobal: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_zoom_global; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_zoom_global; + model->callback_trigger_hand = hid_ptt_trigger_hand_zoom_global; + model->callback_start_ptt = hid_ptt_trigger_mute_macos_zoom_global; + model->callback_stop_ptt = hid_ptt_trigger_mute_macos_zoom_global; + break; } } else if(osIndex == HidPushToTalkLinux) { switch(appIndex) { @@ -569,6 +611,14 @@ static void hid_ptt_menu_callback( "Teams:\n" "Go to Settings > Privacy. Make sure Keyboard shortcut to unmute is toggled on.\n\n"; break; + case HidPushToTalkAppIndexZoomGlobal: + app_specific_help = + "Zoom (Global):\n" + "1. Go to Settings > Keyboard Shortcuts.\n" + "2. Find the 'Mute/Unmute' shortcut and click 'Edit'.\n" + "3. Press the Mute button in the app to bind it.\n" + "4. Check global checkbox.\n" + "5. Repeat for video and hand shortcuts.\n\n"; } FuriString* msg = furi_string_alloc(); @@ -1030,6 +1080,13 @@ HidPushToTalk* hid_ptt_alloc(Hid* hid) { HidPushToTalkAppIndexZoom, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Zoom Global", + HidPushToTalkAppIndexZoomGlobal, + hid_ptt_menu_callback, + hid_ptt); ptt_menu_add_item_to_list( hid->hid_ptt_menu, HidPushToTalkLinux, From df035ad51cb09d83b894648f85e000fa08532919 Mon Sep 17 00:00:00 2001 From: Roman Belyakovsky Date: Sat, 11 Oct 2025 15:50:05 +0300 Subject: [PATCH 03/30] HID PTT: adding global meet shortcuts for macos --- applications/system/hid_app/views/hid_ptt.c | 50 +++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/applications/system/hid_app/views/hid_ptt.c b/applications/system/hid_app/views/hid_ptt.c index a0a313e75..ff57c6bb7 100644 --- a/applications/system/hid_app/views/hid_ptt.c +++ b/applications/system/hid_app/views/hid_ptt.c @@ -44,6 +44,7 @@ enum HidPushToTalkAppIndex { HidPushToTalkAppIndexFaceTime, HidPushToTalkAppIndexGather, HidPushToTalkAppIndexGoogleMeet, + HidPushToTalkAppIndexGoogleMeetGlobal, HidPushToTalkAppIndexGoogleHangouts, HidPushToTalkAppIndexJamulus, HidPushToTalkAppIndexSignal, @@ -89,6 +90,32 @@ static void hid_ptt_trigger_hand_linux_meet(HidPushToTalk* hid_ptt) { hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT | HID_KEYBOARD_H); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT | HID_KEYBOARD_H); } + +// meet global macos +static void hid_ptt_trigger_mute_macos_meet_global(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_7); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_7); +} +static void hid_ptt_trigger_camera_macos_meet_global(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_8); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_8); +} +static void hid_ptt_trigger_hand_macos_meet_global(HidPushToTalk* hid_ptt) { + hid_hal_keyboard_press( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_9); + hid_hal_keyboard_release( + hid_ptt->hid, + KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_9); +} static void hid_ptt_trigger_mute_macos_zoom(HidPushToTalk* hid_ptt) { hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); @@ -438,6 +465,13 @@ static void hid_ptt_menu_callback( model->callback_start_ptt = hid_ptt_start_ptt_meet_zoom; model->callback_stop_ptt = hid_ptt_stop_ptt_meet_zoom; break; + case HidPushToTalkAppIndexGoogleMeetGlobal: + model->callback_trigger_mute = hid_ptt_trigger_mute_macos_meet_global; + model->callback_trigger_camera = hid_ptt_trigger_camera_macos_meet_global; + model->callback_trigger_hand = hid_ptt_trigger_hand_macos_meet_global; + model->callback_start_ptt = hid_ptt_trigger_mute_macos_meet_global; + model->callback_stop_ptt = hid_ptt_trigger_mute_macos_meet_global; + break; case HidPushToTalkAppIndexJamulus: model->callback_trigger_mute = hid_ptt_trigger_mute_jamulus; model->callback_start_ptt = hid_ptt_trigger_mute_jamulus; @@ -593,6 +627,15 @@ static void hid_ptt_menu_callback( "and may not work for Windows users who use their screen " "reader. In this situation, the spacebar performs a different action.\n\n"; break; + case HidPushToTalkAppIndexGoogleMeetGlobal: + app_specific_help = + "Google Meet (Global):\n" + "1. Install \"Google Meet - Global Shortcuts\" extension.\n" + "2. Open chrome://extensions/shortcuts.\n" + "3. Set 'Toggle microphone' to Cmd+Ctrl+7 and enable Global.\n" + "4. Set 'Toggle camera' to Cmd+Ctrl+8 and enable Global.\n" + "5. Set 'Raise hand' to Cmd+Ctrl+9 and enable Global.\n\n"; + break; case HidPushToTalkAppIndexDiscord: app_specific_help = "Discord:\n" @@ -926,6 +969,13 @@ HidPushToTalk* hid_ptt_alloc(Hid* hid) { HidPushToTalkAppIndexGoogleMeet, hid_ptt_menu_callback, hid_ptt); + ptt_menu_add_item_to_list( + hid->hid_ptt_menu, + HidPushToTalkMacOS, + "Google Meet Global", + HidPushToTalkAppIndexGoogleMeetGlobal, + hid_ptt_menu_callback, + hid_ptt); ptt_menu_add_item_to_list( hid->hid_ptt_menu, HidPushToTalkMacOS, From b9feece2c2d38c9fd8479c0cff8662a3cf3e46eb Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Oct 2025 03:28:58 +0300 Subject: [PATCH 04/30] ofw pr 4283 NFC lib: Expose nfc_common.h by zinongli --- lib/nfc/SConscript | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index 737a356e2..787af9e43 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -14,6 +14,7 @@ env.Append( File("nfc_listener.h"), File("nfc_poller.h"), File("nfc_scanner.h"), + File("nfc_common.h"), # Protocols File("protocols/iso14443_3a/iso14443_3a.h"), File("protocols/iso14443_3b/iso14443_3b.h"), From de35de4e5832eb0166dc3ee35b69c839f7c9c7e2 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Oct 2025 03:30:31 +0300 Subject: [PATCH 05/30] ofw pr 4290 Storage: Dont send mount event if SD mounted at boot by WillyJL --- applications/services/storage/storage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/services/storage/storage.c b/applications/services/storage/storage.c index bfe2a08b2..9ab81eda6 100644 --- a/applications/services/storage/storage.c +++ b/applications/services/storage/storage.c @@ -44,11 +44,11 @@ Storage* storage_app_alloc(void) { storage_ext_init(&app->storage[ST_EXT]); // sd icon gui - app->sd_gui.enabled = false; + app->sd_gui.enabled = (app->storage[ST_EXT].status != StorageStatusNotReady); app->sd_gui.view_port = view_port_alloc(); view_port_set_width(app->sd_gui.view_port, icon_get_width(ICON_SD_MOUNTED)); view_port_draw_callback_set(app->sd_gui.view_port, storage_app_sd_icon_draw_callback, app); - view_port_enabled_set(app->sd_gui.view_port, false); + view_port_enabled_set(app->sd_gui.view_port, app->sd_gui.enabled); Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, app->sd_gui.view_port, GuiLayerStatusBarLeft); From fc34205f97c35079bc0746a0207b538d2afd7441 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Oct 2025 03:34:40 +0300 Subject: [PATCH 06/30] ofw pr 4285 ViewStack: Store View by value to save memory by CookiePLMonster --- applications/services/gui/view.c | 6 ++- applications/services/gui/view_i.h | 3 ++ applications/services/gui/view_stack.c | 51 ++++++++++++-------------- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/applications/services/gui/view.c b/applications/services/gui/view.c index 3ac85794a..ded1eb8de 100644 --- a/applications/services/gui/view.c +++ b/applications/services/gui/view.c @@ -2,7 +2,7 @@ View* view_alloc(void) { View* view = malloc(sizeof(View)); - view->orientation = ViewOrientationHorizontal; + view_init(view); return view; } @@ -12,6 +12,10 @@ void view_free(View* view) { free(view); } +void view_init(View* view) { + view->orientation = ViewOrientationHorizontal; +} + void view_tie_icon_animation(View* view, IconAnimation* icon_animation) { furi_check(view); icon_animation_set_update_callback(icon_animation, view_icon_animation_callback, view); diff --git a/applications/services/gui/view_i.h b/applications/services/gui/view_i.h index 1cc84c745..bd4ea16bd 100644 --- a/applications/services/gui/view_i.h +++ b/applications/services/gui/view_i.h @@ -31,6 +31,9 @@ struct View { void* context; }; +/** Initialize View (for internal use) */ +void view_init(View* view); + /** IconAnimation tie callback */ void view_icon_animation_callback(IconAnimation* instance, void* context); diff --git a/applications/services/gui/view_stack.c b/applications/services/gui/view_stack.c index 0a106f1ba..5359a62b9 100644 --- a/applications/services/gui/view_stack.c +++ b/applications/services/gui/view_stack.c @@ -1,9 +1,6 @@ #include "view_stack.h" #include "view_i.h" -#include -#include - #define MAX_VIEWS 3 typedef struct { @@ -11,7 +8,7 @@ typedef struct { } ViewStackModel; struct ViewStack { - View* view; + View view; }; static void view_stack_draw(Canvas* canvas, void* model); @@ -32,26 +29,26 @@ static void view_stack_enter(void* context) { furi_assert(context); ViewStack* view_stack = context; - ViewStackModel* model = view_get_model(view_stack->view); + ViewStackModel* model = view_get_model(&view_stack->view); /* if more than 1 Stack View hold same view they have to reassign update_callback_context */ for(int i = 0; i < MAX_VIEWS; ++i) { if(model->views[i]) { - view_set_update_callback_context(model->views[i], view_stack->view); + view_set_update_callback_context(model->views[i], &view_stack->view); if(model->views[i]->enter_callback) { model->views[i]->enter_callback(model->views[i]->context); } } } - view_commit_model(view_stack->view, false); + view_commit_model(&view_stack->view, false); } static void view_stack_exit(void* context) { furi_assert(context); ViewStack* view_stack = context; - ViewStackModel* model = view_get_model(view_stack->view); + ViewStackModel* model = view_get_model(&view_stack->view); for(int i = 0; i < MAX_VIEWS; ++i) { if(model->views[i] && model->views[i]->exit_callback) { @@ -59,35 +56,35 @@ static void view_stack_exit(void* context) { } } - view_commit_model(view_stack->view, false); + view_commit_model(&view_stack->view, false); } ViewStack* view_stack_alloc(void) { ViewStack* view_stack = malloc(sizeof(ViewStack)); - view_stack->view = view_alloc(); + view_init(&view_stack->view); - view_allocate_model(view_stack->view, ViewModelTypeLocking, sizeof(ViewStackModel)); - view_set_draw_callback(view_stack->view, view_stack_draw); - view_set_input_callback(view_stack->view, view_stack_input); - view_set_context(view_stack->view, view_stack); - view_set_enter_callback(view_stack->view, view_stack_enter); - view_set_exit_callback(view_stack->view, view_stack_exit); + view_allocate_model(&view_stack->view, ViewModelTypeLocking, sizeof(ViewStackModel)); + view_set_draw_callback(&view_stack->view, view_stack_draw); + view_set_input_callback(&view_stack->view, view_stack_input); + view_set_context(&view_stack->view, view_stack); + view_set_enter_callback(&view_stack->view, view_stack_enter); + view_set_exit_callback(&view_stack->view, view_stack_exit); return view_stack; } void view_stack_free(ViewStack* view_stack) { furi_assert(view_stack); - ViewStackModel* model = view_get_model(view_stack->view); + ViewStackModel* model = view_get_model(&view_stack->view); for(int i = 0; i < MAX_VIEWS; ++i) { if(model->views[i]) { view_set_update_callback(model->views[i], NULL); view_set_update_callback_context(model->views[i], NULL); } } - view_commit_model(view_stack->view, false); + view_commit_model(&view_stack->view, false); - view_free(view_stack->view); + view_free_model(&view_stack->view); free(view_stack); } @@ -109,14 +106,14 @@ static bool view_stack_input(InputEvent* event, void* context) { ViewStack* view_stack = context; bool consumed = false; - ViewStackModel* model = view_get_model(view_stack->view); + ViewStackModel* model = view_get_model(&view_stack->view); for(int i = MAX_VIEWS - 1; i >= 0; i--) { if(model->views[i] && view_input(model->views[i], event)) { consumed = true; break; } } - view_commit_model(view_stack->view, false); + view_commit_model(&view_stack->view, false); return consumed; } @@ -126,12 +123,12 @@ void view_stack_add_view(ViewStack* view_stack, View* view) { furi_assert(view); bool result = false; - ViewStackModel* model = view_get_model(view_stack->view); + ViewStackModel* model = view_get_model(&view_stack->view); for(int i = 0; i < MAX_VIEWS; ++i) { if(!model->views[i]) { model->views[i] = view; view_set_update_callback(model->views[i], view_stack_update_callback); - view_set_update_callback_context(model->views[i], view_stack->view); + view_set_update_callback_context(model->views[i], &view_stack->view); if(view->enter_callback) { view->enter_callback(view->context); } @@ -139,7 +136,7 @@ void view_stack_add_view(ViewStack* view_stack, View* view) { break; } } - view_commit_model(view_stack->view, result); + view_commit_model(&view_stack->view, result); furi_assert(result); } @@ -150,7 +147,7 @@ void view_stack_remove_view(ViewStack* view_stack, View* view) { /* Removing view on-the-go is dangerous, but it is protected with * Locking model, so system is consistent at any time. */ bool result = false; - ViewStackModel* model = view_get_model(view_stack->view); + ViewStackModel* model = view_get_model(&view_stack->view); for(int i = 0; i < MAX_VIEWS; ++i) { if(model->views[i] == view) { if(view->exit_callback) { @@ -163,11 +160,11 @@ void view_stack_remove_view(ViewStack* view_stack, View* view) { break; } } - view_commit_model(view_stack->view, result); + view_commit_model(&view_stack->view, result); furi_assert(result); } View* view_stack_get_view(ViewStack* view_stack) { furi_assert(view_stack); - return view_stack->view; + return &view_stack->view; } From eed1d3367a5058f174614ceea01ee906b58b0795 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Oct 2025 03:39:38 +0300 Subject: [PATCH 07/30] ofw pr 4293 NFC FeliCa Improvement: Dump All Systems by zinongli --- .../helpers/protocol_support/felica/felica.c | 44 +- .../protocol_support/felica/felica_render.c | 23 +- .../protocol_support/felica/felica_render.h | 3 +- .../main/nfc/scenes/nfc_scene_config.h | 1 + .../nfc/scenes/nfc_scene_felica_more_info.c | 84 +--- .../main/nfc/scenes/nfc_scene_felica_system.c | 114 +++++ lib/nfc/protocols/felica/felica.c | 419 +++++++++--------- lib/nfc/protocols/felica/felica.h | 27 +- lib/nfc/protocols/felica/felica_i.c | 38 ++ lib/nfc/protocols/felica/felica_i.h | 5 + lib/nfc/protocols/felica/felica_poller.c | 91 +++- lib/nfc/protocols/felica/felica_poller_i.c | 32 +- lib/nfc/protocols/felica/felica_poller_i.h | 8 + targets/f7/api_symbols.csv | 5 +- 14 files changed, 569 insertions(+), 325 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_felica_system.c diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index 87c69722a..1b52b86bd 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -102,19 +102,21 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) { temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str); } else { - bool all_unlocked = data->blocks_read == data->blocks_total; - furi_string_cat_printf( - temp_str, - "\e#%s\n", - all_unlocked ? "All Blocks Are Unlocked" : "Some Blocks Are Locked"); - nfc_render_felica_idm(data, NfcProtocolFormatTypeShort, temp_str); - uint8_t* ck_data = instance->felica_auth->card_key.data; - furi_string_cat_printf(temp_str, "Key:"); - for(uint8_t i = 0; i < 7; i++) { - furi_string_cat_printf(temp_str, " %02X", ck_data[i]); - if(i == 6) furi_string_cat_printf(temp_str, "..."); + if(data->workflow_type == FelicaLite) { + bool all_unlocked = data->blocks_read == data->blocks_total; + furi_string_cat_printf( + temp_str, + "\e#%s\n", + all_unlocked ? "All Blocks Are Unlocked" : "Some Blocks Are Locked"); + nfc_render_felica_idm(data, NfcProtocolFormatTypeShort, temp_str); + uint8_t* ck_data = instance->felica_auth->card_key.data; + furi_string_cat_printf(temp_str, "Key:"); + for(uint8_t i = 0; i < 7; i++) { + furi_string_cat_printf(temp_str, " %02X", ck_data[i]); + if(i == 6) furi_string_cat_printf(temp_str, "..."); + } + nfc_render_felica_blocks_count(data, temp_str, false); } - nfc_render_felica_blocks_count(data, temp_str, false); } felica_auth_reset(instance->felica_auth); @@ -124,6 +126,15 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) { furi_string_free(temp_str); } +static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) { + if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) { const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica); instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolFelica, data); @@ -183,7 +194,7 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_protocol_support_common_on_event_empty, + .on_event = nfc_scene_saved_menu_on_event_felica, }, .scene_save_name = { @@ -195,11 +206,4 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = { .on_enter = nfc_scene_emulate_on_enter_felica, .on_event = nfc_protocol_support_common_on_event_empty, }, - .scene_write = - { - .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_protocol_support_common_on_event_empty, - }, }; - -NFC_PROTOCOL_SUPPORT_PLUGIN(felica, NfcProtocolFelica); diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica_render.c b/applications/main/nfc/helpers/protocol_support/felica/felica_render.c index 1ca992bcd..8773fa1f3 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica_render.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica_render.c @@ -6,14 +6,10 @@ void nfc_render_felica_blocks_count( bool render_auth_notification) { if(data->workflow_type == FelicaLite) { furi_string_cat_printf(str, "Blocks: %u\n", data->blocks_total); - furi_string_cat_printf(str, "\nBlocks Read: %u/%u", data->blocks_read, data->blocks_total); if(render_auth_notification && data->blocks_read != data->blocks_total) { furi_string_cat_printf(str, "\nAuth-protected blocks!"); } - } else if(data->workflow_type == FelicaStandard) { - furi_string_cat_printf( - str, "Public blocks Read: %lu", simple_array_get_count(data->public_blocks)); } } @@ -54,11 +50,7 @@ void nfc_render_felica_info( } furi_string_cat_printf(str, "\n"); - furi_string_cat_printf( - str, - "Services found: %lu \nAreas found: %lu\n", - simple_array_get_count(data->services), - simple_array_get_count(data->areas)); + furi_string_cat_printf(str, "Systems found: %lu \n", simple_array_get_count(data->systems)); nfc_render_felica_blocks_count(data, str, true); } @@ -136,9 +128,9 @@ void nfc_more_info_render_felica_lite_dump(const FelicaData* data, FuriString* s nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17); } -void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str) { - const size_t area_count = simple_array_get_count(data->areas); - const size_t service_count = simple_array_get_count(data->services); +void nfc_more_info_render_felica_dir(const FelicaSystem* system, FuriString* str) { + const size_t area_count = simple_array_get_count(system->areas); + const size_t service_count = simple_array_get_count(system->services); furi_string_cat_printf(str, "\e#Directory Tree:\n"); @@ -150,11 +142,12 @@ void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str) { furi_string_cat_printf( str, "::: ... are readable services\n||| ... are locked services\n"); } - felica_write_directory_tree(data, str); + felica_write_directory_tree(system, str); } void nfc_more_info_render_felica_blocks( const FelicaData* data, + const FelicaSystem* system, FuriString* str, const uint16_t service_code_key) { furi_string_cat_printf(str, "\n"); @@ -190,9 +183,9 @@ void nfc_more_info_render_felica_blocks( nfc_render_felica_block(&data->data.fs.crc_check, str, "CRC_CHCK", 15, 17); } else if(data->workflow_type == FelicaStandard) { - uint32_t public_blocks_count = simple_array_get_count(data->public_blocks); + uint32_t public_blocks_count = simple_array_get_count(system->public_blocks); for(size_t i = 0; i < public_blocks_count; i++) { - FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i); + FelicaPublicBlock* public_block = simple_array_get(system->public_blocks, i); if(public_block->service_code != service_code_key) { continue; // Skip blocks not matching the requested service code } diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica_render.h b/applications/main/nfc/helpers/protocol_support/felica/felica_render.h index 5c7c6d036..e0c270a18 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica_render.h +++ b/applications/main/nfc/helpers/protocol_support/felica/felica_render.h @@ -21,9 +21,10 @@ void nfc_render_felica_idm( NfcProtocolFormatType format_type, FuriString* str); -void nfc_more_info_render_felica_dir(const FelicaData* data, FuriString* str); +void nfc_more_info_render_felica_dir(const FelicaSystem* system, FuriString* str); void nfc_more_info_render_felica_blocks( const FelicaData* data, + const FelicaSystem* system, FuriString* str, const uint16_t service_code_key); diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 9761fa9fe..6634827ed 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -70,5 +70,6 @@ ADD_SCENE(nfc, slix_unlock, SlixUnlock) ADD_SCENE(nfc, slix_unlock_success, SlixUnlockSuccess) ADD_SCENE(nfc, felica_more_info, FelicaMoreInfo) +ADD_SCENE(nfc, felica_system, FelicaSystem) ADD_SCENE(nfc, generate_info, GenerateInfo) diff --git a/applications/main/nfc/scenes/nfc_scene_felica_more_info.c b/applications/main/nfc/scenes/nfc_scene_felica_more_info.c index 7eee3d7b3..1401dcaf7 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_more_info.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_more_info.c @@ -9,7 +9,6 @@ enum { }; enum SubmenuIndex { - SubmenuIndexDirectory, SubmenuIndexDynamic, // dynamic indices start here }; @@ -21,33 +20,22 @@ void nfc_scene_felica_more_info_on_enter(void* context) { scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo); const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica); - submenu_add_item( - submenu, - "Directory", - SubmenuIndexDirectory, - nfc_protocol_support_common_submenu_callback, - nfc); - - FuriString* label = furi_string_alloc(); - switch(data->workflow_type) { case FelicaLite: - furi_string_printf(label, "All blocks"); - submenu_add_item( - submenu, - furi_string_get_cstr(label), - SubmenuIndexDynamic, - nfc_protocol_support_common_submenu_callback, - nfc); + widget_reset(nfc->widget); + FuriString* temp_str = furi_string_alloc(); + nfc_more_info_render_felica_lite_dump(data, temp_str); + widget_add_text_scroll_element(nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + return; break; case FelicaStandard: - for(uint32_t i = 0; i < simple_array_get_count(data->services); ++i) { - const FelicaService* service = simple_array_cget(data->services, i); - bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 1; - if(!is_public) { - continue; - } - furi_string_printf(label, "Readable serv %04X", service->code); + FuriString* label = furi_string_alloc(); + + for(uint32_t i = 0; i < simple_array_get_count(data->systems); ++i) { + const FelicaSystem* system = simple_array_cget(data->systems, i); + furi_string_printf(label, "System %04X", system->system_code); submenu_add_item( submenu, furi_string_get_cstr(label), @@ -55,13 +43,12 @@ void nfc_scene_felica_more_info_on_enter(void* context) { nfc_protocol_support_common_submenu_callback, nfc); } + furi_string_free(label); break; default: break; } - furi_string_free(label); - if(state >= FelicaMoreInfoStateItem) { submenu_set_selected_item( nfc->submenu, state - FelicaMoreInfoStateItem + SubmenuIndexDynamic); @@ -78,49 +65,16 @@ bool nfc_scene_felica_more_info_on_event(void* context, SceneManagerEvent event) const uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaMoreInfo); - const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica); if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexDirectory) { - FuriString* temp_str = furi_string_alloc(); - nfc_more_info_render_felica_dir(data, temp_str); + const uint32_t index = event.event - SubmenuIndexDynamic; - widget_add_text_scroll_element( - nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); - furi_string_free(temp_str); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateItem + index); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneFelicaSystem, index << 4); + scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaSystem); + consumed = true; - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - scene_manager_set_scene_state( - nfc->scene_manager, - NfcSceneFelicaMoreInfo, - FelicaMoreInfoStateItem + SubmenuIndexDirectory); - consumed = true; - } else { - const uint16_t service_ind = event.event - 1; // offset the three enums above - - text_box_reset(nfc->text_box); - furi_string_reset(nfc->text_box_store); - - switch(data->workflow_type) { - case FelicaLite: - nfc_more_info_render_felica_lite_dump(data, nfc->text_box_store); - break; - case FelicaStandard: - const FelicaService* service = simple_array_cget(data->services, service_ind); - furi_string_cat_printf(nfc->text_box_store, "Service 0x%04X\n", service->code); - nfc_more_info_render_felica_blocks(data, nfc->text_box_store, service->code); - break; - default: - furi_string_set_str(nfc->text_box_store, "IC type not implemented yet"); - break; - } - text_box_set_font(nfc->text_box, TextBoxFontHex); - text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneFelicaMoreInfo, FelicaMoreInfoStateItem + event.event); - consumed = true; - } } else if(event.type == SceneManagerEventTypeBack) { if(state >= FelicaMoreInfoStateItem) { widget_reset(nfc->widget); diff --git a/applications/main/nfc/scenes/nfc_scene_felica_system.c b/applications/main/nfc/scenes/nfc_scene_felica_system.c new file mode 100644 index 000000000..87ebbbc36 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_felica_system.c @@ -0,0 +1,114 @@ +#include "../nfc_app_i.h" + +#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h" +#include "../helpers/protocol_support/felica/felica_render.h" + +enum SubmenuIndex { + SubmenuIndexDirectory, + SubmenuIndexDynamic, // dynamic indices start here +}; + +static void nfc_scene_felica_system_submenu_callback(void* context, uint32_t index) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_felica_system_on_enter(void* context) { + NfcApp* nfc = context; + Submenu* submenu = nfc->submenu; + submenu_reset(nfc->submenu); + + const uint32_t system_index = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaSystem) >> 4; + const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica); + + submenu_add_item( + submenu, "Directory", SubmenuIndexDirectory, nfc_scene_felica_system_submenu_callback, nfc); + + FuriString* label = furi_string_alloc(); + + const FelicaSystem* system = simple_array_cget(data->systems, system_index); + for(uint32_t i = 0; i < simple_array_get_count(system->services); ++i) { + const FelicaService* service = simple_array_cget(system->services, i); + bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 1; + if(!is_public) { + continue; + } + furi_string_printf(label, "Readable serv %04X", service->code); + submenu_add_item( + submenu, + furi_string_get_cstr(label), + i + SubmenuIndexDynamic, + nfc_protocol_support_common_submenu_callback, + nfc); + } + + furi_string_free(label); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_felica_system_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + const uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneFelicaSystem); + const FelicaData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolFelica); + + const uint32_t system_index = state >> 4; + const FelicaSystem* system = simple_array_cget(data->systems, system_index); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else { + if(event.event == SubmenuIndexDirectory) { + FuriString* temp_str = furi_string_alloc(); + nfc_more_info_render_felica_dir(system, temp_str); + + widget_add_text_scroll_element( + nfc->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + } else { + const uint32_t service_ind = + event.event - SubmenuIndexDynamic; // offset the three enums above + + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + const FelicaService* service = simple_array_cget(system->services, service_ind); + furi_string_cat_printf(nfc->text_box_store, "Service 0x%04X\n", service->code); + nfc_more_info_render_felica_blocks( + data, system, nfc->text_box_store, service->code); + + text_box_set_font(nfc->text_box, TextBoxFontHex); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneFelicaSystem, state | 1); + consumed = true; + } + + } else if(event.type == SceneManagerEventTypeBack) { + if(state & 1) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneFelicaSystem, state & ~1); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_felica_system_on_exit(void* context) { + NfcApp* nfc = context; + + // Clear views + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + submenu_reset(nfc->submenu); +} diff --git a/lib/nfc/protocols/felica/felica.c b/lib/nfc/protocols/felica/felica.c index 4e0ff7472..2e8d5f92a 100644 --- a/lib/nfc/protocols/felica/felica.c +++ b/lib/nfc/protocols/felica/felica.c @@ -42,26 +42,16 @@ FelicaData* felica_alloc(void) { FelicaData* data = malloc(sizeof(FelicaData)); furi_check(data); - data->services = simple_array_alloc(&felica_service_array_cfg); - data->areas = simple_array_alloc(&felica_area_array_cfg); - data->public_blocks = simple_array_alloc(&felica_public_block_array_cfg); - furi_check(data->services); - furi_check(data->areas); - furi_check(data->public_blocks); + data->systems = simple_array_alloc(&felica_system_array_cfg); + furi_check(data->systems); return data; } void felica_free(FelicaData* data) { furi_check(data); - furi_check(data->services); - simple_array_free(data->services); - - furi_check(data->areas); - simple_array_free(data->areas); - - furi_check(data->public_blocks); - simple_array_free(data->public_blocks); + furi_check(data->systems); + simple_array_free(data->systems); free(data); } @@ -69,16 +59,8 @@ void felica_free(FelicaData* data) { void felica_reset(FelicaData* data) { furi_check(data); - if(data->services) { - simple_array_reset(data->services); - } - - if(data->areas) { - simple_array_reset(data->areas); - } - - if(data->public_blocks) { - simple_array_reset(data->public_blocks); + if(data->systems) { + simple_array_reset(data->systems); } data->blocks_read = 0; @@ -102,9 +84,7 @@ void felica_copy(FelicaData* data, const FelicaData* other) { data->data = other->data; data->workflow_type = other->workflow_type; - simple_array_copy(data->services, other->services); - simple_array_copy(data->areas, other->areas); - simple_array_copy(data->public_blocks, other->public_blocks); + simple_array_copy(data->systems, other->systems); } bool felica_verify(FelicaData* data, const FuriString* device_type) { @@ -175,99 +155,125 @@ bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) { } while(false); break; case FelicaStandard: - // Areas - do { - uint32_t area_count = 0; - if(!flipper_format_read_uint32(ff, "Area found", &area_count, 1)) break; - simple_array_init(data->areas, area_count); + uint32_t systems_total = 0; + if(!flipper_format_read_uint32(ff, "System found", &systems_total, 1)) break; + simple_array_init(data->systems, systems_total); + + for(uint8_t sys_idx = 0; sys_idx < systems_total; sys_idx++) { + FelicaSystem* system = simple_array_get(data->systems, sys_idx); + uint16_t system_code = 0; furi_string_reset(str_key_buffer); furi_string_reset(str_data_buffer); - for(uint16_t i = 0; i < area_count; i++) { - furi_string_printf(str_key_buffer, "Area %03X", i); - if(!flipper_format_read_string( - ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) { - break; - } - FelicaArea* area = simple_array_get(data->areas, i); - if(sscanf( - furi_string_get_cstr(str_data_buffer), - "| Code %04hX | Services #%03hX-#%03hX |", - &area->code, - &area->first_idx, - &area->last_idx) != 3) { - break; - } - } - } while(false); - - // Services - do { - uint32_t service_count = 0; - if(!flipper_format_read_uint32(ff, "Service found", &service_count, 1)) break; - simple_array_init(data->services, service_count); - - furi_string_reset(str_key_buffer); - furi_string_reset(str_data_buffer); - for(uint16_t i = 0; i < service_count; i++) { - furi_string_printf(str_key_buffer, "Service %03X", i); - if(!flipper_format_read_string( - ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) { - break; - } - FelicaService* service = simple_array_get(data->services, i); - - // all unread in the beginning. reserved for future block load - if(!sscanf( - furi_string_get_cstr(str_data_buffer), "| Code %04hX |", &service->code)) { - break; - } - service->attr = service->code & 0x3F; - } - } while(false); - - // Public blocks - do { - furi_string_reset(str_data_buffer); - furi_string_reset(str_key_buffer); - uint32_t public_block_count = 0; - if(!flipper_format_read_uint32(ff, "Public blocks read", &public_block_count, 1)) + furi_string_printf(str_key_buffer, "System %02X", sys_idx); + if(!flipper_format_read_string( + ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) break; - simple_array_init(data->public_blocks, public_block_count); - for(uint16_t i = 0; i < public_block_count; i++) { - furi_string_printf(str_key_buffer, "Block %04X", i); - if(!flipper_format_read_string( - ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) { - break; - } - FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i); - if(sscanf( - furi_string_get_cstr(str_data_buffer), - "| Service code %04hX | Block index %02hhX |", - &public_block->service_code, - &public_block->block_idx) != 2) { - break; - } - - size_t needle = furi_string_search_str(str_data_buffer, "Data: "); - if(needle == FURI_STRING_FAILURE) { - break; - } - needle += 6; // length of "Data: " = 6 - furi_string_mid(str_data_buffer, needle, 3 * FELICA_DATA_BLOCK_SIZE); - furi_string_replace_all(str_data_buffer, " ", ""); - if(!hex_chars_to_uint8( - furi_string_get_cstr(str_data_buffer), public_block->block.data)) { - break; - } - - furi_string_reset(str_data_buffer); - for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) { - furi_string_cat_printf(str_data_buffer, "%02X ", public_block->block.data[j]); - } + if(!sscanf(furi_string_get_cstr(str_data_buffer), "%04hX", &system_code)) { + break; } - } while(false); + + system->system_code = system_code; + system->system_code_idx = sys_idx; + + // Areas + do { + uint32_t area_count = 0; + if(!flipper_format_read_uint32(ff, "Area found", &area_count, 1)) break; + simple_array_init(system->areas, area_count); + + furi_string_reset(str_key_buffer); + furi_string_reset(str_data_buffer); + for(uint16_t i = 0; i < area_count; i++) { + furi_string_printf(str_key_buffer, "Area %03X", i); + if(!flipper_format_read_string( + ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) { + break; + } + FelicaArea* area = simple_array_get(system->areas, i); + if(sscanf( + furi_string_get_cstr(str_data_buffer), + "| Code %04hX | Services #%03hX-#%03hX |", + &area->code, + &area->first_idx, + &area->last_idx) != 3) { + break; + } + } + } while(false); + + // Services + do { + uint32_t service_count = 0; + if(!flipper_format_read_uint32(ff, "Service found", &service_count, 1)) break; + simple_array_init(system->services, service_count); + + furi_string_reset(str_key_buffer); + furi_string_reset(str_data_buffer); + for(uint16_t i = 0; i < service_count; i++) { + furi_string_printf(str_key_buffer, "Service %03X", i); + if(!flipper_format_read_string( + ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) { + break; + } + FelicaService* service = simple_array_get(system->services, i); + + // all unread in the beginning. reserved for future block load + if(!sscanf( + furi_string_get_cstr(str_data_buffer), + "| Code %04hX |", + &service->code)) { + break; + } + service->attr = service->code & 0x3F; + } + } while(false); + + // Public blocks + do { + furi_string_reset(str_data_buffer); + furi_string_reset(str_key_buffer); + uint32_t public_block_count = 0; + if(!flipper_format_read_uint32(ff, "Public blocks read", &public_block_count, 1)) + break; + simple_array_init(system->public_blocks, public_block_count); + for(uint16_t i = 0; i < public_block_count; i++) { + furi_string_printf(str_key_buffer, "Block %04X", i); + if(!flipper_format_read_string( + ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) { + break; + } + + FelicaPublicBlock* public_block = simple_array_get(system->public_blocks, i); + if(sscanf( + furi_string_get_cstr(str_data_buffer), + "| Service code %04hX | Block index %02hhX |", + &public_block->service_code, + &public_block->block_idx) != 2) { + break; + } + + size_t needle = furi_string_search_str(str_data_buffer, "Data: "); + if(needle == FURI_STRING_FAILURE) { + break; + } + needle += 6; // length of "Data: " = 6 + furi_string_mid(str_data_buffer, needle, 3 * FELICA_DATA_BLOCK_SIZE); + furi_string_replace_all(str_data_buffer, " ", ""); + if(!hex_chars_to_uint8( + furi_string_get_cstr(str_data_buffer), public_block->block.data)) { + break; + } + + furi_string_reset(str_data_buffer); + for(size_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) { + furi_string_cat_printf( + str_data_buffer, "%02X ", public_block->block.data[j]); + } + } + } while(false); + } break; default: break; @@ -334,88 +340,107 @@ bool felica_save(const FelicaData* data, FlipperFormat* ff) { case FelicaStandard: if(!flipper_format_write_comment_cstr(ff, "Felica Standard specific data")) break; + uint32_t systems_count = simple_array_get_count(data->systems); + if(!flipper_format_write_uint32(ff, "System found", &systems_count, 1)) break; + for(uint32_t sys_idx = 0; sys_idx < systems_count; sys_idx++) { + FelicaSystem* system = simple_array_get(data->systems, sys_idx); - do { - uint32_t area_count = simple_array_get_count(data->areas); - uint32_t service_count = simple_array_get_count(data->services); - // Note: The theoretical max area/service count is 2^10 - // So uint16_t is already enough for practical usage - // The following key index print will use %03X because 12 bits are enough to cover 0-1023 - - // Area count - if(!flipper_format_write_uint32(ff, "Area found", &area_count, 1)) break; - - // Area data furi_string_reset(str_data_buffer); furi_string_reset(str_key_buffer); - for(uint16_t i = 0; i < area_count; i++) { - FelicaArea* area = simple_array_get(data->areas, i); - furi_string_printf(str_key_buffer, "Area %03X", i); - furi_string_printf( - str_data_buffer, - "| Code %04X | Services #%03X-#%03X |", - area->code, - area->first_idx, - area->last_idx); - if(!flipper_format_write_string( - ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) - break; - } - if(!flipper_format_write_empty_line(ff)) break; - - // Service count - if(!flipper_format_write_uint32(ff, "Service found", &service_count, 1)) break; - - // Service data - furi_string_reset(str_data_buffer); - furi_string_reset(str_key_buffer); - for(uint16_t i = 0; i < service_count; i++) { - FelicaService* service = simple_array_get(data->services, i); - furi_string_printf(str_key_buffer, "Service %03X", i); - furi_string_printf( - str_data_buffer, "| Code %04X | Attrib. %02X ", service->code, service->attr); - felica_service_get_attribute_string(service, str_data_buffer); - if(!flipper_format_write_string( - ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) - break; - } - if(!flipper_format_write_empty_line(ff)) break; - - // Directory tree - furi_string_reset(str_data_buffer); - furi_string_reset(str_key_buffer); - furi_string_printf( - str_data_buffer, "\n::: ... are public services\n||| ... are private services"); - felica_write_directory_tree(data, str_data_buffer); - furi_string_replace_all(str_data_buffer, ":", "+"); - // We use a clearer marker in saved text files - if(!flipper_format_write_string(ff, "Directory Tree", str_data_buffer)) break; - } while(false); - - // Public blocks - do { - uint32_t public_block_count = simple_array_get_count(data->public_blocks); - if(!flipper_format_write_uint32(ff, "Public blocks read", &public_block_count, 1)) + furi_string_printf(str_key_buffer, "\n\nSystem %02X", (uint8_t)sys_idx); + furi_string_printf(str_data_buffer, "%04X", system->system_code); + if(!flipper_format_write_string( + ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) break; - furi_string_reset(str_data_buffer); - furi_string_reset(str_key_buffer); - for(uint16_t i = 0; i < public_block_count; i++) { - FelicaPublicBlock* public_block = simple_array_get(data->public_blocks, i); - furi_string_printf(str_key_buffer, "Block %04X", i); + if(!flipper_format_write_empty_line(ff)) break; + + do { + uint32_t area_count = simple_array_get_count(system->areas); + uint32_t service_count = simple_array_get_count(system->services); + // Note: The theoretical max area/service count is 2^10 + // So uint16_t is already enough for practical usage + // The following key index print will use %03X because 12 bits are enough to cover 0-1023 + + // Area count + if(!flipper_format_write_uint32(ff, "Area found", &area_count, 1)) break; + + // Area data + furi_string_reset(str_data_buffer); + furi_string_reset(str_key_buffer); + for(uint16_t i = 0; i < area_count; i++) { + FelicaArea* area = simple_array_get(system->areas, i); + furi_string_printf(str_key_buffer, "Area %03X", i); + furi_string_printf( + str_data_buffer, + "| Code %04X | Services #%03X-#%03X |", + area->code, + area->first_idx, + area->last_idx); + if(!flipper_format_write_string( + ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) + break; + } + if(!flipper_format_write_empty_line(ff)) break; + + // Service count + if(!flipper_format_write_uint32(ff, "Service found", &service_count, 1)) break; + + // Service data + furi_string_reset(str_data_buffer); + furi_string_reset(str_key_buffer); + for(uint16_t i = 0; i < service_count; i++) { + FelicaService* service = simple_array_get(system->services, i); + furi_string_printf(str_key_buffer, "Service %03X", i); + furi_string_printf( + str_data_buffer, + "| Code %04X | Attrib. %02X ", + service->code, + service->attr); + felica_service_get_attribute_string(service, str_data_buffer); + if(!flipper_format_write_string( + ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) + break; + } + if(!flipper_format_write_empty_line(ff)) break; + + // Directory tree + furi_string_reset(str_data_buffer); + furi_string_reset(str_key_buffer); furi_string_printf( str_data_buffer, - "| Service code %04X | Block index %02X | Data: ", - public_block->service_code, - public_block->block_idx); - for(uint8_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) { - furi_string_cat_printf(str_data_buffer, "%02X ", public_block->block.data[j]); - } - furi_string_cat_printf(str_data_buffer, "|"); - if(!flipper_format_write_string( - ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) + "\n::: ... are public services\n||| ... are private services"); + felica_write_directory_tree(system, str_data_buffer); + furi_string_replace_all(str_data_buffer, ":", "+"); + // We use a clearer marker in saved text files + if(!flipper_format_write_string(ff, "Directory Tree", str_data_buffer)) break; + } while(false); + + // Public blocks + do { + uint32_t public_block_count = simple_array_get_count(system->public_blocks); + if(!flipper_format_write_uint32(ff, "Public blocks read", &public_block_count, 1)) break; - } - } while(false); + furi_string_reset(str_data_buffer); + furi_string_reset(str_key_buffer); + for(uint16_t i = 0; i < public_block_count; i++) { + FelicaPublicBlock* public_block = simple_array_get(system->public_blocks, i); + furi_string_printf(str_key_buffer, "Block %04X", i); + furi_string_printf( + str_data_buffer, + "| Service code %04X | Block index %02X | Data: ", + public_block->service_code, + public_block->block_idx); + for(uint8_t j = 0; j < FELICA_DATA_BLOCK_SIZE; j++) { + furi_string_cat_printf( + str_data_buffer, "%02X ", public_block->block.data[j]); + } + furi_string_cat_printf(str_data_buffer, "|"); + if(!flipper_format_write_string( + ff, furi_string_get_cstr(str_key_buffer), str_data_buffer)) + break; + } + } while(false); + } break; default: break; @@ -436,9 +461,7 @@ bool felica_is_equal(const FelicaData* data, const FelicaData* other) { memcmp(data->pmm.data, other->pmm.data, sizeof(FelicaPMm)) == 0 && data->blocks_total == other->blocks_total && data->blocks_read == other->blocks_read && memcmp(&data->data, &other->data, sizeof(data->data)) == 0 && - simple_array_is_equal(data->services, other->services) && - simple_array_is_equal(data->areas, other->areas) && - simple_array_is_equal(data->public_blocks, other->public_blocks); + simple_array_is_equal(data->systems, other->systems); } const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) { @@ -640,8 +663,8 @@ void felica_calculate_mac_write( felica_calculate_mac(ctx, session_swapped, rc, first_block, data, FELICA_DATA_BLOCK_SIZE, mac); } -void felica_write_directory_tree(const FelicaData* data, FuriString* str) { - furi_check(data); +void felica_write_directory_tree(const FelicaSystem* system, FuriString* str) { + furi_check(system); furi_check(str); furi_string_cat_str(str, "\n"); @@ -650,12 +673,12 @@ void felica_write_directory_tree(const FelicaData* data, FuriString* str) { uint8_t depth = 0; size_t area_iter = 0; - const size_t area_count = simple_array_get_count(data->areas); - const size_t service_count = simple_array_get_count(data->services); + const size_t area_count = simple_array_get_count(system->areas); + const size_t service_count = simple_array_get_count(system->services); for(size_t svc_idx = 0; svc_idx < service_count; ++svc_idx) { while(area_iter < area_count) { - const FelicaArea* next_area = simple_array_get(data->areas, area_iter); + const FelicaArea* next_area = simple_array_get(system->areas, area_iter); if(next_area->first_idx != svc_idx) break; for(uint8_t i = 0; i < depth - 1; ++i) @@ -667,7 +690,7 @@ void felica_write_directory_tree(const FelicaData* data, FuriString* str) { area_iter++; } - const FelicaService* service = simple_array_get(data->services, svc_idx); + const FelicaService* service = simple_array_get(system->services, svc_idx); bool is_public = (service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) != 0; for(uint8_t i = 0; i < depth - 1; ++i) diff --git a/lib/nfc/protocols/felica/felica.h b/lib/nfc/protocols/felica/felica.h index 5a5a75215..bc83ff846 100644 --- a/lib/nfc/protocols/felica/felica.h +++ b/lib/nfc/protocols/felica/felica.h @@ -50,8 +50,10 @@ extern "C" { #define FELICA_TIME_SLOT_8 (0x07U) #define FELICA_TIME_SLOT_16 (0x0FU) -#define FELICA_CMD_LIST_SERVICE_CODE 0x0A -#define FELICA_CMD_LIST_SERVICE_CODE_RESP 0x0B +#define FELICA_CMD_LIST_SERVICE_CODE 0x0A +#define FELICA_CMD_LIST_SERVICE_CODE_RESP 0x0B +#define FELICA_CMD_REQUEST_SYSTEM_CODE 0x0C +#define FELICA_CMD_REQUEST_SYSTEM_CODE_RESP 0x0D #define FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ (0b000001) #define FELICA_SERVICE_ATTRIBUTE_READ_ONLY (0b000010) @@ -182,6 +184,14 @@ typedef struct { uint8_t block_idx; } FelicaPublicBlock; +typedef struct { + uint8_t system_code_idx; + uint16_t system_code; + SimpleArray* services; + SimpleArray* areas; + SimpleArray* public_blocks; +} FelicaSystem; + /** @brief Structure used to store Felica data and additional values about reading */ typedef struct { FelicaIDm idm; @@ -190,9 +200,8 @@ typedef struct { uint8_t blocks_read; FelicaFSUnion data; - SimpleArray* services; - SimpleArray* areas; - SimpleArray* public_blocks; + SimpleArray* systems; + FelicaWorkflowType workflow_type; } FelicaData; @@ -248,6 +257,12 @@ typedef struct { uint8_t data[]; } FelicaListServiceCommandResponse; +typedef struct { + FelicaCommandHeaderRaw header; + uint8_t system_count; + uint8_t system_code[]; +} FelicaListSystemCodeCommandResponse; + typedef FelicaCommandResponseHeader FelicaListenerWriteCommandResponse; typedef FelicaCommandResponseHeader FelicaPollerWriteCommandResponse; @@ -309,7 +324,7 @@ void felica_calculate_mac_write( const uint8_t* data, uint8_t* mac); -void felica_write_directory_tree(const FelicaData* data, FuriString* str); +void felica_write_directory_tree(const FelicaSystem* system, FuriString* str); void felica_get_workflow_type(FelicaData* data); diff --git a/lib/nfc/protocols/felica/felica_i.c b/lib/nfc/protocols/felica/felica_i.c index e265ea862..c5ecdf121 100644 --- a/lib/nfc/protocols/felica/felica_i.c +++ b/lib/nfc/protocols/felica/felica_i.c @@ -1,5 +1,36 @@ #include "felica_i.h" +void felica_system_init(FelicaSystem* system) { + system->system_code = 0; + system->system_code_idx = 0; + system->services = simple_array_alloc(&felica_service_array_cfg); + system->areas = simple_array_alloc(&felica_area_array_cfg); + system->public_blocks = simple_array_alloc(&felica_public_block_array_cfg); +} + +void felica_system_reset(FelicaSystem* system) { + furi_check(system); + system->system_code = 0; + system->system_code_idx = 0; + furi_check(system->services); + furi_check(system->areas); + furi_check(system->public_blocks); + simple_array_free(system->services); + simple_array_free(system->areas); + simple_array_free(system->public_blocks); + memset(system, 0, sizeof(FelicaSystem)); +} + +void felica_system_copy(FelicaSystem* system, const FelicaSystem* other) { + furi_check(system); + furi_check(other); + system->system_code = other->system_code; + system->system_code_idx = other->system_code_idx; + simple_array_copy(system->services, other->services); + simple_array_copy(system->areas, other->areas); + simple_array_copy(system->public_blocks, other->public_blocks); +} + const SimpleArrayConfig felica_service_array_cfg = { .init = NULL, .copy = NULL, @@ -20,3 +51,10 @@ const SimpleArrayConfig felica_public_block_array_cfg = { .reset = NULL, .type_size = sizeof(FelicaPublicBlock), }; + +const SimpleArrayConfig felica_system_array_cfg = { + .init = (SimpleArrayInit)felica_system_init, + .copy = (SimpleArrayCopy)felica_system_copy, + .reset = (SimpleArrayReset)felica_system_reset, + .type_size = sizeof(FelicaSystem), +}; diff --git a/lib/nfc/protocols/felica/felica_i.h b/lib/nfc/protocols/felica/felica_i.h index a708aa729..d55077c5e 100644 --- a/lib/nfc/protocols/felica/felica_i.h +++ b/lib/nfc/protocols/felica/felica_i.h @@ -8,3 +8,8 @@ extern const SimpleArrayConfig felica_service_array_cfg; extern const SimpleArrayConfig felica_area_array_cfg; extern const SimpleArrayConfig felica_public_block_array_cfg; +extern const SimpleArrayConfig felica_system_array_cfg; + +void felica_system_init(FelicaSystem* system); +void felica_system_reset(FelicaSystem* system); +void felica_system_copy(FelicaSystem* system, const FelicaSystem* other); diff --git a/lib/nfc/protocols/felica/felica_poller.c b/lib/nfc/protocols/felica/felica_poller.c index a6964c685..98e07df42 100644 --- a/lib/nfc/protocols/felica/felica_poller.c +++ b/lib/nfc/protocols/felica/felica_poller.c @@ -12,6 +12,7 @@ ARRAY_DEF(felica_service_array, FelicaService, M_POD_OPLIST); // -V658 ARRAY_DEF(felica_area_array, FelicaArea, M_POD_OPLIST); // -V658 ARRAY_DEF(felica_public_block_array, FelicaPublicBlock, M_POD_OPLIST); // -V658 +ARRAY_DEF(felica_system_array, FelicaSystem, M_POD_OPLIST); // -V658 typedef NfcCommand (*FelicaPollerReadHandler)(FelicaPoller* instance); @@ -43,6 +44,9 @@ static FelicaPoller* felica_poller_alloc(Nfc* nfc) { instance->general_event.event_data = &instance->felica_event; instance->general_event.instance = instance; + instance->systems_read = 0; + instance->systems_total = 0; + return instance; } @@ -94,7 +98,7 @@ NfcCommand felica_poller_state_handler_activate(FelicaPoller* instance) { switch(instance->data->workflow_type) { case FelicaStandard: - instance->state = FelicaPollerStateTraverseStandardSystem; + instance->state = FelicaPollerStateListSystem; break; case FelicaLite: instance->state = FelicaPollerStateReadLiteBlocks; @@ -117,6 +121,44 @@ NfcCommand felica_poller_state_handler_activate(FelicaPoller* instance) { return command; } +NfcCommand felica_poller_state_handler_list_system(FelicaPoller* instance) { + FURI_LOG_D(TAG, "List System"); + + NfcCommand command = NfcCommandContinue; + + FelicaListSystemCodeCommandResponse* response_system_code; + FelicaError error = felica_poller_list_system_code(instance, &response_system_code); + + instance->systems_total = response_system_code->system_count; + simple_array_init(instance->data->systems, instance->systems_total); + uint8_t* system_codes = response_system_code->system_code; + + for(uint8_t i = 0; i < instance->systems_total; i++) { + FelicaSystem* system = simple_array_get(instance->data->systems, i); + system->system_code = system_codes[i * 2] << 8 | system_codes[i * 2 + 1]; + system->system_code_idx = i; + } + + if(error == FelicaErrorNone) { + instance->state = FelicaPollerStateSelectSystemIndex; + } else if(error != FelicaErrorTimeout) { + instance->felica_event.type = FelicaPollerEventTypeError; + instance->felica_event_data.error = error; + instance->state = FelicaPollerStateReadFailed; + } + return command; +} + +NfcCommand felica_poller_state_handler_select_system_idx(FelicaPoller* instance) { + FURI_LOG_D(TAG, "Select System Index %d", instance->systems_read); + uint8_t system_index_mask = instance->systems_read << 4; + instance->data->idm.data[0] &= 0x0F; + instance->data->idm.data[0] |= system_index_mask; + instance->state = FelicaPollerStateTraverseStandardSystem; + + return NfcCommandContinue; +} + NfcCommand felica_poller_state_handler_auth_internal(FelicaPoller* instance) { FURI_LOG_D(TAG, "Auth Internal"); @@ -274,6 +316,8 @@ NfcCommand felica_poller_state_handler_traverse_standard_system(FelicaPoller* in service->code = code_begin; service->attr = (uint8_t)(code_begin & 0x3F); + FURI_LOG_D(TAG, "Service %04X", service->code); + if(felica_area_array_size(area_buffer)) { FelicaArea* current_area = felica_area_array_back(area_buffer); current_area->last_idx = (uint16_t)(felica_service_array_size(service_buffer) - 1); @@ -284,31 +328,30 @@ NfcCommand felica_poller_state_handler_traverse_standard_system(FelicaPoller* in const size_t service_num = felica_service_array_size(service_buffer); const size_t area_num = felica_area_array_size(area_buffer); + FelicaSystem* system = simple_array_get(instance->data->systems, instance->systems_read); if(service_num) { - simple_array_init(instance->data->services, (uint32_t)service_num); + simple_array_init(system->services, (uint32_t)service_num); memcpy( - simple_array_get(instance->data->services, 0), + simple_array_get(system->services, 0), service_buffer->ptr, service_num * sizeof(FelicaService)); } else { - simple_array_reset(instance->data->services); + simple_array_reset(system->services); } if(area_num) { - simple_array_init(instance->data->areas, (uint32_t)area_num); + simple_array_init(system->areas, (uint32_t)area_num); memcpy( - simple_array_get(instance->data->areas, 0), - area_buffer->ptr, - area_num * sizeof(FelicaArea)); + simple_array_get(system->areas, 0), area_buffer->ptr, area_num * sizeof(FelicaArea)); } else { - simple_array_reset(instance->data->areas); + simple_array_reset(system->areas); } FURI_LOG_I( TAG, "Services found: %lu, Areas found: %lu", - simple_array_get_count(instance->data->services), - simple_array_get_count(instance->data->areas)); + simple_array_get_count(system->services), + simple_array_get_count(system->areas)); felica_service_array_clear(service_buffer); felica_area_array_clear(area_buffer); @@ -320,22 +363,22 @@ NfcCommand felica_poller_state_handler_traverse_standard_system(FelicaPoller* in NfcCommand felica_poller_state_handler_read_standard_blocks(FelicaPoller* instance) { FURI_LOG_D(TAG, "Read Standard Blocks"); - const uint32_t service_count = simple_array_get_count(instance->data->services); + FelicaSystem* system = simple_array_get(instance->data->systems, instance->systems_read); + const uint32_t service_count = simple_array_get_count(system->services); felica_public_block_array_t public_block_buffer; felica_public_block_array_init(public_block_buffer); - instance->state = FelicaPollerStateReadSuccess; bool have_read_anything = false; + FelicaError error = FelicaErrorNone; for(uint32_t i = 0; i < service_count; i++) { - const FelicaService* service = simple_array_get(instance->data->services, i); + const FelicaService* service = simple_array_get(system->services, i); if((service->attr & FELICA_SERVICE_ATTRIBUTE_UNAUTH_READ) == 0) continue; uint8_t block_count = 1; uint8_t block_list[1] = {0}; - FelicaError error = FelicaErrorNone; FelicaPollerReadCommandResponse* response; do { error = felica_poller_read_blocks( @@ -369,11 +412,20 @@ NfcCommand felica_poller_state_handler_read_standard_blocks(FelicaPoller* instan } } + if(error == FelicaErrorNone) { + instance->systems_read++; + if(instance->systems_read == instance->systems_total) { + instance->state = FelicaPollerStateReadSuccess; + } else { + instance->state = FelicaPollerStateSelectSystemIndex; + } + } + if(have_read_anything) { const size_t n = felica_public_block_array_size(public_block_buffer); - simple_array_init(instance->data->public_blocks, (uint32_t)n); + simple_array_init(system->public_blocks, (uint32_t)n); memcpy( - simple_array_get(instance->data->public_blocks, 0), + simple_array_get(system->public_blocks, 0), public_block_buffer->ptr, n * sizeof(FelicaPublicBlock)); } @@ -446,6 +498,8 @@ NfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) { instance->felica_event.type = FelicaPollerEventTypeReady; } + instance->data->idm.data[0] &= 0x0F; + instance->felica_event_data.error = FelicaErrorNone; return instance->callback(instance->general_event, instance->context); } @@ -453,6 +507,7 @@ NfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) { NfcCommand felica_poller_state_handler_read_failed(FelicaPoller* instance) { FURI_LOG_D(TAG, "Read Fail"); instance->callback(instance->general_event, instance->context); + instance->data->idm.data[0] &= 0x0F; return NfcCommandStop; } @@ -460,6 +515,8 @@ NfcCommand felica_poller_state_handler_read_failed(FelicaPoller* instance) { static const FelicaPollerReadHandler felica_poller_handler[FelicaPollerStateNum] = { [FelicaPollerStateIdle] = felica_poller_state_handler_idle, [FelicaPollerStateActivated] = felica_poller_state_handler_activate, + [FelicaPollerStateListSystem] = felica_poller_state_handler_list_system, + [FelicaPollerStateSelectSystemIndex] = felica_poller_state_handler_select_system_idx, [FelicaPollerStateAuthenticateInternal] = felica_poller_state_handler_auth_internal, [FelicaPollerStateAuthenticateExternal] = felica_poller_state_handler_auth_external, [FelicaPollerStateTraverseStandardSystem] = diff --git a/lib/nfc/protocols/felica/felica_poller_i.c b/lib/nfc/protocols/felica/felica_poller_i.c index 4ab9a8e4c..bbbb824d3 100644 --- a/lib/nfc/protocols/felica/felica_poller_i.c +++ b/lib/nfc/protocols/felica/felica_poller_i.c @@ -233,7 +233,9 @@ static void felica_poller_prepare_tx_buffer_raw( cmd.length = sizeof(FelicaCommandHeaderRaw) + data_length; bit_buffer_reset(instance->tx_buffer); bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&cmd, sizeof(FelicaCommandHeaderRaw)); - bit_buffer_append_bytes(instance->tx_buffer, data, data_length); + if(data_length > 0) { + bit_buffer_append_bytes(instance->tx_buffer, data, data_length); + } } FelicaError felica_poller_list_service_by_cursor( @@ -264,3 +266,31 @@ FelicaError felica_poller_list_service_by_cursor( *response_ptr = (FelicaListServiceCommandResponse*)bit_buffer_get_data(instance->rx_buffer); return error; } + +FelicaError felica_poller_list_system_code( + FelicaPoller* instance, + FelicaListSystemCodeCommandResponse** const response_ptr) { + furi_assert(instance); + furi_assert(response_ptr); + + uint8_t data[] = {0}; + + felica_poller_prepare_tx_buffer_raw(instance, FELICA_CMD_REQUEST_SYSTEM_CODE, data, 0); + + bit_buffer_reset(instance->rx_buffer); + + FelicaError error = felica_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT); + if(error != FelicaErrorNone) { + FURI_LOG_E(TAG, "Request system code failed with error: %d", error); + return error; + } + + size_t rx_len = bit_buffer_get_size_bytes(instance->rx_buffer); + if(rx_len < sizeof(FelicaCommandHeaderRaw) + 3) return FelicaErrorProtocol; + // at least 1 system code + the count being 0x01 + + // error is known to be FelicaErrorNone here + *response_ptr = (FelicaListSystemCodeCommandResponse*)bit_buffer_get_data(instance->rx_buffer); + return error; +} diff --git a/lib/nfc/protocols/felica/felica_poller_i.h b/lib/nfc/protocols/felica/felica_poller_i.h index 98948db86..446f34c43 100644 --- a/lib/nfc/protocols/felica/felica_poller_i.h +++ b/lib/nfc/protocols/felica/felica_poller_i.h @@ -17,6 +17,8 @@ extern "C" { typedef enum { FelicaPollerStateIdle, FelicaPollerStateActivated, + FelicaPollerStateListSystem, + FelicaPollerStateSelectSystemIndex, FelicaPollerStateAuthenticateInternal, FelicaPollerStateAuthenticateExternal, FelicaPollerStateTraverseStandardSystem, @@ -42,6 +44,8 @@ struct FelicaPoller { FelicaPollerEventData felica_event_data; NfcGenericCallback callback; uint8_t block_index; + uint8_t systems_read; + uint8_t systems_total; void* context; }; @@ -116,6 +120,10 @@ FelicaError felica_poller_list_service_by_cursor( uint16_t cursor, FelicaListServiceCommandResponse** response_ptr); +FelicaError felica_poller_list_system_code( + FelicaPoller* instance, + FelicaListSystemCodeCommandResponse** response_ptr); + #ifdef __cplusplus } #endif diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 801e0b2a3..429b3995e 100755 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -135,6 +135,7 @@ Header,+,lib/nfc/helpers/iso14443_crc.h,, Header,+,lib/nfc/helpers/nfc_data_generator.h,, Header,+,lib/nfc/helpers/nfc_util.h,, Header,+,lib/nfc/nfc.h,, +Header,+,lib/nfc/nfc_common.h,, Header,+,lib/nfc/nfc_device.h,, Header,+,lib/nfc/nfc_listener.h,, Header,+,lib/nfc/nfc_poller.h,, @@ -636,8 +637,8 @@ Function,-,arc4random_uniform,__uint32_t,__uint32_t Function,+,args_char_to_hex,_Bool,"char, char, uint8_t*" Function,+,args_get_first_word_length,size_t,FuriString* Function,+,args_length,size_t,FuriString* -Function,+,args_read_float_and_trim,_Bool,"FuriString*, float*" Function,+,args_read_duration,_Bool,"FuriString*, uint32_t*, const char*" +Function,+,args_read_float_and_trim,_Bool,"FuriString*, float*" Function,+,args_read_hex_bytes,_Bool,"FuriString*, uint8_t*, size_t" Function,+,args_read_int_and_trim,_Bool,"FuriString*, int*" Function,+,args_read_probably_quoted_string_and_trim,_Bool,"FuriString*, FuriString*" @@ -1119,7 +1120,7 @@ Function,+,felica_save,_Bool,"const FelicaData*, FlipperFormat*" Function,+,felica_service_get_attribute_string,void,"const FelicaService*, FuriString*" Function,+,felica_set_uid,_Bool,"FelicaData*, const uint8_t*, size_t" Function,+,felica_verify,_Bool,"FelicaData*, const FuriString*" -Function,+,felica_write_directory_tree,void,"const FelicaData*, FuriString*" +Function,+,felica_write_directory_tree,void,"const FelicaSystem*, FuriString*" Function,-,feof,int,FILE* Function,-,feof_unlocked,int,FILE* Function,-,ferror,int,FILE* From d8924c02adf1a704fdd9b0f681f052571bd59f81 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Oct 2025 03:54:01 +0300 Subject: [PATCH 08/30] upd changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a74d9b9a..6706f0288 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,12 @@ * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) ## Other changes * SubGHz: Fix crash in add manually menu +* OFW PR 4293: NFC FeliCa Improvement: Dump All Systems (by @zinongli) +* OFW PR 4285: ViewStack: Store View by value to save memory (by @CookiePLMonster) +* OFW PR 4290: Storage: Dont send mount event if SD mounted at boot (by @WillyJL) +* OFW PR 4283: NFC lib: Expose nfc_common.h (by @zinongli) +* OFW: Fix wrbl command tooltip +* OFW: VSCode: Reduce file watcher resource usage * OFW: cli: Buzzer command * OFW: Update demo_windows.txt * OFW: Fix PVS warnings From eb2bdf7ac219a42095719b451954e1f7214abfdf Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Oct 2025 03:57:40 +0300 Subject: [PATCH 09/30] upd changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6706f0288..7c92771a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * SubGHz: Add **ZKTeco 430.5 MHz** add manually support * SubGHz: Add variant of 'Add Manually' menu with manual editing for each value (PR #909 #911 #914 | by @MrLego8-9) * SubGHz: Temporarily remove HoneywellSec protocol due to unstable decoding and incorrect encoding +* Apps: HID PTT: adding global zoom and google meet shortcuts for MacOS (PR #921 | by @hryamzik) * OFW: NFC FeliCa: Service Directory Traverse + Dump All Unencrypted-Readable Services' Blocks * OFW: **NFC CLI commands** * OFW: LFRFID: **Show ISO-3166 Country Names For Pet Chips** From 2efcc8f6ccf034e6d39e39a7c3d4021cc8d6b323 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Oct 2025 03:58:30 +0300 Subject: [PATCH 10/30] fmt --- applications/system/hid_app/views/hid_ptt.c | 74 +++++++-------------- 1 file changed, 24 insertions(+), 50 deletions(-) diff --git a/applications/system/hid_app/views/hid_ptt.c b/applications/system/hid_app/views/hid_ptt.c index ff57c6bb7..8a28cd292 100644 --- a/applications/system/hid_app/views/hid_ptt.c +++ b/applications/system/hid_app/views/hid_ptt.c @@ -93,28 +93,16 @@ static void hid_ptt_trigger_hand_linux_meet(HidPushToTalk* hid_ptt) { // meet global macos static void hid_ptt_trigger_mute_macos_meet_global(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_7); - hid_hal_keyboard_release( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_7); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_7); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_7); } static void hid_ptt_trigger_camera_macos_meet_global(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_8); - hid_hal_keyboard_release( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_8); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_8); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_8); } static void hid_ptt_trigger_hand_macos_meet_global(HidPushToTalk* hid_ptt) { - hid_hal_keyboard_press( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_9); - hid_hal_keyboard_release( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_9); + hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_9); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL | HID_KEYBOARD_9); } static void hid_ptt_trigger_mute_macos_zoom(HidPushToTalk* hid_ptt) { hid_hal_keyboard_press(hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); @@ -140,35 +128,23 @@ static void hid_ptt_trigger_hand_zoom(HidPushToTalk* hid_ptt) { // zoom global macos static void hid_ptt_trigger_mute_macos_zoom_global(HidPushToTalk* hid_ptt) { hid_hal_keyboard_press( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | - HID_KEYBOARD_M); + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); hid_hal_keyboard_release( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | - HID_KEYBOARD_M); + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); } static void hid_ptt_trigger_camera_macos_zoom_global(HidPushToTalk* hid_ptt) { hid_hal_keyboard_press( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | - HID_KEYBOARD_U); + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_U); hid_hal_keyboard_release( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | - HID_KEYBOARD_U); + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_U); } static void hid_ptt_trigger_hand_zoom_global(HidPushToTalk* hid_ptt) { hid_hal_keyboard_press( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | - HID_KEYBOARD_Y); + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_Y); hid_hal_keyboard_release( - hid_ptt->hid, - KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | - HID_KEYBOARD_Y); + hid_ptt->hid, KEY_MOD_LEFT_GUI | KEY_MOD_RIGHT_ALT | KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_Y); } // this one is widely used across different apps @@ -628,13 +604,12 @@ static void hid_ptt_menu_callback( "reader. In this situation, the spacebar performs a different action.\n\n"; break; case HidPushToTalkAppIndexGoogleMeetGlobal: - app_specific_help = - "Google Meet (Global):\n" - "1. Install \"Google Meet - Global Shortcuts\" extension.\n" - "2. Open chrome://extensions/shortcuts.\n" - "3. Set 'Toggle microphone' to Cmd+Ctrl+7 and enable Global.\n" - "4. Set 'Toggle camera' to Cmd+Ctrl+8 and enable Global.\n" - "5. Set 'Raise hand' to Cmd+Ctrl+9 and enable Global.\n\n"; + app_specific_help = "Google Meet (Global):\n" + "1. Install \"Google Meet - Global Shortcuts\" extension.\n" + "2. Open chrome://extensions/shortcuts.\n" + "3. Set 'Toggle microphone' to Cmd+Ctrl+7 and enable Global.\n" + "4. Set 'Toggle camera' to Cmd+Ctrl+8 and enable Global.\n" + "5. Set 'Raise hand' to Cmd+Ctrl+9 and enable Global.\n\n"; break; case HidPushToTalkAppIndexDiscord: app_specific_help = @@ -655,13 +630,12 @@ static void hid_ptt_menu_callback( "Go to Settings > Privacy. Make sure Keyboard shortcut to unmute is toggled on.\n\n"; break; case HidPushToTalkAppIndexZoomGlobal: - app_specific_help = - "Zoom (Global):\n" - "1. Go to Settings > Keyboard Shortcuts.\n" - "2. Find the 'Mute/Unmute' shortcut and click 'Edit'.\n" - "3. Press the Mute button in the app to bind it.\n" - "4. Check global checkbox.\n" - "5. Repeat for video and hand shortcuts.\n\n"; + app_specific_help = "Zoom (Global):\n" + "1. Go to Settings > Keyboard Shortcuts.\n" + "2. Find the 'Mute/Unmute' shortcut and click 'Edit'.\n" + "3. Press the Mute button in the app to bind it.\n" + "4. Check global checkbox.\n" + "5. Repeat for video and hand shortcuts.\n\n"; } FuriString* msg = furi_string_alloc(); From b604c93eb01ff8456937030b94b4c52726a5b7e0 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Oct 2025 21:51:25 +0300 Subject: [PATCH 11/30] fix merge artifact --- .../helpers/protocol_support/felica/felica.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index 1b52b86bd..26dc86d41 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -126,15 +126,6 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) { furi_string_free(temp_str); } -static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) { - if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) { - scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); - return true; - } - - return false; -} - static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) { const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica); instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolFelica, data); @@ -194,7 +185,7 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = { .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = nfc_scene_saved_menu_on_event_felica, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { @@ -206,4 +197,11 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = { .on_enter = nfc_scene_emulate_on_enter_felica, .on_event = nfc_protocol_support_common_on_event_empty, }, + .scene_write = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, }; + +NFC_PROTOCOL_SUPPORT_PLUGIN(felica, NfcProtocolFelica); From 90f446880ab216164c1d9f256d11d29aecf404e7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Oct 2025 21:54:34 +0300 Subject: [PATCH 12/30] Add MIFARE Classic "Show Keys" UI by aaronjamt --- .../protocol_support/mf_classic/mf_classic.c | 21 +++++ .../main/nfc/scenes/nfc_scene_config.h | 1 + .../scenes/nfc_scene_mf_classic_show_keys.c | 85 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_classic_show_keys.c diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index fdf7fb35a..53218bb9f 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -15,6 +15,7 @@ enum { SubmenuIndexDictAttack, SubmenuIndexCrackNonces, SubmenuIndexUpdate, + SubmenuIndexShowKeys, }; static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { @@ -139,6 +140,13 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { nfc_protocol_support_common_submenu_callback, instance); } + + submenu_add_item( + submenu, + "Show Keys", + SubmenuIndexShowKeys, + nfc_protocol_support_common_submenu_callback, + instance); } static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { //-V524 @@ -186,6 +194,13 @@ static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) { SubmenuIndexUpdate, nfc_protocol_support_common_submenu_callback, instance); + + submenu_add_item( + submenu, + "Show Keys", + SubmenuIndexShowKeys, + nfc_protocol_support_common_submenu_callback, + instance); } static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) { @@ -218,6 +233,9 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManag instance->scene_manager, NfcSceneSaveConfirm, NfcSceneSaveConfirmStateCrackNonces); scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm); consumed = true; + } else if(event.event == SubmenuIndexShowKeys) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicShowKeys); + consumed = true; } } @@ -240,6 +258,9 @@ static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, SceneMana } else if(event.event == SubmenuIndexUpdate) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); consumed = true; + } else if(event.event == SubmenuIndexShowKeys) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicShowKeys); + consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 6634827ed..2cace2925 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -46,6 +46,7 @@ ADD_SCENE(nfc, mf_classic_mfkey_complete, MfClassicMfkeyComplete) ADD_SCENE(nfc, mf_classic_update_initial, MfClassicUpdateInitial) ADD_SCENE(nfc, mf_classic_update_initial_success, MfClassicUpdateInitialSuccess) ADD_SCENE(nfc, mf_classic_update_initial_wrong_card, MfClassicUpdateInitialWrongCard) +ADD_SCENE(nfc, mf_classic_show_keys, MfClassicShowKeys) ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_show_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_show_keys.c new file mode 100644 index 000000000..9493f2e7a --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_show_keys.c @@ -0,0 +1,85 @@ +#include "../nfc_app_i.h" + +#include +#include + +#define TAG "NfcMfClassicShowKeys" + +void nfc_scene_mf_classic_show_keys_callback(GuiButtonType button, InputType type, void* context) { + NfcApp* instance = context; + if(button == GuiButtonTypeLeft && type == InputTypeShort) { + scene_manager_previous_scene(instance->scene_manager); + } +} + +void nfc_scene_mf_classic_show_keys_on_enter(void* context) { + NfcApp* instance = context; + + const MfClassicData* mfc_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + + furi_string_reset(instance->text_box_store); + nfc_append_filename_string_when_present(instance, instance->text_box_store); + + furi_string_cat_printf(instance->text_box_store, "\e#Found MFC Keys:"); + + uint8_t num_sectors = mf_classic_get_total_sectors_num(mfc_data->type); + uint8_t found_keys_a = 0, found_keys_b = 0; + for(uint8_t i = 0; i < num_sectors; i++) { + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(mfc_data, i); + + bool key_a = FURI_BIT(mfc_data->key_a_mask, i); + bool key_b = FURI_BIT(mfc_data->key_b_mask, i); + + if(key_a || key_b) { + furi_string_cat_printf(instance->text_box_store, "\n -> Sector %d\n\e*AccBits:", i); + for(uint8_t j = 0; j < MF_CLASSIC_ACCESS_BYTES_SIZE; j++) { + furi_string_cat_printf( + instance->text_box_store, " %02X", sec_tr->access_bits.data[j]); + } + } + + if(key_a) { + found_keys_a++; + furi_string_cat_printf(instance->text_box_store, "\n\e*A:"); + for(uint8_t j = 0; j < MF_CLASSIC_KEY_SIZE; j++) + furi_string_cat_printf(instance->text_box_store, " %02X", sec_tr->key_a.data[j]); + } + if(key_b) { + found_keys_b++; + furi_string_cat_printf(instance->text_box_store, "\n\e*B:"); + for(uint8_t j = 0; j < MF_CLASSIC_KEY_SIZE; j++) + furi_string_cat_printf(instance->text_box_store, " %02X", sec_tr->key_b.data[j]); + } + } + + furi_string_cat_printf( + instance->text_box_store, + "\nTotal keys found:\n -> %d/%d A keys\n -> %d/%d B keys", + found_keys_a, + num_sectors, + found_keys_b, + num_sectors); + + widget_add_text_scroll_element( + instance->widget, 2, 2, 124, 60, furi_string_get_cstr(instance->text_box_store)); + widget_add_button_element( + instance->widget, + GuiButtonTypeLeft, + "Back", + nfc_scene_mf_classic_show_keys_callback, + instance); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_show_keys_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void nfc_scene_mf_classic_show_keys_on_exit(void* context) { + NfcApp* instance = context; + widget_reset(instance->widget); + furi_string_reset(instance->text_box_store); +} From c6f26c696c905766d89f8e4aca6a481b436a3b93 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Oct 2025 21:55:06 +0300 Subject: [PATCH 13/30] upd changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c92771a6..9deca902a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * SubGHz: Add **ZKTeco 430.5 MHz** add manually support * SubGHz: Add variant of 'Add Manually' menu with manual editing for each value (PR #909 #911 #914 | by @MrLego8-9) * SubGHz: Temporarily remove HoneywellSec protocol due to unstable decoding and incorrect encoding +* NFC: Add MIFARE Classic "Show Keys" UI (by @aaronjamt) * Apps: HID PTT: adding global zoom and google meet shortcuts for MacOS (PR #921 | by @hryamzik) * OFW: NFC FeliCa: Service Directory Traverse + Dump All Unencrypted-Readable Services' Blocks * OFW: **NFC CLI commands** From 5a933f13ad3a1b51dd9d5d80c81dda1d47cbbb02 Mon Sep 17 00:00:00 2001 From: Mykhailo Shevchuk Date: Sun, 12 Oct 2025 22:53:25 +0300 Subject: [PATCH 14/30] Returning fix for reading PWD locked MFUL --- .../protocols/mf_ultralight/mf_ultralight_poller.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c index c3dbe88d6..e29e1cb6b 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c @@ -565,16 +565,12 @@ static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* in instance->error = mf_ultralight_poller_read_page(instance, start_page, &data); } - // Regression review - const uint8_t read_cnt = instance->data->type == MfUltralightTypeMfulC ? 1 : 4; if(instance->error == MfUltralightErrorNone) { - for(size_t i = 0; i < read_cnt; i++) { - if(start_page + i < instance->pages_total) { - FURI_LOG_D(TAG, "Read page %d success", start_page + i); - instance->data->page[start_page + i] = data.page[i]; - instance->pages_read++; - instance->data->pages_read = instance->pages_read; - } + if(start_page < instance->pages_total) { + FURI_LOG_D(TAG, "Read page %d success", start_page); + instance->data->page[start_page] = data.page[0]; + instance->pages_read++; + instance->data->pages_read = instance->pages_read; } if(instance->pages_read == instance->pages_total) { From b3a5e2c28215d0d6df8de81e5721bde4f38788de Mon Sep 17 00:00:00 2001 From: Mykhailo Shevchuk Date: Sun, 12 Oct 2025 23:09:14 +0300 Subject: [PATCH 15/30] Added UL-C keys to the dictionary --- .../main/nfc/resources/nfc/assets/mf_ultralight_c_dict.nfc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/applications/main/nfc/resources/nfc/assets/mf_ultralight_c_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_ultralight_c_dict.nfc index fa5dbb1fb..b68e82867 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_ultralight_c_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_ultralight_c_dict.nfc @@ -8,6 +8,10 @@ 49454D4B41455242214E4143554F5900 # Modified Semnox Key (IEMKAERB!NACUOYF) 49454D4B41455242214E4143554F5946 +# iKey UL-C Magic Tag Key +921AAC1654B8D83A669A9012AE7C1222 +# Kyiv NKS Vizit Key +D2C8C2CEC8CCC420204E4042544E5846 # Mix of Proxmark and ChameleonMiniLiveDebugger 00000000000000000000000000000000 From d4658779192fa9cd2fc7b54cae396215bf337b7c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Oct 2025 23:36:50 +0300 Subject: [PATCH 16/30] upd changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9deca902a..05406c7c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ * SubGHz: Add **ZKTeco 430.5 MHz** add manually support * SubGHz: Add variant of 'Add Manually' menu with manual editing for each value (PR #909 #911 #914 | by @MrLego8-9) * SubGHz: Temporarily remove HoneywellSec protocol due to unstable decoding and incorrect encoding +* NFC: Returning fix for reading PWD locked MFUL (PR #922 | by @mishamyte) +* NFC: Added UL-C keys to the dictionary (PR #923 | by @mishamyte) * NFC: Add MIFARE Classic "Show Keys" UI (by @aaronjamt) * Apps: HID PTT: adding global zoom and google meet shortcuts for MacOS (PR #921 | by @hryamzik) * OFW: NFC FeliCa: Service Directory Traverse + Dump All Unencrypted-Readable Services' Blocks From b03316d89d652035fb3829d09b1ca8473e89cb56 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 13 Oct 2025 01:32:43 +0300 Subject: [PATCH 17/30] add support for top44rbn remotes --- CHANGELOG.md | 1 + lib/subghz/protocols/came_atomo.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05406c7c2..f05a88aab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## Main changes - Current API: 87.0 +* SubGHz: Add support for **Came Atomo (TOP44RBN)** remotes (thanks @mishamyte for recordings) * SubGHz: Add **Elplast 18bit** static code protocol (hello Hackcat ^_^) * SubGHz: Try to **decode BFT** (2 buttons remotes only) **on the fly** in regular Read mode (no more KL Unknown and all of that for free?!) (for 4 button remote follow docs [here](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)) * SubGHz: **Tune Linear** (edited by @WillyJL in PR #919 #920) (add better EZCode support) and **Dickert MAHS** protocol decoders diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 5b9e6defd..2162169b2 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -418,8 +418,11 @@ void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t ManchesterEvent event = ManchesterEventReset; switch(instance->decoder.parser_step) { case CameAtomoDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 60) < - subghz_protocol_came_atomo_const.te_delta * 40)) { + // There are two known options for the header: 72K us (TOP42R, TOP44R) or 12k us (found on TOP44RBN) + if((!level) && ((DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 10) < + subghz_protocol_came_atomo_const.te_delta * 20) || + (DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 60) < + subghz_protocol_came_atomo_const.te_delta * 40))) { //Found header CAME instance->decoder.parser_step = CameAtomoDecoderStepDecoderData; instance->decoder.decode_data = 0; From dbc5895b30f3afb2f692dd7a8681945832cf80a3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 13 Oct 2025 22:26:31 +0300 Subject: [PATCH 18/30] upd anims --- .../external/L1_Halloween_128x64/frame_0.png | Bin 0 -> 1597 bytes .../external/L1_Halloween_128x64/frame_1.png | Bin 0 -> 1592 bytes .../external/L1_Halloween_128x64/frame_2.png | Bin 0 -> 1605 bytes .../external/L1_Halloween_128x64/frame_3.png | Bin 0 -> 1594 bytes .../external/L1_Halloween_128x64/meta.txt | 14 ++++++++++++++ assets/dolphin/external/manifest.txt | 7 +++++++ 6 files changed, 21 insertions(+) create mode 100644 assets/dolphin/external/L1_Halloween_128x64/frame_0.png create mode 100644 assets/dolphin/external/L1_Halloween_128x64/frame_1.png create mode 100644 assets/dolphin/external/L1_Halloween_128x64/frame_2.png create mode 100644 assets/dolphin/external/L1_Halloween_128x64/frame_3.png create mode 100644 assets/dolphin/external/L1_Halloween_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Halloween_128x64/frame_0.png b/assets/dolphin/external/L1_Halloween_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..f69a43f5e75a4efee946984b5f551d2af692bf72 GIT binary patch literal 1597 zcmV-D2EzG?P)Px)_en%SRCt{2T-kEuFbK4_=Kp`$`=k`X91TLp#O|upB(@GD5J%gS^SZ9<*bwJ= z-mYyb>R036Y5_RT^Zcd!ru6H&M5B1ohtFyO8n|-lX@8YJ4(F~R;Kdwl7Xn_)#cKc$ z$7=u&$7=u&$7=u&$7=u&$2L5`H^SmBz6Fmn0F6+JD6fsd`)Jp9#$$<~?6ZdRJl%Fk zn_Zw9fJeQ4%K{Nd&mOdPoR-$we~S@_NuYK*=+9pQU6W_{O|k7~#4`$OOzB>a3^xye zS~tZAgIMK(tT?WnYVH}RF?k-vn@bvlUgP(pp66E*=n15u*B~+$T#;}^H!ETk=y=lO z@AkK(XnLaRX#;FJoG9tYd{q)9L)*%+**4{v6_dG#zjL}gIfk>EGy%_SLGEE zS-ibK*N`zh+FtV*^d2gUX#iFnvV=s+q==9sAgwD?;(2+2H9u{e1>#LCIerX4vPx7z zQH63hwes{w-FK#sgzD-+^D3@EOI1MRR8-Wvk@|WyDu^H{rjIZPL?)34iV`CgK$EiS zOgw`MDp)#bt?vkKLCtHjRInBMtVz9?Wo>{p-U^S=6EeN5-trPrBe6VsnE_NOB66A| zN$M`ttVir1eKUrj?NjP$pfnJ|czNwcK_XUGK%;Td{HI;dtU+7*@E1ZPV#7kzsw3@I zE1yk;CAGPE0LiNUf)XUV&FgJG zQajV@Rqa+`OAio%NAn_dszz6%28Ft2%_CF+8JEkNG5}=38Du?6FS$b7X4i%8iDn4M zy0Z_}tmg{uJAu*3su0PJt#;@dXlO%YyOblK;Gv;+y|?m?8H+Z4nhC7V1tQNJBJ=Lf zB3h-MwjW3-Dvd2c4FPmgt0o?qp*D9Z{ZvBRVDD6pSUX)GQbA8_nH$d4g28SI9gvC-Cm|%qu<&ia^R@BNT4MsX_Vu zwwUG_hMMk?RSoVEB_A=8X-(91#4{vXZSGp7Ps_(*celTAal zERp!!JASXRsb3DY7LieY)}3l- zU{=B;8BjGwh6Q9?%~)%*(~&9#EnzJ$V9n3NfJg*YDXD@KnaQk4uyTzCD}mIMAob&YwQF8^>?xmqy8j|Z z5+oACxW!psz)QL;7pKXu@fq}5PaW+6^)%kzSnYd0MGp{Fga%3ZJ&1TFD~ZiOGo+|2 z6x0(`DmC&6JqtjyWUVb%J~yx#F5a38^#T#Okv2RWtT2L-he7Px)@<~KNRCt{2TiK4IKn#Rc{{NS=PrY&+A63Rp!-kSZnSnms_%0^<`~7}z7sS5r zU)L@w>aFo-wE!CXzJDlxDE<9@n?~{A7+$Ld7|_a<-u@_m9`>uIfCuN`aw*`!xp)lV z$MG1zkK-|bAID<=KaR%$ejJzK0q%&1ySNK}&Hy|@iBaBIL(lQ9Ul~731XbTP?E9XM zL&jVMt^s7!>s=0rz1R%cgUar3Gz&t;Sa@*pBe8cqBX61M`ZYl z0E{m=!UMWuih+kWmrD%DtLymtb;E3`i*c5R@iTdHOb2~yfS`+BKz44Y}y$EBii>9)Hv<;Lw4+R^- zSJo8P-qpemJx9gxHxLN3+f-OfIb7yc1Ze3HQf}`9QZJOn*%m}W;zaL4NSydlJ3D!uWh~8!ZD|&OL zNWBU*n-M$6+=?N1&#Ck_&{_y(4Oe>VY9sJyT(qzp*DGi6&OZGgLM&n<0&CUGxYf#+ z=}kw>!L>+skR`e*=Yt}E4mno{r5afZx{C6nwCRy0o6tls|!Q00&N zC|J@g8uyg~^x)NbfA#*ekfm0uR?u^!G1gkv7_$bjTD-d}VV3+Q{vD_btZPhh=Z(>0 zUrZ~t!U$&3&CK|S^~~7L0W|klXn2q5^TtR}JOE2U9>M5=I}MJJUy?^s9z-^)m1AU- zGOz`-)geT<26~%xc#I}2R;Qedd6VO2MT>|L zL}fubk1qqTdT?2|pqt+fY&$t{h7nj!kwu3wCzJ2%yMc(6b*6)v^fE)q9tr6yXmX~& zjqZPaH_-Y!y5|YC3bN&qUJwy79g3TEc7|K1;Dsu;qgS@p?Cc^Cc`V|lOOCT6LB|=E zy$hJpLPms8RuJhLpz2*`L_sFCdGp8kmp*9Ldq$pyD7Bkhvi8hEyhq}6M*?f_4#@S7 z%ItnQG&)2@`IQI4vSXQXGHV?L;xL_iAdY8^`RD>NFQ{5uwL6evjQ}RniN2{d9Ew3@}RHHtc1JJa0#8$qbiW&stYrle~yN zFWm-eg2hs{cb(;`SCUFrgkhtD_t3LB)p3!>OF87+X*>XG1hes`xQ+D!TKlm2!v zQhY`0RJx2aNEM{u%xVP-TG>&dj7CUS$vwM~VhoJj$QYdtRv1CczlCd0w;n__ZFHg2*s=&& z-RH6qpsOfE)?@cVJl9G^UaDXe*JVsnj%5+xsFIJ&uX>VKSTV}<9?UA79Rc=j+t!^H q0OH-&oU9PE=64pX3?c)b{rMLrmm-QE=NLNx0000Px)|4BqaRCt{2T-&mnFbp)E^Z&n`J~^W(y6nm}m+s^_}r^4&Vt&sL_t&aNG9lQJdZH zvqDhiT|+mlB~^QEcZVARGU@dW4;MkMzdtKtG~MPdhW21afM0zRm*sHJ*g7)YDHVK0 z03^IwZISi}u8ch0h@Rmh)IFSoh?5s(X=^fvk6DhOxZ; zN}!>>13htQ7}rjy_f8&6>(Ab}Xe0<7zzaiO+rGFN?N-BjF624UGLRVqf#)Qi1Hrn6 ztOBau%uG1iZ#GzCR!T)?4I`*1lV4>Zb1G(#D38^4T{tt!kY7IEfp!aa%z$Ls1sC;xJ%k;&5pFKt@{AhJ;*{Wsc*`spsyX9DpeY zy}z#jtHND%`lH%CJ2b8NPJzyC_1Wrs04;&`YiM5&J46O;=+-_BY4L#$W)IkB-O2$v zDvDM#*46OTRPE%w9ZRrLeqa*q*4oFFh{3Zthv^fIwSsh>g^F+#aRPKdtoI0}ZJTm{ z>V&Is_57niqZt~hD(O*pFP^tbBBGG{Mk;R#U)0)WSk<>O|r1= zRTfs-@y3FLph&U06Oi@yIz2L2)}|uV$i8|m>mJe{X-_GEtDT^dZYpvpI;JI8qj|Tt zx70nBu&vzlErp;eqEvM*T{!gJ(wpl*yx~tb&X!>$V=#XB0iQj*no; z$r)K~4g9x(E42V??6dRa%=uXDN7o^d2`{}3 z%QX#uYliZqjXq1FS5IBSjEh3qm+Q93V4_o}{_~XBl7S!E7@a z4xsz3b^B*E;2^;H%#gSiD8w&r*Q*(q|V=U_m~2b>2VE<1=O)u~CFa z%vAld{Ak_x)_;d|Fj_jx{%;Nt5j=D3)#EK))yrV)3gHctI8GqE-K7^pmFz94D zw^o3VIT`*KW$g@%aEZzs$YHYDd?|rmJt-$-7C7pmsKX&LacjQFwOWK)nHEM4rAY!= z5yFG_(Aj1%Tq0VZtc4@=u~5yRidkc#&g9J}ue()1R41dX+UvLSq@hOF>yrJnC^TCE zjs89p5&3nc;0$`RqLAwKlh=^aY$ce}RE-Q#l~Oqj5l6GG$%6-_fDuH-UyaBiSstS| zD|31a-lk`Ls_mkEm+MGVz|0&tYW&f4cwz}Mu4g>W8LT~{AdXqMS*C3od%$9 z0_Ew+$QR=AY?ZOHl*-`M^pP=jJIe@GKi${6mT4N9@#Sy$KZHaNmO*`<+E_Bys#cJ+mMxr zoQ*N87g3n`tg)_g0*V97LNp#b7h*Uo9WiWP14&gCC%?i8-W*`Ik!OJ+ld_M;L9A$n zp6XSwxYlV-&;~yUSqPx)^hrcPRCt{2TF?KB^t$xK73XS(7=^TPy4I4%GvA)aw%#XMxJ!)`}>Nw|R=9HHcM!b~@y`E>GY%cA7n%{H`)u+6S{%~aA`|aImPqSD)`^yZ$ozEP zED&#E8Y1NZs!j!hT?`?r%uK21&0B%2C%6$okyBBTL`m~CtTqk}y;QIi(?=MDH0Mng zi3yy|bXi6qp{MK`S*}KNC27s0-hWi}n4yBDjr5ELxsMn@Z`IH{Mg@?p-trPrBe6Vs z*Z`{Bz9(2_%!unKOS z>~=&vlvg=78+H|*Vx)c^D8tgObY?M zkT~$3u~x#8UsJqQ&-3vl!{Xu8Og51uFnSs*Yxz-0=PaPc98Koh_lO))pO06|W)!en zBDO=uUzMP04}7vAP<r3M&cH4wpOQ=MlYh5P zaaJa-%xFR_RR|U!B8U#lcTBGA19Jo@~zA zS!2!iq7iIK1Z82>$4EYdk|%sz-q&k~jGF;+fcQRye;qu^3slB(O}=O%7Lf$!saPGEGh3LrJDbiGI3Xo!#uHfk{Fyr^+#yntn#sSr?I9(tsR)>e`$3%aJ6 zv07qY`8_L%?A;IOw}B(M0B!7ij6gaRo_A<{Jo#?F4UAC3SvNRK`4LLC$EB~J z%p*y!b^mJs7AGxbnAydS-YBnBk~XbEp3ymhHD}MfxAy@wcVX4aJGk>6;-*Ue*?Up3 zhLV31xB5AOl(+EvUP8ok05lQgn6Vd;(5Cfo?LT~>UhS!!G&ClS;&NQR4lu7StN|5^TzS=dfr1g|fKcN>w z=;`s2FT~|VtBe_>3X*VI9zg?Fwx)E+JtOjTe_n?4J;{%C+C29c@j#x-)RI|xEmBr# zb6R~o!{&0#{DeF}W^=5K<%%A4vZe1@Tf_(=awBbcI9Oo>C9j3^(=Kg<6*gv|(%2AB zm6>N-4^WvDdgDdsLQbv}j-*^Bns8Pb6P06`2k2FDPyg&m(h{p^T$eUx6=r*Y^Ei%m ss|A3ZZmAEF2=e|~(mEQJ0gwLtA4(N8cxHshp8x;=07*qoM6N<$g2^=a*8l(j literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Halloween_128x64/meta.txt b/assets/dolphin/external/L1_Halloween_128x64/meta.txt new file mode 100644 index 000000000..9762d4caa --- /dev/null +++ b/assets/dolphin/external/L1_Halloween_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 4 +Active frames: 0 +Frames order: 0 1 2 3 +Active cycles: 0 +Frame rate: 3 +Duration: 3600 +Active cooldown: 0 + +Bubble slots: 0 diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index 951a4cdf7..2048d09c4 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -245,3 +245,10 @@ Max butthurt: 13 Min level: 1 Max level: 3 Weight: 4 + +Name: L1_Halloween_128x64 +Min butthurt: 0 +Max butthurt: 14 +Min level: 1 +Max level: 3 +Weight: 4 From 9506ccde2e54ea5c2cd5a5e95d8d960f54b40110 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 13 Oct 2025 22:56:32 +0300 Subject: [PATCH 19/30] upd changelog --- CHANGELOG.md | 46 ++---------------------- lib/nfc/protocols/felica/felica_poller.c | 9 +++-- 2 files changed, 8 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f05a88aab..a6bf4b4a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,51 +1,9 @@ ## Main changes - Current API: 87.0 -* SubGHz: Add support for **Came Atomo (TOP44RBN)** remotes (thanks @mishamyte for recordings) -* SubGHz: Add **Elplast 18bit** static code protocol (hello Hackcat ^_^) -* SubGHz: Try to **decode BFT** (2 buttons remotes only) **on the fly** in regular Read mode (no more KL Unknown and all of that for free?!) (for 4 button remote follow docs [here](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md)) -* SubGHz: **Tune Linear** (edited by @WillyJL in PR #919 #920) (add better EZCode support) and **Dickert MAHS** protocol decoders -* SubGHz: RAW protocol fixes (by @WillyJL) -* SubGHz: Add **ZKTeco 430.5 MHz** add manually support -* SubGHz: Add variant of 'Add Manually' menu with manual editing for each value (PR #909 #911 #914 | by @MrLego8-9) -* SubGHz: Temporarily remove HoneywellSec protocol due to unstable decoding and incorrect encoding -* NFC: Returning fix for reading PWD locked MFUL (PR #922 | by @mishamyte) -* NFC: Added UL-C keys to the dictionary (PR #923 | by @mishamyte) -* NFC: Add MIFARE Classic "Show Keys" UI (by @aaronjamt) -* Apps: HID PTT: adding global zoom and google meet shortcuts for MacOS (PR #921 | by @hryamzik) -* OFW: NFC FeliCa: Service Directory Traverse + Dump All Unencrypted-Readable Services' Blocks -* OFW: **NFC CLI commands** -* OFW: LFRFID: **Show ISO-3166 Country Names For Pet Chips** -* OFW: **JS views finished** -* OFW: BLE: improved pairing security -* OFW: FeliCa Emulation: Handle certain Polling commands in firmware -* OFW PR 4287: Fix Ultralight EV1 regression (by @noproto) -* OFW PR 4271: NFC: **Ultralight C NFC App Key Management, Dictionary Attack** (by @noproto) -* OFW PR 4265: NFC: **Fix read crash** with unexpectedly large MFC AUTH(0) response (by @WillyJL) -* OFW PR 4251: CLI: **Fix long delay** with quick connect/disconnect (by @WillyJL) -* LFRFID: Add additional procotols supported by **EM4305** chipset (by @jamisonderek) +* OFW PR 4279: NFC FeliCa Minor Fix: FelicaPollerEventType should only be Incomplete if the tag is FeliCa Lite (by @zinongli) * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) ## Other changes -* SubGHz: Fix crash in add manually menu -* OFW PR 4293: NFC FeliCa Improvement: Dump All Systems (by @zinongli) -* OFW PR 4285: ViewStack: Store View by value to save memory (by @CookiePLMonster) -* OFW PR 4290: Storage: Dont send mount event if SD mounted at boot (by @WillyJL) -* OFW PR 4283: NFC lib: Expose nfc_common.h (by @zinongli) -* OFW: Fix wrbl command tooltip -* OFW: VSCode: Reduce file watcher resource usage -* OFW: cli: Buzzer command -* OFW: Update demo_windows.txt -* OFW: Fix PVS warnings -* OFW: NFC: Amusement IC Card Parser (FeliCa Lite & Lite-S) -* OFW: hid_app mouse clicker: make mouse button selectable -* OFW: JS: Expose button event type in gui/widget button callback -* OFW: NFC: MFC 1k Banapass Parser -* OFW: GUI Bug Fix: Number Input Save Icon -* Add possibility to use custom buttons when using the SubGHz remote app (by @MrLego8-9) -* Input Settings: Add Vibro Trigger option (by @956MB & @WillyJL) -* BT Remote: Add Rename Option (by @aaronjamt & @WillyJL) -* Simplify Bad USB BLE profile (by @aaronjamt & @WillyJL) -* NFC: Fix incorrect Saflok year formula (by @Eltrick) -* JS: Expose button event type in gui/widget button callback (by @WillyJL) +* Enable halloween anim

#### Known NFC post-refactor regressions list: - Mifare Mini clones reading is broken (original mini working fine) (OFW) diff --git a/lib/nfc/protocols/felica/felica_poller.c b/lib/nfc/protocols/felica/felica_poller.c index 98e07df42..056ed4320 100644 --- a/lib/nfc/protocols/felica/felica_poller.c +++ b/lib/nfc/protocols/felica/felica_poller.c @@ -486,9 +486,12 @@ NfcCommand felica_poller_state_handler_read_lite_blocks(FelicaPoller* instance) NfcCommand felica_poller_state_handler_read_success(FelicaPoller* instance) { FURI_LOG_D(TAG, "Read Success"); - if(!instance->auth.context.auth_status.internal || - !instance->auth.context.auth_status.external) { - instance->data->blocks_read--; + if((!instance->auth.context.auth_status.internal || + !instance->auth.context.auth_status.external) && + instance->data->workflow_type == FelicaLite) { + if(instance->data->blocks_read != 0) { + instance->data->blocks_read--; + } instance->felica_event.type = FelicaPollerEventTypeIncomplete; } else { memcpy( From d673fd5573eaa2755f3004ec35a7445c2620cb6d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 16 Oct 2025 10:15:26 +0300 Subject: [PATCH 20/30] return honeywellsec with some fixes --- CHANGELOG.md | 1 + ReadMe.md | 2 +- lib/subghz/protocols/honeywell.c | 408 ++++++++++++++++++++++++++ lib/subghz/protocols/honeywell.h | 109 +++++++ lib/subghz/protocols/protocol_items.c | 1 + lib/subghz/protocols/protocol_items.h | 1 + 6 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 lib/subghz/protocols/honeywell.c create mode 100644 lib/subghz/protocols/honeywell.h diff --git a/CHANGELOG.md b/CHANGELOG.md index a6bf4b4a8..414be0a43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## Main changes - Current API: 87.0 +* SubGHz: Return Honeywell Sec with fixes and improvements (by htotoo & LiQuiDz & xMasterX) * OFW PR 4279: NFC FeliCa Minor Fix: FelicaPollerEventType should only be Incomplete if the tag is FeliCa Lite (by @zinongli) * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) ## Other changes diff --git a/ReadMe.md b/ReadMe.md index 5e8903f9c..fee5aeb74 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -81,7 +81,7 @@ Before getting started: > - FAAC SLH, 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, (right arrow button for other protocols)) > - Debug mode counter increase settings (+1 → +5, +10, default: +1) > - Debug PIN output settings for protocol development -> - Ignore options - Alarms: Hollarm, GangQi | Cars: Kia, Starline, ScherKhan | Sensors: Magellan, Honeywell WDB (doorbells), Legrand (doorbells), Feron (RGB lights) +> - Ignore options - Alarms: Hollarm, GangQi | Cars: Kia, Starline, ScherKhan | Sensors: Magellan, Honeywell Sec, Honeywell WDB (doorbells), Legrand (doorbells), Feron (RGB lights) > >
diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c new file mode 100644 index 000000000..66e1c5053 --- /dev/null +++ b/lib/subghz/protocols/honeywell.c @@ -0,0 +1,408 @@ +#include "honeywell.h" + +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +// Created by HTotoo 2023-10-30 +// Got a lot of help from LiQuiDz. +// Protocol decoding help from: https://github.com/merbanan/rtl_433/blob/master/src/devices/honeywell.c +// Fixes and style changes to use similar codebase as other protocols by @xMasterX 2025-10 + +/* +64 bit packets, repeated multiple times per open/close event. + +Protocol whitepaper: "DEFCON 22: Home Insecurity" by Logan Lamb. + +Data layout: + + PP PP C IIIII EE SS SS + +- P: 16bit Preamble and sync bit (always ff fe) +- C: 4bit Channel +- I: 20bit Device serial number / or counter value +- E: 8bit Event, where 0x80 = Open/Close, 0x04 = Heartbeat / or id +- S: 16bit CRC +*/ + +#define TAG "SubGhzProtocolHoneywell" + +static const SubGhzBlockConst subghz_protocol_honeywell_const = { + .te_long = 280, + .te_short = 143, + .te_delta = 51, + .min_count_bit_for_found = 62, +}; + +struct SubGhzProtocolDecoderHoneywell { + SubGhzProtocolDecoderBase base; + SubGhzBlockGeneric generic; + SubGhzBlockDecoder decoder; + ManchesterState manchester_saved_state; +}; + +struct SubGhzProtocolEncoderHoneywell { + SubGhzProtocolEncoderBase base; + SubGhzBlockGeneric generic; + SubGhzProtocolBlockEncoder encoder; +}; + +const SubGhzProtocolDecoder subghz_protocol_honeywell_decoder = { + .alloc = subghz_protocol_decoder_honeywell_alloc, + .free = subghz_protocol_decoder_honeywell_free, + .feed = subghz_protocol_decoder_honeywell_feed, + .reset = subghz_protocol_decoder_honeywell_reset, + .get_hash_data = subghz_protocol_decoder_honeywell_get_hash_data, + .serialize = subghz_protocol_decoder_honeywell_serialize, + .deserialize = subghz_protocol_decoder_honeywell_deserialize, + .get_string = subghz_protocol_decoder_honeywell_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_honeywell_encoder = { + .alloc = subghz_protocol_encoder_honeywell_alloc, + .free = subghz_protocol_encoder_honeywell_free, + .deserialize = subghz_protocol_encoder_honeywell_deserialize, + .stop = subghz_protocol_encoder_honeywell_stop, + .yield = subghz_protocol_encoder_honeywell_yield, +}; + +const SubGhzProtocol subghz_protocol_honeywell = { + .name = SUBGHZ_PROTOCOL_HONEYWELL_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | + SubGhzProtocolFlag_Sensors, + .encoder = &subghz_protocol_honeywell_encoder, + .decoder = &subghz_protocol_honeywell_decoder, + +}; + +static void subghz_protocol_decoder_honeywell_addbit(void* context, bool data); + +void* subghz_protocol_decoder_honeywell_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHoneywell* instance = malloc(sizeof(SubGhzProtocolDecoderHoneywell)); + instance->base.protocol = &subghz_protocol_honeywell; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void* subghz_protocol_encoder_honeywell_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHoneywell* instance = malloc(sizeof(SubGhzProtocolEncoderHoneywell)); + + instance->base.protocol = &subghz_protocol_honeywell; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 4; + instance->encoder.size_upload = 64 * 2 + 10; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_honeywell_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHoneywell* instance = context; + free(instance->encoder.upload); + free(instance); +} + +void subghz_protocol_decoder_honeywell_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell* instance = context; + free(instance); +} + +uint16_t subghz_protocol_honeywell_crc16( + uint8_t const message[], + unsigned nBytes, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + unsigned byte, bit; + + for(byte = 0; byte < nBytes; ++byte) { + remainder ^= message[byte] << 8; + for(bit = 0; bit < 8; ++bit) { + if(remainder & 0x8000) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +static LevelDuration + subghz_protocol_encoder_honeywell_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_honeywell_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_honeywell_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_honeywell_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_honeywell_const.te_short; + data.level = true; + break; + + default: + furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +static void + subghz_protocol_encoder_honeywell_get_upload(SubGhzProtocolEncoderHoneywell* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + for(uint8_t i = 63; i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_honeywell_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_honeywell_add_duration_to_upload(result); + } + instance->encoder.upload[index] = subghz_protocol_encoder_honeywell_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + //Send delay + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_honeywell_const.te_long * 300); + + instance->encoder.size_upload = index; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_honeywell_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHoneywell* instance = context; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; + do { + if(SubGhzProtocolStatusOk != + subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_honeywell_get_upload(instance); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + instance->encoder.is_running = true; + + res = SubGhzProtocolStatusOk; + } while(false); + + return res; +} + +void subghz_protocol_encoder_honeywell_stop(void* context) { + SubGhzProtocolEncoderHoneywell* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_honeywell_yield(void* context) { + SubGhzProtocolEncoderHoneywell* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + return ret; +} + +void subghz_protocol_decoder_honeywell_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell* instance = context; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; +} + +void subghz_protocol_decoder_honeywell_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell* instance = context; + + ManchesterEvent event = ManchesterEventReset; + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_short) < + subghz_protocol_honeywell_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_long) < + subghz_protocol_honeywell_const.te_delta * 2) { + event = ManchesterEventLongLow; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_short) < + subghz_protocol_honeywell_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_honeywell_const.te_long) < + subghz_protocol_honeywell_const.te_delta * 2) { + event = ManchesterEventLongHigh; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + if(data_ok) { + subghz_protocol_decoder_honeywell_addbit(instance, data); + } + } else { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } +} + +static void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) { + SubGhzProtocolDecoderHoneywell* instance = context; + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; + instance->decoder.decode_count_bit++; + + if(instance->decoder.decode_count_bit < 62) return; + + uint16_t preamble = (instance->decoder.decode_data >> 48) & 0xFFFF; + //can be multiple, since flipper can't read it well.. (it can, but the sensors are not that good, there are multiple of variations seen) + if(preamble == 0b0011111111111110 || preamble == 0b0111111111111110 || + preamble == 0b1111111111111110) { + uint8_t datatocrc[4]; + datatocrc[0] = (instance->decoder.decode_data >> 40) & 0xFFFF; + datatocrc[1] = (instance->decoder.decode_data >> 32) & 0xFFFF; + datatocrc[2] = (instance->decoder.decode_data >> 24) & 0xFFFF; + datatocrc[3] = (instance->decoder.decode_data >> 16) & 0xFFFF; + uint8_t channel = (instance->decoder.decode_data >> 44) & 0xF; + uint16_t crc_calc = 0; + if(channel == 0x2 || channel == 0x4 || channel == 0xA) { + // 2GIG brand + crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8050, 0); + } else if(channel == 0x8) { + crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8005, 0); + } else { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + return; + } + uint16_t crc = instance->decoder.decode_data & 0xFFFF; + if(crc == crc_calc) { + //the data is good. process it. + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = + instance->decoder + .decode_count_bit; //maybe set it to 64, and hack the first 2 bits to 1! will see if replay needs it + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + return; + } + } else if(instance->decoder.decode_count_bit >= 64) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + return; + } +} + +uint8_t subghz_protocol_decoder_honeywell_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_honeywell_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + subghz_protocol_decoder_honeywell_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell* instance = context; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_honeywell_const.min_count_bit_for_found); +} + +void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell* instance = context; + + instance->generic.serial = (instance->generic.data >> 24) & 0xFFFFF; + uint8_t sensor_status = (instance->generic.data >> 16) & 0xFF; + uint16_t crc = instance->generic.data & 0xFFFF; + + uint8_t channel = (instance->generic.data >> 44) & 0xF; + uint8_t contact = (sensor_status & 0x80) >> 7; + uint8_t tamper = (sensor_status & 0x40) >> 6; + uint8_t reed = (sensor_status & 0x20) >> 5; + uint8_t alarm = (sensor_status & 0x10) >> 4; + uint8_t battery_low = (sensor_status & 0x08) >> 3; + uint8_t heartbeat = (sensor_status & 0x04) >> 2; + + furi_string_cat_printf( + output, + "%s\r\n%dbit " + "Sn:%07lu Ch:%u\r\n" + "BatLow:%d Hb: %d Cont: %s\r\n" + "Reed: %u Alrm: %u Tmpr: %u\r\n" + "CRC: %04X", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.serial, + channel, + battery_low, + heartbeat, + contact ? "open" : "closed", + reed, + alarm, + tamper, + crc); +} diff --git a/lib/subghz/protocols/honeywell.h b/lib/subghz/protocols/honeywell.h new file mode 100644 index 000000000..92ec38a44 --- /dev/null +++ b/lib/subghz/protocols/honeywell.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HONEYWELL_NAME "Honeywell Sec" + +typedef struct SubGhzProtocolDecoderHoneywell SubGhzProtocolDecoderHoneywell; +typedef struct SubGhzProtocolEncoderHoneywell SubGhzProtocolEncoderHoneywell; + +extern const SubGhzProtocolDecoder subghz_protocol_honeywell_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_honeywell_encoder; +extern const SubGhzProtocol subghz_protocol_honeywell; + +/** + * Allocate SubGhzProtocolEncoderHoneywell. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHoneywell* pointer to a SubGhzProtocolEncoderHoneywell instance + */ +void* subghz_protocol_encoder_honeywell_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHoneywell. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell instance + */ +void subghz_protocol_encoder_honeywell_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_honeywell_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell instance + */ +void subghz_protocol_encoder_honeywell_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_honeywell_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHoneywell. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHoneywell* pointer to a SubGhzProtocolDecoderHoneywell instance + */ +void* subghz_protocol_decoder_honeywell_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHoneywell. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell instance + */ +void subghz_protocol_decoder_honeywell_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHoneywell. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell instance + */ +void subghz_protocol_decoder_honeywell_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_honeywell_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_honeywell_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHoneywell. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus subghz_protocol_decoder_honeywell_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHoneywell. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_honeywell_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell instance + * @param output Resulting text + */ +void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 27297f293..e7b282b8a 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -44,6 +44,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = { &subghz_protocol_kinggates_stylo_4k, &subghz_protocol_bin_raw, &subghz_protocol_mastercode, + &subghz_protocol_honeywell, &subghz_protocol_legrand, &subghz_protocol_dickert_mahs, &subghz_protocol_gangqi, diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index ab7fd14f8..9820ee9e7 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -45,6 +45,7 @@ #include "kinggates_stylo_4k.h" #include "bin_raw.h" #include "mastercode.h" +#include "honeywell.h" #include "legrand.h" #include "dickert_mahs.h" #include "gangqi.h" From 94076e6c5c1890cdc6b565254e24fea24bc7edcc Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 17 Oct 2025 14:05:25 +0300 Subject: [PATCH 21/30] honeywell show whole key and loop states instead --- lib/subghz/protocols/honeywell.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c index 66e1c5053..6e212356e 100644 --- a/lib/subghz/protocols/honeywell.c +++ b/lib/subghz/protocols/honeywell.c @@ -375,9 +375,11 @@ void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* out furi_assert(context); SubGhzProtocolDecoderHoneywell* instance = context; + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + instance->generic.serial = (instance->generic.data >> 24) & 0xFFFFF; uint8_t sensor_status = (instance->generic.data >> 16) & 0xFF; - uint16_t crc = instance->generic.data & 0xFFFF; uint8_t channel = (instance->generic.data >> 44) & 0xF; uint8_t contact = (sensor_status & 0x80) >> 7; @@ -391,9 +393,9 @@ void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* out output, "%s\r\n%dbit " "Sn:%07lu Ch:%u\r\n" - "BatLow:%d Hb: %d Cont: %s\r\n" - "Reed: %u Alrm: %u Tmpr: %u\r\n" - "CRC: %04X", + "LowBat:%d HB: %d Cont: %s\r\n" + "Key:%08lX%08lX\r\n" + "State: L1:%u L2:%u L3:%u L4:%u", instance->generic.protocol_name, instance->generic.data_count_bit, instance->generic.serial, @@ -401,8 +403,10 @@ void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* out battery_low, heartbeat, contact ? "open" : "closed", + code_found_hi, + code_found_lo, + contact, reed, alarm, - tamper, - crc); + tamper); } From bd02e2f53cf7dc818b6f6d279cc816f0b4242e33 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 17 Oct 2025 19:19:49 +0300 Subject: [PATCH 22/30] honeywell fix uint8 array using uint16 values, fix header, fix bits --- lib/subghz/protocols/honeywell.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c index 6e212356e..dd556d094 100644 --- a/lib/subghz/protocols/honeywell.c +++ b/lib/subghz/protocols/honeywell.c @@ -35,7 +35,7 @@ static const SubGhzBlockConst subghz_protocol_honeywell_const = { .te_long = 280, .te_short = 143, .te_delta = 51, - .min_count_bit_for_found = 62, + .min_count_bit_for_found = 64, }; struct SubGhzProtocolDecoderHoneywell { @@ -299,17 +299,19 @@ static void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) { instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; instance->decoder.decode_count_bit++; - if(instance->decoder.decode_count_bit < 62) return; + if(instance->decoder.decode_count_bit < 62) { + return; + } uint16_t preamble = (instance->decoder.decode_data >> 48) & 0xFFFF; //can be multiple, since flipper can't read it well.. (it can, but the sensors are not that good, there are multiple of variations seen) if(preamble == 0b0011111111111110 || preamble == 0b0111111111111110 || preamble == 0b1111111111111110) { uint8_t datatocrc[4]; - datatocrc[0] = (instance->decoder.decode_data >> 40) & 0xFFFF; - datatocrc[1] = (instance->decoder.decode_data >> 32) & 0xFFFF; - datatocrc[2] = (instance->decoder.decode_data >> 24) & 0xFFFF; - datatocrc[3] = (instance->decoder.decode_data >> 16) & 0xFFFF; + datatocrc[0] = (instance->decoder.decode_data >> 40) & 0xFF; + datatocrc[1] = (instance->decoder.decode_data >> 32) & 0xFF; + datatocrc[2] = (instance->decoder.decode_data >> 24) & 0xFF; + datatocrc[3] = (instance->decoder.decode_data >> 16) & 0xFF; uint8_t channel = (instance->decoder.decode_data >> 44) & 0xF; uint16_t crc_calc = 0; if(channel == 0x2 || channel == 0x4 || channel == 0xA) { @@ -324,11 +326,15 @@ static void subghz_protocol_decoder_honeywell_addbit(void* context, bool data) { } uint16_t crc = instance->decoder.decode_data & 0xFFFF; if(crc == crc_calc) { - //the data is good. process it. - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = - instance->decoder - .decode_count_bit; //maybe set it to 64, and hack the first 2 bits to 1! will see if replay needs it + // Removing possible artifacts from higher bits and setting header to FF FE + instance->generic.data = + ((((((0xFF << 16) | ((instance->decoder.decode_data >> 40) & 0xFFFF)) << 16) | + ((instance->decoder.decode_data >> 24) & 0xFFFF)) + << 16) | + ((instance->decoder.decode_data >> 8) & 0xFFFF)) + << 8 | + (instance->decoder.decode_data & 0xFF); + instance->generic.data_count_bit = 64; if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); instance->decoder.decode_data = 0; From a1c8dfb61b3b667d7c4ee5d2c3e6b1827f7b6c9f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 21 Oct 2025 01:32:02 +0300 Subject: [PATCH 23/30] subghz overflow experimental mode --- .../scenes/subghz_scene_radio_settings.c | 4 ++- lib/subghz/protocols/alutech_at_4n.c | 25 +++++++++++---- lib/subghz/protocols/came_atomo.c | 25 +++++++++++---- lib/subghz/protocols/faac_slh.c | 6 ++-- lib/subghz/protocols/hay21.c | 27 +++++++++++----- lib/subghz/protocols/keeloq.c | 31 +++++++++++++------ lib/subghz/protocols/kinggates_stylo_4k.c | 25 +++++++++++---- lib/subghz/protocols/nice_flor_s.c | 26 ++++++++++++---- lib/subghz/protocols/phoenix_v2.c | 25 +++++++++++---- lib/subghz/protocols/somfy_keytis.c | 25 +++++++++++---- lib/subghz/protocols/somfy_telis.c | 25 +++++++++++---- lib/subghz/protocols/star_line.c | 25 +++++++++++---- 12 files changed, 199 insertions(+), 70 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_radio_settings.c b/applications/main/subghz/scenes/subghz_scene_radio_settings.c index 8e808618b..93afe5e31 100644 --- a/applications/main/subghz/scenes/subghz_scene_radio_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_radio_settings.c @@ -26,7 +26,7 @@ const char* const debug_pin_text[DEBUG_P_COUNT] = { "17(1W)", }; -#define DEBUG_COUNTER_COUNT 16 +#define DEBUG_COUNTER_COUNT 17 const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = { "+1", "+2", @@ -36,6 +36,7 @@ const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = { "+10", "+50", "OVFL", + "OFEX", "No", "-1", "-2", @@ -54,6 +55,7 @@ const int32_t debug_counter_val[DEBUG_COUNTER_COUNT] = { 10, 50, 65535, + 65534, 0, -1, -2, diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index 873f61ba9..55978e90f 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -273,14 +273,27 @@ static bool subghz_protocol_alutech_at_4n_gen_data( instance->generic.serial = (uint32_t)(data >> 24) & 0xFFFFFFFF; } - if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + // Check for OFEX (overflow experimental) mode + if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) { + if(instance->generic.cnt < 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if( + (instance->generic.cnt >= 0xFFFF) && + (furi_hal_subghz_get_rolling_counter_mult() != 0)) { instance->generic.cnt = 0; - } else { - instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); } - } else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) { - instance->generic.cnt = 0; + } else { + if((instance->generic.cnt + 0x1) > 0xFFFF) { + instance->generic.cnt = 0; + } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) { + instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult(); + } else { + instance->generic.cnt++; + } } crc = subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)(instance->generic.cnt & 0xFF)); data = (uint64_t)crc << 56 | (uint64_t)instance->generic.serial << 24 | diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 2162169b2..7a3181341 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -185,14 +185,27 @@ static void subghz_protocol_encoder_came_atomo_get_upload( uint8_t pack[8] = {}; - if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + // Check for OFEX (overflow experimental) mode + if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) { + if(instance->generic.cnt < 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if( + (instance->generic.cnt >= 0xFFFF) && + (furi_hal_subghz_get_rolling_counter_mult() != 0)) { instance->generic.cnt = 0; - } else { - instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); } - } else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) { - instance->generic.cnt = 0; + } else { + if((instance->generic.cnt + 0x1) > 0xFFFF) { + instance->generic.cnt = 0; + } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) { + instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult(); + } else { + instance->generic.cnt++; + } } // Save original button for later use diff --git a/lib/subghz/protocols/faac_slh.c b/lib/subghz/protocols/faac_slh.c index 489fbdbc2..d44385ac7 100644 --- a/lib/subghz/protocols/faac_slh.c +++ b/lib/subghz/protocols/faac_slh.c @@ -140,7 +140,7 @@ static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* inst data_prg[0] = 0x00; if(allow_zero_seed || (instance->generic.seed != 0x0)) { - if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFF)) { + if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFE)) { if(instance->generic.cnt < 0xFFFFF) { if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFFF) { @@ -158,7 +158,7 @@ static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* inst } if(temp_counter_backup != 0x0) { - if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFF)) { + if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFE)) { if(temp_counter_backup < 0xFFFFF) { if((temp_counter_backup + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFFF) { @@ -240,7 +240,7 @@ static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* inst } if(allow_zero_seed || (instance->generic.seed != 0x0)) { - if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFF)) { + if(!(furi_hal_subghz_get_rolling_counter_mult() >= 0xFFFE)) { if(instance->generic.cnt < 0xFFFFF) { if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFFF) { diff --git a/lib/subghz/protocols/hay21.c b/lib/subghz/protocols/hay21.c index 1e3576459..d622e4446 100644 --- a/lib/subghz/protocols/hay21.c +++ b/lib/subghz/protocols/hay21.c @@ -145,17 +145,28 @@ static void subghz_protocol_encoder_hay21_get_upload(SubGhzProtocolEncoderHay21* instance->generic.btn = subghz_protocol_hay21_get_btn_code(); // Counter increment - if(instance->generic.cnt < 0xF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xF) { + // Check for OFEX (overflow experimental) mode + if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) { + if(instance->generic.cnt < 0xF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + if(furi_hal_subghz_get_rolling_counter_mult() >= 0xF) { + instance->generic.cnt = 0xF; + } + } else if(instance->generic.cnt >= 0xF) { instance->generic.cnt = 0; + } + } else { + if((instance->generic.cnt + 0x1) > 0xF) { + instance->generic.cnt = 0; + } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xE) { + instance->generic.cnt = 0xE; } else { - instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + instance->generic.cnt++; } - if(furi_hal_subghz_get_rolling_counter_mult() >= 0xF) { - instance->generic.cnt = 0xF; - } - } else if(instance->generic.cnt >= 0xF) { - instance->generic.cnt = 0; } // Reconstruction of the data diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index ace6e0d6e..22070f1ec 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -182,18 +182,29 @@ static bool subghz_protocol_keeloq_gen_data( if(counter_up && prog_mode == PROG_MODE_OFF) { // Counter increment conditions - // If counter is 0xFFFF we will reset it to 0 - if(instance->generic.cnt < 0xFFFF) { - // Increase counter with value set in global settings (mult) - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + // Check for OFEX (overflow experimental) mode + if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) { + // If counter is 0xFFFF we will reset it to 0 + if(instance->generic.cnt < 0xFFFF) { + // Increase counter with value set in global settings (mult) + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if( + (instance->generic.cnt >= 0xFFFF) && + (furi_hal_subghz_get_rolling_counter_mult() != 0)) { instance->generic.cnt = 0; - } else { - instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); } - } else if( - (instance->generic.cnt >= 0xFFFF) && - (furi_hal_subghz_get_rolling_counter_mult() != 0)) { - instance->generic.cnt = 0; + } else { + if((instance->generic.cnt + 0x1) > 0xFFFF) { + instance->generic.cnt = 0; + } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) { + instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult(); + } else { + instance->generic.cnt++; + } } } if(prog_mode == PROG_MODE_OFF) { diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c index 119a198bc..77624f5dc 100644 --- a/lib/subghz/protocols/kinggates_stylo_4k.c +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -155,14 +155,27 @@ static bool subghz_protocol_kinggates_stylo_4k_gen_data( } instance->generic.cnt = decrypt & 0xFFFF; - if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + // Check for OFEX (overflow experimental) mode + if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) { + if(instance->generic.cnt < 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if( + (instance->generic.cnt >= 0xFFFF) && + (furi_hal_subghz_get_rolling_counter_mult() != 0)) { instance->generic.cnt = 0; - } else { - instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); } - } else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) { - instance->generic.cnt = 0; + } else { + if((instance->generic.cnt + 0x1) > 0xFFFF) { + instance->generic.cnt = 0; + } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) { + instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult(); + } else { + instance->generic.cnt++; + } } instance->generic.btn = (fix >> 17) & 0x0F; diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index 50adb43dc..201284724 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -151,15 +151,29 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( instance->encoder.size_upload = size_upload; } - if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + // Check for OFEX (overflow experimental) mode + if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) { + if(instance->generic.cnt < 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if( + (instance->generic.cnt >= 0xFFFF) && + (furi_hal_subghz_get_rolling_counter_mult() != 0)) { instance->generic.cnt = 0; - } else { - instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); } - } else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) { - instance->generic.cnt = 0; + } else { + if((instance->generic.cnt + 0x1) > 0xFFFF) { + instance->generic.cnt = 0; + } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) { + instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult(); + } else { + instance->generic.cnt++; + } } + uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt; uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt(decrypt, file_name); diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c index a6a8a7108..5764871fb 100644 --- a/lib/subghz/protocols/phoenix_v2.c +++ b/lib/subghz/protocols/phoenix_v2.c @@ -252,14 +252,27 @@ static bool btn = subghz_protocol_phoenix_v2_get_btn_code(); // Reconstruction of the data - if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + // Check for OFEX (overflow experimental) mode + if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) { + if(instance->generic.cnt < 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if( + (instance->generic.cnt >= 0xFFFF) && + (furi_hal_subghz_get_rolling_counter_mult() != 0)) { instance->generic.cnt = 0; - } else { - instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); } - } else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) { - instance->generic.cnt = 0; + } else { + if((instance->generic.cnt + 0x1) > 0xFFFF) { + instance->generic.cnt = 0; + } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) { + instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult(); + } else { + instance->generic.cnt++; + } } uint64_t local_data_rev = subghz_protocol_blocks_reverse_key( diff --git a/lib/subghz/protocols/somfy_keytis.c b/lib/subghz/protocols/somfy_keytis.c index 0606b9bf5..07f950095 100644 --- a/lib/subghz/protocols/somfy_keytis.c +++ b/lib/subghz/protocols/somfy_keytis.c @@ -130,14 +130,27 @@ static bool instance->generic.cnt = (data >> 24) & 0xFFFF; instance->generic.serial = data & 0xFFFFFF; - if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + // Check for OFEX (overflow experimental) mode + if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) { + if(instance->generic.cnt < 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if( + (instance->generic.cnt >= 0xFFFF) && + (furi_hal_subghz_get_rolling_counter_mult() != 0)) { instance->generic.cnt = 0; - } else { - instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); } - } else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) { - instance->generic.cnt = 0; + } else { + if((instance->generic.cnt + 0x1) > 0xFFFF) { + instance->generic.cnt = 0; + } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) { + instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult(); + } else { + instance->generic.cnt++; + } } uint8_t frame[10]; diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index fd41180d5..4218ad8c5 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -124,14 +124,27 @@ static bool subghz_protocol_somfy_telis_gen_data( 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) { + // Check for OFEX (overflow experimental) mode + if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) { + if(instance->generic.cnt < 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if( + (instance->generic.cnt >= 0xFFFF) && + (furi_hal_subghz_get_rolling_counter_mult() != 0)) { instance->generic.cnt = 0; - } else { - instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); } - } else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) { - instance->generic.cnt = 0; + } else { + if((instance->generic.cnt + 0x1) > 0xFFFF) { + instance->generic.cnt = 0; + } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) { + instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult(); + } else { + instance->generic.cnt++; + } } uint8_t frame[7]; diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 991957abb..d6706704c 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -129,14 +129,27 @@ void subghz_protocol_encoder_star_line_free(void* context) { */ static bool subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) { - if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + // Check for OFEX (overflow experimental) mode + if(furi_hal_subghz_get_rolling_counter_mult() != 0xFFFE) { + if(instance->generic.cnt < 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if( + (instance->generic.cnt >= 0xFFFF) && + (furi_hal_subghz_get_rolling_counter_mult() != 0)) { instance->generic.cnt = 0; - } else { - instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); } - } else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) { - instance->generic.cnt = 0; + } else { + if((instance->generic.cnt + 0x1) > 0xFFFF) { + instance->generic.cnt = 0; + } else if(instance->generic.cnt >= 0x1 && instance->generic.cnt != 0xFFFE) { + instance->generic.cnt = furi_hal_subghz_get_rolling_counter_mult(); + } else { + instance->generic.cnt++; + } } uint32_t fix = btn << 24 | instance->generic.serial; uint32_t decrypt = btn << 24 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; From 01c168e351efb3077aef914a37512b6e122918a9 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 21 Oct 2025 01:44:30 +0300 Subject: [PATCH 24/30] subghz il100 smart add manually support --- applications/main/subghz/helpers/subghz_custom_event.h | 1 + applications/main/subghz/helpers/subghz_gen_info.c | 10 ++++++++++ .../main/subghz/scenes/subghz_scene_set_type.c | 1 + 3 files changed, 12 insertions(+) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index d212de4ef..e118948a0 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -91,6 +91,7 @@ typedef enum { SetTypeSommer_FM238_868, SetTypeStilmatic, SetTypeIronLogic, + SetTypeIronLogicSmart, SetTypeDeaMio433, SetTypeDTMNeo433, SetTypeGibidi433, diff --git a/applications/main/subghz/helpers/subghz_gen_info.c b/applications/main/subghz/helpers/subghz_gen_info.c index 459f600ca..fe5cd0e8a 100644 --- a/applications/main/subghz/helpers/subghz_gen_info.c +++ b/applications/main/subghz/helpers/subghz_gen_info.c @@ -398,6 +398,16 @@ void subghz_scene_set_type_fill_generation_infos(GenInfo* infos_dest, SetType ty .keeloq.cnt = 0x05, .keeloq.manuf = "IronLogic"}; break; + case SetTypeIronLogicSmart: + gen_info = (GenInfo){ + .type = GenKeeloq, + .mod = "AM650", + .freq = 433920000, + .keeloq.serial = key & 0x00FFFFF0, + .keeloq.btn = 0x04, + .keeloq.cnt = 0x05, + .keeloq.manuf = "IL-100(Smart)"}; + break; case SetTypeStilmatic: gen_info = (GenInfo){ .type = GenKeeloq, diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index d3bb2482e..8ca5b95ab 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -39,6 +39,7 @@ static const char* submenu_names[SetTypeMAX] = { [SetTypeSommer_FM238_868] = "KL: Sommer fm2 868Mhz", [SetTypeStilmatic] = "KL: Stilmatic 433MHz", [SetTypeIronLogic] = "KL: IronLogic 433MHz", + [SetTypeIronLogicSmart] = "KL: IronLogic SM 433MHz", [SetTypeDeaMio433] = "KL: DEA Mio 433MHz", [SetTypeDTMNeo433] = "KL: DTM Neo 433MHz", [SetTypeGibidi433] = "KL: Gibidi 433MHz", From cf5761860f320b9756b663a6d96aa4ea655b4a33 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 21 Oct 2025 01:48:02 +0300 Subject: [PATCH 25/30] upd changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 414be0a43..70e041f44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Main changes - Current API: 87.0 +* SubGHz: Add IL-100 Smart support for Add manually +* SubGHz: Add experimental counter overflow mode (OFEX), replicates how some key duplicators work, DO NOT USE if you don't know what you are doing, it will reset your counter value! (accesible with debug on in radio settings - counter incr.) * SubGHz: Return Honeywell Sec with fixes and improvements (by htotoo & LiQuiDz & xMasterX) * OFW PR 4279: NFC FeliCa Minor Fix: FelicaPollerEventType should only be Incomplete if the tag is FeliCa Lite (by @zinongli) * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) From 3e968069620215b3420467a8ade5249b7ee1dd8f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 21 Oct 2025 04:12:22 +0300 Subject: [PATCH 26/30] honeywell read old files with 62-63bits change them on the fly to new format during reading, files are not replaced, they will contain old format, you can fix them manually by replacing header to FF FE and bits to 64 --- lib/subghz/protocols/honeywell.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c index dd556d094..106ddeead 100644 --- a/lib/subghz/protocols/honeywell.c +++ b/lib/subghz/protocols/honeywell.c @@ -371,10 +371,26 @@ SubGhzProtocolStatus subghz_protocol_decoder_honeywell_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderHoneywell* instance = context; - return subghz_block_generic_deserialize_check_count_bit( - &instance->generic, - flipper_format, - subghz_protocol_honeywell_const.min_count_bit_for_found); + + SubGhzProtocolStatus res = SubGhzProtocolStatusError; + res = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(res != SubGhzProtocolStatusOk) { + return res; + } + + if(instance->generic.data_count_bit != 64) { + // Removing possible artifacts from higher bits and setting header to FF FE + instance->generic.data = + ((((((0xFF << 16) | ((instance->generic.data >> 40) & 0xFFFF)) << 16) | + ((instance->generic.data >> 24) & 0xFFFF)) + << 16) | + ((instance->generic.data >> 8) & 0xFFFF)) + << 8 | + (instance->generic.data & 0xFF); + instance->generic.data_count_bit = 64; + } + + return res; } void subghz_protocol_decoder_honeywell_get_string(void* context, FuriString* output) { From a553bc2f57afcfe4de7ceca66aea30001bcaed1a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 21 Oct 2025 04:22:09 +0300 Subject: [PATCH 27/30] add extra check --- lib/subghz/protocols/honeywell.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/subghz/protocols/honeywell.c b/lib/subghz/protocols/honeywell.c index 106ddeead..f1ed6e4b8 100644 --- a/lib/subghz/protocols/honeywell.c +++ b/lib/subghz/protocols/honeywell.c @@ -379,6 +379,9 @@ SubGhzProtocolStatus } if(instance->generic.data_count_bit != 64) { + if(instance->generic.data_count_bit < 62) { + return SubGhzProtocolStatusErrorValueBitCount; + } // Removing possible artifacts from higher bits and setting header to FF FE instance->generic.data = ((((((0xFF << 16) | ((instance->generic.data >> 40) & 0xFFFF)) << 16) | From 5db6a0381126aec2d1e44b623ccb6c9419f500ba Mon Sep 17 00:00:00 2001 From: Dmitry422 Date: Thu, 30 Oct 2025 16:48:23 +0700 Subject: [PATCH 28/30] Remove display_back_light bug from "DisplayBacklightEnforceOn" --- .../services/notification/notification_app.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index c0ab417b3..de6f50d2e 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -488,9 +488,10 @@ static void notification_process_notification_message( break; case NotificationMessageTypeLedDisplayBacklightEnforceOn: furi_check(app->display_led_lock < UINT8_MAX); - app->display_led_lock++; + // --- NIGHT SHIFT --- - if(app->display_led_lock == 1) { + if(app->display_led_lock <1 ) { + app->display_led_lock = 1; notification_apply_internal_led_layer( &app->display, notification_message->data.led.value * display_brightness_setting * @@ -499,13 +500,11 @@ static void notification_process_notification_message( break; case NotificationMessageTypeLedDisplayBacklightEnforceAuto: if(app->display_led_lock > 0) { - app->display_led_lock--; - if(app->display_led_lock == 0) { - notification_apply_internal_led_layer( - &app->display, - notification_message->data.led.value * display_brightness_setting * - app->current_night_shift * 1.0f); - } + app->display_led_lock = 0; + notification_apply_internal_led_layer( + &app->display, + notification_message->data.led.value * display_brightness_setting * + app->current_night_shift * 1.0f); // --- NIGHT SHIFT END --- } else { FURI_LOG_E(TAG, "Incorrect BacklightEnforce use"); From e392bff8081bb940d09222d2f274ccdbb15aab4b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 31 Oct 2025 12:12:07 +0300 Subject: [PATCH 29/30] fmt and upd changelog --- CHANGELOG.md | 1 + applications/services/notification/notification_app.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70e041f44..422b5e698 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * SubGHz: Add IL-100 Smart support for Add manually * SubGHz: Add experimental counter overflow mode (OFEX), replicates how some key duplicators work, DO NOT USE if you don't know what you are doing, it will reset your counter value! (accesible with debug on in radio settings - counter incr.) * SubGHz: Return Honeywell Sec with fixes and improvements (by htotoo & LiQuiDz & xMasterX) +* Display: Remove display_back_light bug from "DisplayBacklightEnforceOn" (PR #928 | by @Dmitry422) * OFW PR 4279: NFC FeliCa Minor Fix: FelicaPollerEventType should only be Incomplete if the tag is FeliCa Lite (by @zinongli) * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) ## Other changes diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index de6f50d2e..3cc39ed01 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -490,7 +490,7 @@ static void notification_process_notification_message( furi_check(app->display_led_lock < UINT8_MAX); // --- NIGHT SHIFT --- - if(app->display_led_lock <1 ) { + if(app->display_led_lock < 1) { app->display_led_lock = 1; notification_apply_internal_led_layer( &app->display, From ed2c40de4b0287c51e59d61d399092a535d8df09 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 31 Oct 2025 12:28:08 +0300 Subject: [PATCH 30/30] to bool --- applications/services/notification/notification_app.c | 10 ++++------ applications/services/notification/notification_app.h | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index 3cc39ed01..68573c201 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -487,11 +487,9 @@ static void notification_process_notification_message( } break; case NotificationMessageTypeLedDisplayBacklightEnforceOn: - furi_check(app->display_led_lock < UINT8_MAX); - // --- NIGHT SHIFT --- - if(app->display_led_lock < 1) { - app->display_led_lock = 1; + if(!app->display_led_lock) { + app->display_led_lock = true; notification_apply_internal_led_layer( &app->display, notification_message->data.led.value * display_brightness_setting * @@ -499,8 +497,8 @@ static void notification_process_notification_message( } break; case NotificationMessageTypeLedDisplayBacklightEnforceAuto: - if(app->display_led_lock > 0) { - app->display_led_lock = 0; + if(app->display_led_lock) { + app->display_led_lock = false; notification_apply_internal_led_layer( &app->display, notification_message->data.led.value * display_brightness_setting * diff --git a/applications/services/notification/notification_app.h b/applications/services/notification/notification_app.h index 239bf69c0..754c8d4c3 100644 --- a/applications/services/notification/notification_app.h +++ b/applications/services/notification/notification_app.h @@ -77,7 +77,7 @@ struct NotificationApp { NotificationLedLayer display; NotificationLedLayer led[NOTIFICATION_LED_COUNT]; - uint8_t display_led_lock; + bool display_led_lock; NotificationSettings settings;