diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e11de350..ea74ea21d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,8 +63,11 @@ - UI: Dictionary attack scene and menu options - OFW: FeliCa Service Directory Traverse + Dump All Unencrypted-Readable Services' Blocks (by @zinongli) - OFW: FeliCa Emulation Handle certain Polling commands in firmware (by @dogtopus) + - OFW: FeliCa Dump All Systems (by @zinongli) - OFW: Amusement IC Card Parser for FeliCa Lite & Lite-S (by @zinongli) - OFW: MFC 1k Banapass Parser (by @zinongli) + - UL: Returning fix for reading PWD locked MFUL (by @mishamyte) + - UL: Added UL-C keys to the dictionary (by @mishamyte) - Add MIFARE Classic "Show Keys" UI (#473 by @aaronjamt) - SubGHz: - UL: Roger (static 28 bit) with add manually support (by @xMasterX & @mishamyte) @@ -77,6 +80,9 @@ - UL: Add ZKTeco 430.5 MHz add manually support (by @xMasterX) - UL: Add Elplast 18bit static code protocol (hello Hackcat ^_^) - UL: Try to decode BFT (2 buttons remotes only) on the fly in regular Read mode (by @xMasterX) + - UL: Add support for Came Atomo TOP44RBN remotes (by @xMasterX & @mishamyte) + - UL: Add IL-100 Smart support for Add manually (by @xMasterX) + - UL: 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! (by @xMasterX) - RFID: - Support writing Securakey, Jablotron and FDX-B to EM4305 cards (#434 by @jamisonderek) - OFW: Show ISO-3166 Country Names For Pet Chips (by @zinongli) @@ -106,6 +112,7 @@ - Apps: - XERO: MFKey: Key recovery is 20% faster, new write buffering of Static Encrypted Nested key candidates performs recovery 70x faster (by @noproto) - UL: Sub-GHz Remote: Add possibility to use custom buttons (by @MrLego8-9) + - UL: BT/USB Remote: PTT global zoom and google meet shortcuts for MacOS (by @hryamzik) - Asteroids: Bugfixes, title screen, Drone Buddy power-up (by @SimplyMinimal) - Combo Cracker: Allow press and hold to change values, add tutorial (by @TAxelAnderson), support alphabetic combination locks (by @henrygab) - ESP Flasher: Bump Marauder 1.8.4 (by @justcallmekoko), add C5 support (by @Play2BReal), more reliable bootloader mode on SWCLK (by @WillyJL) @@ -131,11 +138,14 @@ - UL: Add 868.46 MHz to default subghz freqs list (by @xMasterX) - UL: Reduce less popular freqs in default hopper preset, make it faster (by @xMasterX) - UL: Tune Linear (add better EZCode support), Dickert MAHS decoders (by @xMasterX) + - UL: Some fixes and improvements to Honeywell Sec protocol (by @xMasterX) - Infrared: - OFW: Add an old JVC model to universal remotes (by @zgracem) - OFW: Add Daikin FTXN25LV1B9 and Toyotomi KTN22-12R32 to universal remotes (by @minchogaydarov) - OFW: BLE: Improved pairing security (by @hedger) - JS: Expose button event type in `gui/widget` button callback, see breaking changes above (by @WillyJL) +- OFW: NFC: Expose nfc_common.h (by @zinongli) +- OFW: GUI: Store View by value inViewStack to save memory (by @CookiePLMonster) - UL: Docs: Update Sub-GHz DoorHan programming instructions (by @li0ard) ### Fixed: diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index 87c69722a..26dc86d41 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); 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/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 diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 3f4c78598..2cace2925 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -71,5 +71,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/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index fecdef610..85b6ffbee 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -99,6 +99,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_radio_settings.c b/applications/main/subghz/scenes/subghz_scene_radio_settings.c index 240b18233..06f4769c7 100644 --- a/applications/main/subghz/scenes/subghz_scene_radio_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_radio_settings.c @@ -36,7 +36,7 @@ const char* const gps_text[GPS_COUNT] = { "115200", }; -#define DEBUG_COUNTER_COUNT 16 +#define DEBUG_COUNTER_COUNT 17 const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = { "+1", "+2", @@ -46,6 +46,7 @@ const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = { "+10", "+50", "OVFL", + "OFEX", "No", "-1", "-2", @@ -64,6 +65,7 @@ const int32_t debug_counter_val[DEBUG_COUNTER_COUNT] = { 10, 50, 65535, + 65534, 0, -1, -2, 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", diff --git a/applications/services/gui/view.c b/applications/services/gui/view.c index 37916ba42..1f39a2961 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 771d3adc6..2b649a3ba 100644 --- a/applications/services/gui/view_i.h +++ b/applications/services/gui/view_i.h @@ -33,6 +33,9 @@ struct View { ViewAsciiCallback ascii_callback; }; +/** 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 0149c96fb..6c3685ac6 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); @@ -33,26 +30,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) { @@ -60,36 +57,36 @@ 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_ascii_callback(view_stack->view, view_stack_ascii); - 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_ascii_callback(&view_stack->view, view_stack_ascii); + 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); } @@ -111,14 +108,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; } @@ -130,14 +127,14 @@ static bool view_stack_ascii(AsciiEvent* 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_ascii(model->views[i], event)) { consumed = true; break; } } - view_commit_model(view_stack->view, false); + view_commit_model(&view_stack->view, false); return consumed; } @@ -147,12 +144,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); } @@ -160,7 +157,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); } @@ -171,7 +168,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) { @@ -184,11 +181,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; } diff --git a/applications/system/hid_app/views/hid_ptt.c b/applications/system/hid_app/views/hid_ptt.c index 58599eb51..8a28cd292 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, @@ -54,6 +55,7 @@ enum HidPushToTalkAppIndex { HidPushToTalkAppIndexTeamSpeak, HidPushToTalkAppIndexWebex, HidPushToTalkAppIndexZoom, + HidPushToTalkAppIndexZoomGlobal, HidPushToTalkAppIndexSize, }; @@ -88,6 +90,20 @@ 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); @@ -109,6 +125,28 @@ 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); @@ -403,6 +441,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; @@ -457,6 +502,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) { @@ -551,6 +603,14 @@ 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" @@ -569,6 +629,13 @@ 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(); @@ -876,6 +943,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, @@ -1030,6 +1104,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, 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"), 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..056ed4320 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)); } @@ -434,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( @@ -446,6 +501,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 +510,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 +518,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/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c index 5f872952e..80518b1a8 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c @@ -566,16 +566,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) { diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index a9fb5b012..6632fb85c 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -275,14 +275,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 ddbf477b2..6d74c9fcb 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -187,14 +187,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 @@ -420,8 +433,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; diff --git a/lib/subghz/protocols/faac_slh.c b/lib/subghz/protocols/faac_slh.c index 4868c3dd7..655305956 100644 --- a/lib/subghz/protocols/faac_slh.c +++ b/lib/subghz/protocols/faac_slh.c @@ -142,7 +142,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) { @@ -160,7 +160,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) { @@ -242,7 +242,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 2d6f14983..37b8e4a44 100644 --- a/lib/subghz/protocols/hay21.c +++ b/lib/subghz/protocols/hay21.c @@ -147,17 +147,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/honeywell.c b/lib/subghz/protocols/honeywell.c index 8b783c1fd..aec4813b1 100644 --- a/lib/subghz/protocols/honeywell.c +++ b/lib/subghz/protocols/honeywell.c @@ -1,9 +1,17 @@ #include "honeywell.h" -#include -//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 +#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. @@ -23,6 +31,95 @@ Data layout: #define TAG "SubGhzProtocolHoneywell" +static const SubGhzBlockConst subghz_protocol_honeywell_const = { + .te_long = 280, + .te_short = 143, + .te_delta = 51, + .min_count_bit_for_found = 64, +}; + +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 = NULL, + .get_hash_data_long = 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, + .get_string_brief = NULL, +}; + +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, + .encoder = &subghz_protocol_honeywell_encoder, + .decoder = &subghz_protocol_honeywell_decoder, + .filter = SubGhzProtocolFilter_Sensors, + +}; + +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, @@ -44,195 +141,6 @@ uint16_t subghz_protocol_honeywell_crc16( return remainder; } -void subghz_protocol_decoder_honeywell_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHoneywell* instance = context; - free(instance); -} - -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_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.. - 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; - } -} - -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; - } -} - -uint32_t subghz_protocol_decoder_honeywell_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHoneywell* instance = context; - return subghz_protocol_blocks_get_hash_data_long( - &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; - - // Parse here and not in decode to avoid visual glitches when loading from file - instance->generic.serial = (instance->generic.data >> 24) & 0xFFFFF; - instance->generic.btn = (instance->generic.data >> 16) & - 0xFF; //not exactly button, but can contain btn data too. - - uint8_t channel = (instance->generic.data >> 44) & 0xF; - uint8_t contact = (instance->generic.btn & 0x80) >> 7; - uint8_t tamper = (instance->generic.btn & 0x40) >> 6; - uint8_t reed = (instance->generic.btn & 0x20) >> 5; - uint8_t alarm = (instance->generic.btn & 0x10) >> 4; - uint8_t battery_low = (instance->generic.btn & 0x08) >> 3; - uint8_t heartbeat = (instance->generic.btn & 0x04) >> 2; - - furi_string_cat_printf( - output, - "%s\r\n%dbit " - "Sn:%07lu\r\nCh:%u Bat:%d Hb: %d\r\n" - "L1: %u, L2: %u, L3: %u, L4: %u\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - instance->generic.serial, - channel, - battery_low, - heartbeat, - contact, - reed, - alarm, - tamper); -} - -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 = 3; - 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); -} static LevelDuration subghz_protocol_encoder_honeywell_add_duration_to_upload(ManchesterEncoderResult result) { LevelDuration data = {.duration = 0, .level = 0}; @@ -286,6 +194,10 @@ static void 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; } @@ -340,35 +252,188 @@ LevelDuration subghz_protocol_encoder_honeywell_yield(void* context) { return ret; } -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 = NULL, - .get_hash_data_long = 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, - .get_string_brief = NULL, -}; +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; +} -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, -}; +void subghz_protocol_decoder_honeywell_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell* instance = context; -const SubGhzProtocol subghz_protocol_honeywell = { - .name = SUBGHZ_PROTOCOL_HONEYWELL_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | - SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - .encoder = &subghz_protocol_honeywell_encoder, - .decoder = &subghz_protocol_honeywell_decoder, + 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; + } +} - .filter = SubGhzProtocolFilter_Sensors, -}; +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) & 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) { + // 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) { + // 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; + 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; + } +} + +uint32_t subghz_protocol_decoder_honeywell_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell* instance = context; + return subghz_protocol_blocks_get_hash_data_long( + &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; + + SubGhzProtocolStatus res = SubGhzProtocolStatusError; + res = subghz_block_generic_deserialize(&instance->generic, flipper_format); + if(res != SubGhzProtocolStatusOk) { + return res; + } + + 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) | + ((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) { + 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; + + 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" + "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, + channel, + battery_low, + heartbeat, + contact ? "open" : "closed", + code_found_hi, + code_found_lo, + contact, + reed, + alarm, + tamper); +} diff --git a/lib/subghz/protocols/honeywell.h b/lib/subghz/protocols/honeywell.h index af648622b..058a5d4c4 100644 --- a/lib/subghz/protocols/honeywell.h +++ b/lib/subghz/protocols/honeywell.h @@ -1,15 +1,6 @@ #pragma once -#include - -#include -#include -#include #include "base.h" -#include "../blocks/generic.h" -#include -#include -#include #define SUBGHZ_PROTOCOL_HONEYWELL_NAME "Honeywell Sec" @@ -20,42 +11,99 @@ 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 + */ uint32_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); - -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; -}; diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 3777433ee..1ec92c47a 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -184,18 +184,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 d93363618..f312ec2d1 100644 --- a/lib/subghz/protocols/kinggates_stylo_4k.c +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -157,14 +157,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 301e03f47..35598eadb 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -155,15 +155,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 32d3db0a5..19bf15249 100644 --- a/lib/subghz/protocols/phoenix_v2.c +++ b/lib/subghz/protocols/phoenix_v2.c @@ -254,14 +254,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 da2b730a2..620a1409d 100644 --- a/lib/subghz/protocols/somfy_keytis.c +++ b/lib/subghz/protocols/somfy_keytis.c @@ -132,14 +132,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 78f53378a..b1c0048b1 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -126,14 +126,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 cae4bb2f4..2700834cc 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -132,14 +132,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; diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index d0242cd70..c7b092a74 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -142,6 +142,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,, @@ -648,8 +649,8 @@ Function,+,archive_favorites_handle_setting_pin_unpin,void,"const char*, const c 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*" @@ -1144,7 +1145,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*