From a2cd122f78e48ca2bfdbb95a8eccb80a06c2e364 Mon Sep 17 00:00:00 2001 From: dogtopus Date: Thu, 9 Mar 2023 21:43:29 -0400 Subject: [PATCH 001/282] Move FeliCa IDm/PMm display to the new NFC-F info screen The goal is to gradually split out NfcWorkerEventReadFelica into more specific decoders, and have the generic NFC-F info screen display info for tags that don't have a specific decoder, similar to how NFC-A works currently. --- applications/main/nfc/nfc_cli.c | 2 +- .../main/nfc/scenes/nfc_scene_config.h | 2 + .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 80 +++++++++++++++-- .../main/nfc/scenes/nfc_scene_nfcf_menu.c | 80 +++++++++++++++++ .../nfc/scenes/nfc_scene_nfcf_read_success.c | 88 +++++++++++++++++++ applications/main/nfc/scenes/nfc_scene_read.c | 6 +- firmware/targets/f7/api_symbols.csv | 3 +- lib/nfc/protocols/felica.c | 8 +- lib/nfc/protocols/felica_util.c | 9 ++ lib/nfc/protocols/felica_util.h | 1 + 10 files changed, 264 insertions(+), 15 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcf_menu.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcf_read_success.c diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 0fd8e1e14..12be6ac09 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -45,7 +45,7 @@ static void nfc_cli_detect(Cli* cli, FuriString* args) { for(size_t i = 0; i < 8; i++) { printf("%02X", dev_data.uid[i]); } - printf("\r\nPMm:"); + printf(", PMm:"); for(size_t i = 0; i < 8; i++) { printf("%02X", dev_data.f_data.pmm[i]); } diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a89d6c40b..c896a61ba 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -14,6 +14,8 @@ ADD_SCENE(nfc, file_select, FileSelect) ADD_SCENE(nfc, emulate_uid, EmulateUid) ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) ADD_SCENE(nfc, nfca_menu, NfcaMenu) +ADD_SCENE(nfc, nfcf_read_success, NfcfReadSuccess) +ADD_SCENE(nfc, nfcf_menu, NfcfMenu) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 305b57ece..8f8391a64 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -1,4 +1,5 @@ #include "../nfc_i.h" +#include void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType type, void* context) { Nfc* nfc = context; @@ -11,6 +12,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { Nfc* nfc = context; Widget* widget = nfc->widget; FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; + FuriHalNfcType type = nfc_data->type; NfcDeviceData* dev_data = &nfc->dev->dev_data; NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; @@ -40,20 +42,80 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n"); + } else if(protocol == NfcDeviceProtocolFelica) { + furi_string_cat_printf(temp_str, "\e#%s\n", nfc_felica_type(dev_data->felica_data.type)); } else { furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); } - // Set tag iso data - char iso_type = FURI_BIT(nfc_data->a_data.sak, 5) ? '4' : '3'; - furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); - furi_string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + // Set tag general data + if(type == FuriHalNfcTypeF) { + // Set NFC-F data + furi_string_cat_printf(temp_str, "ISO 18092 (NFC-F)\n"); + furi_string_cat_printf(temp_str, "CIN:"); + // NFC-F Card Identification Number (CIN) starts at "UID" byte 2. + for(size_t i = 2; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + // The first 2 bytes of the "UID" are Manufacturer Code (MC) + furi_string_cat_printf( + temp_str, + "\nMC: %02X %02X ROM: %02X IC: %02X\n\n", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->f_data.pmm[0], + nfc_data->f_data.pmm[1]); + + furi_string_cat_printf(temp_str, "Timings (1 node/blk):\n"); + furi_string_cat_printf( + temp_str, + "- ReqSvc: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[2], 1)); + furi_string_cat_printf( + temp_str, + "- Fixed: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[3], 0)); + furi_string_cat_printf( + temp_str, + "- Auth1: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[4], 1)); + furi_string_cat_printf( + temp_str, + "- Auth2: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[4], 0)); + furi_string_cat_printf( + temp_str, + "- Read: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[5], 1)); + furi_string_cat_printf( + temp_str, + "- Write: %" PRIuLEAST32 "us\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[6], 1)); + furi_string_cat_printf( + temp_str, + "- Other: %" PRIuLEAST32 "us\n\n", + felica_estimate_timing_us(nfc_data->f_data.pmm[7], 0)); + + furi_string_cat_printf(temp_str, "IDm:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf(temp_str, "\nPMm:"); + for(size_t i = 0; i < sizeof(nfc_data->f_data.pmm); i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->f_data.pmm[i]); + } + } else { // FuriHalNfcTypeA + // Set tag iso data + char iso_type = FURI_BIT(nfc_data->a_data.sak, 5) ? '4' : '3'; + furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf( + temp_str, "\nATQA: %02X %02X ", nfc_data->a_data.atqa[1], nfc_data->a_data.atqa[0]); + furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->a_data.sak); } - furi_string_cat_printf( - temp_str, "\nATQA: %02X %02X ", nfc_data->a_data.atqa[1], nfc_data->a_data.atqa[0]); - furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->a_data.sak); // Set application specific data if(protocol == NfcDeviceProtocolMifareDesfire) { diff --git a/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c new file mode 100644 index 000000000..cae7055b1 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c @@ -0,0 +1,80 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + /* + SubmenuIndexSave, + SubmenuIndexEmulate, + */ + SubmenuIndexInfo, +}; + +void nfc_scene_nfcf_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcf_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + // FelicaData* data = &nfc->dev->dev_data.felica_data; + + /* + submenu_add_item( + submenu, "Save IDm", SubmenuIndexSave, nfc_scene_felica_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Emulate IDm", SubmenuIndexEmulate, nfc_scene_felica_menu_submenu_callback, nfc); + */ + submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_nfcf_menu_submenu_callback, nfc); + + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcfMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcf_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + /* + if(event.event == SubmenuIndexSave) { + nfc->dev->format = NfcDeviceSaveFormatFelica; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaEmulate); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } + consumed = true; + } else if(event.event == SubmenuIndexUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaUnlockMenu); + consumed = true; + } else + */ + if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcfMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_nfcf_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcf_read_success.c b/applications/main/nfc/scenes/nfc_scene_nfcf_read_success.c new file mode 100644 index 000000000..da695ba83 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcf_read_success.c @@ -0,0 +1,88 @@ +#include "../nfc_i.h" +#include "nfc_device.h" + +void nfc_scene_nfcf_read_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcf_read_success_on_enter(void* context) { + Nfc* nfc = context; + + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + NfcDeviceData* dev_data = &nfc->dev->dev_data; + + // Setup view + Widget* widget = nfc->widget; + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_nfcf_read_success_widget_callback, nfc); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_scene_nfcf_read_success_widget_callback, nfc); + + FuriString* temp_str = furi_string_alloc(); + + if(dev_data->protocol == NfcDeviceProtocolFelica) { + furi_string_cat_printf(temp_str, "\e#%s", nfc_felica_type(dev_data->felica_data.type)); + } else { + furi_string_cat_printf(temp_str, "\e#Unknown ISO tag"); + } + + furi_string_cat_printf(temp_str, "\nISO 18092 (NFC-F)"); + + furi_string_cat_printf(temp_str, "\nCIN:"); + // NFC-F Card Identification Number (CIN) starts at "UID" byte 2. + for(size_t i = 2; i < data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", data->uid[i]); + } + + // The first 2 bytes of the "UID" are Manufacturer Code (MC) + furi_string_cat_printf( + temp_str, + "\nMC: %02X %02X ROM: %02X IC: %02X", + data->uid[0], + data->uid[1], + data->f_data.pmm[0], + data->f_data.pmm[1]); + + widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + notification_message_block(nfc->notifications, &sequence_set_green_255); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_nfcf_read_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcfMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + return consumed; +} + +void nfc_scene_nfcf_read_success_on_exit(void* context) { + Nfc* nfc = context; + + notification_message_block(nfc->notifications, &sequence_reset_green); + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index a4d7be2c3..40645c353 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -57,7 +57,6 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if((event.event == NfcWorkerEventReadUidNfcB) || - (event.event == NfcWorkerEventReadUidNfcF) || (event.event == NfcWorkerEventReadUidNfcV)) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); @@ -97,6 +96,11 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } consumed = true; + } else if(event.event == NfcWorkerEventReadUidNfcF) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcfReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + consumed = true; } else if(event.event == NfcWorkerEventReadFelica) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaReadSuccess); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 4da897958..935f0eca8 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.10,, +Version,+,11.13,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -803,6 +803,7 @@ Function,-,fdopen,FILE*,"int, const char*" Function,-,felica_check_ic_type,_Bool,uint8_t* Function,-,felica_clear,void,FelicaData* Function,-,felica_define_normal_block,void,"FelicaService*, uint16_t, uint8_t*" +Function,-,felica_estimate_timing_us,uint_least32_t,"uint_least8_t, uint_least8_t" Function,-,felica_get_ic_type,FelicaICType,uint8_t* Function,-,felica_get_service_name,FuriString*,FelicaService* Function,-,felica_get_system_name,FuriString*,FelicaSystem* diff --git a/lib/nfc/protocols/felica.c b/lib/nfc/protocols/felica.c index d9489cb50..b3405cf6b 100644 --- a/lib/nfc/protocols/felica.c +++ b/lib/nfc/protocols/felica.c @@ -8,13 +8,15 @@ #define TAG "FeliCa" bool felica_check_ic_type(uint8_t* PMm) { - uint8_t ic_type = PMm[0]; - uint8_t rom_type = PMm[1]; + uint8_t rom_type = PMm[0]; + uint8_t ic_type = PMm[1]; bool is_valid_ic = false; if(ic_type == 0xff) { // RC-S967 in nfc-dep is_valid_ic = true; - } else if(ic_type == 0xf0 || ic_type == 0xf2) { // Lite(S) + } else if(ic_type == 0xf2) { // RC-S732? + is_valid_ic = true; + } else if(ic_type == 0xf0 || ic_type == 0xf1) { // Lite(S) is_valid_ic = true; } else if(ic_type == 0xe1) { // RC-S967 in plug mode is_valid_ic = true; diff --git a/lib/nfc/protocols/felica_util.c b/lib/nfc/protocols/felica_util.c index a5198aa09..aba13c4be 100644 --- a/lib/nfc/protocols/felica_util.c +++ b/lib/nfc/protocols/felica_util.c @@ -1,6 +1,15 @@ #include "./felica.h" #include +static const uint32_t TIME_CONSTANT_US = 302; + +uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t units) { + uint_least32_t base_cost_factor = 1 + (timing & 0x7); + uint_least32_t unit_cost_factor = 1 + ((timing >> 3) & 0x7); + uint_least32_t scale = 1 << ((timing >> 6) * 2); + return TIME_CONSTANT_US * scale * (base_cost_factor + unit_cost_factor * units); +} + FuriString* felica_get_system_name(FelicaSystem* system) { uint16_t code = system->code; diff --git a/lib/nfc/protocols/felica_util.h b/lib/nfc/protocols/felica_util.h index 4224668eb..e53d66805 100644 --- a/lib/nfc/protocols/felica_util.h +++ b/lib/nfc/protocols/felica_util.h @@ -1,4 +1,5 @@ #include "./felica.h" +uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t units); FuriString* felica_get_system_name(FelicaSystem* system); FuriString* felica_get_service_name(FelicaService* service); \ No newline at end of file From 43341980cc2f23535fe53f0b30bd354591ea90f9 Mon Sep 17 00:00:00 2001 From: dogtopus Date: Sat, 18 Mar 2023 03:04:00 -0300 Subject: [PATCH 002/282] Array use refactor Move all arrays to allocating actual data rather than pointers to simplify construction and destruction. Also moved to M_EACH for iterating over arrays for less boilerplate code. Also did some function renaming for extra clarity. root_area is now a node type for simplified area traversal (coming soon). --- .../nfc/scenes/nfc_scene_felica_info_select.c | 87 +++--- .../scenes/nfc_scene_felica_read_success.c | 26 +- .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 16 +- .../nfc/scenes/nfc_scene_read_card_type.c | 12 + firmware/targets/f7/api_symbols.csv | 5 +- lib/nfc/nfc_device.h | 1 + lib/nfc/nfc_worker.c | 4 + lib/nfc/protocols/felica.c | 251 +++++++++++------- lib/nfc/protocols/felica.h | 101 +++++-- lib/nfc/protocols/felica_util.c | 1 + 10 files changed, 323 insertions(+), 181 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_felica_info_select.c b/applications/main/nfc/scenes/nfc_scene_felica_info_select.c index 3b7c570d7..7a59521fa 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_info_select.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_info_select.c @@ -19,24 +19,24 @@ void nfc_scene_felica_info_select_on_enter(void* context) { uint8_t i = 1; if(state->selected_system == NULL || state->selected_system->code == LITE_SYSTEM_CODE) { submenu_set_header(submenu, "Systems"); - FelicaSystemList_it_t it; - for(FelicaSystemList_it(it, data->systems); !FelicaSystemList_end_p(it); - FelicaSystemList_next(it)) { - FelicaSystem* current_system = *FelicaSystemList_ref(it); - FuriString* system_name = felica_get_system_name(current_system); - submenu_add_item( - submenu, - furi_string_get_cstr(system_name), - i++, - nfc_scene_felica_info_select_submenu_callback, - nfc); - furi_string_free(system_name); - } + for + M_EACH(current_system, data->systems, FelicaSystemArray_t) { + FuriString* system_name = felica_get_system_name(current_system); + submenu_add_item( + submenu, + furi_string_get_cstr(system_name), + i++, + nfc_scene_felica_info_select_submenu_callback, + nfc); + furi_string_free(system_name); + } } else { FelicaSystem* system = state->selected_system; FuriString* header = furi_string_alloc_printf("%04X/", system->code); - FelicaArea* area = &system->root_area; + FelicaNode* root = &system->root; + furi_assert(root->type == FelicaNodeTypeArea); + FelicaArea* area = root->area; if(FelicaAreaPath_size(state->selected_areas) > 0) { FelicaAreaPath_it_t it; for(FelicaAreaPath_it(it, state->selected_areas); !FelicaAreaPath_end_p(it); @@ -51,32 +51,30 @@ void nfc_scene_felica_info_select_on_enter(void* context) { submenu_set_header(submenu, furi_string_get_cstr(header)); furi_string_free(header); - FelicaNodeList_it_t it; - for(FelicaNodeList_it(it, area->nodes); !FelicaNodeList_end_p(it); - FelicaNodeList_next(it)) { - FelicaNode* node = *FelicaNodeList_ref(it); - FuriString* node_name = furi_string_alloc(); - if(node->type == FelicaNodeTypeArea) { - furi_string_printf(node_name, "Area %d", node->area->number); - submenu_add_item( - submenu, - furi_string_get_cstr(node_name), - i++, - nfc_scene_felica_info_select_submenu_callback, - nfc); - } else { - uint16_t service_code = node->service->number << 6; - furi_string_printf(node_name, "Service %04X", service_code); - submenu_add_item( - submenu, - furi_string_get_cstr(node_name), - i++, - nfc_scene_felica_info_select_submenu_callback, - nfc); - } + for + M_EACH(node, area->nodes, FelicaNodeArray_t) { + FuriString* node_name = furi_string_alloc(); + if(node->type == FelicaNodeTypeArea) { + furi_string_printf(node_name, "Area %d", node->area->number); + submenu_add_item( + submenu, + furi_string_get_cstr(node_name), + i++, + nfc_scene_felica_info_select_submenu_callback, + nfc); + } else { + uint16_t service_code = node->service->number << 6; + furi_string_printf(node_name, "Service %04X", service_code); + submenu_add_item( + submenu, + furi_string_get_cstr(node_name), + i++, + nfc_scene_felica_info_select_submenu_callback, + nfc); + } - furi_string_free(node_name); - } + furi_string_free(node_name); + } } state->selected_service = NULL; @@ -102,7 +100,7 @@ bool nfc_scene_felica_info_select_on_event(void* context, SceneManagerEvent even index -= 1; if(state->selected_system == NULL) { - state->selected_system = *FelicaSystemList_get(data->systems, index); + state->selected_system = FelicaSystemArray_get(data->systems, index); if(state->selected_system->code == LITE_SYSTEM_CODE) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaServiceData); } else { @@ -111,12 +109,15 @@ bool nfc_scene_felica_info_select_on_event(void* context, SceneManagerEvent even consumed = true; } else { FelicaNode* selected_node = NULL; + + FelicaNode* root = &(state->selected_system->root); + furi_assert(root->type == FelicaNodeTypeArea); + if(FelicaAreaPath_size(state->selected_areas) == 0) { - selected_node = - *FelicaNodeList_get(state->selected_system->root_area.nodes, index); + selected_node = FelicaNodeArray_get(root->area->nodes, index); } else { FelicaArea* current_area = *FelicaAreaPath_back(state->selected_areas); - selected_node = *FelicaNodeList_get(current_area->nodes, index); + selected_node = FelicaNodeArray_get(current_area->nodes, index); } if(selected_node->type == FelicaNodeTypeArea) { diff --git a/applications/main/nfc/scenes/nfc_scene_felica_read_success.c b/applications/main/nfc/scenes/nfc_scene_felica_read_success.c index 52bba0ee3..79dd82297 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_read_success.c @@ -29,21 +29,19 @@ void nfc_scene_felica_read_success_on_enter(void* context) { } else { temp_str = furi_string_alloc_printf("\e#%s", nfc_felica_type(felica_data->type)); - FelicaSystemList_it_t it; - for(FelicaSystemList_it(it, felica_data->systems); !FelicaSystemList_end_p(it); - FelicaSystemList_next(it)) { - FelicaSystem* current_system = *FelicaSystemList_ref(it); - furi_string_cat_printf( - temp_str, "\nSystem %04X (#%d):", current_system->code, current_system->number); - furi_string_cat_printf(temp_str, "\nIDm:\n "); - for(size_t i = 0; i < 8; i++) { - furi_string_cat_printf(temp_str, "%02X", current_system->idm[i]); + for + M_EACH(current_system, felica_data->systems, FelicaSystemArray_t) { + furi_string_cat_printf( + temp_str, "\nSystem %04X (#%d):", current_system->code, current_system->number); + furi_string_cat_printf(temp_str, "\nIDm:\n "); + for(size_t i = 0; i < 8; i++) { + furi_string_cat_printf(temp_str, "%02X", current_system->idm[i]); + } + furi_string_cat_printf(temp_str, "\nPMm:\n "); + for(size_t i = 0; i < 8; i++) { + furi_string_cat_printf(temp_str, "%02X", current_system->pmm[i]); + } } - furi_string_cat_printf(temp_str, "\nPMm:\n "); - for(size_t i = 0; i < 8; i++) { - furi_string_cat_printf(temp_str, "%02X", current_system->pmm[i]); - } - } } widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 8f8391a64..26f9c4eb3 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -66,35 +66,35 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { nfc_data->f_data.pmm[0], nfc_data->f_data.pmm[1]); - furi_string_cat_printf(temp_str, "Timings (1 node/blk):\n"); + furi_string_cat_printf(temp_str, "MRT (1 node/blk):\n"); furi_string_cat_printf( temp_str, "- ReqSvc: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[2], 1)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_VARIABLE_MRT], 1)); furi_string_cat_printf( temp_str, "- Fixed: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[3], 0)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_FIXED_MRT], 0)); furi_string_cat_printf( temp_str, "- Auth1: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[4], 1)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_MUTUAL_AUTH_MRT], 1)); furi_string_cat_printf( temp_str, "- Auth2: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[4], 0)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_MUTUAL_AUTH_MRT], 0)); furi_string_cat_printf( temp_str, "- Read: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[5], 1)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_READ_MRT], 1)); furi_string_cat_printf( temp_str, "- Write: %" PRIuLEAST32 "us\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[6], 1)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_WRITE_MRT], 1)); furi_string_cat_printf( temp_str, "- Other: %" PRIuLEAST32 "us\n\n", - felica_estimate_timing_us(nfc_data->f_data.pmm[7], 0)); + felica_estimate_timing_us(nfc_data->f_data.pmm[FELICA_PMM_OTHER_MRT], 0)); furi_string_cat_printf(temp_str, "IDm:"); for(size_t i = 0; i < nfc_data->uid_len; i++) { diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_type.c b/applications/main/nfc/scenes/nfc_scene_read_card_type.c index 865b3f54b..ed33515d4 100644 --- a/applications/main/nfc/scenes/nfc_scene_read_card_type.c +++ b/applications/main/nfc/scenes/nfc_scene_read_card_type.c @@ -8,6 +8,7 @@ enum SubmenuIndex { SubmenuIndexReadEMV, SubmenuIndexReadNFCA, SubmenuIndexReadFelica, + SubmenuIndexReadNFCF, }; void nfc_scene_read_card_type_submenu_callback(void* context, uint32_t index) { @@ -56,6 +57,12 @@ void nfc_scene_read_card_type_on_enter(void* context) { SubmenuIndexReadFelica, nfc_scene_read_card_type_submenu_callback, nfc); + submenu_add_item( + submenu, + "Read NFC-F data", + SubmenuIndexReadNFCF, + nfc_scene_read_card_type_submenu_callback, + nfc); uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneReadCardType); submenu_set_selected_item(submenu, state); @@ -97,6 +104,11 @@ bool nfc_scene_read_card_type_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); consumed = true; } + if(event.event == SubmenuIndexReadNFCF) { + nfc->dev->dev_data.read_mode = NfcReadModeNFCF; + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + consumed = true; + } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, event.event); } return consumed; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 935f0eca8..fb5603041 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.13,, +Version,+,11.14,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -808,14 +808,15 @@ Function,-,felica_get_ic_type,FelicaICType,uint8_t* Function,-,felica_get_service_name,FuriString*,FelicaService* Function,-,felica_get_system_name,FuriString*,FelicaSystem* Function,-,felica_lite_can_read_without_mac,_Bool,"uint8_t*, uint8_t" +Function,-,felica_lite_dump_data,_Bool,"FuriHalNfcTxRxContext*, FelicaReader*, FelicaData*, FelicaSystem*" Function,-,felica_lite_prepare_unencrypted_read,uint8_t,"uint8_t*, const FelicaReader*, _Bool, const uint8_t*, uint8_t" Function,-,felica_lite_prepare_unencrypted_write,uint8_t,"uint8_t*, const FelicaReader*, const uint8_t*, uint8_t, const uint8_t*" Function,-,felica_parse_unencrypted_read,uint16_t,"uint8_t*, uint8_t, FelicaReader*, uint8_t*, uint16_t" Function,-,felica_parse_unencrypted_write,_Bool,"uint8_t*, uint8_t, FelicaReader*" Function,-,felica_prepare_unencrypted_read,uint8_t,"uint8_t*, const FelicaReader*, const uint16_t*, uint8_t, const uint32_t*, uint8_t" Function,-,felica_prepare_unencrypted_write,uint8_t,"uint8_t*, FelicaReader*, const uint16_t*, uint8_t, const uint32_t*, uint8_t, const uint8_t*" +Function,-,felica_push_normal_block,void,"FelicaService*, uint8_t*" Function,-,felica_read_card,_Bool,"FuriHalNfcTxRxContext*, FelicaData*, uint8_t*, uint8_t*" -Function,-,felica_read_lite_system,_Bool,"FuriHalNfcTxRxContext*, FelicaReader*, FelicaData*, FelicaSystem*" Function,-,feof,int,FILE* Function,-,feof_unlocked,int,FILE* Function,-,ferror,int,FILE* diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index fc0e5bfbc..3edd01070 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -63,6 +63,7 @@ typedef enum { NfcReadModeEMV, NfcReadModeNFCA, NfcReadModeFelica, + NfcReadModeNFCF, } NfcReadMode; typedef struct { diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index b034dd202..96cab9f5b 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -508,6 +508,10 @@ void nfc_worker_read_type(NfcWorker* nfc_worker) { break; } } + } else if(read_mode == NfcReadModeNFCF) { + nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + event = NfcWorkerEventReadUidNfcF; + break; } } } else { diff --git a/lib/nfc/protocols/felica.c b/lib/nfc/protocols/felica.c index b3405cf6b..827187564 100644 --- a/lib/nfc/protocols/felica.c +++ b/lib/nfc/protocols/felica.c @@ -151,6 +151,55 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) { return FelicaICType2K; } +/** Parse common FeliCa response headers. + * + * This parses and validates the most commonly occurring response header types. + * + * The header needs to match the (length, res, idm) format, and also (sf1, sf2) when always_succeed + * is set to false. + * + * @param buf RX buffer. + * @param len RX buffer length. + * @param reader The FeliCa reader context. + * @param expected_resp Expected response code. Must be an odd number. + * @param always_succeed When set to true, skip status flags (sf1 and sf2) parsing. + * @return The number of bytes parsed, or 0 when response is invalid or status flags are set. + */ +static uint8_t felica_consume_header( + uint8_t* buf, + uint8_t len, + FelicaReader* reader, + uint8_t expected_resp, + bool always_succeed) { + furi_assert(expected_resp & 1); + furi_assert(buf != NULL); + furi_assert(reader != NULL); + + uint8_t header_size = always_succeed ? 10 : 12; + if(len < header_size) { + FURI_LOG_E(TAG, "Malformed header: too short."); + return 0; + } + if(buf[1] != expected_resp) { + FURI_LOG_E(TAG, "Expecting %u, got %u.", expected_resp, buf[1]); + return 0; + } + if(memcmp(&buf[2], reader->current_idm, 8) != 0) { + FURI_LOG_E(TAG, "IDm mismatch."); + return 0; + } + if(always_succeed) { + reader->status_flags[0] = buf[10]; + reader->status_flags[1] = buf[11]; + if(reader->status_flags[0] != 0 || reader->status_flags[1] != 0) { + FURI_LOG_W( + TAG, "SF1: %02X SF2: %02X", reader->status_flags[0], reader->status_flags[1]); + return 0; + } + } + return header_size; +} + uint8_t felica_prepare_unencrypted_read( uint8_t* dest, const FelicaReader* reader, @@ -212,32 +261,12 @@ uint16_t felica_parse_unencrypted_read( FelicaReader* reader, uint8_t* out, uint16_t out_len) { - if(len < 12) { - return false; - } - len--; - buf++; - - if(*buf != FELICA_UNENCRYPTED_READ_RES) { - return false; - } - len--; - buf++; - - if(memcmp(buf, reader->current_idm, 8) != 0) { - return false; - } - len -= 8; - buf += 8; - - reader->status_flags[0] = buf[0]; - reader->status_flags[1] = buf[1]; - len -= 2; - buf += 2; - if(reader->status_flags[0] != 0) { - FURI_LOG_W(TAG, "SF1: %02X SF2: %02X", reader->status_flags[0], reader->status_flags[1]); + uint8_t consumed = felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_READ_RES, false); + if(!consumed) { return 0; } + len -= consumed; + buf += consumed; if(len < 1) { return 0; @@ -315,48 +344,77 @@ uint8_t felica_lite_prepare_unencrypted_write( } bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* reader) { - if(len < 12) { + uint8_t consumed = + felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_WRITE_RES, false); + if(!consumed) { return false; } + return true; +} + +uint8_t felica_prepare_request_system_code(uint8_t* dest, FelicaReader* reader) { + dest[0] = FELICA_REQUEST_SYSTEM_CODE_CMD; + memcpy(&dest[1], reader->current_idm, 8); + return 9; +} + +bool felica_parse_request_system_code( + uint8_t* buf, + uint8_t len, + FelicaReader* reader, + FelicaSystemArray_t* systems) { + uint8_t consumed = + felica_consume_header(buf, len, reader, FELICA_REQUEST_SYSTEM_CODE_RES, true); + if(consumed == 0) { + return false; + } + len -= consumed; + buf += consumed; + + uint8_t entries = *buf; len--; buf++; - if(*buf != FELICA_UNENCRYPTED_WRITE_RES) { + if(len < 2 * entries) { + FURI_LOG_E(TAG, "FELICA_REQUEST_SYSTEM_CODE_RES: Response too short"); return false; } - len--; - buf++; - if(memcmp(buf, reader->current_idm, 8) != 0) { - return false; - } - len -= 8; - buf += 8; + for(uint8_t idx = 0; idx < entries; idx++) { + FelicaSystem* system = FelicaSystemArray_push_new(*systems); + furi_assert(system != NULL); - reader->status_flags[0] = buf[0]; - reader->status_flags[1] = buf[1]; - len -= 2; - buf += 2; - if(reader->status_flags[0] != 0) { - FURI_LOG_W(TAG, "SF1: %02X SF2: %02X", reader->status_flags[0], reader->status_flags[1]); - return 0; + // Set system code + system->number = idx; + system->code = buf[2 * idx] | (buf[2 * idx + 1] << 8); + + FURI_LOG_D(TAG, "Found system code %04X", system->code); + + // Fill in IDm and PMm + memcpy(system->idm, reader->current_idm, 8); + memcpy(system->pmm, reader->current_pmm, 8); + + // Set system index field in IDm + system->idm[0] &= 0x0f; + system->idm[0] |= ((idx & 0xf) << 4); } return true; } -void felica_parse_system_info(FelicaSystem* system, uint8_t* IDm, uint8_t* PMm) { - memcpy(system->idm, IDm, 8); - memcpy(system->pmm, PMm, 8); - for(int i = 0; i < 6; i++) { - char MRT_byte = PMm[2 + i]; - FelicaMRTParts* mrt_data = &system->maximum_response_times[i]; - mrt_data->real_a = (MRT_byte & 7) + 1; - MRT_byte >>= 3; - mrt_data->real_b = (MRT_byte & 7) + 1; - MRT_byte >>= 3; - mrt_data->exponent = (MRT_byte & 3); - } +static FelicaSystem* felica_gen_monolithic_system_code( + FelicaReader* reader, + FelicaSystemArray_t* systems, + uint16_t system_code) { + FelicaSystem* system = FelicaSystemArray_push_new(*systems); + furi_assert(reader != NULL); + furi_assert(system != NULL); + + memcpy(system->idm, reader->current_idm, 8); + memcpy(system->pmm, reader->current_pmm, 8); + system->code = system_code; + + return system; } bool felica_lite_can_read_without_mac(uint8_t* mc_r_restr, uint8_t block_number) { @@ -368,12 +426,16 @@ bool felica_lite_can_read_without_mac(uint8_t* mc_r_restr, uint8_t block_number) } void felica_define_normal_block(FelicaService* service, uint16_t number, uint8_t* data) { - FelicaBlock* block = malloc(sizeof(FelicaBlock)); + FelicaBlock* block = FelicaBlockArray_safe_get(service->blocks, number); memcpy(block->data, data, FELICA_BLOCK_SIZE); - FelicaBlockList_set_at(service->blocks, number, block); } -bool felica_read_lite_system( +void felica_push_normal_block(FelicaService* service, uint8_t* data) { + FelicaBlock* block = FelicaBlockArray_push_new(service->blocks); + memcpy(block->data, data, FELICA_BLOCK_SIZE); +} + +bool felica_lite_dump_data( FuriHalNfcTxRxContext* tx_rx, FelicaReader* reader, FelicaData* data, @@ -425,8 +487,6 @@ bool felica_read_lite_system( return false; } - system->code = LITE_SYSTEM_CODE; - FelicaLiteInfo* lite_info = &system->lite_info; lite_info->card_key_1 = NULL; lite_info->card_key_2 = NULL; @@ -568,6 +628,22 @@ bool felica_read_lite_system( return true; } +bool felica_std_request_system_code( + FuriHalNfcTxRxContext* tx_rx, + FelicaReader* reader, + FelicaSystemArray_t* systems) { + tx_rx->tx_bits = 8 * felica_prepare_request_system_code(tx_rx->tx_data, reader); + if(!furi_hal_nfc_tx_rx_full(tx_rx)) { + FURI_LOG_E(TAG, "Bad exchange requesting system code"); + return false; + } + if(!felica_parse_request_system_code(tx_rx->rx_data, tx_rx->rx_bits / 8, reader, systems)) { + FURI_LOG_E(TAG, "Bad response to Request System Code command"); + return false; + } + return true; +} + bool felica_read_card( FuriHalNfcTxRxContext* tx_rx, FelicaData* data, @@ -583,31 +659,24 @@ bool felica_read_card( memcpy(reader.current_idm, polled_idm, 8); memcpy(reader.current_pmm, polled_pmm, 8); - FelicaSystem* current_system = malloc(sizeof(FelicaSystem)); - FelicaSystemList_init(data->systems); - FelicaSystemList_push_back(data->systems, current_system); - - felica_parse_system_info(current_system, polled_idm, polled_pmm); + FelicaSystemArray_init(data->systems); if(data->type == FelicaICTypeLite || data->type == FelicaICTypeLiteS) { FURI_LOG_I(TAG, "Reading Felica Lite system"); - felica_read_lite_system(tx_rx, &reader, data, current_system); + FelicaSystem* lite_system = + felica_gen_monolithic_system_code(&reader, &(data->systems), LITE_SYSTEM_CODE); + felica_lite_dump_data(tx_rx, &reader, data, lite_system); card_read = true; break; } + FURI_LOG_I(TAG, "Reading Felica Standard system"); } while(false); return card_read; } void felica_service_clear(FelicaService* service) { - FelicaBlockList_it_t it; - for(FelicaBlockList_it(it, service->blocks); !FelicaBlockList_end_p(it); - FelicaBlockList_next(it)) { - FelicaBlock* block = *FelicaBlockList_ref(it); - free(block); - } - FelicaBlockList_clear(service->blocks); + FelicaBlockArray_clear(service->blocks); } void felica_lite_clear(FelicaLiteInfo* lite_info) { @@ -630,31 +699,33 @@ void felica_lite_clear(FelicaLiteInfo* lite_info) { } } +void felica_node_clear(FelicaNode* node); + void felica_area_clear(FelicaArea* area) { - FelicaNodeList_it_t it; - for(FelicaNodeList_it(it, area->nodes); !FelicaNodeList_end_p(it); FelicaNodeList_next(it)) { - FelicaNode* node = *FelicaNodeList_ref(it); - if(node->type == FelicaNodeTypeArea) { - felica_area_clear(node->area); - } else if(node->type == FelicaNodeTypeService) { - felica_service_clear(node->service); + for + M_EACH(node, area->nodes, FelicaNodeArray_t) { + felica_node_clear(node); } - free(node); + FelicaNodeArray_clear(area->nodes); +} + +void felica_node_clear(FelicaNode* node) { + if(node->type == FelicaNodeTypeArea) { + felica_area_clear(node->area); + } else if(node->type == FelicaNodeTypeService) { + felica_service_clear(node->service); } - FelicaNodeList_clear(area->nodes); } void felica_clear(FelicaData* data) { - FelicaSystemList_it_t it; - for(FelicaSystemList_it(it, data->systems); !FelicaSystemList_end_p(it); - FelicaSystemList_next(it)) { - FelicaSystem* system = *FelicaSystemList_ref(it); - if(system->code == LITE_SYSTEM_CODE) { - felica_lite_clear(&system->lite_info); - ; - } else { - felica_area_clear(&system->root_area); + for + M_EACH(system, data->systems, FelicaSystemArray_t) { + if(system->code == LITE_SYSTEM_CODE) { + felica_lite_clear(&system->lite_info); + } else { + felica_node_clear(&system->root); + FelicaPublicServiceDict_clear(system->public_services); + } } - } - FelicaSystemList_clear(data->systems); + FelicaSystemArray_clear(data->systems); } \ No newline at end of file diff --git a/lib/nfc/protocols/felica.h b/lib/nfc/protocols/felica.h index fa81cba2f..21bd5532c 100644 --- a/lib/nfc/protocols/felica.h +++ b/lib/nfc/protocols/felica.h @@ -10,6 +10,8 @@ #define MRT_T_SIG_x16 4833038.24 //ns, MRT_T_SIG * (4 ** 2) #define MRT_T_SIG_x64 19332152.96 //ns, MRT_T_SIG * (4 ** 2) +#define FELICA_PMM_MRT_BASE 2 + #define FELICA_VARIABLE_MRT 0 #define FELICA_FIXED_MRT 1 #define FELICA_MUTUAL_AUTH_MRT 2 @@ -17,6 +19,13 @@ #define FELICA_WRITE_MRT 4 #define FELICA_OTHER_MRT 5 +#define FELICA_PMM_VARIABLE_MRT (FELICA_PMM_MRT_BASE + FELICA_VARIABLE_MRT) +#define FELICA_PMM_FIXED_MRT (FELICA_PMM_MRT_BASE + FELICA_FIXED_MRT) +#define FELICA_PMM_MUTUAL_AUTH_MRT (FELICA_PMM_MRT_BASE + FELICA_MUTUAL_AUTH_MRT) +#define FELICA_PMM_READ_MRT (FELICA_PMM_MRT_BASE + FELICA_READ_MRT) +#define FELICA_PMM_WRITE_MRT (FELICA_PMM_MRT_BASE + FELICA_WRITE_MRT) +#define FELICA_PMM_OTHER_MRT (FELICA_PMM_MRT_BASE + FELICA_OTHER_MRT) + #define FELICA_BLOCK_SIZE 16 #define CYBERNET_SYSTEM_CODE 0x0003 @@ -55,9 +64,13 @@ #define FELICA_UNENCRYPTED_READ_CMD 0x06 #define FELICA_UNENCRYPTED_WRITE_CMD 0x08 +#define FELICA_SEARCH_SERVICE_CODE_CMD 0x0a +#define FELICA_REQUEST_SYSTEM_CODE_CMD 0x0c #define FELICA_UNENCRYPTED_READ_RES 0x07 #define FELICA_UNENCRYPTED_WRITE_RES 0x09 +#define FELICA_SEARCH_SERVICE_CODE_RES 0x0b +#define FELICA_REQUEST_SYSTEM_CODE_RES 0x0d typedef enum { FelicaICTypeRC_SA24_10K, // RC-SA24/1x @@ -88,13 +101,6 @@ typedef enum { FelicaICTypeSuica, // https://www.tuv-nederland.nl/assets/files/cerfiticaten/2019/07/cr-nscib-cc-10-30076-cr.pdf } FelicaICType; -typedef struct { - uint8_t exponent : 2; - // Incremented at read - uint8_t real_a : 4; - uint8_t real_b : 4; -} FelicaMRTParts; - typedef enum { FelicaServiceTypeRandom = (0b0010 << 2), FelicaServiceTypeCyclic = (0b0011 << 2), @@ -121,20 +127,22 @@ DICT_SET_DEF( FelicaServiceAttribute, M_ENUM_OPLIST(FelicaServiceAttribute, FelicaServiceAttributeAuthRW)) -typedef FelicaMRTParts FelicaMRTParameters[6]; - typedef struct { uint8_t data[FELICA_BLOCK_SIZE]; } FelicaBlock; -ARRAY_DEF(FelicaBlockList, FelicaBlock*, M_PTR_OPLIST) +// TODO properly remove this +//ARRAY_DEF(FelicaBlockList, FelicaBlock*, M_PTR_OPLIST) +ARRAY_DEF(FelicaBlockArray, FelicaBlock, M_POD_OPLIST) +#define M_OPL_FelicaBlockArray_t() ARRAY_OPLIST(FelicaBlockArray, M_POD_OPLIST) typedef struct { uint16_t number; FelicaServiceAttributeList_t access_control_list; // accounts for overlap services - bool is_extended_overlap; + bool is_extended_overlap; // We don't know much about this currently. will always be false union { - FelicaBlockList_t blocks; + // TODO change this to use FelicaBlockArray_t + FelicaBlockArray_t blocks; struct { uint16_t overlap_target; uint8_t block_start; @@ -149,23 +157,44 @@ typedef enum { } FelicaNodeType; struct _FelicaArea_t; -typedef struct { +typedef struct _FelicaArea_t FelicaArea; + +struct _FelicaNode_s; +typedef struct _FelicaNode_s FelicaNode; + +struct _FelicaNode_s { + /** Node type. */ FelicaNodeType type; + /** Borrowed pointer to its parent node. */ + FelicaNode* parent; union { - struct _FelicaArea_t* area; + /** (Area/dir type only) The area struct. */ + FelicaArea* area; + /** (Service/file type only) The service struct. */ FelicaService* service; }; -} FelicaNode; +}; -ARRAY_DEF(FelicaNodeList, FelicaNode*, M_PTR_OPLIST) +// TODO properly remove this +//ARRAY_DEF(FelicaNodeList, FelicaNode*, M_PTR_OPLIST) +ARRAY_DEF(FelicaNodeArray, FelicaNode, M_POD_OPLIST) +#define M_OPL_FelicaNodeArray_t() ARRAY_OPLIST(FelicaNodeArray, M_POD_OPLIST) -typedef struct _FelicaArea_t { +ARRAY_DEF(FelicaNodeRefArray, FelicaNode*, M_PTR_OPLIST) +#define M_OPL_FelicaNodeRefArray_t() ARRAY_OPLIST(FelicaNodeRefArray, M_PTR_OPLIST) + +// { service_number: service_ptr_in_tree } +DICT_DEF2(FelicaPublicServiceDict, uint16_t, M_DEFAULT_OPLIST, FelicaService*, M_PTR_OPLIST) +#define M_OPL_FelicaPublicServiceDict_t() \ + DICT_OPLIST(FelicaPublicServiceDict, M_DEFAULT_OPLIST, M_PTR_OPLIST) + +struct _FelicaArea_t { uint16_t number; bool can_create_subareas; uint16_t end_service_code; - FelicaNodeList_t nodes; -} FelicaArea; + FelicaNodeArray_t nodes; +}; typedef struct { uint8_t* S_PAD[14]; @@ -186,24 +215,39 @@ typedef struct { } FelicaLiteInfo; typedef struct _FelicaSystem_t { + /** FeliCa system index. */ uint8_t number; + /** If the system belongs to a FeliCa Lite (and be its only system). */ + bool is_lite; + /** FeliCa system code. */ uint16_t code; + /** System IDm with system index bitfield properly set. */ uint8_t idm[8]; + /** Cached card PMm. */ uint8_t pmm[8]; - FelicaMRTParameters maximum_response_times; union { + /** (For FeliCa Lite only) Card content. */ FelicaLiteInfo lite_info; - FelicaArea root_area; + struct { + /** (For FeliCa Standard only) The root of the raw filesystem tree. */ + FelicaNode root; + /** (For FeliCa Standard only) Shortcut for all publicly accessible services for quick + * access by card parsers. */ + FelicaPublicServiceDict_t public_services; + }; }; } FelicaSystem; -ARRAY_DEF(FelicaSystemList, FelicaSystem*, M_PTR_OPLIST) +// TODO properly remove this +//ARRAY_DEF(FelicaSystemList, FelicaSystem*, M_PTR_OPLIST) +ARRAY_DEF(FelicaSystemArray, FelicaSystem, M_POD_OPLIST) +#define M_OPL_FelicaSystemArray_t() ARRAY_OPLIST(FelicaSystemArray, M_POD_OPLIST) typedef struct { FelicaICType type; uint8_t subtype; - FelicaSystemList_t systems; + FelicaSystemArray_t systems; } FelicaData; typedef struct { @@ -255,8 +299,17 @@ bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* rea bool felica_lite_can_read_without_mac(uint8_t* mc_r_restr, uint8_t block_number); void felica_define_normal_block(FelicaService* service, uint16_t number, uint8_t* data); +void felica_push_normal_block(FelicaService* service, uint8_t* data); -bool felica_read_lite_system( +/** Dump a FeliCa Lite or Lite-S tag. + * + * @param tx_rx NFC context. + * @param reader FeliCa reader context. + * @param data Output data object. + * @param system FeliCa system description. + * @return true if successful. + */ +bool felica_lite_dump_data( FuriHalNfcTxRxContext* tx_rx, FelicaReader* reader, FelicaData* data, diff --git a/lib/nfc/protocols/felica_util.c b/lib/nfc/protocols/felica_util.c index aba13c4be..481ea5ed7 100644 --- a/lib/nfc/protocols/felica_util.c +++ b/lib/nfc/protocols/felica_util.c @@ -3,6 +3,7 @@ static const uint32_t TIME_CONSTANT_US = 302; +// TODO move this to felica.c uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t units) { uint_least32_t base_cost_factor = 1 + (timing & 0x7); uint_least32_t unit_cost_factor = 1 + ((timing >> 3) & 0x7); From c2997bb1fcaac33df4f3431f6e1f88fd09926bb5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 9 Apr 2023 23:56:41 +0300 Subject: [PATCH 003/282] Trying to fix button issues in sbughz remote --- applications/main/subghz/views/transmitter.c | 1 + applications/main/subghz_remote/subghz_remote_app.c | 2 ++ lib/subghz/blocks/custom_btn.c | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index e8c53d1ed..2bec77690 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -137,6 +137,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { true); if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { + subghz_custom_btn_set(0); with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index 040273838..a6225e1b5 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -354,6 +354,8 @@ bool subghz_remote_key_load( bool res = false; + subghz_custom_btn_set(0); + do { // load frequency from file if(!flipper_format_read_uint32(fff_file, "Frequency", &preset->frequency, 1)) { diff --git a/lib/subghz/blocks/custom_btn.c b/lib/subghz/blocks/custom_btn.c index dd1436798..3023e09ea 100644 --- a/lib/subghz/blocks/custom_btn.c +++ b/lib/subghz/blocks/custom_btn.c @@ -1,7 +1,7 @@ #include "custom_btn.h" -static uint8_t custom_btn_id; -static uint8_t custom_btn_original; +static uint8_t custom_btn_id = 0; +static uint8_t custom_btn_original = 0; static uint8_t custom_btn_max_btns = 0; void subghz_custom_btn_set(uint8_t b) { From 4c7fa05bfe46fdddd0863bb252f7e6b7c7f26169 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 10 Apr 2023 02:55:16 +0300 Subject: [PATCH 004/282] Add alutech table to enviroment alloc and free --- lib/subghz/environment.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index b39b259d4..5ded243c4 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -16,6 +16,7 @@ SubGhzEnvironment* subghz_environment_alloc() { instance->protocol_registry = NULL; instance->came_atomo_rainbow_table_file_name = NULL; instance->nice_flor_s_rainbow_table_file_name = NULL; + instance->alutech_at_4n_rainbow_table_file_name = NULL; return instance; } @@ -26,6 +27,7 @@ void subghz_environment_free(SubGhzEnvironment* instance) { instance->protocol_registry = NULL; instance->came_atomo_rainbow_table_file_name = NULL; instance->nice_flor_s_rainbow_table_file_name = NULL; + instance->alutech_at_4n_rainbow_table_file_name = NULL; subghz_keystore_free(instance->keystore); free(instance); From 5cd11deeb7be084361fb8b1f869901d9da0ea8c3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 10 Apr 2023 02:20:51 +0100 Subject: [PATCH 005/282] Fix release script artifacts --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db717f6f2..1c4c39f8f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,8 +69,8 @@ jobs: draft: false prerelease: false files: | - dist/${{ env.ARTIFACT_TGZ }} - dist/${{ env.ARTIFACT_ZIP }} + ${{ env.ARTIFACT_TGZ }} + ${{ env.ARTIFACT_ZIP }} name: "${{ env.VERSION_TAG }}" tag_name: "${{ env.VERSION_TAG }}" target_commitish: ${{ github.event.pull_request.base.ref }} From b9ccb274a77bf1f8aac56afbab7a3cc98fb6cf8f Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 10 Apr 2023 18:46:22 +0400 Subject: [PATCH 006/282] ufbt: project & debugging updates (#2572) * ufbt: removed warning in "channel=dev" update mode * ufbt: removed API version warning; added get_blackmagic & get_apiversion targets * ufbt: updater project template to include blackmagic & jlink targets * ufbt: project template: fixes & updates * ufbt: project template: added config update shortcut * sdk: using fixed names for file components --- .gitignore | 2 +- scripts/sconsdist.py | 6 +- scripts/ufbt/SConstruct | 22 +++++++ .../ufbt/project_template/.vscode/launch.json | 63 +++++++++---------- .../ufbt/project_template/.vscode/tasks.json | 36 +++++++---- .../app_template/application.fam | 2 +- scripts/ufbt/site_tools/ufbt_state.py | 6 -- site_scons/extapps.scons | 2 +- 8 files changed, 83 insertions(+), 56 deletions(-) diff --git a/.gitignore b/.gitignore index 81e985db7..89e129ace 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,7 @@ bindings/ Brewfile.lock.json # Visual Studio Code -.vscode/ +/.vscode/ # Kate .kateproject diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index 2e28ebef4..af2554d0a 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -181,9 +181,9 @@ class Main(App): ) as zf: for component_key in sdk_components_keys: component_path = self._dist_components.get(component_key) - components_paths[component_key] = basename(component_path) if component_key.endswith(".dir"): + components_paths[component_key] = basename(component_path) for root, dirnames, files in walk(component_path): if "__pycache__" in dirnames: dirnames.remove("__pycache__") @@ -199,7 +199,9 @@ class Main(App): ), ) else: - zf.write(component_path, basename(component_path)) + # We use fixed names for files to avoid having to regenerate VSCode project + components_paths[component_key] = component_key + zf.write(component_path, component_key) zf.writestr( "components.json", diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index a82189c14..7228e2f51 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -163,6 +163,18 @@ dist_env.Alias("flash", openocd_target) if env["FORCE"]: env.AlwaysBuild(openocd_target) + +firmware_jflash = dist_env.JFlash( + dist_env["UFBT_STATE_DIR"].File("jflash"), + dist_env["FW_BIN"], + JFLASHADDR="0x20000000", +) +dist_env.Alias("firmware_jflash", firmware_jflash) +dist_env.Alias("jflash", firmware_jflash) +if env["FORCE"]: + env.AlwaysBuild(firmware_jflash) + + firmware_debug = dist_env.PhonyTarget( "debug", "${GDBPYCOM}", @@ -391,3 +403,13 @@ AddPostAction( dist_env.Precious(app_template_dist) dist_env.NoClean(app_template_dist) dist_env.Alias("create", app_template_dist) + +dist_env.PhonyTarget( + "get_blackmagic", + "@echo $( ${BLACKMAGIC_ADDR} $)", +) + +dist_env.PhonyTarget( + "get_apiversion", + "@echo $( ${UFBT_API_VERSION} $)", +) diff --git a/scripts/ufbt/project_template/.vscode/launch.json b/scripts/ufbt/project_template/.vscode/launch.json index d9c98dcc1..697de9a49 100644 --- a/scripts/ufbt/project_template/.vscode/launch.json +++ b/scripts/ufbt/project_template/.vscode/launch.json @@ -2,19 +2,16 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "inputs": [ - // { - // "id": "BLACKMAGIC", - // "type": "command", - // "command": "shellCommand.execute", - // "args": { - // "useSingleResult": true, - // "env": { - // "PATH": "${workspaceFolder};${env:PATH}" - // }, - // "command": "./fbt get_blackmagic", - // "description": "Get Blackmagic device", - // } - // }, + { + "id": "BLACKMAGIC", + "type": "command", + "command": "shellCommand.execute", + "args": { + "description": "Get Blackmagic device", + "useSingleResult": true, + "command": "ufbt -s get_blackmagic", + } + }, ], "configurations": [ { @@ -57,26 +54,26 @@ ], // "showDevDebugOutput": "raw", }, - // { - // "name": "Attach FW (blackmagic)", - // "cwd": "${workspaceFolder}", - // "executable": "@UFBT_FIRMWARE_ELF@", - // "request": "attach", - // "type": "cortex-debug", - // "servertype": "external", - // "gdbTarget": "${input:BLACKMAGIC}", - // "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", - // "rtos": "FreeRTOS", - // "postAttachCommands": [ - // "monitor swdp_scan", - // "attach 1", - // "set confirm off", - // "set mem inaccessible-by-default off", - // "source @UFBT_DEBUG_DIR@/flipperapps.py", - // "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" - // ] - // // "showDevDebugOutput": "raw", - // }, + { + "name": "Attach FW (blackmagic)", + "cwd": "${workspaceFolder}", + "executable": "@UFBT_FIRMWARE_ELF@", + "request": "attach", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "${input:BLACKMAGIC}", + "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "postAttachCommands": [ + "monitor swdp_scan", + "attach 1", + "set confirm off", + "set mem inaccessible-by-default off", + "source @UFBT_DEBUG_DIR@/flipperapps.py", + "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" + ] + // "showDevDebugOutput": "raw", + }, { "name": "Attach FW (JLink)", "cwd": "${workspaceFolder}", diff --git a/scripts/ufbt/project_template/.vscode/tasks.json b/scripts/ufbt/project_template/.vscode/tasks.json index 6343bba7b..4b3f4bda5 100644 --- a/scripts/ufbt/project_template/.vscode/tasks.json +++ b/scripts/ufbt/project_template/.vscode/tasks.json @@ -20,24 +20,30 @@ "type": "shell", "command": "ufbt" }, + { + "label": "Clean", + "group": "build", + "type": "shell", + "command": "ufbt -c" + }, { "label": "Flash FW (ST-Link)", "group": "build", "type": "shell", "command": "ufbt FORCE=1 flash" }, - // { - // "label": "[NOTIMPL] Flash FW (blackmagic)", - // "group": "build", - // "type": "shell", - // "command": "ufbt flash_blackmagic" - // }, - // { - // "label": "[NOTIMPL] Flash FW (JLink)", - // "group": "build", - // "type": "shell", - // "command": "ufbt FORCE=1 jflash" - // }, + { + "label": "Flash FW (blackmagic)", + "group": "build", + "type": "shell", + "command": "ufbt flash_blackmagic" + }, + { + "label": "Flash FW (JLink)", + "group": "build", + "type": "shell", + "command": "ufbt FORCE=1 jflash" + }, { "label": "Flash FW (USB, with resources)", "group": "build", @@ -49,6 +55,12 @@ "group": "build", "type": "shell", "command": "ufbt update" + }, + { + "label": "Update VSCode config for current SDK", + "group": "build", + "type": "shell", + "command": "ufbt vscode_dist" } ] } \ No newline at end of file diff --git a/scripts/ufbt/project_template/app_template/application.fam b/scripts/ufbt/project_template/app_template/application.fam index 31fadb207..37a4ce665 100644 --- a/scripts/ufbt/project_template/app_template/application.fam +++ b/scripts/ufbt/project_template/app_template/application.fam @@ -6,7 +6,7 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="@FBT_APPID@_app", stack_size=2 * 1024, - fap_category="Misc", + fap_category="Examples", # Optional values # fap_version=(0, 1), # (major, minor) fap_icon="@FBT_APPID@.png", # 10x10 1-bit PNG diff --git a/scripts/ufbt/site_tools/ufbt_state.py b/scripts/ufbt/site_tools/ufbt_state.py index 6ba8c6962..76c6e9acf 100644 --- a/scripts/ufbt/site_tools/ufbt_state.py +++ b/scripts/ufbt/site_tools/ufbt_state.py @@ -75,12 +75,6 @@ def generate(env, **kw): if not sdk_state["meta"]["hw_target"].endswith(sdk_data["hardware"]): raise StopError("SDK state file doesn't match hardware target") - if sdk_state["meta"]["version"] != ufbt_state["version"]: - warn( - WarningOnByDefault, - f"Version mismatch: SDK state vs uFBT: {sdk_state['meta']['version']} vs {ufbt_state['version']}", - ) - scripts_dir = sdk_current_sdk_dir_node.Dir(sdk_components["scripts.dir"]) env.SetDefault( # Paths diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 798b85ea1..89ee49242 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -1,5 +1,4 @@ from dataclasses import dataclass, field -from os.path import dirname from SCons.Node import NodeList from SCons.Warnings import warn, WarningOnByDefault @@ -131,6 +130,7 @@ Depends(sdk_source, appenv.ProcessSdkDepends(f"{amalgamated_api}.d")) appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk_headers") sdk_header_tree = appenv.SDKHeaderTreeExtractor(appenv["SDK_DIR"], amalgamated_api) +Depends(sdk_header_tree, appenv["SDK_DEFINITION"]) # AlwaysBuild(sdk_tree) Alias("sdk_tree", sdk_header_tree) extapps.sdk_tree = sdk_header_tree From 8f752b7eee90320691cec6c5485dbe45776c803b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 10 Apr 2023 18:47:20 +0300 Subject: [PATCH 007/282] Revert changes due to Null pointer dereference --- applications/external/avr_isp_programmer/helpers/avr_isp.c | 1 - .../external/avr_isp_programmer/helpers/avr_isp_worker_rw.c | 5 ++--- .../external/avr_isp_programmer/lib/driver/avr_isp_prog.c | 1 - .../external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c | 2 ++ 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c index 51b4f8846..76e0a80b0 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.c @@ -152,7 +152,6 @@ bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { } } } - if(instance->spi) avr_isp_spi_sw_free(instance->spi); return false; } diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index 209551a47..f586e1645 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -198,10 +198,9 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { } avr_isp_end_pmode(instance->avr_isp); + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + } while(0); - - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); - if(instance->callback) { if(instance->chip_arr_ind > avr_isp_chip_arr_size) { instance->callback(instance->context, "No detect", instance->chip_detect, 0); diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c index b3c81f3b1..b457e4c27 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -317,7 +317,6 @@ static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { } } } - if(instance->spi) avr_isp_spi_sw_free(instance->spi); return false; } diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c index c6d9d54c8..f60850c84 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c @@ -18,6 +18,7 @@ struct AvrIspSpiSw { AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed) { AvrIspSpiSw* instance = malloc(sizeof(AvrIspSpiSw)); instance->speed_wait_time = speed; + instance->miso = AVR_ISP_SPI_SW_MISO; instance->mosi = AVR_ISP_SPI_SW_MOSI; instance->sck = AVR_ISP_SPI_SW_SCK; @@ -39,6 +40,7 @@ void avr_isp_spi_sw_free(AvrIspSpiSw* instance) { furi_hal_gpio_init(instance->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(instance->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(instance->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + free(instance); } From 7ac7b708840d29daf8f358f629119f61b859aaa0 Mon Sep 17 00:00:00 2001 From: gornekich Date: Mon, 10 Apr 2023 19:51:55 +0400 Subject: [PATCH 008/282] [FL-3241] NFC disable EMV support (#2571) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfc: remove read emv from extra actions * nfc: remove read emv Co-authored-by: あく --- applications/main/nfc/scenes/nfc_scene_read.c | 5 -- .../nfc/scenes/nfc_scene_read_card_type.c | 12 --- lib/nfc/nfc_device.h | 1 - lib/nfc/nfc_worker.c | 80 ------------------- lib/nfc/nfc_worker.h | 1 - lib/nfc/nfc_worker_i.h | 1 - 6 files changed, 100 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 4252883b2..938f2da67 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -85,11 +85,6 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; - } else if(event.event == NfcWorkerEventReadBankCard) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack); diff --git a/applications/main/nfc/scenes/nfc_scene_read_card_type.c b/applications/main/nfc/scenes/nfc_scene_read_card_type.c index 94262aa1e..8023026c3 100644 --- a/applications/main/nfc/scenes/nfc_scene_read_card_type.c +++ b/applications/main/nfc/scenes/nfc_scene_read_card_type.c @@ -5,7 +5,6 @@ enum SubmenuIndex { SubmenuIndexReadMifareClassic, SubmenuIndexReadMifareDesfire, SubmenuIndexReadMfUltralight, - SubmenuIndexReadEMV, SubmenuIndexReadNFCA, }; @@ -37,12 +36,6 @@ void nfc_scene_read_card_type_on_enter(void* context) { SubmenuIndexReadMfUltralight, nfc_scene_read_card_type_submenu_callback, nfc); - submenu_add_item( - submenu, - "Read EMV card", - SubmenuIndexReadEMV, - nfc_scene_read_card_type_submenu_callback, - nfc); submenu_add_item( submenu, "Read NFC-A data", @@ -75,11 +68,6 @@ bool nfc_scene_read_card_type_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); consumed = true; } - if(event.event == SubmenuIndexReadEMV) { - nfc->dev->dev_data.read_mode = NfcReadModeEMV; - scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); - consumed = true; - } if(event.event == SubmenuIndexReadNFCA) { nfc->dev->dev_data.read_mode = NfcReadModeNFCA; scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 8b2e6e5ba..df37ec3df 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -56,7 +56,6 @@ typedef enum { NfcReadModeMfClassic, NfcReadModeMfUltralight, NfcReadModeMfDesfire, - NfcReadModeEMV, NfcReadModeNFCA, } NfcReadMode; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index c2b89c71a..28a1f6827 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -229,69 +229,6 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont return read_success; } -static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { - bool read_success = false; - EmvApplication emv_app = {}; - EmvData* result = &nfc_worker->dev_data->emv_data; - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); - reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); - } - - // Bank cards require strong field to start application. If we find AID, try at least several - // times to start EMV application - uint8_t start_application_attempts = 0; - while(start_application_attempts < 3) { - if(nfc_worker->state != NfcWorkerStateRead) break; - start_application_attempts++; - if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; - if(emv_read_bank_card(tx_rx, &emv_app)) { - FURI_LOG_D(TAG, "Bank card number read from %d attempt", start_application_attempts); - break; - } else if(emv_app.aid_len && !emv_app.app_started) { - FURI_LOG_D( - TAG, - "AID found but failed to start EMV app from %d attempt", - start_application_attempts); - furi_hal_nfc_sleep(); - continue; - } else { - FURI_LOG_D(TAG, "Failed to find AID"); - break; - } - } - // Copy data - if(emv_app.aid_len) { - result->aid_len = emv_app.aid_len; - memcpy(result->aid, emv_app.aid, result->aid_len); - read_success = true; - } - if(emv_app.card_number_len) { - result->number_len = emv_app.card_number_len; - memcpy(result->number, emv_app.card_number, result->number_len); - } - if(emv_app.name_found) { - memcpy(result->name, emv_app.name, sizeof(emv_app.name)); - } - if(emv_app.exp_month) { - result->exp_mon = emv_app.exp_month; - result->exp_year = emv_app.exp_year; - } - if(emv_app.country_code) { - result->country_code = emv_app.country_code; - } - if(emv_app.currency_code) { - result->currency_code = emv_app.currency_code; - } - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - reader_analyzer_stop(nfc_worker->reader_analyzer); - } - - return read_success; -} - static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; @@ -315,14 +252,6 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; } card_read = true; - } else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { - FURI_LOG_I(TAG, "ISO14443-4 card detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; - if(!nfc_worker_read_bank_card(nfc_worker, tx_rx)) { - FURI_LOG_I(TAG, "Unknown card. Save UID"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; - } - card_read = true; } else { nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; card_read = true; @@ -358,9 +287,6 @@ void nfc_worker_read(NfcWorker* nfc_worker) { } else if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { event = NfcWorkerEventReadMfDesfire; break; - } else if(dev_data->protocol == NfcDeviceProtocolEMV) { - event = NfcWorkerEventReadBankCard; - break; } else if(dev_data->protocol == NfcDeviceProtocolUnknown) { event = NfcWorkerEventReadUidNfcA; break; @@ -444,12 +370,6 @@ void nfc_worker_read_type(NfcWorker* nfc_worker) { event = NfcWorkerEventReadMfDesfire; break; } - } else if(read_mode == NfcReadModeEMV) { - nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; - if(nfc_worker_read_bank_card(nfc_worker, &tx_rx)) { - event = NfcWorkerEventReadBankCard; - break; - } } else if(read_mode == NfcReadModeNFCA) { nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; event = NfcWorkerEventReadUidNfcA; diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index ce542828a..8e993fc6a 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -39,7 +39,6 @@ typedef enum { NfcWorkerEventReadMfClassicDone, NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, - NfcWorkerEventReadBankCard, // Nfc worker common events NfcWorkerEventSuccess, diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 9733426ab..701ecb90c 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -6,7 +6,6 @@ #include #include -#include #include #include #include From c2fa8a2b184e0cb86770c0b3092aa1833de3f37e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 10 Apr 2023 18:47:20 +0300 Subject: [PATCH 009/282] Revert "Revert changes due to Null pointer dereference" This reverts commit 8f752b7eee90320691cec6c5485dbe45776c803b. --- applications/external/avr_isp_programmer/helpers/avr_isp.c | 1 + .../external/avr_isp_programmer/helpers/avr_isp_worker_rw.c | 5 +++-- .../external/avr_isp_programmer/lib/driver/avr_isp_prog.c | 1 + .../external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c | 2 -- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c index 76e0a80b0..51b4f8846 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.c @@ -152,6 +152,7 @@ bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { } } } + if(instance->spi) avr_isp_spi_sw_free(instance->spi); return false; } diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index f586e1645..209551a47 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -198,9 +198,10 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { } avr_isp_end_pmode(instance->avr_isp); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); - } while(0); + + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + if(instance->callback) { if(instance->chip_arr_ind > avr_isp_chip_arr_size) { instance->callback(instance->context, "No detect", instance->chip_detect, 0); diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c index b457e4c27..b3c81f3b1 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -317,6 +317,7 @@ static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { } } } + if(instance->spi) avr_isp_spi_sw_free(instance->spi); return false; } diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c index f60850c84..c6d9d54c8 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c @@ -18,7 +18,6 @@ struct AvrIspSpiSw { AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed) { AvrIspSpiSw* instance = malloc(sizeof(AvrIspSpiSw)); instance->speed_wait_time = speed; - instance->miso = AVR_ISP_SPI_SW_MISO; instance->mosi = AVR_ISP_SPI_SW_MOSI; instance->sck = AVR_ISP_SPI_SW_SCK; @@ -40,7 +39,6 @@ void avr_isp_spi_sw_free(AvrIspSpiSw* instance) { furi_hal_gpio_init(instance->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(instance->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(instance->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - free(instance); } From 243fbfc19a0ebfde591e439696d2283875635e37 Mon Sep 17 00:00:00 2001 From: SkorP Date: Mon, 10 Apr 2023 20:39:33 +0400 Subject: [PATCH 010/282] AVR_ISP: fix NULL pointer dereference --- applications/external/avr_isp_programmer/helpers/avr_isp.c | 1 + .../external/avr_isp_programmer/lib/driver/avr_isp_prog.c | 1 + 2 files changed, 2 insertions(+) diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c index 51b4f8846..ab7c6f496 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.c @@ -153,6 +153,7 @@ bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { } } if(instance->spi) avr_isp_spi_sw_free(instance->spi); + instance->spi = NULL; return false; } diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c index b3c81f3b1..ad6489cf0 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -318,6 +318,7 @@ static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { } } if(instance->spi) avr_isp_spi_sw_free(instance->spi); + instance->spi = NULL; return false; } From 206eb7946bef5534af9c7d3f3a0f5120528ebc66 Mon Sep 17 00:00:00 2001 From: p0ns Date: Mon, 10 Apr 2023 20:37:57 -0300 Subject: [PATCH 011/282] Implements an array for baudrates --- applications/external/gps_nmea_uart/gps.c | 20 +++++++------------ .../external/gps_nmea_uart/gps_uart.c | 2 +- .../external/gps_nmea_uart/gps_uart.h | 6 +++--- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/applications/external/gps_nmea_uart/gps.c b/applications/external/gps_nmea_uart/gps.c index 0f4a7a1d5..a0c6ead03 100644 --- a/applications/external/gps_nmea_uart/gps.c +++ b/applications/external/gps_nmea_uart/gps.c @@ -139,20 +139,14 @@ int32_t gps_app(void* p) { switch(event.input.key) { case InputKeyUp: gps_uart_deinit_thread(gps_uart); - switch(gps_uart->baudrate) { - case GPS_BAUDRATE_9k: - gps_uart->baudrate = GPS_BAUDRATE_57k; - break; - case GPS_BAUDRATE_57k: - gps_uart->baudrate = GPS_BAUDRATE_115k; - break; - case GPS_BAUDRATE_115k: - gps_uart->baudrate = GPS_BAUDRATE_9k; - break; - - default: - break; + const int baudrate_length = + sizeof(gps_baudrates) / sizeof(gps_baudrates[0]); + current_gps_baudrate++; + if(current_gps_baudrate >= baudrate_length) { + current_gps_baudrate = 0; } + gps_uart->baudrate = gps_baudrates[current_gps_baudrate]; + gps_uart_init_thread(gps_uart); gps_uart->changing_baudrate = true; view_port_update(view_port); diff --git a/applications/external/gps_nmea_uart/gps_uart.c b/applications/external/gps_nmea_uart/gps_uart.c index 39538b74b..4e66aa284 100644 --- a/applications/external/gps_nmea_uart/gps_uart.c +++ b/applications/external/gps_nmea_uart/gps_uart.c @@ -169,7 +169,7 @@ GpsUart* gps_uart_enable() { gps_uart->notifications = furi_record_open(RECORD_NOTIFICATION); - gps_uart->baudrate = GPS_BAUDRATE_57k; + gps_uart->baudrate = gps_baudrates[current_gps_baudrate]; gps_uart_init_thread(gps_uart); diff --git a/applications/external/gps_nmea_uart/gps_uart.h b/applications/external/gps_nmea_uart/gps_uart.h index 5a42b9c58..152f4cd7f 100644 --- a/applications/external/gps_nmea_uart/gps_uart.h +++ b/applications/external/gps_nmea_uart/gps_uart.h @@ -3,11 +3,11 @@ #include #include -#define GPS_BAUDRATE_9k 9600 -#define GPS_BAUDRATE_57k 57600 -#define GPS_BAUDRATE_115k 115200 #define RX_BUF_SIZE 1024 +static const int gps_baudrates[5] = {9600, 19200, 38400, 57600, 115200}; +static int current_gps_baudrate = 3; + typedef struct { bool valid; float latitude; From 54e3941272ded31dfdb81fbcfbf4574c70682d05 Mon Sep 17 00:00:00 2001 From: Clara K Date: Tue, 11 Apr 2023 17:56:33 +0200 Subject: [PATCH 012/282] correctly crediting nano for his work, not a clown that stole code --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index c9773fdbc..87e6df32e 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -123,7 +123,7 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t - Custom subghz presets - Multiple NFC protocols - Multiple Sub-Ghz protocols | Merged from Unleashed, thanks @xMasterX -- Subghz and IR signal replication via gpio | Credits to @ankris812 +- Subghz and IR signal replication via gpio | Credits to @xMasterX - New API Routes for Locale settings ``` From 33e8bae78bc19821d662edd131a956580ac8bdb2 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 12 Apr 2023 10:07:05 +0400 Subject: [PATCH 013/282] Bugfix: ISP Programmer and SubGhz (#2574) * AVR_ISP: fix NULL pointer dereference * SubGhz: double back with a blocked transmission in this region * SubGhz: fix speaker, when a transmission is blocked in this region * SubGhz: fix speaker * SubGhz: return region * AVR Flasher: cleanup code Co-authored-by: Aleksandr Kutuzov --- applications/external/avr_isp_programmer/helpers/avr_isp.c | 7 ++++++- .../external/avr_isp_programmer/lib/driver/avr_isp_prog.c | 7 ++++++- applications/main/subghz/scenes/subghz_scene_read_raw.c | 6 +++++- applications/main/subghz/scenes/subghz_scene_transmitter.c | 4 +--- applications/main/subghz/subghz_i.c | 7 +++++-- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c index 51b4f8846..283c17bfd 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.c @@ -152,7 +152,12 @@ bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { } } } - if(instance->spi) avr_isp_spi_sw_free(instance->spi); + + if(instance->spi) { + avr_isp_spi_sw_free(instance->spi); + instance->spi = NULL; + } + return false; } diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c index b3c81f3b1..bbb6d4739 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -317,7 +317,12 @@ static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { } } } - if(instance->spi) avr_isp_spi_sw_free(instance->spi); + + if(instance->spi) { + avr_isp_spi_sw_free(instance->spi); + instance->spi = NULL; + } + return false; } diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 96acc90ee..09440b32b 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -230,7 +230,11 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + subghz_read_raw_set_status( + subghz->subghz_read_raw, + SubGhzReadRAWStatusIDLE, + "", + subghz->txrx->raw_threshold_rssi); } else { if(scene_manager_has_previous_scene( subghz->scene_manager, SubGhzSceneSaved) || diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index c8663cc8e..712e50071 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -70,9 +70,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { } if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { - if(!subghz_tx_start(subghz, subghz->txrx->fff_data)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); - } else { + if(subghz_tx_start(subghz, subghz->txrx->fff_data)) { subghz->state_notifications = SubGhzNotificationStateTx; subghz_scene_transmitter_update_data_show(subghz); DOLPHIN_DEED(DolphinDeedSubGhzSend); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index 94713ddba..18d87c76b 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -105,9 +105,11 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_gpio_write(&gpio_cc1101_g0, false); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - subghz_speaker_on(subghz); bool ret = furi_hal_subghz_tx(); - subghz->txrx->txrx_state = SubGhzTxRxStateTx; + if(ret) { + subghz_speaker_on(subghz); + subghz->txrx->txrx_state = SubGhzTxRxStateTx; + } return ret; } @@ -115,6 +117,7 @@ void subghz_idle(SubGhz* subghz) { furi_assert(subghz); furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); + subghz_speaker_off(subghz); subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; } From 5d7bdca835e83c6925d7802774ac1ffe03766368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 12 Apr 2023 19:45:13 +0800 Subject: [PATCH 014/282] FuriHal: pwr pulls for some pins (#2579) --- firmware/targets/f18/furi_hal/furi_hal_resources.c | 5 +++-- firmware/targets/f7/furi_hal/furi_hal_resources.c | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index 19bc9f998..4a7015d13 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -166,8 +166,9 @@ void furi_hal_resources_init() { furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); // Explicit pulls pins - furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullDown, GpioSpeedLow); - furi_hal_gpio_init(&gpio_vibro, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + LL_PWR_EnablePUPDCfg(); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_8); // gpio_speaker + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_A, LL_PWR_GPIO_BIT_8); // gpio_vibro // Display pins furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index df9530079..912912b4a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -153,10 +153,11 @@ void furi_hal_resources_init() { // Button pins furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); - // Explicit pulls pins - furi_hal_gpio_init(&gpio_infrared_tx, GpioModeAnalog, GpioPullDown, GpioSpeedLow); - furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullDown, GpioSpeedLow); - furi_hal_gpio_init(&gpio_vibro, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + // Explicit, surviving reset, pulls + LL_PWR_EnablePUPDCfg(); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_9); // gpio_infrared_tx + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_8); // gpio_speaker + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_A, LL_PWR_GPIO_BIT_8); // gpio_vibro // Display pins furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); From 39186004cc2e988388f84d13ed07a93500b284ae Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 12 Apr 2023 19:42:24 +0300 Subject: [PATCH 015/282] Update UART terminal https://github.com/cool4uma/UART_Terminal --- .../scenes/uart_terminal_scene_console_output.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c index 38a5a20e4..7882a900a 100644 --- a/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c +++ b/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c @@ -113,14 +113,10 @@ void uart_terminal_scene_console_output_on_enter(void* context) { // Send command with CR+LF or newline '\n' if(app->is_command && app->selected_tx_string) { - if(app->TERMINAL_MODE == 1) { - // char buffer[240]; - // snprintf(buffer, 240, "%s\r\n", (app->selected_tx_string)); - // uart_terminal_uart_tx((unsigned char *)buffer, strlen(buffer)); + if(app->TERMINAL_MODE == 1){ uart_terminal_uart_tx( (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); - uart_terminal_uart_tx((uint8_t*)("\r"), 1); - uart_terminal_uart_tx((uint8_t*)("\n"), 1); + uart_terminal_uart_tx((uint8_t*)("\r\n"), 2); } else { uart_terminal_uart_tx( (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); @@ -149,9 +145,4 @@ void uart_terminal_scene_console_output_on_exit(void* context) { // Unregister rx callback uart_terminal_uart_set_handle_rx_data_cb(app->uart, NULL); - - // Automatically logut when exiting view - //if(app->is_command) { - // uart_terminal_uart_tx((uint8_t*)("exit\n"), strlen("exit\n")); - //} } \ No newline at end of file From 1609f3c500e444cab894f14d8b713b253daa1654 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 13 Apr 2023 02:14:03 +0100 Subject: [PATCH 016/282] Fix critical boot modes (dfu / recovery) --- applications/services/bt/bt_service/bt.c | 2 +- applications/services/cli/cli.c | 2 +- applications/services/desktop/desktop.c | 2 +- applications/services/dolphin/dolphin.c | 2 +- applications/services/namechangersrv/namechangersrv.c | 2 +- applications/services/power/power_service/power.c | 2 +- applications/services/rgb_backlight/rgb_backlight.c | 3 +-- applications/services/xtreme/assets.c | 2 +- applications/services/xtreme/settings.c | 5 ++--- firmware/targets/f18/furi_hal/furi_hal.c | 7 +++++++ firmware/targets/f7/api_symbols.csv | 1 + firmware/targets/f7/furi_hal/furi_hal.c | 7 +++++++ firmware/targets/furi_hal_include/furi_hal.h | 3 +++ 13 files changed, 28 insertions(+), 12 deletions(-) diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 9cb9f1b24..ddf3b1544 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -469,7 +469,7 @@ int32_t bt_srv(void* p) { UNUSED(p); Bt* bt = bt_alloc(); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT); furi_record_create(RECORD_BT, bt); diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index ad3bbd665..a6aaa8429 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -460,7 +460,7 @@ int32_t cli_srv(void* p) { furi_thread_set_stdout_callback(NULL); } - if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { + if(furi_hal_is_normal_boot()) { cli_session_open(cli, &cli_vcp); } else { FURI_LOG_W(TAG, "Skipping start in special boot mode"); diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index a578ecfcb..54760e970 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -304,7 +304,7 @@ static bool desktop_check_file_flag(const char* flag_path) { int32_t desktop_srv(void* p) { UNUSED(p); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); return 0; } diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index cf37c5cd9..df34abe78 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -161,7 +161,7 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin) { int32_t dolphin_srv(void* p) { UNUSED(p); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); return 0; } diff --git a/applications/services/namechangersrv/namechangersrv.c b/applications/services/namechangersrv/namechangersrv.c index fe25906ae..33aeb3fdd 100644 --- a/applications/services/namechangersrv/namechangersrv.c +++ b/applications/services/namechangersrv/namechangersrv.c @@ -4,7 +4,7 @@ #include void namechanger_on_system_start() { - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "NameChangerSRV load skipped. Device is in special startup mode."); } else { Storage* storage = furi_record_open(RECORD_STORAGE); diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 7c1b9be58..4c4e1404e 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -515,7 +515,7 @@ static void power_check_battery_level_change(Power* power) { int32_t power_srv(void* p) { UNUSED(p); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "Skipping start in special boot mode"); return 0; } diff --git a/applications/services/rgb_backlight/rgb_backlight.c b/applications/services/rgb_backlight/rgb_backlight.c index d7ab89975..2dbfa7e8c 100644 --- a/applications/services/rgb_backlight/rgb_backlight.c +++ b/applications/services/rgb_backlight/rgb_backlight.c @@ -63,8 +63,7 @@ const char* rgb_backlight_get_color_text(uint8_t index) { void rgb_backlight_load_settings(void) { //Не загружать данные из внутренней памяти при загрузке в режиме DFU - FuriHalRtcBootMode bm = furi_hal_rtc_get_boot_mode(); - if(bm == FuriHalRtcBootModeDfu) { + if(!furi_hal_is_normal_boot()) { rgb_settings.settings_is_loaded = true; return; } diff --git a/applications/services/xtreme/assets.c b/applications/services/xtreme/assets.c index 2e4e0bda9..574e932ce 100644 --- a/applications/services/xtreme/assets.c +++ b/applications/services/xtreme/assets.c @@ -143,7 +143,7 @@ void XTREME_ASSETS_LOAD() { xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; xtreme_assets->I_Error_62x31 = &I_Error_62x31; - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "Load skipped. Device is in special startup mode."); return; } diff --git a/applications/services/xtreme/settings.c b/applications/services/xtreme/settings.c index 799c1a768..a5b7ee6cb 100644 --- a/applications/services/xtreme/settings.c +++ b/applications/services/xtreme/settings.c @@ -15,9 +15,8 @@ void XTREME_SETTINGS_LOAD() { if(xtreme_settings == NULL) { xtreme_settings = malloc(sizeof(XtremeSettings)); bool loaded = false; - bool skip = furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal; - if(skip) { + if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "Load skipped. Device is in special startup mode."); loaded = false; } else { @@ -74,7 +73,7 @@ bool XTREME_SETTINGS_SAVE() { XTREME_SETTINGS_LOAD(); } - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + if(!furi_hal_is_normal_boot()) { return true; } diff --git a/firmware/targets/f18/furi_hal/furi_hal.c b/firmware/targets/f18/furi_hal/furi_hal.c index 4064dd647..9b234143c 100644 --- a/firmware/targets/f18/furi_hal/furi_hal.c +++ b/firmware/targets/f18/furi_hal/furi_hal.c @@ -6,6 +6,12 @@ #define TAG "FuriHal" +bool normal_boot = false; + +bool furi_hal_is_normal_boot() { + return normal_boot; +} + void furi_hal_init_early() { furi_hal_cortex_init_early(); furi_hal_clock_init_early(); @@ -26,6 +32,7 @@ void furi_hal_deinit_early() { } void furi_hal_init() { + normal_boot = true; furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_console_init(); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 66536e574..be5934b82 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1261,6 +1261,7 @@ Function,-,furi_hal_init_early,void, Function,-,furi_hal_interrupt_init,void, Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,-,furi_hal_is_normal_boot,_Bool, Function,+,furi_hal_light_blink_set_color,void,Light Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" Function,+,furi_hal_light_blink_stop,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index c9b4c8088..01ec37bc6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -6,6 +6,12 @@ #define TAG "FuriHal" +bool normal_boot = false; + +bool furi_hal_is_normal_boot() { + return normal_boot; +} + void furi_hal_init_early() { furi_hal_cortex_init_early(); furi_hal_clock_init_early(); @@ -26,6 +32,7 @@ void furi_hal_deinit_early() { } void furi_hal_init() { + normal_boot = true; furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_console_init(); diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index 2eb4688d4..7d6dc7f24 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -42,6 +42,9 @@ struct STOP_EXTERNING_ME {}; extern "C" { #endif +/** True if normally with all subsystems */ +bool furi_hal_is_normal_boot(); + /** Early FuriHal init, only essential subsystems */ void furi_hal_init_early(); From f948ab33f19ec53cddbab1f1e58b6ce9dccb7390 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 13 Apr 2023 03:07:07 +0100 Subject: [PATCH 017/282] Fix resetting PIN code with key combo --- applications/services/desktop/desktop.c | 8 ++++++++ firmware/targets/f7/src/recovery.c | 1 + firmware/targets/furi_hal_include/furi_hal_rtc.h | 1 + 3 files changed, 10 insertions(+) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 54760e970..89e4f6105 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -309,6 +309,14 @@ int32_t desktop_srv(void* p) { return 0; } + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagResetPin)) { + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_remove(storage, DESKTOP_SETTINGS_PATH); + storage_common_remove(storage, DESKTOP_SETTINGS_OLD_PATH); + furi_record_close(RECORD_STORAGE); + furi_hal_rtc_reset_flag(FuriHalRtcFlagResetPin); + } + Desktop* desktop = desktop_alloc(); bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings); diff --git a/firmware/targets/f7/src/recovery.c b/firmware/targets/f7/src/recovery.c index 49d780d47..700fb03c6 100644 --- a/firmware/targets/f7/src/recovery.c +++ b/firmware/targets/f7/src/recovery.c @@ -56,6 +56,7 @@ void flipper_boot_recovery_exec() { } if(!counter) { + furi_hal_rtc_set_flag(FuriHalRtcFlagResetPin); furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); furi_hal_rtc_set_pin_fails(0); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index b16b04a68..96acb5848 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -30,6 +30,7 @@ typedef enum { FuriHalRtcFlagLock = (1 << 2), FuriHalRtcFlagC2Update = (1 << 3), FuriHalRtcFlagHandOrient = (1 << 4), + FuriHalRtcFlagResetPin = (1 << 5), } FuriHalRtcFlag; typedef enum { From a163f5b0cffc6a825cb95e2fab5ac2b514c5bd0d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 13 Apr 2023 03:56:25 +0100 Subject: [PATCH 018/282] Fix BT settings inconsistency with lock menu --- applications/settings/bt_settings_app/bt_settings_app.c | 4 ++-- applications/settings/bt_settings_app/bt_settings_app.h | 2 +- .../bt_settings_app/scenes/bt_settings_scene_start.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/applications/settings/bt_settings_app/bt_settings_app.c b/applications/settings/bt_settings_app/bt_settings_app.c index f211c7128..71d0495e3 100644 --- a/applications/settings/bt_settings_app/bt_settings_app.c +++ b/applications/settings/bt_settings_app/bt_settings_app.c @@ -16,9 +16,9 @@ BtSettingsApp* bt_settings_app_alloc() { BtSettingsApp* app = malloc(sizeof(BtSettingsApp)); // Load settings - bt_settings_load(&app->settings); app->gui = furi_record_open(RECORD_GUI); app->bt = furi_record_open(RECORD_BT); + bt_settings_load(&app->bt->bt_settings); // View Dispatcher and Scene Manager app->view_dispatcher = view_dispatcher_alloc(); @@ -79,7 +79,7 @@ extern int32_t bt_settings_app(void* p) { UNUSED(p); BtSettingsApp* app = bt_settings_app_alloc(); view_dispatcher_run(app->view_dispatcher); - bt_settings_save(&app->settings); + bt_settings_save(&app->bt->bt_settings); bt_settings_app_free(app); return 0; } diff --git a/applications/settings/bt_settings_app/bt_settings_app.h b/applications/settings/bt_settings_app/bt_settings_app.h index b79e36951..4dc20b020 100644 --- a/applications/settings/bt_settings_app/bt_settings_app.h +++ b/applications/settings/bt_settings_app/bt_settings_app.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -24,7 +25,6 @@ enum BtSettingsCustomEvent { }; typedef struct { - BtSettings settings; Bt* bt; Gui* gui; SceneManager* scene_manager; diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c index 5db98e9de..696e5c8d0 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c @@ -46,7 +46,7 @@ void bt_settings_scene_start_on_enter(void* context) { BtSettingNum, bt_settings_scene_start_var_list_change_callback, app); - if(app->settings.enabled) { + if(app->bt->bt_settings.enabled) { variable_item_set_current_value_index(item, BtSettingOn); variable_item_set_current_value_text(item, bt_settings_text[BtSettingOn]); } else { @@ -71,10 +71,10 @@ bool bt_settings_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == BtSettingOn) { furi_hal_bt_start_advertising(); - app->settings.enabled = true; + app->bt->bt_settings.enabled = true; consumed = true; } else if(event.event == BtSettingOff) { - app->settings.enabled = false; + app->bt->bt_settings.enabled = false; furi_hal_bt_stop_advertising(); consumed = true; } else if(event.event == BtSettingsCustomEventForgetDevices) { From a6587f9b9cdc29cf86ca8ec8e0da9f6fdd421227 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 13 Apr 2023 04:27:05 +0100 Subject: [PATCH 019/282] Add 5V GPIO pin option in infrared settings (#185) --- applications/main/infrared/infrared.c | 2 ++ .../scenes/infrared_scene_debug_settings.c | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index a88306cc5..b8195941b 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -478,6 +478,8 @@ int32_t infrared_app(void* p) { view_dispatcher_run(infrared->view_dispatcher); + furi_hal_power_disable_otg(); + infrared_free(infrared); return 0; } diff --git a/applications/main/infrared/scenes/infrared_scene_debug_settings.c b/applications/main/infrared/scenes/infrared_scene_debug_settings.c index 0bc830788..28ec7dfdc 100644 --- a/applications/main/infrared/scenes/infrared_scene_debug_settings.c +++ b/applications/main/infrared/scenes/infrared_scene_debug_settings.c @@ -18,6 +18,20 @@ static void infrared_scene_debug_settings_changed(VariableItem* item) { furi_hal_infrared_set_debug_out(value_index_ir); } + +static void infrared_scene_debug_settings_power_changed(VariableItem* item) { + bool value = variable_item_get_current_value_index(item); + if(value) { + for(int i = 0; i < 5 && !furi_hal_power_is_otg_enabled(); i++) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + } else { + furi_hal_power_disable_otg(); + } + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); +} + static void infrared_debug_settings_start_var_list_enter_callback(void* context, uint32_t index) { Infrared* infrared = context; view_dispatcher_send_custom_event(infrared->view_dispatcher, index); @@ -42,6 +56,16 @@ void infrared_scene_debug_settings_on_enter(void* context) { variable_item_set_current_value_index(item, value_index_ir); variable_item_set_current_value_text(item, infrared_debug_cfg_variables_text[value_index_ir]); + item = variable_item_list_add( + variable_item_list, + "Ext Module 5v", + 2, + infrared_scene_debug_settings_power_changed, + infrared); + bool enabled = furi_hal_power_is_otg_enabled(); + variable_item_set_current_value_index(item, enabled); + variable_item_set_current_value_text(item, enabled ? "ON" : "OFF"); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewVariableItemList); } From b84adc9d96f45fdf95c20b27f4f68369cf67930f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 13 Apr 2023 04:47:05 +0100 Subject: [PATCH 020/282] Double PIN timeout after each wrong PIN --- .../services/desktop/helpers/pin_lock.c | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/applications/services/desktop/helpers/pin_lock.c b/applications/services/desktop/helpers/pin_lock.c index 22fcabe7d..decd98320 100644 --- a/applications/services/desktop/helpers/pin_lock.c +++ b/applications/services/desktop/helpers/pin_lock.c @@ -30,20 +30,6 @@ static const NotificationSequence sequence_pin_fail = { NULL, }; -static const uint8_t desktop_helpers_fails_timeout[] = { - 0, - 0, - 0, - 0, - 30, - 60, - 90, - 120, - 150, - 180, - /* +60 for every next fail */ -}; - void desktop_pin_lock_error_notify() { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_pin_fail); @@ -52,15 +38,10 @@ void desktop_pin_lock_error_notify() { uint32_t desktop_pin_lock_get_fail_timeout() { uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); - uint32_t pin_timeout = 0; - uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1; - if(pin_fails <= max_index) { - pin_timeout = desktop_helpers_fails_timeout[pin_fails]; - } else { - pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60; + if(pin_fails < 3) { + return 0; } - - return pin_timeout; + return 30 * pow(2, pin_fails - 3); } void desktop_pin_lock(DesktopSettings* settings) { From cab614ba4e052d6a46922e4bbfb7ef283bea58a3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 13 Apr 2023 05:27:53 +0100 Subject: [PATCH 021/282] U2F keys on internal storage --- applications/main/u2f/u2f_app.c | 6 ++++++ applications/main/u2f/u2f_data.c | 6 ------ applications/main/u2f/u2f_data.h | 8 ++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/applications/main/u2f/u2f_app.c b/applications/main/u2f/u2f_app.c index 216481976..60500152a 100644 --- a/applications/main/u2f/u2f_app.c +++ b/applications/main/u2f/u2f_app.c @@ -2,6 +2,7 @@ #include "u2f_data.h" #include #include +#include static bool u2f_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -27,6 +28,11 @@ U2fApp* u2f_app_alloc() { app->gui = furi_record_open(RECORD_GUI); app->notifications = furi_record_open(RECORD_NOTIFICATION); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_copy(storage, U2F_CNT_OLD_FILE, U2F_CNT_FILE); + storage_common_copy(storage, U2F_KEY_OLD_FILE, U2F_KEY_FILE); + furi_record_close(RECORD_STORAGE); + app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&u2f_scene_handlers, app); view_dispatcher_enable_queue(app->view_dispatcher); diff --git a/applications/main/u2f/u2f_data.c b/applications/main/u2f/u2f_data.c index 217733c94..34f037aed 100644 --- a/applications/main/u2f/u2f_data.c +++ b/applications/main/u2f/u2f_data.c @@ -7,12 +7,6 @@ #define TAG "U2F" -#define U2F_DATA_FOLDER ANY_PATH("u2f/") -#define U2F_CERT_FILE U2F_DATA_FOLDER "assets/cert.der" -#define U2F_CERT_KEY_FILE U2F_DATA_FOLDER "assets/cert_key.u2f" -#define U2F_KEY_FILE U2F_DATA_FOLDER "key.u2f" -#define U2F_CNT_FILE U2F_DATA_FOLDER "cnt.u2f" - #define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY 2 #define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE 11 diff --git a/applications/main/u2f/u2f_data.h b/applications/main/u2f/u2f_data.h index 8d3923464..f3c62f89d 100644 --- a/applications/main/u2f/u2f_data.h +++ b/applications/main/u2f/u2f_data.h @@ -6,6 +6,14 @@ extern "C" { #include +#define U2F_DATA_FOLDER EXT_PATH("u2f/") +#define U2F_CERT_FILE U2F_DATA_FOLDER "assets/cert.der" +#define U2F_CERT_KEY_FILE U2F_DATA_FOLDER "assets/cert_key.u2f" +#define U2F_KEY_OLD_FILE U2F_DATA_FOLDER "key.u2f" +#define U2F_CNT_OLD_FILE U2F_DATA_FOLDER "cnt.u2f" +#define U2F_KEY_FILE INT_PATH(".key.u2f") +#define U2F_CNT_FILE INT_PATH(".cnt.u2f") + bool u2f_data_check(bool cert_only); bool u2f_data_cert_check(); From 37fb330b362dba3b01b7c6ca6a200564211dc1b7 Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Thu, 13 Apr 2023 17:47:38 +0300 Subject: [PATCH 022/282] [FL-3226] Deep Sleep Idle (#2569) * Improve RNG error handling * Sync RTC shadow registers on Stop mode exit * Implement working STOP2 mode * Fix formatting * FuriHal: disable SWD pins if debug is disabled * Power: cleanup battery info view, handle zero current report from gauge * Fbt: add command line argument for extra global defines * FuriHal: cleanup debug defines in power and os, drop deep_insomnia counter. * Add a setting to disable deep sleep * Clean up furi_hal_power * FuriHal,FapLoader,Debug: implement debug in stop mode, workaround resume in stop * FuriHal: document OS and power subsystems debugging * Furi: enable debug interface on crash --------- Co-authored-by: Aleksandr Kutuzov --- applications/main/fap_loader/fap_loader_app.c | 5 +- .../power_settings_app/views/battery_info.c | 51 +++++++------- .../settings/system/system_settings.c | 21 ++++++ debug/flipperapps.py | 3 +- documentation/FuriHalDebuging.md | 26 +++++++ documentation/fbt.md | 1 + firmware/targets/f18/api_symbols.csv | 13 ++-- .../targets/f18/furi_hal/furi_hal_resources.c | 3 + .../targets/f18/furi_hal/furi_hal_resources.h | 3 + firmware/targets/f7/api_symbols.csv | 13 ++-- firmware/targets/f7/ble_glue/ble_glue.c | 6 -- firmware/targets/f7/furi_hal/furi_hal_clock.c | 9 ++- firmware/targets/f7/furi_hal/furi_hal_debug.c | 21 ++++++ firmware/targets/f7/furi_hal/furi_hal_os.c | 30 ++++++-- firmware/targets/f7/furi_hal/furi_hal_power.c | 69 +++++++++---------- .../targets/f7/furi_hal/furi_hal_random.c | 42 ++++++----- .../targets/f7/furi_hal/furi_hal_resources.c | 3 + .../targets/f7/furi_hal/furi_hal_resources.h | 3 + firmware/targets/f7/furi_hal/furi_hal_rtc.c | 15 ++-- .../targets/furi_hal_include/furi_hal_debug.h | 3 + .../targets/furi_hal_include/furi_hal_power.h | 6 -- .../targets/furi_hal_include/furi_hal_rtc.h | 4 ++ furi/core/check.c | 3 + furi/core/core_defines.h | 4 ++ site_scons/cc.scons | 1 + site_scons/commandline.scons | 8 +++ 26 files changed, 246 insertions(+), 120 deletions(-) create mode 100644 documentation/FuriHalDebuging.md diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index f5c7af024..7af5244ae 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -1,6 +1,7 @@ #include "fap_loader_app.h" #include +#include #include #include @@ -23,8 +24,6 @@ struct FapLoader { Loading* loading; }; -volatile bool fap_loader_debug_active = false; - bool fap_loader_load_name_and_icon( FuriString* path, Storage* storage, @@ -111,7 +110,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) { FuriThread* thread = flipper_application_spawn(loader->app, NULL); /* This flag is set by the debugger - to break on app start */ - if(fap_loader_debug_active) { + if(furi_hal_debug_is_gdb_session_active()) { FURI_LOG_W(TAG, "Triggering BP for debugger"); /* After hitting this, you can set breakpoints in your .fap's code * Note that you have to toggle breakpoints that were set before */ diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index 7394fd3c5..0956cae4f 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -4,8 +4,8 @@ #include #include -#define LOW_CHARGE_THRESHOLD 10 -#define HIGH_DRAIN_CURRENT_THRESHOLD 100 +#define LOW_CHARGE_THRESHOLD (10) +#define HIGH_DRAIN_CURRENT_THRESHOLD (-100) struct BatteryInfo { View* view; @@ -25,14 +25,13 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { char header[20] = {}; char value[20] = {}; - int32_t drain_current = data->gauge_current * (-1000); - uint32_t charge_current = data->gauge_current * 1000; + int32_t current = 1000.0f * data->gauge_current; // Draw battery canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); - if(charge_current > 0) { + if(current > 0) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); - } else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) { + } else if(current < HIGH_DRAIN_CURRENT_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); } else if(data->charge < LOW_CHARGE_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); @@ -44,7 +43,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { elements_bubble(canvas, 53, 0, 71, 39); // Set text - if(charge_current > 0) { + if(current > 0) { snprintf(emote, sizeof(emote), "%s", "Yummy!"); snprintf(header, sizeof(header), "%s", "Charging at"); snprintf( @@ -53,34 +52,36 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { "%lu.%luV %lumA", (uint32_t)(data->vbus_voltage), (uint32_t)(data->vbus_voltage * 10) % 10, - charge_current); - } else if(drain_current > 0) { + current); + } else if(current < 0) { snprintf( emote, sizeof(emote), "%s", - drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); + current < HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); snprintf(header, sizeof(header), "%s", "Consumption is"); snprintf( value, sizeof(value), "%ld %s", - drain_current, - drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); - } else if(drain_current != 0) { - snprintf(header, 20, "..."); - } else if(data->charge_voltage_limit < 4.2) { - // Non-default battery charging limit, mention it - snprintf(emote, sizeof(emote), "Charged!"); - snprintf(header, sizeof(header), "Limited to"); - snprintf( - value, - sizeof(value), - "%lu.%luV", - (uint32_t)(data->charge_voltage_limit), - (uint32_t)(data->charge_voltage_limit * 10) % 10); + ABS(current), + current < HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); + } else if(data->vbus_voltage > 0) { + if(data->charge_voltage_limit < 4.2) { + // Non-default battery charging limit, mention it + snprintf(emote, sizeof(emote), "Charged!"); + snprintf(header, sizeof(header), "Limited to"); + snprintf( + value, + sizeof(value), + "%lu.%luV", + (uint32_t)(data->charge_voltage_limit), + (uint32_t)(data->charge_voltage_limit * 10) % 10); + } else { + snprintf(header, sizeof(header), "Charged!"); + } } else { - snprintf(header, sizeof(header), "Charged!"); + snprintf(header, sizeof(header), "Napping..."); } canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index cb74d7a83..597710a53 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -141,6 +141,21 @@ static void hand_orient_changed(VariableItem* item) { loader_update_menu(); } +const char* const sleep_method[] = { + "Default", + "Legacy", +}; + +static void sleep_method_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, sleep_method[index]); + if(index) { + furi_hal_rtc_set_flag(FuriHalRtcFlagLegacySleep); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagLegacySleep); + } +} + static uint32_t system_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; @@ -218,6 +233,12 @@ SystemSettings* system_settings_alloc() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, heap_trace_mode_text[value_index]); + item = variable_item_list_add( + app->var_item_list, "Sleep Method", COUNT_OF(sleep_method), sleep_method_changed, app); + value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) ? 1 : 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, sleep_method[value_index]); + view_set_previous_callback( variable_item_list_get_view(app->var_item_list), system_settings_exit); view_dispatcher_add_view( diff --git a/debug/flipperapps.py b/debug/flipperapps.py index 1dc5ebd04..90582c1e4 100644 --- a/debug/flipperapps.py +++ b/debug/flipperapps.py @@ -135,6 +135,7 @@ class FlipperAppStateHelper: self.app_list_ptr = None self.app_list_entry_type = None self._current_apps: list[AppState] = [] + self.set_debug_mode(True) def _walk_app_list(self, list_head): while list_head: @@ -195,7 +196,7 @@ class FlipperAppStateHelper: self.set_debug_mode(False) def set_debug_mode(self, mode: bool) -> None: - gdb.execute(f"set variable fap_loader_debug_active = {int(mode)}") + gdb.execute(f"set variable furi_hal_debug_gdb_session_active = {int(mode)}") # Init additional 'fap-set-debug-elf-root' command and set up hooks diff --git a/documentation/FuriHalDebuging.md b/documentation/FuriHalDebuging.md new file mode 100644 index 000000000..8ff770163 --- /dev/null +++ b/documentation/FuriHalDebuging.md @@ -0,0 +1,26 @@ +# Furi HAL Debugging + +Some Furi subsystem got additional debugging features that can be enabled by adding additional defines to firmware compilation. +Usually they are used for low level tracing and profiling or signal redirection/duplication. + + +## FuriHalOs + +`--extra-define=FURI_HAL_OS_DEBUG` enables tick, tick suppression, idle and time flow. + +There are 3 signals that will be exposed to external GPIO pins: + +- `AWAKE` - `PA7` - High when system is busy with computations, low when sleeping. Can be used to track transitions to sleep mode. +- `TICK` - `PA6` - Flipped on system tick, only flips when no tick suppression in progress. Can be used to track tick skew and abnormal task scheduling. +- `SECOND` - `PA4` - Flipped each second. Can be used for tracing RT issue: time flow disturbance means system doesn't conforms Hard RT. + + + +## FuriHalPower + +`--extra-define=FURI_HAL_POWER_DEBUG` enables power subsystem mode transitions tracing. + +There are 2 signals that will be exposed to external GPIO pins: + +- `WFI` - `PB2` - Light sleep (wait for interrupt) used. Basically this is lightest and most non-breaking things power save mode. All function and debug should work correctly in this mode. +- `STOP` - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible. \ No newline at end of file diff --git a/documentation/fbt.md b/documentation/fbt.md index 14d63e9ce..23b2e2b55 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -105,6 +105,7 @@ To run cleanup (think of `make clean`) for specified targets, add the `-c` optio - `--options optionfile.py` (default value `fbt_options.py`) - load a file with multiple configuration values - `--extra-int-apps=app1,app2,appN` - force listed apps to be built as internal with the `firmware` target - `--extra-ext-apps=app1,app2,appN` - force listed apps to be built as external with the `firmware_extapps` target +- `--extra-define=A --extra-define=B=C ` - extra global defines that will be passed to the C/C++ compiler, can be specified multiple times - `--proxy-env=VAR1,VAR2` - additional environment variables to expose to subprocesses spawned by `fbt`. By default, `fbt` sanitizes the execution environment and doesn't forward all inherited environment variables. You can find the list of variables that are always forwarded in the `environ.scons` file. ## Configuration diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 5d5b7bbcf..493f59634 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,21.0,, +Version,+,22.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -901,6 +901,7 @@ Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, +Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,-,furi_hal_deinit_early,void, Function,-,furi_hal_flash_erase,void,uint8_t Function,-,furi_hal_flash_get_base,size_t, @@ -983,7 +984,6 @@ Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, Function,+,furi_hal_power_check_otg_status,void, Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" -Function,+,furi_hal_power_deep_sleep_available,_Bool, Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_otg,void, Function,+,furi_hal_power_enable_external_3_3v,void, @@ -1059,6 +1059,7 @@ Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" +Function,+,furi_hal_rtc_sync_shadow,void, Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, @@ -2149,6 +2150,8 @@ Variable,+,gpio_ext_pd0,const GpioPin, Variable,+,gpio_ext_pe4,const GpioPin, Variable,+,gpio_i2c_power_scl,const GpioPin, Variable,+,gpio_i2c_power_sda,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_periph_power,const GpioPin, Variable,+,gpio_pins,const GpioPinRecord[], Variable,+,gpio_pins_count,const size_t, Variable,+,gpio_sdcard_cd,const GpioPin, @@ -2157,11 +2160,13 @@ Variable,+,gpio_speaker,const GpioPin, Variable,+,gpio_spi_d_miso,const GpioPin, Variable,+,gpio_spi_d_mosi,const GpioPin, Variable,+,gpio_spi_d_sck,const GpioPin, +Variable,+,gpio_swclk,const GpioPin, +Variable,+,gpio_swdio,const GpioPin, Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, Variable,+,gpio_usb_dp,const GpioPin, -Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_vibro,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, Variable,+,message_blink_set_color_blue,const NotificationMessage, @@ -2309,7 +2314,6 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, -Variable,+,gpio_periph_power,const GpioPin, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -2364,4 +2368,3 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, -Variable,+,gpio_vibro,const GpioPin, diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index 4a7015d13..6db483dbc 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -6,6 +6,9 @@ #define TAG "FuriHalResources" +const GpioPin gpio_swdio = {.port = GPIOA, .pin = LL_GPIO_PIN_13}; +const GpioPin gpio_swclk = {.port = GPIOA, .pin = LL_GPIO_PIN_14}; + const GpioPin gpio_vibro = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; const GpioPin gpio_ibutton = {.port = GPIOB, .pin = LL_GPIO_PIN_14}; diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.h b/firmware/targets/f18/furi_hal/furi_hal_resources.h index 3c4708d15..7d2caab36 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.h @@ -50,6 +50,9 @@ extern const size_t input_pins_count; extern const GpioPinRecord gpio_pins[]; extern const size_t gpio_pins_count; +extern const GpioPin gpio_swdio; +extern const GpioPin gpio_swclk; + extern const GpioPin gpio_vibro; extern const GpioPin gpio_ibutton; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 89984352a..3ef57813b 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,21.0,, +Version,+,22.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1082,6 +1082,7 @@ Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, +Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,-,furi_hal_deinit_early,void, Function,-,furi_hal_flash_erase,void,uint8_t Function,-,furi_hal_flash_get_base,size_t, @@ -1212,7 +1213,6 @@ Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, Function,+,furi_hal_power_check_otg_status,void, Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" -Function,+,furi_hal_power_deep_sleep_available,_Bool, Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_otg,void, Function,+,furi_hal_power_enable_external_3_3v,void, @@ -1313,6 +1313,7 @@ Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" +Function,+,furi_hal_rtc_sync_shadow,void, Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, @@ -3076,10 +3077,12 @@ Variable,+,gpio_ext_pc1,const GpioPin, Variable,+,gpio_ext_pc3,const GpioPin, Variable,+,gpio_i2c_power_scl,const GpioPin, Variable,+,gpio_i2c_power_sda,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, Variable,+,gpio_infrared_rx,const GpioPin, Variable,+,gpio_infrared_tx,const GpioPin, Variable,+,gpio_nfc_cs,const GpioPin, Variable,+,gpio_nfc_irq_rfid_pull,const GpioPin, +Variable,+,gpio_periph_power,const GpioPin, Variable,+,gpio_pins,const GpioPinRecord[], Variable,+,gpio_pins_count,const size_t, Variable,+,gpio_rf_sw_0,const GpioPin, @@ -3096,11 +3099,13 @@ Variable,+,gpio_spi_r_miso,const GpioPin, Variable,+,gpio_spi_r_mosi,const GpioPin, Variable,+,gpio_spi_r_sck,const GpioPin, Variable,+,gpio_subghz_cs,const GpioPin, +Variable,+,gpio_swclk,const GpioPin, +Variable,+,gpio_swdio,const GpioPin, Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, Variable,+,gpio_usb_dp,const GpioPin, -Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_vibro,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, Variable,+,lfrfid_protocols,const ProtocolBase*[], @@ -3249,7 +3254,6 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, -Variable,+,gpio_periph_power,const GpioPin, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -3307,4 +3311,3 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, -Variable,+,gpio_vibro,const GpioPin, diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index 83562c73e..a2f2f1a94 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -58,12 +58,6 @@ void ble_glue_init() { ble_glue = malloc(sizeof(BleGlue)); ble_glue->status = BleGlueStatusStartup; - // Configure the system Power Mode - // Select HSI as system clock source after Wake Up from Stop mode - LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); - /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */ - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - #ifdef BLE_GLUE_DEBUG APPD_Init(); #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index cf19451ec..d85524ce4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -63,6 +63,10 @@ void furi_hal_clock_init() { LL_RCC_HSI_Enable(); while(!HS_CLOCK_IS_READY()) ; + /* Select HSI as system clock source after Wake Up from Stop mode + * Must be set before enabling CSS */ + LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); + LL_RCC_HSE_EnableCSS(); /* LSE and LSI1 configuration and activation */ @@ -215,11 +219,14 @@ void furi_hal_clock_switch_to_hsi() { void furi_hal_clock_switch_to_pll() { LL_RCC_HSE_Enable(); LL_RCC_PLL_Enable(); + LL_RCC_PLLSAI1_Enable(); while(!LL_RCC_HSE_IsReady()) ; while(!LL_RCC_PLL_IsReady()) ; + while(!LL_RCC_PLLSAI1_IsReady()) + ; LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); @@ -296,4 +303,4 @@ void furi_hal_clock_mco_disable() { LL_RCC_MSI_Disable(); while(LL_RCC_MSI_IsReady() != 0) ; -} \ No newline at end of file +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_debug.c b/firmware/targets/f7/furi_hal/furi_hal_debug.c index 3b5dfe622..3dc03ea69 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_debug.c +++ b/firmware/targets/f7/furi_hal/furi_hal_debug.c @@ -3,12 +3,26 @@ #include #include +#include +#include + +volatile bool furi_hal_debug_gdb_session_active = false; + void furi_hal_debug_enable() { // Low power mode debug LL_DBGMCU_EnableDBGSleepMode(); LL_DBGMCU_EnableDBGStopMode(); LL_DBGMCU_EnableDBGStandbyMode(); LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); + // SWD GPIO + furi_hal_gpio_init_ex( + &gpio_swdio, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn0JTMS_SWDIO); + furi_hal_gpio_init_ex( + &gpio_swclk, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn0JTCK_SWCLK); } void furi_hal_debug_disable() { @@ -17,4 +31,11 @@ void furi_hal_debug_disable() { LL_DBGMCU_DisableDBGStopMode(); LL_DBGMCU_DisableDBGStandbyMode(); LL_EXTI_DisableIT_32_63(LL_EXTI_LINE_48); + // SWD GPIO + furi_hal_gpio_init_simple(&gpio_swdio, GpioModeAnalog); + furi_hal_gpio_init_simple(&gpio_swclk, GpioModeAnalog); } + +bool furi_hal_debug_is_gdb_session_active() { + return furi_hal_debug_gdb_session_active; +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.c b/firmware/targets/f7/furi_hal/furi_hal_os.c index ee9743e62..3fc1fbea8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_os.c +++ b/firmware/targets/f7/furi_hal/furi_hal_os.c @@ -28,11 +28,24 @@ // Arbitrary (but small) number for better tick consistency #define FURI_HAL_OS_EXTRA_CNT 3 +#ifndef FURI_HAL_OS_DEBUG_AWAKE_GPIO +#define FURI_HAL_OS_DEBUG_AWAKE_GPIO (&gpio_ext_pa7) +#endif + +#ifndef FURI_HAL_OS_DEBUG_TICK_GPIO +#define FURI_HAL_OS_DEBUG_TICK_GPIO (&gpio_ext_pa6) +#endif + +#ifndef FURI_HAL_OS_DEBUG_SECOND_GPIO +#define FURI_HAL_OS_DEBUG_SECOND_GPIO (&gpio_ext_pa4) +#endif + #ifdef FURI_HAL_OS_DEBUG #include void furi_hal_os_timer_callback() { - furi_hal_gpio_write(&gpio_ext_pa4, !furi_hal_gpio_read(&gpio_ext_pa4)); + furi_hal_gpio_write( + FURI_HAL_OS_DEBUG_SECOND_GPIO, !furi_hal_gpio_read(FURI_HAL_OS_DEBUG_SECOND_GPIO)); } #endif @@ -44,9 +57,11 @@ void furi_hal_os_init() { furi_hal_idle_timer_init(); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_AWAKE_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_TICK_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_SECOND_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 1); + FuriTimer* second_timer = furi_timer_alloc(furi_hal_os_timer_callback, FuriTimerTypePeriodic, NULL); furi_timer_start(second_timer, FURI_HAL_OS_TICK_HZ); @@ -58,7 +73,8 @@ void furi_hal_os_init() { void furi_hal_os_tick() { if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa6, !furi_hal_gpio_read(&gpio_ext_pa6)); + furi_hal_gpio_write( + FURI_HAL_OS_DEBUG_TICK_GPIO, !furi_hal_gpio_read(FURI_HAL_OS_DEBUG_TICK_GPIO)); #endif xPortSysTickHandler(); } @@ -121,14 +137,14 @@ static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) { furi_hal_idle_timer_start(FURI_HAL_OS_TICKS_TO_IDLE_CNT(expected_idle_ticks)); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa7, 0); + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 0); #endif // Go to sleep mode furi_hal_power_sleep(); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa7, 1); + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 1); #endif // Calculate how much time we spent in the sleep diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index fd601ec7e..9a87cef15 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -19,15 +21,16 @@ #define TAG "FuriHalPower" -#ifdef FURI_HAL_POWER_DEEP_SLEEP_ENABLED -#define FURI_HAL_POWER_DEEP_INSOMNIA 0 -#else -#define FURI_HAL_POWER_DEEP_INSOMNIA 1 +#ifndef FURI_HAL_POWER_DEBUG_WFI_GPIO +#define FURI_HAL_POWER_DEBUG_WFI_GPIO (&gpio_ext_pb2) +#endif + +#ifndef FURI_HAL_POWER_DEBUG_STOP_GPIO +#define FURI_HAL_POWER_DEBUG_STOP_GPIO (&gpio_ext_pc3) #endif typedef struct { volatile uint8_t insomnia; - volatile uint8_t deep_insomnia; volatile uint8_t suppress_charge; uint8_t gauge_initialized; @@ -36,7 +39,6 @@ typedef struct { static volatile FuriHalPower furi_hal_power = { .insomnia = 0, - .deep_insomnia = FURI_HAL_POWER_DEEP_INSOMNIA, .suppress_charge = 0, }; @@ -79,19 +81,23 @@ const ParamCEDV cedv = { }; void furi_hal_power_init() { +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_WFI_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_STOP_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); +#endif + LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN); + LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2); + LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2); furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); bq27220_init(&furi_hal_i2c_handle_power, &cedv); bq25896_init(&furi_hal_i2c_handle_power); furi_hal_i2c_release(&furi_hal_i2c_handle_power); -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_init_simple(&gpio_ext_pb2, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); -#endif - FURI_LOG_I(TAG, "Init OK"); } @@ -140,11 +146,12 @@ bool furi_hal_power_sleep_available() { return furi_hal_power.insomnia == 0; } -bool furi_hal_power_deep_sleep_available() { - return furi_hal_bt_is_alive() && furi_hal_power.deep_insomnia == 0; +static inline bool furi_hal_power_deep_sleep_available() { + return furi_hal_bt_is_alive() && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) && + !furi_hal_debug_is_gdb_session_active(); } -void furi_hal_power_light_sleep() { +static inline void furi_hal_power_light_sleep() { __WFI(); } @@ -152,17 +159,15 @@ static inline void furi_hal_power_suspend_aux_periphs() { // Disable USART furi_hal_uart_suspend(FuriHalUartIdUSART1); furi_hal_uart_suspend(FuriHalUartIdLPUART1); - // TODO: Disable USB } static inline void furi_hal_power_resume_aux_periphs() { // Re-enable USART furi_hal_uart_resume(FuriHalUartIdUSART1); furi_hal_uart_resume(FuriHalUartIdLPUART1); - // TODO: Re-enable USB } -void furi_hal_power_deep_sleep() { +static inline void furi_hal_power_deep_sleep() { furi_hal_power_suspend_aux_periphs(); while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) @@ -187,8 +192,6 @@ void furi_hal_power_deep_sleep() { LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); // Prepare deep sleep - LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2); LL_LPM_EnableDeepSleep(); #if defined(__CC_ARM) @@ -200,13 +203,6 @@ void furi_hal_power_deep_sleep() { LL_LPM_EnableSleep(); - // Make sure that values differ to prevent disaster on wfi - LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - - LL_PWR_ClearFlag_C1STOP_C1STB(); - LL_PWR_ClearFlag_C2STOP_C2STB(); - /* Release ENTRY_STOP_MODE semaphore */ LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); @@ -220,28 +216,25 @@ void furi_hal_power_deep_sleep() { LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); furi_hal_power_resume_aux_periphs(); + furi_hal_rtc_sync_shadow(); } void furi_hal_power_sleep() { if(furi_hal_power_deep_sleep_available()) { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pc3, 1); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 1); #endif - furi_hal_power_deep_sleep(); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pc3, 0); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); #endif } else { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pb2, 1); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 1); #endif - furi_hal_power_light_sleep(); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pb2, 0); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); #endif } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_random.c b/firmware/targets/f7/furi_hal/furi_hal_random.c index f36407cc1..d3461c4d1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_random.c +++ b/firmware/targets/f7/furi_hal/furi_hal_random.c @@ -9,19 +9,35 @@ #define TAG "FuriHalRandom" +static uint32_t furi_hal_random_read_rng() { + while(LL_RNG_IsActiveFlag_CECS(RNG) && LL_RNG_IsActiveFlag_SECS(RNG) && + !LL_RNG_IsActiveFlag_DRDY(RNG)) { + /* Error handling as described in RM0434, pg. 582-583 */ + if(LL_RNG_IsActiveFlag_CECS(RNG)) { + /* Clock error occurred */ + LL_RNG_ClearFlag_CEIS(RNG); + } + + if(LL_RNG_IsActiveFlag_SECS(RNG)) { + /* Noise source error occurred */ + LL_RNG_ClearFlag_SEIS(RNG); + + for(uint32_t i = 0; i < 12; ++i) { + const volatile uint32_t discard = LL_RNG_ReadRandData32(RNG); + UNUSED(discard); + } + } + } + + return LL_RNG_ReadRandData32(RNG); +} + uint32_t furi_hal_random_get() { while(LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID)) ; LL_RNG_Enable(RNG); - while(!LL_RNG_IsActiveFlag_DRDY(RNG)) - ; - - if((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) { - furi_crash("TRNG error"); - } - - uint32_t random_val = LL_RNG_ReadRandData32(RNG); + const uint32_t random_val = furi_hal_random_read_rng(); LL_RNG_Disable(RNG); LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0); @@ -35,15 +51,7 @@ void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len) { LL_RNG_Enable(RNG); for(uint32_t i = 0; i < len; i += 4) { - while(!LL_RNG_IsActiveFlag_DRDY(RNG)) - ; - - if((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) { - furi_crash("TRNG error"); - } - - uint32_t random_val = LL_RNG_ReadRandData32(RNG); - + const uint32_t random_val = furi_hal_random_read_rng(); uint8_t len_cur = ((i + 4) < len) ? (4) : (len - i); memcpy(&buf[i], &random_val, len_cur); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 912912b4a..abfd977e5 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -6,6 +6,9 @@ #define TAG "FuriHalResources" +const GpioPin gpio_swdio = {.port = GPIOA, .pin = LL_GPIO_PIN_13}; +const GpioPin gpio_swclk = {.port = GPIOA, .pin = LL_GPIO_PIN_14}; + const GpioPin gpio_vibro = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; const GpioPin gpio_ibutton = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index f29917300..6e585c518 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -50,6 +50,9 @@ extern const size_t input_pins_count; extern const GpioPinRecord gpio_pins[]; extern const size_t gpio_pins_count; +extern const GpioPin gpio_swdio; +extern const GpioPin gpio_swclk; + extern const GpioPin gpio_vibro; extern const GpioPin gpio_ibutton; diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index 84e7fe395..7bd45c35d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -165,6 +165,14 @@ void furi_hal_rtc_init() { FURI_LOG_I(TAG, "Init OK"); } +void furi_hal_rtc_sync_shadow() { + if(!LL_RTC_IsShadowRegBypassEnabled(RTC)) { + LL_RTC_ClearFlag_RS(RTC); + while(!LL_RTC_IsActiveFlag_RS(RTC)) { + }; + } +} + uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg) { return LL_RTC_BAK_GetRegister(RTC, reg); } @@ -312,12 +320,7 @@ void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime) { /* Exit Initialization mode */ LL_RTC_DisableInitMode(RTC); - /* If RTC_CR_BYPSHAD bit = 0, wait for synchro else this check is not needed */ - if(!LL_RTC_IsShadowRegBypassEnabled(RTC)) { - LL_RTC_ClearFlag_RS(RTC); - while(!LL_RTC_IsActiveFlag_RS(RTC)) { - }; - } + furi_hal_rtc_sync_shadow(); /* Enable write protection */ LL_RTC_EnableWriteProtection(RTC); diff --git a/firmware/targets/furi_hal_include/furi_hal_debug.h b/firmware/targets/furi_hal_include/furi_hal_debug.h index 88397bbba..befbb4f40 100644 --- a/firmware/targets/furi_hal_include/furi_hal_debug.h +++ b/firmware/targets/furi_hal_include/furi_hal_debug.h @@ -18,6 +18,9 @@ void furi_hal_debug_enable(); /** Disable MCU debug */ void furi_hal_debug_disable(); +/** Check if GDB debug session is active */ +bool furi_hal_debug_is_gdb_session_active(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index 462e20e41..00182fa28 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -58,12 +58,6 @@ void furi_hal_power_insomnia_exit(); */ bool furi_hal_power_sleep_available(); -/** Check if deep sleep availble - * - * @return true if available - */ -bool furi_hal_power_deep_sleep_available(); - /** Go to sleep */ void furi_hal_power_sleep(); diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index b16b04a68..0d9f46f01 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -30,6 +30,7 @@ typedef enum { FuriHalRtcFlagLock = (1 << 2), FuriHalRtcFlagC2Update = (1 << 3), FuriHalRtcFlagHandOrient = (1 << 4), + FuriHalRtcFlagLegacySleep = (1 << 5), } FuriHalRtcFlag; typedef enum { @@ -85,6 +86,9 @@ void furi_hal_rtc_deinit_early(); /** Initialize RTC subsystem */ void furi_hal_rtc_init(); +/** Force sync shadow registers */ +void furi_hal_rtc_sync_shadow(); + /** Get RTC register content * * @param[in] reg The register identifier diff --git a/furi/core/check.c b/furi/core/check.c index 910527cee..64f9f72f1 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -117,6 +118,8 @@ FURI_NORETURN void __furi_crash() { if(debug) { furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); furi_hal_console_puts("\033[0m\r\n"); + furi_hal_debug_enable(); + RESTORE_REGISTERS_AND_HALT_MCU(true); } else { furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h index 03a364abd..830bb191c 100644 --- a/furi/core/core_defines.h +++ b/furi/core/core_defines.h @@ -24,6 +24,10 @@ extern "C" { }) #endif +#ifndef ABS +#define ABS(a) ({ (a) < 0 ? -(a) : (a); }) +#endif + #ifndef ROUND_UP_TO #define ROUND_UP_TO(a, b) \ ({ \ diff --git a/site_scons/cc.scons b/site_scons/cc.scons index 1eb6a3376..55ab72ba6 100644 --- a/site_scons/cc.scons +++ b/site_scons/cc.scons @@ -36,6 +36,7 @@ ENV.AppendUnique( ], CPPDEFINES=[ "_GNU_SOURCE", + *GetOption("extra_defines"), ], LINKFLAGS=[ "-mcpu=cortex-m4", diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index d832a466e..2e9486627 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -26,6 +26,14 @@ AddOption( help="List of applications to add to firmware's built-ins. Also see FIRMWARE_APP_SET and FIRMWARE_APPS", ) +AddOption( + "--extra-define", + action="append", + dest="extra_defines", + default=[], + help="Extra global define that will be passed to C/C++ compiler, can be specified multiple times", +) + AddOption( "--extra-ext-apps", action="store", From 9ede95954bb998239ca01ad42a2ea3c7fff7ee54 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 13 Apr 2023 19:44:03 +0300 Subject: [PATCH 023/282] fbt format --- .../uart_terminal/scenes/uart_terminal_scene_console_output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c index 7882a900a..6988c42de 100644 --- a/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c +++ b/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c @@ -113,7 +113,7 @@ void uart_terminal_scene_console_output_on_enter(void* context) { // Send command with CR+LF or newline '\n' if(app->is_command && app->selected_tx_string) { - if(app->TERMINAL_MODE == 1){ + if(app->TERMINAL_MODE == 1) { uart_terminal_uart_tx( (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); uart_terminal_uart_tx((uint8_t*)("\r\n"), 2); From c7637a0fda141fe63940e5b9e1655a6c29ceb3b0 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 13 Apr 2023 21:20:29 +0300 Subject: [PATCH 024/282] Update TOTP https://github.com/akopachov/flipper-zero_authenticator --- .../external/totp/cli/commands/pin/pin.c | 13 + .../external/totp/cli/commands/reset/reset.c | 2 +- .../external/totp/lib/base32/base32.h | 2 +- .../external/totp/lib/base64/base64.c | 6 +- .../external/totp/lib/polyfills/strnlen.h | 3 + .../external/totp/services/config/config.c | 53 +++- .../external/totp/services/config/config.h | 7 +- .../external/totp/services/config/constants.h | 2 +- .../external/totp/services/crypto/crypto.c | 11 +- .../external/totp/services/hmac/hmac_common.h | 4 - .../external/totp/services/totp/totp.c | 4 +- applications/external/totp/totp_app.c | 13 +- .../external/totp/types/plugin_state.h | 2 +- applications/external/totp/types/token_info.c | 2 - applications/external/totp/types/token_info.h | 19 +- applications/external/totp/ui/constants.h | 4 +- .../totp/ui/fonts/mode-nine/mode_nine.c | 1 + .../totp/ui/fonts/mode-nine/mode_nine.h | 1 - .../external/totp/ui/scene_director.c | 16 -- .../external/totp/ui/scene_director.h | 12 - .../ui/scenes/add_new_token/totp_input_text.c | 1 - .../ui/scenes/add_new_token/totp_input_text.h | 4 +- .../add_new_token/totp_scene_add_new_token.c | 8 - .../add_new_token/totp_scene_add_new_token.h | 4 - .../scenes/app_settings/totp_app_settings.c | 8 - .../scenes/app_settings/totp_app_settings.h | 4 +- .../authenticate/totp_scene_authenticate.c | 8 - .../authenticate/totp_scene_authenticate.h | 2 - .../totp_scene_generate_token.c | 250 ++++++++---------- .../totp_scene_generate_token.h | 2 - .../scenes/token_menu/totp_scene_token_menu.c | 8 - .../scenes/token_menu/totp_scene_token_menu.h | 2 - applications/external/totp/ui/ui_controls.c | 4 +- .../totp/workers/bt_type_code/bt_type_code.c | 38 +-- .../totp/workers/bt_type_code/bt_type_code.h | 31 +-- .../generate_totp_code/generate_totp_code.c | 181 +++++++++++++ .../generate_totp_code/generate_totp_code.h | 49 ++++ .../workers/{common.c => type_code_common.c} | 16 +- .../workers/{common.h => type_code_common.h} | 4 +- .../workers/usb_type_code/usb_type_code.c | 26 +- .../workers/usb_type_code/usb_type_code.h | 19 +- 41 files changed, 505 insertions(+), 341 deletions(-) create mode 100644 applications/external/totp/workers/generate_totp_code/generate_totp_code.c create mode 100644 applications/external/totp/workers/generate_totp_code/generate_totp_code.h rename applications/external/totp/workers/{common.c => type_code_common.c} (88%) rename applications/external/totp/workers/{common.h => type_code_common.h} (83%) diff --git a/applications/external/totp/cli/commands/pin/pin.c b/applications/external/totp/cli/commands/pin/pin.c index 92c9c59c4..9b9038ae7 100644 --- a/applications/external/totp/cli/commands/pin/pin.c +++ b/applications/external/totp/cli/commands/pin/pin.c @@ -121,6 +121,19 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl memset(&new_pin[0], 0, TOTP_IV_SIZE); } + char* backup_path = totp_config_file_backup(); + if(backup_path != NULL) { + TOTP_CLI_PRINTF_WARNING("Backup conf file %s has been created\r\n", backup_path); + TOTP_CLI_PRINTF_WARNING( + "Once you make sure everything is fine and works as expected, please delete this backup file\r\n"); + free(backup_path); + } else { + memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); + TOTP_CLI_PRINTF_ERROR( + "An error has occurred during taking backup of config file\r\n"); + break; + } + if(plugin_state->current_scene == TotpSceneGenerateToken) { totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); load_generate_token_scene = true; diff --git a/applications/external/totp/cli/commands/reset/reset.c b/applications/external/totp/cli/commands/reset/reset.c index c7928db31..cd2d1bf46 100644 --- a/applications/external/totp/cli/commands/reset/reset.c +++ b/applications/external/totp/cli/commands/reset/reset.c @@ -1,7 +1,7 @@ #include "reset.h" #include -#include +#include #include "../../cli_helpers.h" #include "../../../services/config/config.h" diff --git a/applications/external/totp/lib/base32/base32.h b/applications/external/totp/lib/base32/base32.h index 8cb9bddcb..a0ec86e82 100644 --- a/applications/external/totp/lib/base32/base32.h +++ b/applications/external/totp/lib/base32/base32.h @@ -27,7 +27,7 @@ #pragma once -#include +#include #include /** diff --git a/applications/external/totp/lib/base64/base64.c b/applications/external/totp/lib/base64/base64.c index 1dfcf8814..dd0a12b5e 100644 --- a/applications/external/totp/lib/base64/base64.c +++ b/applications/external/totp/lib/base64/base64.c @@ -22,14 +22,14 @@ static const uint8_t dtable[] = {0x3e, 0x80, 0x80, 0x80, 0x3f, 0x34, 0x35, 0x36, 0x80, 0x80, 0x80, 0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33}; -// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + static uint8_t get_dtable_value(uint8_t index) { return (index < 43 || index > 122) ? 0x80 : dtable[index - 43]; } uint8_t* base64_decode(const uint8_t* src, size_t len, size_t* out_len, size_t* out_size) { - uint8_t *out; - uint8_t *pos; + uint8_t* out; + uint8_t* pos; uint8_t in[4]; uint8_t block[4]; uint8_t tmp; diff --git a/applications/external/totp/lib/polyfills/strnlen.h b/applications/external/totp/lib/polyfills/strnlen.h index 7dcef3a18..4fe0d540c 100644 --- a/applications/external/totp/lib/polyfills/strnlen.h +++ b/applications/external/totp/lib/polyfills/strnlen.h @@ -1,3 +1,6 @@ +#pragma once +#pragma weak strnlen + #include size_t strnlen(const char* s, size_t maxlen); \ No newline at end of file diff --git a/applications/external/totp/services/config/config.c b/applications/external/totp/services/config/config.c index adc85cbe5..0453338d3 100644 --- a/applications/external/totp/services/config/config.c +++ b/applications/external/totp/services/config/config.c @@ -9,7 +9,7 @@ #define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator") #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf" -#define CONFIG_FILE_BACKUP_PATH CONFIG_FILE_PATH ".backup" +#define CONFIG_FILE_BACKUP_BASE_PATH CONFIG_FILE_PATH ".backup" #define CONFIG_FILE_TEMP_PATH CONFIG_FILE_PATH ".tmp" #define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig" #define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf" @@ -39,6 +39,34 @@ static void totp_close_config_file(FlipperFormat* file) { flipper_format_free(file); } +/** + * @brief Tries to take a config file backup + * @param storage storage record + * @return backup path if backup successfully taken; \c NULL otherwise + */ +static char* totp_config_file_backup_i(Storage* storage) { + uint8_t backup_path_size = sizeof(CONFIG_FILE_BACKUP_BASE_PATH) + 5; + char* backup_path = malloc(backup_path_size); + furi_check(backup_path != NULL); + memcpy(backup_path, CONFIG_FILE_BACKUP_BASE_PATH, sizeof(CONFIG_FILE_BACKUP_BASE_PATH)); + uint16_t i = 1; + bool backup_file_exists; + while((backup_file_exists = storage_common_exists(storage, backup_path)) && i <= 9999) { + snprintf(backup_path, backup_path_size, CONFIG_FILE_BACKUP_BASE_PATH ".%" PRIu16, i); + i++; + } + + if(backup_file_exists || + storage_common_copy(storage, CONFIG_FILE_PATH, backup_path) != FSE_OK) { + FURI_LOG_E(LOGGING_TAG, "Unable to take a backup"); + free(backup_path); + return NULL; + } + + FURI_LOG_I(LOGGING_TAG, "Took config file backup to %s", backup_path); + return backup_path; +} + /** * @brief Opens or creates TOTP application standard config file * @param storage storage record to use @@ -250,6 +278,13 @@ static TotpConfigFileUpdateResult return update_result; } +char* totp_config_file_backup() { + Storage* storage = totp_open_storage(); + char* result = totp_config_file_backup_i(storage); + totp_close_storage(); + return result; +} + TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info) { Storage* cfg_storage = totp_open_storage(); FlipperFormat* file; @@ -513,20 +548,16 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st CONFIG_FILE_ACTUAL_VERSION); totp_close_config_file(fff_data_file); - if(storage_common_stat(storage, CONFIG_FILE_BACKUP_PATH, NULL) == FSE_OK) { - storage_simply_remove(storage, CONFIG_FILE_BACKUP_PATH); - } + char* backup_path = totp_config_file_backup_i(storage); - if(storage_common_copy(storage, CONFIG_FILE_PATH, CONFIG_FILE_BACKUP_PATH) == FSE_OK) { - FURI_LOG_I(LOGGING_TAG, "Took config file backup to %s", CONFIG_FILE_BACKUP_PATH); + if(backup_path != NULL) { if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) { result = TotpConfigFileOpenError; break; } FlipperFormat* fff_backup_data_file = flipper_format_file_alloc(storage); - if(!flipper_format_file_open_existing( - fff_backup_data_file, CONFIG_FILE_BACKUP_PATH)) { + if(!flipper_format_file_open_existing(fff_backup_data_file, backup_path)) { flipper_format_file_close(fff_backup_data_file); flipper_format_free(fff_backup_data_file); result = TotpConfigFileOpenError; @@ -551,12 +582,12 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st flipper_format_file_close(fff_backup_data_file); flipper_format_free(fff_backup_data_file); flipper_format_rewind(fff_data_file); + free(backup_path); } else { FURI_LOG_E( LOGGING_TAG, - "An error occurred during taking backup of %s into %s before migration", - CONFIG_FILE_PATH, - CONFIG_FILE_BACKUP_PATH); + "An error occurred during taking backup of %s before migration", + CONFIG_FILE_PATH); result = TotpConfigFileOpenError; break; } diff --git a/applications/external/totp/services/config/config.h b/applications/external/totp/services/config/config.h index 3d325368d..5bd169525 100644 --- a/applications/external/totp/services/config/config.h +++ b/applications/external/totp/services/config/config.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include "../../types/plugin_state.h" #include "../../types/token_info.h" #include "constants.h" @@ -60,6 +59,12 @@ enum TotpConfigFileUpdateResults { TotpConfigFileUpdateError }; +/** + * @brief Tries to take a config file backup + * @return backup path if backup successfully taken; \c NULL otherwise + */ +char* totp_config_file_backup(); + /** * @brief Saves all the settings and tokens to an application config file * @param plugin_state application state diff --git a/applications/external/totp/services/config/constants.h b/applications/external/totp/services/config/constants.h index 9924aefe2..7137e2374 100644 --- a/applications/external/totp/services/config/constants.h +++ b/applications/external/totp/services/config/constants.h @@ -1,7 +1,7 @@ #pragma once #define CONFIG_FILE_HEADER "Flipper TOTP plugin config file" -#define CONFIG_FILE_ACTUAL_VERSION 4 +#define CONFIG_FILE_ACTUAL_VERSION (4) #define TOTP_CONFIG_KEY_TIMEZONE "Timezone" #define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName" diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index ed4775dfb..00f5ab0a8 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -1,14 +1,15 @@ #include "crypto.h" -#include -#include +#include +#include +#include #include "../config/config.h" #include "../../types/common.h" #include "memset_s.h" -#define CRYPTO_KEY_SLOT 2 +#define CRYPTO_KEY_SLOT (2) #define CRYPTO_VERIFY_KEY "FFF_Crypto_pass" -#define CRYPTO_VERIFY_KEY_LENGTH 16 -#define CRYPTO_ALIGNMENT_FACTOR 16 +#define CRYPTO_VERIFY_KEY_LENGTH (16) +#define CRYPTO_ALIGNMENT_FACTOR (16) uint8_t* totp_crypto_encrypt( const uint8_t* plain_data, diff --git a/applications/external/totp/services/hmac/hmac_common.h b/applications/external/totp/services/hmac/hmac_common.h index 9c5b5828f..0cd56ed99 100644 --- a/applications/external/totp/services/hmac/hmac_common.h +++ b/applications/external/totp/services/hmac/hmac_common.h @@ -9,11 +9,7 @@ #define _GLHMAC_CONCAT_(prefix, suffix) prefix##suffix #define _GLHMAC_CONCAT(prefix, suffix) _GLHMAC_CONCAT_(prefix, suffix) -#if GL_HMAC_NAME == 5 -#define HMAC_ALG md5 -#else #define HMAC_ALG _GLHMAC_CONCAT(sha, GL_HMAC_NAME) -#endif #define GL_HMAC_CTX _GLHMAC_CONCAT(HMAC_ALG, _ctx) #define GL_HMAC_FN _GLHMAC_CONCAT(hmac_, HMAC_ALG) diff --git a/applications/external/totp/services/totp/totp.c b/applications/external/totp/services/totp/totp.c index f6e0401e6..45a283b06 100644 --- a/applications/external/totp/services/totp/totp.c +++ b/applications/external/totp/services/totp/totp.c @@ -1,15 +1,13 @@ #include "totp.h" -#include #include #include -#include #include +#include #include "../hmac/hmac_sha1.h" #include "../hmac/hmac_sha256.h" #include "../hmac/hmac_sha512.h" #include "../hmac/byteswap.h" -#include "../../lib/timezone_utils/timezone_utils.h" #define HMAC_MAX_RESULT_SIZE HMAC_SHA512_RESULT_SIZE diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index 74ec52f2c..0b70167a2 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -1,10 +1,7 @@ -#include -#include #include #include #include #include -#include #include #include #include @@ -21,7 +18,7 @@ #include "services/crypto/crypto.h" #include "cli/cli.h" -#define IDLE_TIMEOUT 60000 +#define IDLE_TIMEOUT (60000) static void render_callback(Canvas* const canvas, void* ctx) { furi_assert(ctx); @@ -97,6 +94,7 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { plugin_state->gui = furi_record_open(RECORD_GUI); plugin_state->notification_app = furi_record_open(RECORD_NOTIFICATION); plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS); + memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); if(totp_config_file_load_base(plugin_state) != TotpConfigFileOpenSuccess) { totp_dialogs_config_loading_error(plugin_state); @@ -104,10 +102,6 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { } plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(plugin_state->mutex == NULL) { - FURI_LOG_E(LOGGING_TAG, "Cannot create mutex\r\n"); - return false; - } #ifdef TOTP_BADBT_TYPE_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt) { @@ -162,7 +156,7 @@ int32_t totp_app() { } TotpCliContext* cli_context = totp_cli_register_command_handler(plugin_state, event_queue); - totp_scene_director_init_scenes(plugin_state); + if(!totp_activate_initial_scene(plugin_state)) { FURI_LOG_E(LOGGING_TAG, "An error ocurred during activating initial scene\r\n"); totp_plugin_state_free(plugin_state); @@ -210,7 +204,6 @@ int32_t totp_app() { totp_cli_unregister_command_handler(cli_context); totp_scene_director_deactivate_active_scene(plugin_state); - totp_scene_director_dispose(plugin_state); view_port_enabled_set(view_port, false); gui_remove_view_port(plugin_state->gui, view_port); diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index b1d34a662..cacf68426 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -12,7 +12,7 @@ #include "../workers/bt_type_code/bt_type_code.h" #endif -#define TOTP_IV_SIZE 16 +#define TOTP_IV_SIZE (16) /** * @brief Application state structure diff --git a/applications/external/totp/types/token_info.c b/applications/external/totp/types/token_info.c index b8196c56b..5b85de719 100644 --- a/applications/external/totp/types/token_info.c +++ b/applications/external/totp/types/token_info.c @@ -1,9 +1,7 @@ #include "token_info.h" -#include #include #include #include -#include #include "common.h" #include "../services/crypto/crypto.h" diff --git a/applications/external/totp/types/token_info.h b/applications/external/totp/types/token_info.h index 688e8028d..21968553f 100644 --- a/applications/external/totp/types/token_info.h +++ b/applications/external/totp/types/token_info.h @@ -2,15 +2,15 @@ #include #include -#include +#include -#define TOTP_TOKEN_DURATION_DEFAULT 30 +#define TOTP_TOKEN_DURATION_DEFAULT (30) #define TOTP_TOKEN_ALGO_SHA1_NAME "sha1" #define TOTP_TOKEN_ALGO_STEAM_NAME "steam" #define TOTP_TOKEN_ALGO_SHA256_NAME "sha256" #define TOTP_TOKEN_ALGO_SHA512_NAME "sha512" -#define TOTP_TOKEN_MAX_LENGTH 255 +#define TOTP_TOKEN_MAX_LENGTH (255) #define PLAIN_TOKEN_ENCODING_BASE32_NAME "base32" #define PLAIN_TOKEN_ENCODING_BASE64_NAME "base64" @@ -95,12 +95,23 @@ enum TokenAutomationFeatures { TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100 }; +/** + * @brief Plain token secret encodings. + */ enum PlainTokenSecretEncodings { + + /** + * @brief Base32 encoding + */ PLAIN_TOKEN_ENCODING_BASE32 = 0, + + /** + * @brief Base64 encoding + */ PLAIN_TOKEN_ENCODING_BASE64 = 1 }; -#define TOTP_TOKEN_DIGITS_MAX_COUNT 8 +#define TOTP_TOKEN_DIGITS_MAX_COUNT (8) /** * @brief TOTP token information diff --git a/applications/external/totp/ui/constants.h b/applications/external/totp/ui/constants.h index 9caf90c4e..81c2edf92 100644 --- a/applications/external/totp/ui/constants.h +++ b/applications/external/totp/ui/constants.h @@ -1,6 +1,6 @@ #pragma once -#define SCREEN_WIDTH 128 -#define SCREEN_HEIGHT 64 +#define SCREEN_WIDTH (128) +#define SCREEN_HEIGHT (64) #define SCREEN_WIDTH_CENTER (SCREEN_WIDTH >> 1) #define SCREEN_HEIGHT_CENTER (SCREEN_HEIGHT >> 1) diff --git a/applications/external/totp/ui/fonts/mode-nine/mode_nine.c b/applications/external/totp/ui/fonts/mode-nine/mode_nine.c index add4f47ef..ca2191789 100644 --- a/applications/external/totp/ui/fonts/mode-nine/mode_nine.c +++ b/applications/external/totp/ui/fonts/mode-nine/mode_nine.c @@ -1,4 +1,5 @@ #include "mode_nine.h" +#include /* GENERATED BY https://github.com/pavius/the-dot-factory */ diff --git a/applications/external/totp/ui/fonts/mode-nine/mode_nine.h b/applications/external/totp/ui/fonts/mode-nine/mode_nine.h index 67fa33afe..516e261f8 100644 --- a/applications/external/totp/ui/fonts/mode-nine/mode_nine.h +++ b/applications/external/totp/ui/fonts/mode-nine/mode_nine.h @@ -3,7 +3,6 @@ /* GENERATED BY https://github.com/pavius/the-dot-factory */ #include "../font_info.h" -#include /* Font data for ModeNine 15pt */ extern const FONT_INFO modeNine_15ptFontInfo; diff --git a/applications/external/totp/ui/scene_director.c b/applications/external/totp/ui/scene_director.c index c77e88ab4..c6f709006 100644 --- a/applications/external/totp/ui/scene_director.c +++ b/applications/external/totp/ui/scene_director.c @@ -62,14 +62,6 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state } } -void totp_scene_director_init_scenes(PluginState* const plugin_state) { - totp_scene_authenticate_init(plugin_state); - totp_scene_generate_token_init(plugin_state); - totp_scene_add_new_token_init(plugin_state); - totp_scene_token_menu_init(plugin_state); - totp_scene_app_settings_init(plugin_state); -} - void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state) { switch(plugin_state->current_scene) { case TotpSceneGenerateToken: @@ -94,14 +86,6 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_ } } -void totp_scene_director_dispose(const PluginState* const plugin_state) { - totp_scene_generate_token_free(plugin_state); - totp_scene_authenticate_free(plugin_state); - totp_scene_add_new_token_free(plugin_state); - totp_scene_token_menu_free(plugin_state); - totp_scene_app_settings_free(plugin_state); -} - bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state) { bool processing = true; switch(plugin_state->current_scene) { diff --git a/applications/external/totp/ui/scene_director.h b/applications/external/totp/ui/scene_director.h index 541a63f1c..71709978f 100644 --- a/applications/external/totp/ui/scene_director.h +++ b/applications/external/totp/ui/scene_director.h @@ -22,12 +22,6 @@ void totp_scene_director_activate_scene( */ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state); -/** - * @brief Initializes all the available scenes - * @param plugin_state application state - */ -void totp_scene_director_init_scenes(PluginState* const plugin_state); - /** * @brief Renders current scene * @param canvas canvas to render at @@ -35,12 +29,6 @@ void totp_scene_director_init_scenes(PluginState* const plugin_state); */ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_state); -/** - * @brief Disposes all the available scenes - * @param plugin_state application state - */ -void totp_scene_director_dispose(const PluginState* const plugin_state); - /** * @brief Handles application event for the current scene * @param event event to be handled diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c index 6956ec1ad..bbe0b7726 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c @@ -1,6 +1,5 @@ #include "totp_input_text.h" #include -#include "../../../lib/polyfills/strnlen.h" void view_draw(View* view, Canvas* canvas) { furi_assert(view); diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h index 145e8904d..ffbfde692 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h +++ b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h @@ -6,6 +6,8 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" +#define INPUT_BUFFER_SIZE (255) + typedef struct { char* user_input; size_t user_input_length; @@ -20,8 +22,6 @@ typedef struct { void* callback_data; } InputTextSceneContext; -#define INPUT_BUFFER_SIZE 255 - typedef struct { TextInput* text_input; View* text_input_view; diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c index 800d7e672..3f8e4fd93 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c @@ -44,10 +44,6 @@ typedef struct { FuriString* duration_text; } SceneState; -void totp_scene_add_new_token_init(const PluginState* plugin_state) { - UNUSED(plugin_state); -} - static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) { SceneState* scene_state = result->callback_data; free(scene_state->token_name); @@ -354,7 +350,3 @@ void totp_scene_add_new_token_deactivate(PluginState* plugin_state) { free(plugin_state->current_scene_state); plugin_state->current_scene_state = NULL; } - -void totp_scene_add_new_token_free(const PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h index c412e5f0f..e05a95dbd 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h @@ -1,8 +1,6 @@ #pragma once #include -#include -#include #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" @@ -10,11 +8,9 @@ typedef struct { uint16_t current_token_index; } TokenAddEditSceneContext; -void totp_scene_add_new_token_init(const PluginState* plugin_state); void totp_scene_add_new_token_activate( PluginState* plugin_state, const TokenAddEditSceneContext* context); void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state); void totp_scene_add_new_token_deactivate(PluginState* plugin_state); -void totp_scene_add_new_token_free(const PluginState* plugin_state); diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c index 93fd3d915..d2cf629d2 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c @@ -44,10 +44,6 @@ typedef struct { Control selected_control; } SceneState; -void totp_scene_app_settings_init(const PluginState* plugin_state) { - UNUSED(plugin_state); -} - void totp_scene_app_settings_activate( PluginState* plugin_state, const AppSettingsSceneContext* context) { @@ -332,7 +328,3 @@ void totp_scene_app_settings_deactivate(PluginState* plugin_state) { free(plugin_state->current_scene_state); plugin_state->current_scene_state = NULL; } - -void totp_scene_app_settings_free(const PluginState* plugin_state) { - UNUSED(plugin_state); -} \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h index 1721186ed..a0e408b00 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h @@ -8,7 +8,6 @@ typedef struct { uint16_t current_token_index; } AppSettingsSceneContext; -void totp_scene_app_settings_init(const PluginState* plugin_state); void totp_scene_app_settings_activate( PluginState* plugin_state, const AppSettingsSceneContext* context); @@ -16,5 +15,4 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu bool totp_scene_app_settings_handle_event( const PluginEvent* const event, PluginState* plugin_state); -void totp_scene_app_settings_deactivate(PluginState* plugin_state); -void totp_scene_app_settings_free(const PluginState* plugin_state); \ No newline at end of file +void totp_scene_app_settings_deactivate(PluginState* plugin_state); \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c index 17beb64c6..c0a0b5744 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -18,10 +18,6 @@ typedef struct { uint8_t code_length; } SceneState; -void totp_scene_authenticate_init(PluginState* plugin_state) { - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); -} - void totp_scene_authenticate_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); @@ -162,7 +158,3 @@ void totp_scene_authenticate_deactivate(PluginState* plugin_state) { free(plugin_state->current_scene_state); plugin_state->current_scene_state = NULL; } - -void totp_scene_authenticate_free(const PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h index b8fe174ae..5ddd44a4a 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h @@ -4,11 +4,9 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -void totp_scene_authenticate_init(PluginState* plugin_state); void totp_scene_authenticate_activate(PluginState* plugin_state); void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_authenticate_handle_event( const PluginEvent* const event, PluginState* plugin_state); void totp_scene_authenticate_deactivate(PluginState* plugin_state); -void totp_scene_authenticate_free(const PluginState* plugin_state); diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index f27b24835..92a45eb4a 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -2,39 +2,44 @@ #include #include #include +#include #include "totp_scene_generate_token.h" #include "../../../types/token_info.h" #include "../../../types/common.h" #include "../../constants.h" -#include "../../../services/totp/totp.h" #include "../../../services/config/config.h" -#include "../../../services/crypto/crypto.h" -#include "../../../services/convert/convert.h" -#include "../../../lib/polyfills/memset_s.h" -#include "../../../lib/roll_value/roll_value.h" #include "../../scene_director.h" #include "../token_menu/totp_scene_token_menu.h" #include "../../../features_config.h" +#include "../../../workers/generate_totp_code/generate_totp_code.h" #include "../../../workers/usb_type_code/usb_type_code.h" #ifdef TOTP_BADBT_TYPE_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" #endif #include "../../fonts/mode-nine/mode_nine.h" -static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY"; -static const uint8_t PROGRESS_BAR_MARGIN = 3; -static const uint8_t PROGRESS_BAR_HEIGHT = 4; +#define PROGRESS_BAR_MARGIN (3) +#define PROGRESS_BAR_HEIGHT (4) + +typedef struct { + uint8_t progress_bar_x; + uint8_t progress_bar_width; + uint8_t code_total_length; + uint8_t code_offset_x; + uint8_t code_offset_x_inc; + uint8_t code_offset_y; +} UiPrecalculatedDimensions; typedef struct { uint16_t current_token_index; char last_code[TOTP_TOKEN_DIGITS_MAX_COUNT + 1]; - bool need_token_update; TokenInfo* current_token; - uint32_t last_token_gen_time; TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context; NotificationMessage const** notification_sequence_new_token; - NotificationMessage const** notification_sequence_badusb; + NotificationMessage const** notification_sequence_automation; FuriMutex* last_code_update_sync; + TotpGenerateCodeWorkerContext* generate_code_worker_context; + UiPrecalculatedDimensions ui_precalculated_dimensions; } SceneState; static const NotificationSequence* @@ -80,7 +85,7 @@ static const NotificationSequence* static const NotificationSequence* get_notification_sequence_automation(const PluginState* plugin_state, SceneState* scene_state) { - if(scene_state->notification_sequence_badusb == NULL) { + if(scene_state->notification_sequence_automation == NULL) { uint8_t i = 0; uint8_t length = 3; if(plugin_state->notification_method & NotificationMethodVibro) { @@ -91,110 +96,100 @@ static const NotificationSequence* length += 6; } - scene_state->notification_sequence_badusb = malloc(sizeof(void*) * length); - furi_check(scene_state->notification_sequence_badusb != NULL); + scene_state->notification_sequence_automation = malloc(sizeof(void*) * length); + furi_check(scene_state->notification_sequence_automation != NULL); - scene_state->notification_sequence_badusb[i++] = &message_blue_255; + scene_state->notification_sequence_automation[i++] = &message_blue_255; if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_badusb[i++] = &message_vibro_on; + scene_state->notification_sequence_automation[i++] = &message_vibro_on; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_badusb[i++] = &message_note_d5; //-V525 - scene_state->notification_sequence_badusb[i++] = &message_delay_50; - scene_state->notification_sequence_badusb[i++] = &message_note_e4; - scene_state->notification_sequence_badusb[i++] = &message_delay_50; - scene_state->notification_sequence_badusb[i++] = &message_note_f3; + scene_state->notification_sequence_automation[i++] = &message_note_d5; //-V525 + scene_state->notification_sequence_automation[i++] = &message_delay_50; + scene_state->notification_sequence_automation[i++] = &message_note_e4; + scene_state->notification_sequence_automation[i++] = &message_delay_50; + scene_state->notification_sequence_automation[i++] = &message_note_f3; } - scene_state->notification_sequence_badusb[i++] = &message_delay_50; + scene_state->notification_sequence_automation[i++] = &message_delay_50; if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_badusb[i++] = &message_vibro_off; + scene_state->notification_sequence_automation[i++] = &message_vibro_off; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_badusb[i++] = &message_sound_off; + scene_state->notification_sequence_automation[i++] = &message_sound_off; } - scene_state->notification_sequence_badusb[i++] = NULL; + scene_state->notification_sequence_automation[i++] = NULL; } - return (NotificationSequence*)scene_state->notification_sequence_badusb; -} - -static void - int_token_to_str(uint64_t i_token_code, char* str, TokenDigitsCount len, TokenHashAlgo algo) { - if(i_token_code == OTP_ERROR) { - memset(&str[0], '-', len); - } else { - if(algo == STEAM) { - for(uint8_t i = 0; i < len; i++) { - str[i] = STEAM_ALGO_ALPHABET[i_token_code % 26]; - i_token_code = i_token_code / 26; - } - } else { - for(int8_t i = len - 1; i >= 0; i--) { - str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10); - i_token_code = i_token_code / 10; - } - } - } - - str[len] = '\0'; -} - -static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) { - switch(algo) { - case SHA1: - case STEAM: - return TOTP_ALGO_SHA1; - case SHA256: - return TOTP_ALGO_SHA256; - case SHA512: - return TOTP_ALGO_SHA512; - default: - break; - } - - return NULL; + return (NotificationSequence*)scene_state->notification_sequence_automation; } static void update_totp_params(PluginState* const plugin_state) { SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; if(scene_state->current_token_index < plugin_state->tokens_count) { - TokenInfo* tokenInfo = + scene_state->current_token = list_element_at(plugin_state->tokens_list, scene_state->current_token_index)->data; - - scene_state->need_token_update = true; - scene_state->current_token = tokenInfo; + totp_generate_code_worker_notify( + scene_state->generate_code_worker_context, TotpGenerateCodeWorkerEventForceUpdate); } } static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_state) { uint8_t code_length = scene_state->current_token->digits; + uint8_t offset_x = scene_state->ui_precalculated_dimensions.code_offset_x; uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width; - uint8_t total_length = code_length * (char_width + modeNine_15ptFontInfo.spacePixels); - uint8_t offset_x = (SCREEN_WIDTH - total_length) >> 1; - uint8_t offset_y = SCREEN_HEIGHT_CENTER - (modeNine_15ptFontInfo.height >> 1); + uint8_t offset_x_inc = scene_state->ui_precalculated_dimensions.code_offset_x_inc; for(uint8_t i = 0; i < code_length; i++) { char ch = scene_state->last_code[i]; - uint8_t char_index = ch - modeNine_15ptFontInfo.startChar; - canvas_draw_xbm( - canvas, - offset_x, - offset_y, - char_width, - modeNine_15ptFontInfo.height, - &modeNine_15ptFontInfo.data[modeNine_15ptFontInfo.charInfo[char_index].offset]); + if(ch >= modeNine_15ptFontInfo.startChar && ch <= modeNine_15ptFontInfo.endChar) { + uint8_t char_index = ch - modeNine_15ptFontInfo.startChar; + canvas_draw_xbm( + canvas, + offset_x, + scene_state->ui_precalculated_dimensions.code_offset_y, + char_width, + modeNine_15ptFontInfo.height, + &modeNine_15ptFontInfo.data[modeNine_15ptFontInfo.charInfo[char_index].offset]); + } - offset_x += char_width + modeNine_15ptFontInfo.spacePixels; + offset_x += offset_x_inc; } } -void totp_scene_generate_token_init(const PluginState* plugin_state) { - UNUSED(plugin_state); +static void on_new_token_code_generated(bool time_left, void* context) { + const PluginState* plugin_state = context; + SceneState* scene_state = plugin_state->current_scene_state; + uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width; + scene_state->ui_precalculated_dimensions.code_total_length = + scene_state->current_token->digits * (char_width + modeNine_15ptFontInfo.spacePixels); + scene_state->ui_precalculated_dimensions.code_offset_x = + (SCREEN_WIDTH - scene_state->ui_precalculated_dimensions.code_total_length) >> 1; + scene_state->ui_precalculated_dimensions.code_offset_x_inc = + char_width + modeNine_15ptFontInfo.spacePixels; + scene_state->ui_precalculated_dimensions.code_offset_y = + SCREEN_HEIGHT_CENTER - (modeNine_15ptFontInfo.height >> 1); + + if(time_left) { + notification_message( + plugin_state->notification_app, + get_notification_sequence_new_token(plugin_state, plugin_state->current_scene_state)); + } +} + +static void on_code_lifetime_updated_generated(float code_lifetime_percent, void* context) { + SceneState* scene_state = context; + scene_state->ui_precalculated_dimensions.progress_bar_width = + (uint8_t)((float)(SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1)) * code_lifetime_percent); + scene_state->ui_precalculated_dimensions.progress_bar_x = + ((SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1) - + scene_state->ui_precalculated_dimensions.progress_bar_width) >> + 1) + + PROGRESS_BAR_MARGIN; } void totp_scene_generate_token_activate( @@ -234,15 +229,14 @@ void totp_scene_generate_token_activate( } else { scene_state->current_token_index = context->current_token_index; } - scene_state->need_token_update = true; + plugin_state->current_scene_state = scene_state; FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset); - update_totp_params(plugin_state); scene_state->last_code_update_sync = furi_mutex_alloc(FuriMutexTypeNormal); if(plugin_state->automation_method & AutomationMethodBadUsb) { scene_state->usb_type_code_worker_context = totp_usb_type_code_worker_start( - &scene_state->last_code[0], + scene_state->last_code, TOTP_TOKEN_DIGITS_MAX_COUNT + 1, scene_state->last_code_update_sync); } @@ -255,11 +249,28 @@ void totp_scene_generate_token_activate( } totp_bt_type_code_worker_start( plugin_state->bt_type_code_worker_context, - &scene_state->last_code[0], + scene_state->last_code, TOTP_TOKEN_DIGITS_MAX_COUNT + 1, scene_state->last_code_update_sync); } #endif + + scene_state->generate_code_worker_context = totp_generate_code_worker_start( + scene_state->last_code, + &scene_state->current_token, + scene_state->last_code_update_sync, + plugin_state->timezone_offset, + plugin_state->iv); + + totp_generate_code_worker_set_code_generated_handler( + scene_state->generate_code_worker_context, &on_new_token_code_generated, plugin_state); + + totp_generate_code_worker_set_lifetime_changed_handler( + scene_state->generate_code_worker_context, + &on_code_lifetime_updated_generated, + scene_state); + + update_totp_params(plugin_state); } void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) { @@ -281,54 +292,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ return; } - SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - FuriHalRtcDateTime curr_dt; - furi_hal_rtc_get_datetime(&curr_dt); - uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); - - bool is_new_token_time = curr_ts % scene_state->current_token->duration == 0; - if(is_new_token_time && scene_state->last_token_gen_time != curr_ts) { - scene_state->need_token_update = true; - } - - if(scene_state->need_token_update) { - scene_state->need_token_update = false; - scene_state->last_token_gen_time = curr_ts; - - const TokenInfo* tokenInfo = scene_state->current_token; - - if(tokenInfo->token != NULL && tokenInfo->token_length > 0) { - furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever); - size_t key_length; - uint8_t* key = totp_crypto_decrypt( - tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length); - - int_token_to_str( - totp_at( - get_totp_algo_impl(tokenInfo->algo), - key, - key_length, - curr_ts, - plugin_state->timezone_offset, - tokenInfo->duration), - scene_state->last_code, - tokenInfo->digits, - tokenInfo->algo); - memset_s(key, key_length, 0, key_length); - free(key); - } else { - furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever); - int_token_to_str(0, scene_state->last_code, tokenInfo->digits, tokenInfo->algo); - } - - furi_mutex_release(scene_state->last_code_update_sync); - - if(is_new_token_time) { - notification_message( - plugin_state->notification_app, - get_notification_sequence_new_token(plugin_state, scene_state)); - } - } + const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; canvas_set_font(canvas, FontPrimary); uint16_t token_name_width = canvas_string_width(canvas, scene_state->current_token->name); @@ -356,17 +320,11 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ draw_totp_code(canvas, scene_state); - const uint8_t TOKEN_LIFETIME = scene_state->current_token->duration; - float percentDone = (float)(TOKEN_LIFETIME - curr_ts % TOKEN_LIFETIME) / (float)TOKEN_LIFETIME; - uint8_t barWidth = (uint8_t)((float)(SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1)) * percentDone); - uint8_t barX = - ((SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1) - barWidth) >> 1) + PROGRESS_BAR_MARGIN; - canvas_draw_box( canvas, - barX, + scene_state->ui_precalculated_dimensions.progress_bar_x, SCREEN_HEIGHT - PROGRESS_BAR_MARGIN - PROGRESS_BAR_HEIGHT, - barWidth, + scene_state->ui_precalculated_dimensions.progress_bar_width, PROGRESS_BAR_HEIGHT); if(plugin_state->tokens_count > 1) { @@ -496,6 +454,8 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { if(plugin_state->current_scene_state == NULL) return; SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; + totp_generate_code_worker_stop(scene_state->generate_code_worker_context); + if(plugin_state->automation_method & AutomationMethodBadUsb) { totp_usb_type_code_worker_stop(scene_state->usb_type_code_worker_context); } @@ -509,8 +469,8 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { free(scene_state->notification_sequence_new_token); } - if(scene_state->notification_sequence_badusb != NULL) { - free(scene_state->notification_sequence_badusb); + if(scene_state->notification_sequence_automation != NULL) { + free(scene_state->notification_sequence_automation); } furi_mutex_free(scene_state->last_code_update_sync); @@ -518,7 +478,3 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { free(scene_state); plugin_state->current_scene_state = NULL; } - -void totp_scene_generate_token_free(const PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h index 44a3b1c0f..e183f53d2 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h @@ -8,7 +8,6 @@ typedef struct { uint16_t current_token_index; } GenerateTokenSceneContext; -void totp_scene_generate_token_init(const PluginState* plugin_state); void totp_scene_generate_token_activate( PluginState* plugin_state, const GenerateTokenSceneContext* context); @@ -17,4 +16,3 @@ bool totp_scene_generate_token_handle_event( const PluginEvent* const event, PluginState* plugin_state); void totp_scene_generate_token_deactivate(PluginState* plugin_state); -void totp_scene_generate_token_free(const PluginState* plugin_state); diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c index 6c6986c65..7b00f0a1b 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c @@ -24,10 +24,6 @@ typedef struct { TotpNullable_uint16_t current_token_index; } SceneState; -void totp_scene_token_menu_init(const PluginState* plugin_state) { - UNUSED(plugin_state); -} - void totp_scene_token_menu_activate( PluginState* plugin_state, const TokenMenuSceneContext* context) { @@ -204,7 +200,3 @@ void totp_scene_token_menu_deactivate(PluginState* plugin_state) { free(plugin_state->current_scene_state); plugin_state->current_scene_state = NULL; } - -void totp_scene_token_menu_free(const PluginState* plugin_state) { - UNUSED(plugin_state); -} diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h index 059b8e571..f9d4b4cbf 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h @@ -8,11 +8,9 @@ typedef struct { uint16_t current_token_index; } TokenMenuSceneContext; -void totp_scene_token_menu_init(const PluginState* plugin_state); void totp_scene_token_menu_activate( PluginState* plugin_state, const TokenMenuSceneContext* context); void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state); void totp_scene_token_menu_deactivate(PluginState* plugin_state); -void totp_scene_token_menu_free(const PluginState* plugin_state); diff --git a/applications/external/totp/ui/ui_controls.c b/applications/external/totp/ui/ui_controls.c index af029dd9f..d5e86aa58 100644 --- a/applications/external/totp/ui/ui_controls.c +++ b/applications/external/totp/ui/ui_controls.c @@ -2,8 +2,8 @@ #include #include "constants.h" -#define TEXT_BOX_HEIGHT 13 -#define TEXT_BOX_MARGIN 4 +#define TEXT_BOX_HEIGHT (13) +#define TEXT_BOX_MARGIN (4) void ui_control_text_box_render( Canvas* const canvas, diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.c b/applications/external/totp/workers/bt_type_code/bt_type_code.c index ec4c1619d..5a1f56298 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.c +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.c @@ -1,10 +1,11 @@ #include "bt_type_code.h" #include +#include #include #include #include "../../types/common.h" #include "../../types/token_info.h" -#include "../common.h" +#include "../type_code_common.h" #define HID_BT_KEYS_STORAGE_PATH EXT_PATH("authenticator/.bt_hid.keys") @@ -16,15 +17,15 @@ static inline bool totp_type_code_worker_stop_requested() { static void totp_type_code_worker_bt_set_app_mac(uint8_t* mac) { uint8_t max_i; size_t uid_size = furi_hal_version_uid_size(); - if(uid_size < 6) { + if(uid_size < TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN) { max_i = uid_size; } else { - max_i = 6; + max_i = TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN; } const uint8_t* uid = furi_hal_version_uid(); memcpy(mac, uid, max_i); - for(uint8_t i = max_i; i < 6; i++) { + for(uint8_t i = max_i; i < TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN; i++) { mac[i] = 0; } @@ -39,23 +40,21 @@ static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context i++; } while(!context->is_connected && i < 100 && !totp_type_code_worker_stop_requested()); - if(context->is_connected && furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) { + if(context->is_connected && + furi_mutex_acquire(context->code_buffer_sync, 500) == FuriStatusOk) { totp_type_code_worker_execute_automation( &furi_hal_bt_hid_kb_press, &furi_hal_bt_hid_kb_release, - context->string, - context->string_length, + context->code_buffer, + context->code_buffer_size, context->flags); - furi_mutex_release(context->string_sync); + furi_mutex_release(context->code_buffer_sync); } } static int32_t totp_type_code_worker_callback(void* context) { furi_check(context); FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(context_mutex == NULL) { - return 251; - } TotpBtTypeCodeWorkerContext* bt_context = context; @@ -92,13 +91,13 @@ static void connection_status_changed_callback(BtStatus status, void* context) { void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, - char* code_buf, - uint8_t code_buf_length, - FuriMutex* code_buf_update_sync) { + char* code_buffer, + uint8_t code_buffer_size, + FuriMutex* code_buffer_sync) { furi_check(context != NULL); - context->string = code_buf; - context->string_length = code_buf_length; - context->string_sync = code_buf_update_sync; + context->code_buffer = code_buffer; + context->code_buffer_size = code_buffer_size; + context->code_buffer_sync = code_buffer_sync; context->thread = furi_thread_alloc(); furi_thread_set_name(context->thread, "TOTPBtHidWorker"); furi_thread_set_stack_size(context->thread, 1024); @@ -137,7 +136,6 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() { bt_keys_storage_set_storage_path(context->bt, HID_BT_KEYS_STORAGE_PATH); #if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME - totp_type_code_worker_bt_set_app_mac(&context->bt_mac[0]); memcpy( &context->previous_bt_name[0], furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard), @@ -148,8 +146,10 @@ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() { TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN); char new_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; snprintf(new_name, sizeof(new_name), "%s TOTP Auth", furi_hal_version_get_name_ptr()); + uint8_t new_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; + totp_type_code_worker_bt_set_app_mac(new_bt_mac); furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, new_name); - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, context->bt_mac); + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, new_bt_mac); #endif if(!bt_set_profile(context->bt, BtProfileHidKeyboard)) { diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.h b/applications/external/totp/workers/bt_type_code/bt_type_code.h index edbe52e14..6c7e502c6 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.h +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.h @@ -1,49 +1,50 @@ #pragma once #include -#include -#include +#include +#include +#include +#include #include #include "../../features_config.h" #if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME -#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN 18 +#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH #define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE #endif typedef uint8_t TotpBtTypeCodeWorkerEvent; typedef struct { - char* string; - uint8_t string_length; + char* code_buffer; + uint8_t code_buffer_size; uint8_t flags; FuriThread* thread; - FuriMutex* string_sync; + FuriMutex* code_buffer_sync; Bt* bt; bool is_advertising; bool is_connected; #if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME - uint8_t bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; - char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN + 1]; + char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; #endif } TotpBtTypeCodeWorkerContext; enum TotpBtTypeCodeWorkerEvents { - TotpBtTypeCodeWorkerEventReserved = 0b0000, - TotpBtTypeCodeWorkerEventStop = 0b0100, - TotpBtTypeCodeWorkerEventType = 0b1000 + TotpBtTypeCodeWorkerEventReserved = 0b00, + TotpBtTypeCodeWorkerEventStop = 0b01, + TotpBtTypeCodeWorkerEventType = 0b10 }; TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init(); void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context); void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, - char* code_buf, - uint8_t code_buf_length, - FuriMutex* code_buf_update_sync); + char* code_buffer, + uint8_t code_buffer_size, + FuriMutex* code_buffer_sync); void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context); void totp_bt_type_code_worker_notify( TotpBtTypeCodeWorkerContext* context, TotpBtTypeCodeWorkerEvent event, - uint8_t flags); \ No newline at end of file + uint8_t flags); diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.c b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c new file mode 100644 index 000000000..4919cf942 --- /dev/null +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c @@ -0,0 +1,181 @@ +#include "generate_totp_code.h" +#include "../../services/crypto/crypto.h" +#include "../../services/totp/totp.h" +#include "../../services/convert/convert.h" +#include +#include + +#define ONE_SEC_MS (1000) + +static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY"; + +static void + int_token_to_str(uint64_t i_token_code, char* str, TokenDigitsCount len, TokenHashAlgo algo) { + str[len] = '\0'; + if(i_token_code == OTP_ERROR) { + memset(&str[0], '-', len); + } else { + if(algo == STEAM) { + for(uint8_t i = 0; i < len; i++) { + str[i] = STEAM_ALGO_ALPHABET[i_token_code % 26]; + i_token_code = i_token_code / 26; + } + } else { + for(int8_t i = len - 1; i >= 0; i--) { + str[i] = CONVERT_DIGIT_TO_CHAR(i_token_code % 10); + i_token_code = i_token_code / 10; + } + } + } +} + +static TOTP_ALGO get_totp_algo_impl(TokenHashAlgo algo) { + switch(algo) { + case SHA1: + case STEAM: + return TOTP_ALGO_SHA1; + case SHA256: + return TOTP_ALGO_SHA256; + case SHA512: + return TOTP_ALGO_SHA512; + default: + break; + } + + return NULL; +} + +static void generate_totp_code( + TotpGenerateCodeWorkerContext* context, + const TokenInfo* token_info, + uint32_t current_ts) { + if(token_info->token != NULL && token_info->token_length > 0) { + size_t key_length; + uint8_t* key = totp_crypto_decrypt( + token_info->token, token_info->token_length, context->iv, &key_length); + + int_token_to_str( + totp_at( + get_totp_algo_impl(token_info->algo), + key, + key_length, + current_ts, + context->timezone_offset, + token_info->duration), + context->code_buffer, + token_info->digits, + token_info->algo); + memset_s(key, key_length, 0, key_length); + free(key); + } else { + int_token_to_str(0, context->code_buffer, token_info->digits, token_info->algo); + } +} + +static int32_t totp_generate_worker_callback(void* context) { + furi_check(context); + + TotpGenerateCodeWorkerContext* t_context = context; + + while(true) { + uint32_t flags = furi_thread_flags_wait( + TotpGenerateCodeWorkerEventStop | TotpGenerateCodeWorkerEventForceUpdate, + FuriFlagWaitAny, + ONE_SEC_MS); + + if(flags == + (uint32_t) + FuriFlagErrorTimeout) { // If timeout, consider as no error, as we expect this and can handle gracefully + flags = 0; + } + + furi_check((flags & FuriFlagError) == 0); //-V562 + + if(flags & TotpGenerateCodeWorkerEventStop) break; + + const TokenInfo* token_info = *(t_context->token_info); + if(token_info == NULL) { + continue; + } + + uint32_t curr_ts = furi_hal_rtc_get_timestamp(); + + bool time_left = false; + if(flags & TotpGenerateCodeWorkerEventForceUpdate || + (time_left = (curr_ts % token_info->duration) == 0)) { + if(furi_mutex_acquire(t_context->code_buffer_sync, FuriWaitForever) == FuriStatusOk) { + generate_totp_code(t_context, token_info, curr_ts); + curr_ts = furi_hal_rtc_get_timestamp(); + furi_mutex_release(t_context->code_buffer_sync); + if(t_context->on_new_code_generated_handler != NULL) { + (*(t_context->on_new_code_generated_handler))( + time_left, t_context->on_new_code_generated_handler_context); + } + } + } + + if(t_context->on_code_lifetime_changed_handler != NULL) { + (*(t_context->on_code_lifetime_changed_handler))( + (float)(token_info->duration - curr_ts % token_info->duration) / + (float)token_info->duration, + t_context->on_code_lifetime_changed_handler_context); + } + } + + return 0; +} + +TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( + char* code_buffer, + TokenInfo** token_info, + FuriMutex* code_buffer_sync, + float timezone_offset, + uint8_t* iv) { + TotpGenerateCodeWorkerContext* context = malloc(sizeof(TotpGenerateCodeWorkerContext)); + furi_check(context != NULL); + context->code_buffer = code_buffer; + context->token_info = token_info; + context->code_buffer_sync = code_buffer_sync; + context->timezone_offset = timezone_offset; + context->iv = iv; + context->thread = furi_thread_alloc(); + furi_thread_set_name(context->thread, "TOTPGenerateWorker"); + furi_thread_set_stack_size(context->thread, 2048); + furi_thread_set_context(context->thread, context); + furi_thread_set_callback(context->thread, totp_generate_worker_callback); + furi_thread_start(context->thread); + return context; +} + +void totp_generate_code_worker_stop(TotpGenerateCodeWorkerContext* context) { + furi_check(context != NULL); + furi_thread_flags_set(furi_thread_get_id(context->thread), TotpGenerateCodeWorkerEventStop); + furi_thread_join(context->thread); + furi_thread_free(context->thread); + free(context); +} + +void totp_generate_code_worker_notify( + TotpGenerateCodeWorkerContext* context, + TotpGenerateCodeWorkerEvent event) { + furi_check(context != NULL); + furi_thread_flags_set(furi_thread_get_id(context->thread), event); +} + +void totp_generate_code_worker_set_code_generated_handler( + TotpGenerateCodeWorkerContext* context, + TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler, + void* on_new_code_generated_handler_context) { + furi_check(context != NULL); + context->on_new_code_generated_handler = on_new_code_generated_handler; + context->on_new_code_generated_handler_context = on_new_code_generated_handler_context; +} + +void totp_generate_code_worker_set_lifetime_changed_handler( + TotpGenerateCodeWorkerContext* context, + TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler, + void* on_code_lifetime_changed_handler_context) { + furi_check(context != NULL); + context->on_code_lifetime_changed_handler = on_code_lifetime_changed_handler; + context->on_code_lifetime_changed_handler_context = on_code_lifetime_changed_handler_context; +} \ No newline at end of file diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.h b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h new file mode 100644 index 000000000..c7a93dc95 --- /dev/null +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include "../../types/token_info.h" + +typedef uint8_t TotpGenerateCodeWorkerEvent; + +typedef void (*TOTP_NEW_CODE_GENERATED_HANDLER)(bool time_left, void* context); +typedef void (*TOTP_CODE_LIFETIME_CHANGED_HANDLER)(float code_lifetime_percent, void* context); + +typedef struct { + char* code_buffer; + FuriThread* thread; + FuriMutex* code_buffer_sync; + TokenInfo** token_info; + float timezone_offset; + uint8_t* iv; + TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler; + void* on_new_code_generated_handler_context; + TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler; + void* on_code_lifetime_changed_handler_context; +} TotpGenerateCodeWorkerContext; + +enum TotGenerateCodeWorkerEvents { + TotpGenerateCodeWorkerEventReserved = 0b00, + TotpGenerateCodeWorkerEventStop = 0b01, + TotpGenerateCodeWorkerEventForceUpdate = 0b10 +}; + +TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( + char* code_buffer, + TokenInfo** token_info, + FuriMutex* code_buffer_sync, + float timezone_offset, + uint8_t* iv); +void totp_generate_code_worker_stop(TotpGenerateCodeWorkerContext* context); +void totp_generate_code_worker_notify( + TotpGenerateCodeWorkerContext* context, + TotpGenerateCodeWorkerEvent event); +void totp_generate_code_worker_set_code_generated_handler( + TotpGenerateCodeWorkerContext* context, + TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler, + void* on_new_code_generated_handler_context); +void totp_generate_code_worker_set_lifetime_changed_handler( + TotpGenerateCodeWorkerContext* context, + TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler, + void* on_code_lifetime_changed_handler_context); \ No newline at end of file diff --git a/applications/external/totp/workers/common.c b/applications/external/totp/workers/type_code_common.c similarity index 88% rename from applications/external/totp/workers/common.c rename to applications/external/totp/workers/type_code_common.c index 8ad0c2b46..fa5e7290f 100644 --- a/applications/external/totp/workers/common.c +++ b/applications/external/totp/workers/type_code_common.c @@ -1,6 +1,6 @@ -#include "common.h" -#include -#include +#include "type_code_common.h" +#include +#include #include "../../services/convert/convert.h" static const uint8_t hid_number_keys[] = { @@ -42,18 +42,18 @@ static void totp_type_code_worker_press_key( void totp_type_code_worker_execute_automation( TOTP_AUTOMATION_KEY_HANDLER key_press_fn, TOTP_AUTOMATION_KEY_HANDLER key_release_fn, - const char* string, - uint8_t string_length, + const char* code_buffer, + uint8_t code_buffer_size, TokenAutomationFeature features) { furi_delay_ms(500); uint8_t i = 0; totp_type_code_worker_press_key( HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features); - while(i < string_length && string[i] != 0) { - uint8_t char_index = CONVERT_CHAR_TO_DIGIT(string[i]); + while(i < code_buffer_size && code_buffer[i] != 0) { + uint8_t char_index = CONVERT_CHAR_TO_DIGIT(code_buffer[i]); if(char_index > 9) { - char_index = string[i] - 0x41 + 10; + char_index = code_buffer[i] - 0x41 + 10; } if(char_index > 35) break; diff --git a/applications/external/totp/workers/common.h b/applications/external/totp/workers/type_code_common.h similarity index 83% rename from applications/external/totp/workers/common.h rename to applications/external/totp/workers/type_code_common.h index 5e3a2006e..1516928cf 100644 --- a/applications/external/totp/workers/common.h +++ b/applications/external/totp/workers/type_code_common.h @@ -7,6 +7,6 @@ typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key); void totp_type_code_worker_execute_automation( TOTP_AUTOMATION_KEY_HANDLER key_press_fn, TOTP_AUTOMATION_KEY_HANDLER key_release_fn, - const char* string, - uint8_t string_length, + const char* code_buffer, + uint8_t code_buffer_size, TokenAutomationFeature features); \ No newline at end of file diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.c b/applications/external/totp/workers/usb_type_code/usb_type_code.c index 5f7ccddf8..10034907d 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.c +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.c @@ -1,7 +1,8 @@ #include "usb_type_code.h" +#include #include "../../services/convert/convert.h" #include "../../types/token_info.h" -#include "../common.h" +#include "../type_code_common.h" static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) { if(context->usb_mode_prev != NULL) { @@ -25,14 +26,14 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex } while(!furi_hal_hid_is_connected() && i < 100 && !totp_type_code_worker_stop_requested()); if(furi_hal_hid_is_connected() && - furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) { + furi_mutex_acquire(context->code_buffer_sync, 500) == FuriStatusOk) { totp_type_code_worker_execute_automation( &furi_hal_hid_kb_press, &furi_hal_hid_kb_release, - context->string, - context->string_length, + context->code_buffer, + context->code_buffer_size, context->flags); - furi_mutex_release(context->string_sync); + furi_mutex_release(context->code_buffer_sync); furi_delay_ms(100); } @@ -43,9 +44,6 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex static int32_t totp_type_code_worker_callback(void* context) { furi_check(context); FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(context_mutex == NULL) { - return 251; - } while(true) { uint32_t flags = furi_thread_flags_wait( @@ -70,14 +68,14 @@ static int32_t totp_type_code_worker_callback(void* context) { } TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( - char* code_buf, - uint8_t code_buf_length, - FuriMutex* code_buf_update_sync) { + char* code_buffer, + uint8_t code_buffer_size, + FuriMutex* code_buffer_sync) { TotpUsbTypeCodeWorkerContext* context = malloc(sizeof(TotpUsbTypeCodeWorkerContext)); furi_check(context != NULL); - context->string = code_buf; - context->string_length = code_buf_length; - context->string_sync = code_buf_update_sync; + context->code_buffer = code_buffer; + context->code_buffer_size = code_buffer_size; + context->code_buffer_sync = code_buffer_sync; context->thread = furi_thread_alloc(); context->usb_mode_prev = NULL; furi_thread_set_name(context->thread, "TOTPUsbHidWorker"); diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.h b/applications/external/totp/workers/usb_type_code/usb_type_code.h index d0ea600ce..21213f4d4 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.h +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.h @@ -1,17 +1,20 @@ #pragma once #include -#include -#include +#include +#include +#include +#include +#include typedef uint8_t TotpUsbTypeCodeWorkerEvent; typedef struct { - char* string; - uint8_t string_length; + char* code_buffer; + uint8_t code_buffer_size; uint8_t flags; FuriThread* thread; - FuriMutex* string_sync; + FuriMutex* code_buffer_sync; FuriHalUsbInterface* usb_mode_prev; } TotpUsbTypeCodeWorkerContext; @@ -22,9 +25,9 @@ enum TotpUsbTypeCodeWorkerEvents { }; TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( - char* code_buf, - uint8_t code_buf_length, - FuriMutex* code_buf_update_sync); + char* code_buffer, + uint8_t code_buffer_size, + FuriMutex* code_buffer_sync); void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context); void totp_usb_type_code_worker_notify( TotpUsbTypeCodeWorkerContext* context, From 7922e3bb16086391f98e3e4241d277e0745f1331 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 14 Apr 2023 00:09:48 +0300 Subject: [PATCH 025/282] docs and changelogs --- CHANGELOG.md | 28 ++++++++++++++-------------- ReadMe.md | 4 ++-- documentation/HowToBuild.md | 18 +++++++++++++----- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63d36c013..21134f6a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,18 @@ ### New changes -* **SubGHz: Keyboard lock fixed** -### Previous changes -* SubGHz: AN-Motors AT4 - Add manually fixes -* SubGHz: StarLine ignore option (in Read -> Config) (by @gid9798 | PR #410) -* Plugins: Fix `Repeat: 200` bug in SubGHz Remote and Bruteforcer -* Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) (Steam guard support) -* Plugins: Update **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) (AT commands support) -* Plugins: Add IR Scope app. (by @kallanreed | PR #407) -* OFW: scripts: sconsdist: added stub file artifact for older ufbt -* OFW: Graphics cleanup and icon rotation -* OFW: Moved ufbt to fbt codebase -* OFW: SD Driver: do not cache sd status. -* OFW: Furi: more gpio checks in HAL -* OFW: WeatherStation: fix protocol TX141TH-BV2 +* If you have copied apps into `apps` folder - remove `apps` folder on your microSD before installing this release to avoid issues! +* SubGHz: (Bug that I decided to keep as a feature) You can change default button (Ok) for remote by holding custom button and pressing back at same time (same can be used to restore your button if you changed it accidentally) - Be careful, it might be unstable, I will make proper option to change button in next releases +* SubGHz: Fixes for custom button bugs in SubGHz Remote app +* SubGHz: Add alutech table to enviroment alloc and free +* Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) +* Plugins: Update **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) +* OFW: Deep Sleep Idle - **Improves battery usage!!!** +* OFW: FuriHal: pwr pulls for some pins +* OFW: Bugfix: ISP Programmer and SubGhz +* OFW: AVR_ISP: fix NULL pointer dereference +* OFW: Fix gpio state isp programmer +* OFW: ufbt: project & debugging updates +* OFW: FuriHal: fix gpio naming and add explicit pulls for vibro, speaker and ir_tx -> **Breaking API change, api was changed from 20.x to 21.x** +**(this will make your manually copied plugins not work, update them in same way you installed them, or delete `apps` folder and then install firmware, if you using extra pack builds (with `e` in version) all apps in _Extra will be updated automatically)** #### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) diff --git a/ReadMe.md b/ReadMe.md index dbe0e5bb6..ec188293d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -10,7 +10,7 @@
-Our goal is to make all features possible on this device without any limitations! +Most stable custom firmware focused on new features and improvements of original firmware components, with almost no UI changes
@@ -103,7 +103,7 @@ Encoders made by @assasinfil & @xMasterX: ## Please support development of the project The majority of this project is developed and maintained by me, @xMasterX. -I'm unemployed because of the war, and the only income I receive is from your donations. +I'm unemployed, and the only income I receive is from your donations. Our team is small and the guys are working on this project as much as they can solely based on the enthusiasm they have for this project and the community. - @assasinfil - SubGHz - @Svaarich - UI design and animations diff --git a/documentation/HowToBuild.md b/documentation/HowToBuild.md index 92bd4e418..bc0084e9a 100644 --- a/documentation/HowToBuild.md +++ b/documentation/HowToBuild.md @@ -7,7 +7,6 @@ For development: - Git -- Python3 - VSCode ## Clone the Repository @@ -16,15 +15,24 @@ You should clone with ```shell $ git clone --recursive https://github.com/DarkFlippers/unleashed-firmware.git ``` +## VSCode integration + +`fbt` includes basic development environment configuration for VS Code. Run `./fbt vscode_dist` to deploy it. That will copy the initial environment configuration to the `.vscode` folder. After that, you can use that configuration by starting VS Code and choosing the firmware root folder in the "File > Open Folder" menu. # Build on Linux/macOS Check out `documentation/fbt.md` for details on building and flashing firmware. +### Compile plugin and run it on connected flipper + +```sh +./fbt COMPACT=1 DEBUG=0 launch_app APPSRC=applications_user/yourplugin +``` + ### Compile everything for development ```sh -./fbt +./fbt FIRMWARE_APP_SET=debug_pack updater_package ``` ### Compile everything for release + get updater package to update from microSD card @@ -35,7 +43,7 @@ Check out `documentation/fbt.md` for details on building and flashing firmware. Check `dist/` for build outputs. -Use **`flipper-z-{target}-full-{suffix}.dfu`** to flash your device. +Use **`flipper-z-{target}-update-{suffix}.tgz`** to flash your device. # Build on Windows @@ -45,7 +53,7 @@ Check out `documentation/fbt.md` for details on building and flashing firmware. ### Compile everything for development ```sh -.\fbt.cmd +.\fbt.cmd FIRMWARE_APP_SET=debug_pack updater_package ``` ### Compile everything for release + get updater package to update from microSD card @@ -56,7 +64,7 @@ Check out `documentation/fbt.md` for details on building and flashing firmware. Check `dist/` for build outputs. -Use **`flipper-z-{target}-full-{suffix}.dfu`** to flash your device. +Use **`flipper-z-{target}-update-{suffix}.tgz`** to flash your device. From 21b7df9ab7ed7ded6f9672a96714b2fe190a6bfb Mon Sep 17 00:00:00 2001 From: ushastoe Date: Fri, 14 Apr 2023 01:59:16 +0300 Subject: [PATCH 026/282] refactoring -fix TikTok Controller -fix icon in Mouse --- .../hid_app/assets/Pause_icon_9x9.png | Bin 0 -> 4559 bytes .../external/hid_app/views/hid_mouse.c | 18 ++++-- .../external/hid_app/views/hid_tiktok.c | 52 ++++++++++++------ 3 files changed, 47 insertions(+), 23 deletions(-) create mode 100644 applications/external/hid_app/assets/Pause_icon_9x9.png diff --git a/applications/external/hid_app/assets/Pause_icon_9x9.png b/applications/external/hid_app/assets/Pause_icon_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..d72d712bbaa47429793bddbab0096de8d95045b4 GIT binary patch literal 4559 zcmb_e2{@Ep`+qDIilk5^(>)X17qL}Ra%XJ#G@vo$l8QAvw7WeLfeqNIjW zie#%4Ws9Ohkt_)zA(8&iw0OVoyWaPIz1Merb6wAz^PF?vzw^8A`*+THjyr6zR#ebX z002PIhGI#DTY1SNCj+Qy;zF6J+Xa^qPC0Hlssu2R|kg7RqqJglr@`Y`O6g3BV+ z1<4fW@`H^El$2u~X{!pn%}yNK7Vf-*@KL)p^VYuL!GVFv7n4Oj$vsoGqPqE^3siKY zyD#_xL~7W2(o5|~drJA^;!tT-O1msVxx4DV_pFBiFeD=i3K3r4FeP=33rGXJM*S5k z6>q;EDs<3w1rX6dJ5r@*u2d8isML3=Tm)1u2Ch5aWGDc!0KgLMHAezh6oLNFHYT%x zmsv5b>cGnjOS9yFQ&K>}D(e-}rgs5P=L1wd>9YGkn%ESnFI$!)73zK)XDuCCJPQbj zaEqF6>IsA4bxECp0$e?RzQ&g%BtdO;x{)KEGme5suB2U zsJ&x($9-0=IjFu`*-O3?nH-;_vyjpwV_Q2905m4*?@bdnMD=uczU}tvVGVwMcwyRa zh>J8?KHN54bW&OtP~K8E*u1N&t4W4eA$8rq)Gup0(Ch;Ev`u&zw8*DzQ+OIY5iTN3 znJ!B`?@+m3YpKHAxdSU{=Zi7#!cD##yydoshm66q(=EY6!0trTgzU2PL*I6ToPZo^ zL{F8hIhHftqcdF*w*Oni>iQ0R?hZZG6T@!D3mlMk6{0)TXv^h~<2qD&_v41O&wP`Q z2~-5cN9@bC+@5o*id7c!QA#>o$*my(Fbx?&oz$2c4Jdo#aiVbmh$)?jG+iqN)Wm1J z1AwCUi+5a(RkwX42LP672i9CSn=|j;B5|2g@;$AhGIeQiwx#L36=my{&1ZLpE>$?c z_>9>Sr!vw@*<06TG}0C%zE#e89DU!h0OZR@BM|qz7rAj(0Z(r z*U4Alj*~~NJde9+=vJuFyW9+2A`R(UZpHzsnq{frpxg@S@X&?*ayt(z-)9|+in4aI zb+fpx*wM>Tb{ad=O0Q7cg0Xv zC_O&ixkX|5$|#)8TpK@13k8+om|@bO%uKe`?TcbRe$y#;BUFrtkIs*pA7y`f^6`Vt z{6(aP$2x4~FRY+u#HwsEgf?`^UOb00Pe{A6wI(a|x!jdwq;uPB*l7U{Xvd>j3-SJm zD{GR}EUGe4>5WelncmECA9Sy{X3^q7p}nbBPOy>$&3OEO6e? zD7rG&>8kVmt2(LNQb(uyV6T`eLMJ-u6W}} z*v7OYlyoP0{-Y(Qma9F&LBnq5O?(%bK0Uv)cC_s`Ci-myW=(1ap!e81S_rOqiM^t| zQo487NGUzaZ66}OO5RkvyjHPx4@w5L0hQb2o9&eyl5N}^++5O38{iM94fHh+`twpu zQ}j|sNo>-%zkH!l;nu>i!WRF!4<^s(eWG6Z(I=xyy|3%7N7yjDp>NWsAcL@*hQ=4 zhnvEU!Yj@+pV?bA>x|u*aq(Ff6Bm)osLKnnP1BA(y*_r6E6YLCHECWFDXHrAP{;MmGB_E?=(^->kBrgg$~d-+2D>Hrq6 zs4wF!{y|oG)`MPGZ%yy>f|Lo(Hr#qPnx1qw zsWtl*`}yl?^22>sC++l;G#`6BAP<^-9vXM+*j@cDB{M1W{zz00dS}2+_nni)%j|C^ z>!vng*p)5i+uMt)_IQ#^$k5l?UKx`|Cer7E&mAqfRDx_Hm*|8vg(QT`nM$2p_qFO< zg3v#L%5eFYA| zA?&P5{EZD=hY;ot=*9M3nH)QRM6slGAr|M(kWHo8E})UhRH`BE}}iCZ|&snP>9WzQE+K%D4AgyEaC# zQ`F6Vq8W4a$#|#EgkYpal@(@NpPom}aZpmTMPFVUw0-gRpaM!UW!rOuHm~Uq6FPGb zs_iX~y6#YL{pQPpAot;RWU19%HJxxr^+0{B2ept9#2jkaSLIW6ODT)t&r%`bVS+uT(8I&ja3(jhI?b#De^`-^U^ z4RZA>w%Rw!Y4~{Xb)*JOv#_)~IQY;N89{eIa|g|LR-TD0uPZg}%o*QSsoTe$9N2?(3xmD+@=z1dI)+2P%BD zZS@F_47xT|(){hioUijg?9ZFdh056NUp&h^>mInzA5@Y*nmQf({n^pC4c67xjXE23 zPDQ3@YilZM6n|pW#=OnEk-4z$Nr=mLQJ>&yPusVS$<1|cQ!j_+PK4K$=7_RJ_=8uo zu8}HqDzuAgMdaa2Q`xmG`vwaJ3)?D+wG&oNnNB;5)PJQ{WInYYH|g0sJ%t`WJTi&0 zCe*AdC7%a*TWO?4z^BKa626W zd=CM-$k$Fw26+^B0RYThD0!rS#N@>QF#9~y$xY~HXG@}U+4>-ZOM~=-*gV)80M>5^ z;(>HONQj_8-b@Y|Ie51eiC{9wNLNEUj2+Jc@?lbf`H*Aq7AJbJADzfRZZJWt4>LmlTt0*#>SIuJ3iVmmUI#!Qs#t0vcqnXxbTq_**sz%ZjtV=&ljp+~as@uz zze&Q(^h_#vBnv(S3b}kIE|>MY%nrX%5I6&UEMk=%NM~{+xT_^0{c?dUK_Ns&!bLYg zVelv{&IxNk!WfYZ@M|$}2lLa^j>}+riT=YB?}WjUuvn6T(Z89(l4F2E@Shml*^z8G z0wKtuLpGLVBuq-5$z+fiSjZU9z++H&JRN3bgojWd7KcGGj17qxF9O}z$dLYn7wm7z zrTa?~DDnR(dj^*dW6b1;M8q1v{uNciiUA{95V3d*BLV@7*@(wttt`zfEePh8ICBeQoB@`IGe#m9bdndB&jw+Y zFxj9tgywO)k%&K;Sa4ZfKAcKPz9kz0MwUbX@tFY-V?4D z7mj`=AOFGJ=x=?|Gh`@C0jwg?uYK+a?f%tcF=1}7{z)KRvS-Oi0SI$O{u=x%!VCfq z1hEEivO!}*6o|(gp@@(d4u$ukVL=8Biy_kBBK`yo^nqZ=84dmr7OX7;!f#>yG!t;W zgn=L*GV_KD{ue68OH}U4tF(yOGazBvY-eDJ=xt?4$Y{_T8~^|S literal 0 HcmV?d00001 diff --git a/applications/external/hid_app/views/hid_mouse.c b/applications/external/hid_app/views/hid_mouse.c index 30a9d9d06..ca299c463 100644 --- a/applications/external/hid_app/views/hid_mouse.c +++ b/applications/external/hid_app/views/hid_mouse.c @@ -93,17 +93,23 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) { // Ok if(model->left_mouse_pressed) { - canvas_draw_icon(canvas, 81, 25, &I_Ok_btn_pressed_13x13); - } else { - canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x9); + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 81, 25, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); } + canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x9); + canvas_set_color(canvas, ColorBlack); // Back if(model->right_mouse_pressed) { - canvas_draw_icon(canvas, 108, 48, &I_Ok_btn_pressed_13x13); - } else { - canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x9); + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 108, 48, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); } + canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x9); + canvas_set_color(canvas, ColorBlack); } static void hid_mouse_process(HidMouse* hid_mouse, InputEvent* event) { diff --git a/applications/external/hid_app/views/hid_tiktok.c b/applications/external/hid_app/views/hid_tiktok.c index e1f9f4bed..d3cf4a110 100644 --- a/applications/external/hid_app/views/hid_tiktok.c +++ b/applications/external/hid_app/views/hid_tiktok.c @@ -19,6 +19,7 @@ typedef struct { bool ok_pressed; bool connected; bool is_cursor_set; + bool back_mouse_pressed; HidTransport transport; } HidTikTokModel; @@ -40,53 +41,63 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontSecondary); // Keypad circles - canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47); + canvas_draw_icon(canvas, 66, 8, &I_Circles_47x47); + + // Pause + if(model->back_mouse_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 106, 46, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 108, 48, &I_Pause_icon_9x9); + canvas_set_color(canvas, ColorBlack); // Up if(model->up_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 83, 9, &I_Pressed_Button_13x13); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 96, 11, &I_Arr_up_7x9); + canvas_draw_icon(canvas, 86, 11, &I_Arr_up_7x9); canvas_set_color(canvas, ColorBlack); // Down if(model->down_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 83, 41, &I_Pressed_Button_13x13); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 96, 44, &I_Arr_dwn_7x9); + canvas_draw_icon(canvas, 86, 44, &I_Arr_dwn_7x9); canvas_set_color(canvas, ColorBlack); // Left if(model->left_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 67, 25, &I_Pressed_Button_13x13); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 81, 29, &I_Voldwn_6x6); + canvas_draw_icon(canvas, 71, 29, &I_Voldwn_6x6); canvas_set_color(canvas, ColorBlack); // Right if(model->right_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 99, 25, &I_Pressed_Button_13x13); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 111, 29, &I_Volup_8x6); + canvas_draw_icon(canvas, 101, 29, &I_Volup_8x6); canvas_set_color(canvas, ColorBlack); // Ok if(model->ok_pressed) { - canvas_draw_icon(canvas, 91, 23, &I_Like_pressed_17x17); + canvas_draw_icon(canvas, 81, 23, &I_Like_pressed_17x17); } else { - canvas_draw_icon(canvas, 94, 27, &I_Like_def_11x9); + canvas_draw_icon(canvas, 84, 27, &I_Like_def_11x9); } // Exit canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); @@ -102,7 +113,8 @@ static void hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) { furi_delay_ms(50); } // Move cursor from the corner - hid_hal_mouse_move(hid_tiktok->hid, 20, 120); + hid_hal_mouse_move(hid_tiktok->hid, 40, 120); + hid_hal_mouse_move(hid_tiktok->hid, 0, 120); furi_delay_ms(50); } @@ -120,6 +132,8 @@ static void hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyOk) { model->ok_pressed = true; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = true; } } @@ -137,6 +151,8 @@ static void hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); } else if(event->key == InputKeyOk) { model->ok_pressed = false; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = false; } } @@ -169,16 +185,16 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) { furi_delay_ms(50); hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); consumed = true; - } else if(event->key == InputKeyUp) { - // Emulate up swipe + } else if(event->key == InputKeyDown) { + // Swipe to new video hid_hal_mouse_scroll(hid_tiktok->hid, -6); hid_hal_mouse_scroll(hid_tiktok->hid, -12); hid_hal_mouse_scroll(hid_tiktok->hid, -19); hid_hal_mouse_scroll(hid_tiktok->hid, -12); hid_hal_mouse_scroll(hid_tiktok->hid, -6); consumed = true; - } else if(event->key == InputKeyDown) { - // Emulate down swipe + } else if(event->key == InputKeyUp) { + // Swipe to previous video hid_hal_mouse_scroll(hid_tiktok->hid, 6); hid_hal_mouse_scroll(hid_tiktok->hid, 12); hid_hal_mouse_scroll(hid_tiktok->hid, 19); @@ -186,7 +202,9 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) { hid_hal_mouse_scroll(hid_tiktok->hid, 6); consumed = true; } else if(event->key == InputKeyBack) { - hid_hal_consumer_key_release_all(hid_tiktok->hid); + // Pause + hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_RIGHT); + hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_RIGHT); consumed = true; } } else if(event->type == InputTypeLong) { From 3191b327084b464685257426339e48e24c48e7f6 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 14 Apr 2023 03:35:42 +0300 Subject: [PATCH 027/282] Youtube shorts remote, swap tiktok control buttons --- applications/external/hid_app/hid.c | 19 ++ applications/external/hid_app/hid.h | 2 + applications/external/hid_app/views.h | 1 + .../external/hid_app/views/hid_tiktok.c | 16 +- .../external/hid_app/views/hid_ytshorts.c | 268 ++++++++++++++++++ .../external/hid_app/views/hid_ytshorts.h | 14 + 6 files changed, 312 insertions(+), 8 deletions(-) create mode 100644 applications/external/hid_app/views/hid_ytshorts.c create mode 100644 applications/external/hid_app/views/hid_ytshorts.h diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index 949ff63b3..b679c395c 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -10,6 +10,7 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexKeyboard, HidSubmenuIndexMedia, HidSubmenuIndexTikTok, + HidSubmenuIndexYTShorts, HidSubmenuIndexMouse, HidSubmenuIndexMouseJiggler, }; @@ -32,6 +33,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexTikTok) { app->view_id = BtHidViewTikTok; view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); + } else if(index == HidSubmenuIndexYTShorts) { + app->view_id = BtHidViewYTShorts; + view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewYTShorts); } else if(index == HidSubmenuIndexMouseJiggler) { app->view_id = HidViewMouseJiggler; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); @@ -55,6 +59,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con hid_mouse_set_connected_status(hid->hid_mouse, connected); hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); hid_tiktok_set_connected_status(hid->hid_tiktok, connected); + hid_ytshorts_set_connected_status(hid->hid_ytshorts, connected); } static void hid_dialog_callback(DialogExResult result, void* context) { @@ -113,6 +118,12 @@ Hid* hid_alloc(HidTransport transport) { HidSubmenuIndexTikTok, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, + "YT Shorts Controller", + HidSubmenuIndexYTShorts, + hid_submenu_callback, + app); } submenu_add_item( app->device_type_submenu, @@ -166,6 +177,12 @@ Hid* hid_app_alloc_view(void* context) { view_dispatcher_add_view( app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok)); + // YTShorts view + app->hid_ytshorts = hid_ytshorts_alloc(app); + view_set_previous_callback(hid_ytshorts_get_view(app->hid_ytshorts), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, BtHidViewYTShorts, hid_ytshorts_get_view(app->hid_ytshorts)); + // Mouse view app->hid_mouse = hid_mouse_alloc(app); view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view); @@ -209,6 +226,8 @@ void hid_free(Hid* app) { hid_mouse_jiggler_free(app->hid_mouse_jiggler); view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); hid_tiktok_free(app->hid_tiktok); + view_dispatcher_remove_view(app->view_dispatcher, BtHidViewYTShorts); + hid_ytshorts_free(app->hid_ytshorts); view_dispatcher_free(app->view_dispatcher); // Close records diff --git a/applications/external/hid_app/hid.h b/applications/external/hid_app/hid.h index 8ed1664a3..1ff42c940 100644 --- a/applications/external/hid_app/hid.h +++ b/applications/external/hid_app/hid.h @@ -22,6 +22,7 @@ #include "views/hid_mouse.h" #include "views/hid_mouse_jiggler.h" #include "views/hid_tiktok.h" +#include "views/hid_ytshorts.h" #define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" @@ -45,6 +46,7 @@ struct Hid { HidMouse* hid_mouse; HidMouseJiggler* hid_mouse_jiggler; HidTikTok* hid_tiktok; + HidYTShorts* hid_ytshorts; HidTransport transport; uint32_t view_id; diff --git a/applications/external/hid_app/views.h b/applications/external/hid_app/views.h index 2a44832e1..14cfa126f 100644 --- a/applications/external/hid_app/views.h +++ b/applications/external/hid_app/views.h @@ -6,5 +6,6 @@ typedef enum { HidViewMouse, HidViewMouseJiggler, BtHidViewTikTok, + BtHidViewYTShorts, HidViewExitConfirm, } HidView; \ No newline at end of file diff --git a/applications/external/hid_app/views/hid_tiktok.c b/applications/external/hid_app/views/hid_tiktok.c index d3cf4a110..3b48d32cb 100644 --- a/applications/external/hid_app/views/hid_tiktok.c +++ b/applications/external/hid_app/views/hid_tiktok.c @@ -187,20 +187,20 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) { consumed = true; } else if(event->key == InputKeyDown) { // Swipe to new video - hid_hal_mouse_scroll(hid_tiktok->hid, -6); - hid_hal_mouse_scroll(hid_tiktok->hid, -12); - hid_hal_mouse_scroll(hid_tiktok->hid, -19); - hid_hal_mouse_scroll(hid_tiktok->hid, -12); - hid_hal_mouse_scroll(hid_tiktok->hid, -6); - consumed = true; - } else if(event->key == InputKeyUp) { - // Swipe to previous video hid_hal_mouse_scroll(hid_tiktok->hid, 6); hid_hal_mouse_scroll(hid_tiktok->hid, 12); hid_hal_mouse_scroll(hid_tiktok->hid, 19); hid_hal_mouse_scroll(hid_tiktok->hid, 12); hid_hal_mouse_scroll(hid_tiktok->hid, 6); consumed = true; + } else if(event->key == InputKeyUp) { + // Swipe to previous video + hid_hal_mouse_scroll(hid_tiktok->hid, -6); + hid_hal_mouse_scroll(hid_tiktok->hid, -12); + hid_hal_mouse_scroll(hid_tiktok->hid, -19); + hid_hal_mouse_scroll(hid_tiktok->hid, -12); + hid_hal_mouse_scroll(hid_tiktok->hid, -6); + consumed = true; } else if(event->key == InputKeyBack) { // Pause hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_RIGHT); diff --git a/applications/external/hid_app/views/hid_ytshorts.c b/applications/external/hid_app/views/hid_ytshorts.c new file mode 100644 index 000000000..44ad02ff0 --- /dev/null +++ b/applications/external/hid_app/views/hid_ytshorts.c @@ -0,0 +1,268 @@ +#include "hid_ytshorts.h" +#include "../hid.h" +#include + +#include "hid_icons.h" + +#define TAG "HidYTShorts" + +struct HidYTShorts { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool connected; + bool is_cursor_set; + bool back_mouse_pressed; + HidTransport transport; +} HidYTShortsModel; + +static void hid_ytshorts_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidYTShortsModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Shorts"); + canvas_set_font(canvas, FontSecondary); + + // Keypad circles + canvas_draw_icon(canvas, 66, 8, &I_Circles_47x47); + + // Pause + if(model->back_mouse_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 106, 46, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 108, 48, &I_Pause_icon_9x9); + canvas_set_color(canvas, ColorBlack); + + // Up + if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 83, 9, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 86, 11, &I_Arr_up_7x9); + canvas_set_color(canvas, ColorBlack); + + // Down + if(model->down_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 83, 41, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 86, 44, &I_Arr_dwn_7x9); + canvas_set_color(canvas, ColorBlack); + + // Left + if(model->left_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 67, 25, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 71, 29, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorBlack); + + // Right + if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 99, 25, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 101, 29, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); + + // Ok + if(model->ok_pressed) { + canvas_draw_icon(canvas, 81, 23, &I_Like_pressed_17x17); + } else { + canvas_draw_icon(canvas, 84, 27, &I_Like_def_11x9); + } + // Exit + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); +} + +static void hid_ytshorts_reset_cursor(HidYTShorts* hid_ytshorts) { + // Set cursor to the phone's left up corner + // Delays to guarantee one packet per connection interval + for(size_t i = 0; i < 8; i++) { + hid_hal_mouse_move(hid_ytshorts->hid, -127, -127); + furi_delay_ms(50); + } + // Move cursor from the corner + hid_hal_mouse_move(hid_ytshorts->hid, 40, 120); + hid_hal_mouse_move(hid_ytshorts->hid, 0, 120); + furi_delay_ms(50); +} + +static void hid_ytshorts_process_press( + HidYTShorts* hid_ytshorts, + HidYTShortsModel* model, + InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = true; + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_consumer_key_press(hid_ytshorts->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_consumer_key_press(hid_ytshorts->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = true; + } +} + +static void hid_ytshorts_process_release( + HidYTShorts* hid_ytshorts, + HidYTShortsModel* model, + InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = false; + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_consumer_key_release(hid_ytshorts->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_consumer_key_release(hid_ytshorts->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + } else if(event->key == InputKeyBack) { + model->back_mouse_pressed = false; + } +} + +static bool hid_ytshorts_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidYTShorts* hid_ytshorts = context; + bool consumed = false; + + with_view_model( + hid_ytshorts->view, + HidYTShortsModel * model, + { + if(event->type == InputTypePress) { + hid_ytshorts_process_press(hid_ytshorts, model, event); + if(model->connected && !model->is_cursor_set) { + hid_ytshorts_reset_cursor(hid_ytshorts); + model->is_cursor_set = true; + } + consumed = true; + } else if(event->type == InputTypeRelease) { + hid_ytshorts_process_release(hid_ytshorts, model, event); + consumed = true; + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + consumed = true; + } else if(event->key == InputKeyDown) { + // Swipe to new video + hid_hal_mouse_scroll(hid_ytshorts->hid, 6); + hid_hal_mouse_scroll(hid_ytshorts->hid, 8); + hid_hal_mouse_scroll(hid_ytshorts->hid, 10); + hid_hal_mouse_scroll(hid_ytshorts->hid, 8); + hid_hal_mouse_scroll(hid_ytshorts->hid, 6); + consumed = true; + } else if(event->key == InputKeyUp) { + // Swipe to previous video + hid_hal_mouse_scroll(hid_ytshorts->hid, -6); + hid_hal_mouse_scroll(hid_ytshorts->hid, -8); + hid_hal_mouse_scroll(hid_ytshorts->hid, -10); + hid_hal_mouse_scroll(hid_ytshorts->hid, -8); + hid_hal_mouse_scroll(hid_ytshorts->hid, -6); + consumed = true; + } else if(event->key == InputKeyBack) { + // Pause + hid_hal_mouse_press(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_ytshorts->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + consumed = true; + } + } else if(event->type == InputTypeLong) { + if(event->key == InputKeyBack) { + hid_hal_consumer_key_release_all(hid_ytshorts->hid); + model->is_cursor_set = false; + consumed = false; + } + } + }, + true); + + return consumed; +} + +HidYTShorts* hid_ytshorts_alloc(Hid* bt_hid) { + HidYTShorts* hid_ytshorts = malloc(sizeof(HidYTShorts)); + hid_ytshorts->hid = bt_hid; + hid_ytshorts->view = view_alloc(); + view_set_context(hid_ytshorts->view, hid_ytshorts); + view_allocate_model(hid_ytshorts->view, ViewModelTypeLocking, sizeof(HidYTShortsModel)); + view_set_draw_callback(hid_ytshorts->view, hid_ytshorts_draw_callback); + view_set_input_callback(hid_ytshorts->view, hid_ytshorts_input_callback); + + with_view_model( + hid_ytshorts->view, + HidYTShortsModel * model, + { model->transport = bt_hid->transport; }, + true); + + return hid_ytshorts; +} + +void hid_ytshorts_free(HidYTShorts* hid_ytshorts) { + furi_assert(hid_ytshorts); + view_free(hid_ytshorts->view); + free(hid_ytshorts); +} + +View* hid_ytshorts_get_view(HidYTShorts* hid_ytshorts) { + furi_assert(hid_ytshorts); + return hid_ytshorts->view; +} + +void hid_ytshorts_set_connected_status(HidYTShorts* hid_ytshorts, bool connected) { + furi_assert(hid_ytshorts); + with_view_model( + hid_ytshorts->view, + HidYTShortsModel * model, + { + model->connected = connected; + model->is_cursor_set = false; + }, + true); +} diff --git a/applications/external/hid_app/views/hid_ytshorts.h b/applications/external/hid_app/views/hid_ytshorts.h new file mode 100644 index 000000000..03264dd36 --- /dev/null +++ b/applications/external/hid_app/views/hid_ytshorts.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidYTShorts HidYTShorts; + +HidYTShorts* hid_ytshorts_alloc(Hid* bt_hid); + +void hid_ytshorts_free(HidYTShorts* hid_ytshorts); + +View* hid_ytshorts_get_view(HidYTShorts* hid_ytshorts); + +void hid_ytshorts_set_connected_status(HidYTShorts* hid_ytshorts, bool connected); From 731c752e94ae12fadcfeef0e39e226d056aa4f72 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 14 Apr 2023 03:41:58 +0300 Subject: [PATCH 028/282] Update changelog --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21134f6a1..2544677e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,13 @@ * SubGHz: (Bug that I decided to keep as a feature) You can change default button (Ok) for remote by holding custom button and pressing back at same time (same can be used to restore your button if you changed it accidentally) - Be careful, it might be unstable, I will make proper option to change button in next releases * SubGHz: Fixes for custom button bugs in SubGHz Remote app * SubGHz: Add alutech table to enviroment alloc and free +* Docs: Fix and update docs - thanks to @lesterrry +* Plugins: Bluetooth Remote - implemented YouTube Shorts Remote (may be unstable) +* Plugins: Bluetooth Remote - improvements and fixes for TikTok remote (by @krolchonok | PR #420) +* Plugins: Implement an array for baudrates on GPS UART app (+ add 19200 baud) (by @p0ns | PR #416) * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) * Plugins: Update **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) -* OFW: Deep Sleep Idle - **Improves battery usage!!!** +* OFW: Deep Sleep Idle - **Improves battery usage!!!** -> **Breaking API change, api was changed from 21.x to 22.x** * OFW: FuriHal: pwr pulls for some pins * OFW: Bugfix: ISP Programmer and SubGhz * OFW: AVR_ISP: fix NULL pointer dereference From 0f6c2bff069d9429c93952ae026863d05c2b75f8 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 14 Apr 2023 04:04:43 +0300 Subject: [PATCH 029/282] Update changelog, disable old app --- CHANGELOG.md | 1 + applications/debug/uart_echo/application.fam | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2544677e3..f84b0985b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Plugins: Bluetooth Remote - implemented YouTube Shorts Remote (may be unstable) * Plugins: Bluetooth Remote - improvements and fixes for TikTok remote (by @krolchonok | PR #420) * Plugins: Implement an array for baudrates on GPS UART app (+ add 19200 baud) (by @p0ns | PR #416) +* Plugins: Remove UART Echo from releases since it is locked on 115200 baud, and we have **UART Terminal** with ability to set baudrate * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) * Plugins: Update **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) * OFW: Deep Sleep Idle - **Improves battery usage!!!** -> **Breaking API change, api was changed from 21.x to 22.x** diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam index 4ad14e6c3..fce9ac809 100644 --- a/applications/debug/uart_echo/application.fam +++ b/applications/debug/uart_echo/application.fam @@ -1,12 +1,12 @@ App( appid="UART_Echo", name="UART Echo", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.DEBUG, entry_point="uart_echo_app", cdefines=["APP_UART_ECHO"], requires=["gui"], stack_size=2 * 1024, order=70, fap_icon="uart_10px.png", - fap_category="GPIO", + fap_category="Debug", ) From addf9092876e3b344386863cab6af6975d3ebb00 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 14 Apr 2023 14:45:23 +0300 Subject: [PATCH 030/282] Revert Deep Sleep due to BLE issues and other random freezes Waiting for proper fix --- applications/main/fap_loader/fap_loader_app.c | 5 +- .../power_settings_app/views/battery_info.c | 51 +++++++------- .../settings/system/system_settings.c | 21 ------ debug/flipperapps.py | 3 +- documentation/FuriHalDebuging.md | 26 ------- documentation/fbt.md | 1 - firmware/targets/f18/api_symbols.csv | 11 ++- .../targets/f18/furi_hal/furi_hal_resources.c | 3 - .../targets/f18/furi_hal/furi_hal_resources.h | 3 - firmware/targets/f7/api_symbols.csv | 11 ++- firmware/targets/f7/ble_glue/ble_glue.c | 6 ++ firmware/targets/f7/furi_hal/furi_hal_clock.c | 9 +-- firmware/targets/f7/furi_hal/furi_hal_debug.c | 21 ------ firmware/targets/f7/furi_hal/furi_hal_os.c | 30 ++------ firmware/targets/f7/furi_hal/furi_hal_power.c | 69 ++++++++++--------- .../targets/f7/furi_hal/furi_hal_random.c | 42 +++++------ .../targets/f7/furi_hal/furi_hal_resources.c | 3 - .../targets/f7/furi_hal/furi_hal_resources.h | 3 - firmware/targets/f7/furi_hal/furi_hal_rtc.c | 15 ++-- .../targets/furi_hal_include/furi_hal_debug.h | 3 - .../targets/furi_hal_include/furi_hal_power.h | 6 ++ .../targets/furi_hal_include/furi_hal_rtc.h | 4 -- furi/core/check.c | 3 - furi/core/core_defines.h | 4 -- site_scons/cc.scons | 1 - site_scons/commandline.scons | 8 --- 26 files changed, 118 insertions(+), 244 deletions(-) delete mode 100644 documentation/FuriHalDebuging.md diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 47dd9cf5e..f4f2550da 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -1,7 +1,6 @@ #include "fap_loader_app.h" #include -#include #include #include @@ -24,6 +23,8 @@ struct FapLoader { Loading* loading; }; +volatile bool fap_loader_debug_active = false; + bool fap_loader_load_name_and_icon( FuriString* path, Storage* storage, @@ -133,7 +134,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader, bool ignore_mismatch) FuriThread* thread = flipper_application_spawn(loader->app, NULL); /* This flag is set by the debugger - to break on app start */ - if(furi_hal_debug_is_gdb_session_active()) { + if(fap_loader_debug_active) { FURI_LOG_W(TAG, "Triggering BP for debugger"); /* After hitting this, you can set breakpoints in your .fap's code * Note that you have to toggle breakpoints that were set before */ diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index 0956cae4f..7394fd3c5 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -4,8 +4,8 @@ #include #include -#define LOW_CHARGE_THRESHOLD (10) -#define HIGH_DRAIN_CURRENT_THRESHOLD (-100) +#define LOW_CHARGE_THRESHOLD 10 +#define HIGH_DRAIN_CURRENT_THRESHOLD 100 struct BatteryInfo { View* view; @@ -25,13 +25,14 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { char header[20] = {}; char value[20] = {}; - int32_t current = 1000.0f * data->gauge_current; + int32_t drain_current = data->gauge_current * (-1000); + uint32_t charge_current = data->gauge_current * 1000; // Draw battery canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); - if(current > 0) { + if(charge_current > 0) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); - } else if(current < HIGH_DRAIN_CURRENT_THRESHOLD) { + } else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); } else if(data->charge < LOW_CHARGE_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); @@ -43,7 +44,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { elements_bubble(canvas, 53, 0, 71, 39); // Set text - if(current > 0) { + if(charge_current > 0) { snprintf(emote, sizeof(emote), "%s", "Yummy!"); snprintf(header, sizeof(header), "%s", "Charging at"); snprintf( @@ -52,36 +53,34 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { "%lu.%luV %lumA", (uint32_t)(data->vbus_voltage), (uint32_t)(data->vbus_voltage * 10) % 10, - current); - } else if(current < 0) { + charge_current); + } else if(drain_current > 0) { snprintf( emote, sizeof(emote), "%s", - current < HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); + drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); snprintf(header, sizeof(header), "%s", "Consumption is"); snprintf( value, sizeof(value), "%ld %s", - ABS(current), - current < HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); - } else if(data->vbus_voltage > 0) { - if(data->charge_voltage_limit < 4.2) { - // Non-default battery charging limit, mention it - snprintf(emote, sizeof(emote), "Charged!"); - snprintf(header, sizeof(header), "Limited to"); - snprintf( - value, - sizeof(value), - "%lu.%luV", - (uint32_t)(data->charge_voltage_limit), - (uint32_t)(data->charge_voltage_limit * 10) % 10); - } else { - snprintf(header, sizeof(header), "Charged!"); - } + drain_current, + drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); + } else if(drain_current != 0) { + snprintf(header, 20, "..."); + } else if(data->charge_voltage_limit < 4.2) { + // Non-default battery charging limit, mention it + snprintf(emote, sizeof(emote), "Charged!"); + snprintf(header, sizeof(header), "Limited to"); + snprintf( + value, + sizeof(value), + "%lu.%luV", + (uint32_t)(data->charge_voltage_limit), + (uint32_t)(data->charge_voltage_limit * 10) % 10); } else { - snprintf(header, sizeof(header), "Napping..."); + snprintf(header, sizeof(header), "Charged!"); } canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index 8cbe62da8..868f87bb3 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -141,21 +141,6 @@ static void hand_orient_changed(VariableItem* item) { loader_update_menu(); } -const char* const sleep_method[] = { - "Default", - "Legacy", -}; - -static void sleep_method_changed(VariableItem* item) { - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, sleep_method[index]); - if(index) { - furi_hal_rtc_set_flag(FuriHalRtcFlagLegacySleep); - } else { - furi_hal_rtc_reset_flag(FuriHalRtcFlagLegacySleep); - } -} - static uint32_t system_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; @@ -233,12 +218,6 @@ SystemSettings* system_settings_alloc() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, heap_trace_mode_text[value_index]); - item = variable_item_list_add( - app->var_item_list, "Sleep Method", COUNT_OF(sleep_method), sleep_method_changed, app); - value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) ? 1 : 0; - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, sleep_method[value_index]); - view_set_previous_callback( variable_item_list_get_view(app->var_item_list), system_settings_exit); view_dispatcher_add_view( diff --git a/debug/flipperapps.py b/debug/flipperapps.py index 90582c1e4..1dc5ebd04 100644 --- a/debug/flipperapps.py +++ b/debug/flipperapps.py @@ -135,7 +135,6 @@ class FlipperAppStateHelper: self.app_list_ptr = None self.app_list_entry_type = None self._current_apps: list[AppState] = [] - self.set_debug_mode(True) def _walk_app_list(self, list_head): while list_head: @@ -196,7 +195,7 @@ class FlipperAppStateHelper: self.set_debug_mode(False) def set_debug_mode(self, mode: bool) -> None: - gdb.execute(f"set variable furi_hal_debug_gdb_session_active = {int(mode)}") + gdb.execute(f"set variable fap_loader_debug_active = {int(mode)}") # Init additional 'fap-set-debug-elf-root' command and set up hooks diff --git a/documentation/FuriHalDebuging.md b/documentation/FuriHalDebuging.md deleted file mode 100644 index 8ff770163..000000000 --- a/documentation/FuriHalDebuging.md +++ /dev/null @@ -1,26 +0,0 @@ -# Furi HAL Debugging - -Some Furi subsystem got additional debugging features that can be enabled by adding additional defines to firmware compilation. -Usually they are used for low level tracing and profiling or signal redirection/duplication. - - -## FuriHalOs - -`--extra-define=FURI_HAL_OS_DEBUG` enables tick, tick suppression, idle and time flow. - -There are 3 signals that will be exposed to external GPIO pins: - -- `AWAKE` - `PA7` - High when system is busy with computations, low when sleeping. Can be used to track transitions to sleep mode. -- `TICK` - `PA6` - Flipped on system tick, only flips when no tick suppression in progress. Can be used to track tick skew and abnormal task scheduling. -- `SECOND` - `PA4` - Flipped each second. Can be used for tracing RT issue: time flow disturbance means system doesn't conforms Hard RT. - - - -## FuriHalPower - -`--extra-define=FURI_HAL_POWER_DEBUG` enables power subsystem mode transitions tracing. - -There are 2 signals that will be exposed to external GPIO pins: - -- `WFI` - `PB2` - Light sleep (wait for interrupt) used. Basically this is lightest and most non-breaking things power save mode. All function and debug should work correctly in this mode. -- `STOP` - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible. \ No newline at end of file diff --git a/documentation/fbt.md b/documentation/fbt.md index 23b2e2b55..14d63e9ce 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -105,7 +105,6 @@ To run cleanup (think of `make clean`) for specified targets, add the `-c` optio - `--options optionfile.py` (default value `fbt_options.py`) - load a file with multiple configuration values - `--extra-int-apps=app1,app2,appN` - force listed apps to be built as internal with the `firmware` target - `--extra-ext-apps=app1,app2,appN` - force listed apps to be built as external with the `firmware_extapps` target -- `--extra-define=A --extra-define=B=C ` - extra global defines that will be passed to the C/C++ compiler, can be specified multiple times - `--proxy-env=VAR1,VAR2` - additional environment variables to expose to subprocesses spawned by `fbt`. By default, `fbt` sanitizes the execution environment and doesn't forward all inherited environment variables. You can find the list of variables that are always forwarded in the `environ.scons` file. ## Configuration diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 493f59634..468c90c53 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -901,7 +901,6 @@ Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, -Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,-,furi_hal_deinit_early,void, Function,-,furi_hal_flash_erase,void,uint8_t Function,-,furi_hal_flash_get_base,size_t, @@ -984,6 +983,7 @@ Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, Function,+,furi_hal_power_check_otg_status,void, Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" +Function,+,furi_hal_power_deep_sleep_available,_Bool, Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_otg,void, Function,+,furi_hal_power_enable_external_3_3v,void, @@ -1059,7 +1059,6 @@ Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" -Function,+,furi_hal_rtc_sync_shadow,void, Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, @@ -2150,8 +2149,6 @@ Variable,+,gpio_ext_pd0,const GpioPin, Variable,+,gpio_ext_pe4,const GpioPin, Variable,+,gpio_i2c_power_scl,const GpioPin, Variable,+,gpio_i2c_power_sda,const GpioPin, -Variable,+,gpio_ibutton,const GpioPin, -Variable,+,gpio_periph_power,const GpioPin, Variable,+,gpio_pins,const GpioPinRecord[], Variable,+,gpio_pins_count,const size_t, Variable,+,gpio_sdcard_cd,const GpioPin, @@ -2160,13 +2157,11 @@ Variable,+,gpio_speaker,const GpioPin, Variable,+,gpio_spi_d_miso,const GpioPin, Variable,+,gpio_spi_d_mosi,const GpioPin, Variable,+,gpio_spi_d_sck,const GpioPin, -Variable,+,gpio_swclk,const GpioPin, -Variable,+,gpio_swdio,const GpioPin, Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, Variable,+,gpio_usb_dp,const GpioPin, -Variable,+,gpio_vibro,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, Variable,+,message_blink_set_color_blue,const NotificationMessage, @@ -2314,6 +2309,7 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, +Variable,+,gpio_periph_power,const GpioPin, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -2368,3 +2364,4 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, +Variable,+,gpio_vibro,const GpioPin, diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index 6db483dbc..4a7015d13 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -6,9 +6,6 @@ #define TAG "FuriHalResources" -const GpioPin gpio_swdio = {.port = GPIOA, .pin = LL_GPIO_PIN_13}; -const GpioPin gpio_swclk = {.port = GPIOA, .pin = LL_GPIO_PIN_14}; - const GpioPin gpio_vibro = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; const GpioPin gpio_ibutton = {.port = GPIOB, .pin = LL_GPIO_PIN_14}; diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.h b/firmware/targets/f18/furi_hal/furi_hal_resources.h index 7d2caab36..3c4708d15 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.h @@ -50,9 +50,6 @@ extern const size_t input_pins_count; extern const GpioPinRecord gpio_pins[]; extern const size_t gpio_pins_count; -extern const GpioPin gpio_swdio; -extern const GpioPin gpio_swclk; - extern const GpioPin gpio_vibro; extern const GpioPin gpio_ibutton; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 59b8c2e8d..53985249f 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1106,7 +1106,6 @@ Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, -Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,-,furi_hal_deinit_early,void, Function,-,furi_hal_flash_erase,void,uint8_t Function,-,furi_hal_flash_get_base,size_t, @@ -1239,6 +1238,7 @@ Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, Function,+,furi_hal_power_check_otg_status,void, Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" +Function,+,furi_hal_power_deep_sleep_available,_Bool, Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_otg,void, Function,+,furi_hal_power_enable_external_3_3v,void, @@ -1338,7 +1338,6 @@ Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" -Function,+,furi_hal_rtc_sync_shadow,void, Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, @@ -4737,12 +4736,10 @@ Variable,+,gpio_ext_pc1,const GpioPin, Variable,+,gpio_ext_pc3,const GpioPin, Variable,+,gpio_i2c_power_scl,const GpioPin, Variable,+,gpio_i2c_power_sda,const GpioPin, -Variable,+,gpio_ibutton,const GpioPin, Variable,+,gpio_infrared_rx,const GpioPin, Variable,+,gpio_infrared_tx,const GpioPin, Variable,+,gpio_nfc_cs,const GpioPin, Variable,+,gpio_nfc_irq_rfid_pull,const GpioPin, -Variable,+,gpio_periph_power,const GpioPin, Variable,+,gpio_pins,const GpioPinRecord[], Variable,+,gpio_pins_count,const size_t, Variable,+,gpio_rf_sw_0,const GpioPin, @@ -4763,13 +4760,11 @@ Variable,+,gpio_spi_r_sck,const GpioPin, Variable,+,gpio_spi_r_sck_ext,const GpioPin, Variable,+,gpio_subghz_cs,const GpioPin, Variable,+,gpio_subghz_cs_ext,const GpioPin, -Variable,+,gpio_swclk,const GpioPin, -Variable,+,gpio_swdio,const GpioPin, Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, Variable,+,gpio_usb_dp,const GpioPin, -Variable,+,gpio_vibro,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, Variable,+,lfrfid_protocols,const ProtocolBase*[], @@ -4918,6 +4913,7 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, +Variable,+,gpio_periph_power,const GpioPin, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -6924,3 +6920,4 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, +Variable,+,gpio_vibro,const GpioPin, diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index a2f2f1a94..83562c73e 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -58,6 +58,12 @@ void ble_glue_init() { ble_glue = malloc(sizeof(BleGlue)); ble_glue->status = BleGlueStatusStartup; + // Configure the system Power Mode + // Select HSI as system clock source after Wake Up from Stop mode + LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); + /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */ + LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); + #ifdef BLE_GLUE_DEBUG APPD_Init(); #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index d85524ce4..cf19451ec 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -63,10 +63,6 @@ void furi_hal_clock_init() { LL_RCC_HSI_Enable(); while(!HS_CLOCK_IS_READY()) ; - /* Select HSI as system clock source after Wake Up from Stop mode - * Must be set before enabling CSS */ - LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); - LL_RCC_HSE_EnableCSS(); /* LSE and LSI1 configuration and activation */ @@ -219,14 +215,11 @@ void furi_hal_clock_switch_to_hsi() { void furi_hal_clock_switch_to_pll() { LL_RCC_HSE_Enable(); LL_RCC_PLL_Enable(); - LL_RCC_PLLSAI1_Enable(); while(!LL_RCC_HSE_IsReady()) ; while(!LL_RCC_PLL_IsReady()) ; - while(!LL_RCC_PLLSAI1_IsReady()) - ; LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); @@ -303,4 +296,4 @@ void furi_hal_clock_mco_disable() { LL_RCC_MSI_Disable(); while(LL_RCC_MSI_IsReady() != 0) ; -} +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_debug.c b/firmware/targets/f7/furi_hal/furi_hal_debug.c index 3dc03ea69..3b5dfe622 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_debug.c +++ b/firmware/targets/f7/furi_hal/furi_hal_debug.c @@ -3,26 +3,12 @@ #include #include -#include -#include - -volatile bool furi_hal_debug_gdb_session_active = false; - void furi_hal_debug_enable() { // Low power mode debug LL_DBGMCU_EnableDBGSleepMode(); LL_DBGMCU_EnableDBGStopMode(); LL_DBGMCU_EnableDBGStandbyMode(); LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); - // SWD GPIO - furi_hal_gpio_init_ex( - &gpio_swdio, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn0JTMS_SWDIO); - furi_hal_gpio_init_ex( - &gpio_swclk, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn0JTCK_SWCLK); } void furi_hal_debug_disable() { @@ -31,11 +17,4 @@ void furi_hal_debug_disable() { LL_DBGMCU_DisableDBGStopMode(); LL_DBGMCU_DisableDBGStandbyMode(); LL_EXTI_DisableIT_32_63(LL_EXTI_LINE_48); - // SWD GPIO - furi_hal_gpio_init_simple(&gpio_swdio, GpioModeAnalog); - furi_hal_gpio_init_simple(&gpio_swclk, GpioModeAnalog); } - -bool furi_hal_debug_is_gdb_session_active() { - return furi_hal_debug_gdb_session_active; -} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.c b/firmware/targets/f7/furi_hal/furi_hal_os.c index 3fc1fbea8..ee9743e62 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_os.c +++ b/firmware/targets/f7/furi_hal/furi_hal_os.c @@ -28,24 +28,11 @@ // Arbitrary (but small) number for better tick consistency #define FURI_HAL_OS_EXTRA_CNT 3 -#ifndef FURI_HAL_OS_DEBUG_AWAKE_GPIO -#define FURI_HAL_OS_DEBUG_AWAKE_GPIO (&gpio_ext_pa7) -#endif - -#ifndef FURI_HAL_OS_DEBUG_TICK_GPIO -#define FURI_HAL_OS_DEBUG_TICK_GPIO (&gpio_ext_pa6) -#endif - -#ifndef FURI_HAL_OS_DEBUG_SECOND_GPIO -#define FURI_HAL_OS_DEBUG_SECOND_GPIO (&gpio_ext_pa4) -#endif - #ifdef FURI_HAL_OS_DEBUG #include void furi_hal_os_timer_callback() { - furi_hal_gpio_write( - FURI_HAL_OS_DEBUG_SECOND_GPIO, !furi_hal_gpio_read(FURI_HAL_OS_DEBUG_SECOND_GPIO)); + furi_hal_gpio_write(&gpio_ext_pa4, !furi_hal_gpio_read(&gpio_ext_pa4)); } #endif @@ -57,11 +44,9 @@ void furi_hal_os_init() { furi_hal_idle_timer_init(); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_AWAKE_GPIO, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_TICK_GPIO, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_SECOND_GPIO, GpioModeOutputPushPull); - furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 1); - + furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); FuriTimer* second_timer = furi_timer_alloc(furi_hal_os_timer_callback, FuriTimerTypePeriodic, NULL); furi_timer_start(second_timer, FURI_HAL_OS_TICK_HZ); @@ -73,8 +58,7 @@ void furi_hal_os_init() { void furi_hal_os_tick() { if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write( - FURI_HAL_OS_DEBUG_TICK_GPIO, !furi_hal_gpio_read(FURI_HAL_OS_DEBUG_TICK_GPIO)); + furi_hal_gpio_write(&gpio_ext_pa6, !furi_hal_gpio_read(&gpio_ext_pa6)); #endif xPortSysTickHandler(); } @@ -137,14 +121,14 @@ static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) { furi_hal_idle_timer_start(FURI_HAL_OS_TICKS_TO_IDLE_CNT(expected_idle_ticks)); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 0); + furi_hal_gpio_write(&gpio_ext_pa7, 0); #endif // Go to sleep mode furi_hal_power_sleep(); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 1); + furi_hal_gpio_write(&gpio_ext_pa7, 1); #endif // Calculate how much time we spent in the sleep diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 9a87cef15..fd601ec7e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -4,8 +4,6 @@ #include #include #include -#include -#include #include #include @@ -21,16 +19,15 @@ #define TAG "FuriHalPower" -#ifndef FURI_HAL_POWER_DEBUG_WFI_GPIO -#define FURI_HAL_POWER_DEBUG_WFI_GPIO (&gpio_ext_pb2) -#endif - -#ifndef FURI_HAL_POWER_DEBUG_STOP_GPIO -#define FURI_HAL_POWER_DEBUG_STOP_GPIO (&gpio_ext_pc3) +#ifdef FURI_HAL_POWER_DEEP_SLEEP_ENABLED +#define FURI_HAL_POWER_DEEP_INSOMNIA 0 +#else +#define FURI_HAL_POWER_DEEP_INSOMNIA 1 #endif typedef struct { volatile uint8_t insomnia; + volatile uint8_t deep_insomnia; volatile uint8_t suppress_charge; uint8_t gauge_initialized; @@ -39,6 +36,7 @@ typedef struct { static volatile FuriHalPower furi_hal_power = { .insomnia = 0, + .deep_insomnia = FURI_HAL_POWER_DEEP_INSOMNIA, .suppress_charge = 0, }; @@ -81,23 +79,19 @@ const ParamCEDV cedv = { }; void furi_hal_power_init() { -#ifdef FURI_HAL_POWER_DEBUG - furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_WFI_GPIO, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_STOP_GPIO, GpioModeOutputPushPull); - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); -#endif - LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN); - LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2); furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); bq27220_init(&furi_hal_i2c_handle_power, &cedv); bq25896_init(&furi_hal_i2c_handle_power); furi_hal_i2c_release(&furi_hal_i2c_handle_power); +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_init_simple(&gpio_ext_pb2, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); +#endif + FURI_LOG_I(TAG, "Init OK"); } @@ -146,12 +140,11 @@ bool furi_hal_power_sleep_available() { return furi_hal_power.insomnia == 0; } -static inline bool furi_hal_power_deep_sleep_available() { - return furi_hal_bt_is_alive() && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) && - !furi_hal_debug_is_gdb_session_active(); +bool furi_hal_power_deep_sleep_available() { + return furi_hal_bt_is_alive() && furi_hal_power.deep_insomnia == 0; } -static inline void furi_hal_power_light_sleep() { +void furi_hal_power_light_sleep() { __WFI(); } @@ -159,15 +152,17 @@ static inline void furi_hal_power_suspend_aux_periphs() { // Disable USART furi_hal_uart_suspend(FuriHalUartIdUSART1); furi_hal_uart_suspend(FuriHalUartIdLPUART1); + // TODO: Disable USB } static inline void furi_hal_power_resume_aux_periphs() { // Re-enable USART furi_hal_uart_resume(FuriHalUartIdUSART1); furi_hal_uart_resume(FuriHalUartIdLPUART1); + // TODO: Re-enable USB } -static inline void furi_hal_power_deep_sleep() { +void furi_hal_power_deep_sleep() { furi_hal_power_suspend_aux_periphs(); while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) @@ -192,6 +187,8 @@ static inline void furi_hal_power_deep_sleep() { LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); // Prepare deep sleep + LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2); + LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2); LL_LPM_EnableDeepSleep(); #if defined(__CC_ARM) @@ -203,6 +200,13 @@ static inline void furi_hal_power_deep_sleep() { LL_LPM_EnableSleep(); + // Make sure that values differ to prevent disaster on wfi + LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0); + LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); + + LL_PWR_ClearFlag_C1STOP_C1STB(); + LL_PWR_ClearFlag_C2STOP_C2STB(); + /* Release ENTRY_STOP_MODE semaphore */ LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); @@ -216,25 +220,28 @@ static inline void furi_hal_power_deep_sleep() { LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); furi_hal_power_resume_aux_periphs(); - furi_hal_rtc_sync_shadow(); } void furi_hal_power_sleep() { if(furi_hal_power_deep_sleep_available()) { -#ifdef FURI_HAL_POWER_DEBUG - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 1); +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_write(&gpio_ext_pc3, 1); #endif + furi_hal_power_deep_sleep(); -#ifdef FURI_HAL_POWER_DEBUG - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); + +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_write(&gpio_ext_pc3, 0); #endif } else { -#ifdef FURI_HAL_POWER_DEBUG - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 1); +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_write(&gpio_ext_pb2, 1); #endif + furi_hal_power_light_sleep(); -#ifdef FURI_HAL_POWER_DEBUG - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); + +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_write(&gpio_ext_pb2, 0); #endif } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_random.c b/firmware/targets/f7/furi_hal/furi_hal_random.c index d3461c4d1..f36407cc1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_random.c +++ b/firmware/targets/f7/furi_hal/furi_hal_random.c @@ -9,35 +9,19 @@ #define TAG "FuriHalRandom" -static uint32_t furi_hal_random_read_rng() { - while(LL_RNG_IsActiveFlag_CECS(RNG) && LL_RNG_IsActiveFlag_SECS(RNG) && - !LL_RNG_IsActiveFlag_DRDY(RNG)) { - /* Error handling as described in RM0434, pg. 582-583 */ - if(LL_RNG_IsActiveFlag_CECS(RNG)) { - /* Clock error occurred */ - LL_RNG_ClearFlag_CEIS(RNG); - } - - if(LL_RNG_IsActiveFlag_SECS(RNG)) { - /* Noise source error occurred */ - LL_RNG_ClearFlag_SEIS(RNG); - - for(uint32_t i = 0; i < 12; ++i) { - const volatile uint32_t discard = LL_RNG_ReadRandData32(RNG); - UNUSED(discard); - } - } - } - - return LL_RNG_ReadRandData32(RNG); -} - uint32_t furi_hal_random_get() { while(LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID)) ; LL_RNG_Enable(RNG); - const uint32_t random_val = furi_hal_random_read_rng(); + while(!LL_RNG_IsActiveFlag_DRDY(RNG)) + ; + + if((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) { + furi_crash("TRNG error"); + } + + uint32_t random_val = LL_RNG_ReadRandData32(RNG); LL_RNG_Disable(RNG); LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0); @@ -51,7 +35,15 @@ void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len) { LL_RNG_Enable(RNG); for(uint32_t i = 0; i < len; i += 4) { - const uint32_t random_val = furi_hal_random_read_rng(); + while(!LL_RNG_IsActiveFlag_DRDY(RNG)) + ; + + if((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) { + furi_crash("TRNG error"); + } + + uint32_t random_val = LL_RNG_ReadRandData32(RNG); + uint8_t len_cur = ((i + 4) < len) ? (4) : (len - i); memcpy(&buf[i], &random_val, len_cur); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index f87f2a31a..9c0b482f7 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -6,9 +6,6 @@ #define TAG "FuriHalResources" -const GpioPin gpio_swdio = {.port = GPIOA, .pin = LL_GPIO_PIN_13}; -const GpioPin gpio_swclk = {.port = GPIOA, .pin = LL_GPIO_PIN_14}; - const GpioPin gpio_vibro = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; const GpioPin gpio_ibutton = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index 391f8f4ff..c0e32c313 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -50,9 +50,6 @@ extern const size_t input_pins_count; extern const GpioPinRecord gpio_pins[]; extern const size_t gpio_pins_count; -extern const GpioPin gpio_swdio; -extern const GpioPin gpio_swclk; - extern const GpioPin gpio_vibro; extern const GpioPin gpio_ibutton; diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index 7bd45c35d..84e7fe395 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -165,14 +165,6 @@ void furi_hal_rtc_init() { FURI_LOG_I(TAG, "Init OK"); } -void furi_hal_rtc_sync_shadow() { - if(!LL_RTC_IsShadowRegBypassEnabled(RTC)) { - LL_RTC_ClearFlag_RS(RTC); - while(!LL_RTC_IsActiveFlag_RS(RTC)) { - }; - } -} - uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg) { return LL_RTC_BAK_GetRegister(RTC, reg); } @@ -320,7 +312,12 @@ void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime) { /* Exit Initialization mode */ LL_RTC_DisableInitMode(RTC); - furi_hal_rtc_sync_shadow(); + /* If RTC_CR_BYPSHAD bit = 0, wait for synchro else this check is not needed */ + if(!LL_RTC_IsShadowRegBypassEnabled(RTC)) { + LL_RTC_ClearFlag_RS(RTC); + while(!LL_RTC_IsActiveFlag_RS(RTC)) { + }; + } /* Enable write protection */ LL_RTC_EnableWriteProtection(RTC); diff --git a/firmware/targets/furi_hal_include/furi_hal_debug.h b/firmware/targets/furi_hal_include/furi_hal_debug.h index befbb4f40..88397bbba 100644 --- a/firmware/targets/furi_hal_include/furi_hal_debug.h +++ b/firmware/targets/furi_hal_include/furi_hal_debug.h @@ -18,9 +18,6 @@ void furi_hal_debug_enable(); /** Disable MCU debug */ void furi_hal_debug_disable(); -/** Check if GDB debug session is active */ -bool furi_hal_debug_is_gdb_session_active(); - #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index 00182fa28..462e20e41 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -58,6 +58,12 @@ void furi_hal_power_insomnia_exit(); */ bool furi_hal_power_sleep_available(); +/** Check if deep sleep availble + * + * @return true if available + */ +bool furi_hal_power_deep_sleep_available(); + /** Go to sleep */ void furi_hal_power_sleep(); diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index 0d9f46f01..b16b04a68 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -30,7 +30,6 @@ typedef enum { FuriHalRtcFlagLock = (1 << 2), FuriHalRtcFlagC2Update = (1 << 3), FuriHalRtcFlagHandOrient = (1 << 4), - FuriHalRtcFlagLegacySleep = (1 << 5), } FuriHalRtcFlag; typedef enum { @@ -86,9 +85,6 @@ void furi_hal_rtc_deinit_early(); /** Initialize RTC subsystem */ void furi_hal_rtc_init(); -/** Force sync shadow registers */ -void furi_hal_rtc_sync_shadow(); - /** Get RTC register content * * @param[in] reg The register identifier diff --git a/furi/core/check.c b/furi/core/check.c index 64f9f72f1..910527cee 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -118,8 +117,6 @@ FURI_NORETURN void __furi_crash() { if(debug) { furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); furi_hal_console_puts("\033[0m\r\n"); - furi_hal_debug_enable(); - RESTORE_REGISTERS_AND_HALT_MCU(true); } else { furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h index 830bb191c..03a364abd 100644 --- a/furi/core/core_defines.h +++ b/furi/core/core_defines.h @@ -24,10 +24,6 @@ extern "C" { }) #endif -#ifndef ABS -#define ABS(a) ({ (a) < 0 ? -(a) : (a); }) -#endif - #ifndef ROUND_UP_TO #define ROUND_UP_TO(a, b) \ ({ \ diff --git a/site_scons/cc.scons b/site_scons/cc.scons index 55ab72ba6..1eb6a3376 100644 --- a/site_scons/cc.scons +++ b/site_scons/cc.scons @@ -36,7 +36,6 @@ ENV.AppendUnique( ], CPPDEFINES=[ "_GNU_SOURCE", - *GetOption("extra_defines"), ], LINKFLAGS=[ "-mcpu=cortex-m4", diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 84ef6ce19..5610478cb 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -26,14 +26,6 @@ AddOption( help="List of applications to add to firmware's built-ins. Also see FIRMWARE_APP_SET and FIRMWARE_APPS", ) -AddOption( - "--extra-define", - action="append", - dest="extra_defines", - default=[], - help="Extra global define that will be passed to C/C++ compiler, can be specified multiple times", -) - AddOption( "--extra-ext-apps", action="store", From afab1bdaad2cf813c2c2ce6fb1a10b491689c7d3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 14 Apr 2023 23:37:20 +0300 Subject: [PATCH 031/282] SubGhz - Temp Fix RAW recording and reading and remove unused file --- .../main/subghz/subghz_history_private.h | 163 ------------------ lib/subghz/protocols/raw.c | 4 +- lib/subghz/subghz_file_encoder_worker.c | 9 +- 3 files changed, 10 insertions(+), 166 deletions(-) delete mode 100644 applications/main/subghz/subghz_history_private.h diff --git a/applications/main/subghz/subghz_history_private.h b/applications/main/subghz/subghz_history_private.h deleted file mode 100644 index 12ce28fff..000000000 --- a/applications/main/subghz/subghz_history_private.h +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once - -#include "subghz_history.h" -#include - -/** - * @brief Generate filename like 000.tmp - * - * @param index - index of file, timestamp doesn't accepted! - */ -FuriString* subghz_history_generate_temp_filename(uint32_t index); - -/** - * @brief Check if directory for temporary files is exists - * - * @param instance SubGhzHistory* - * @return true - * @return false - */ -bool subghz_history_is_tmp_dir_exists(SubGhzHistory* instance); - -/** - * @brief Check SD card and create temporary dir if not exists, - * Result write_tmp_files without this unstable work is GUARANTEED - * - * @param instance - SubGhzHistory* - * @return - true all ok - * @return - false we have a problems - */ -bool subghz_history_check_sdcard(SubGhzHistory* instance); - -/** - * @brief Recursive delete dir and files and create new temp dir - * - * @param instance - SubGhzHistory* - * @return true - if all ok - * @return false - if something failed - */ -void subghz_history_clear_tmp_dir(SubGhzHistory* instance); - -/** - * @brief Free item and free all resources - * - * @param current_item - SubGhzHistoryItem* - */ -void subghz_history_item_free(void* current_item); - -/** - * @brief free all items in array - * - * @param instance - */ -void subghz_history_clean_item_array(SubGhzHistory* instance); - -/** - * @brief Write temp file fully, without splitting - * - * @param instance - SubGhzHistory* - * @param current_item - SubGhzHistoryItem* - * @param dir_path - full path to file - */ -void subghz_history_tmp_write_file_full( - SubGhzHistory* instance, - void* current_item, - FuriString* dir_path); - -/** - * @brief Write temp spited to lines - * - * @param instance - SubGhzHistory* - * @param current_item - SubGhzHistoryItem* - * @param dir_path - full path to file - * @return true - file saved - * @return false - error occurred - */ -bool subghz_history_tmp_write_file_split( - SubGhzHistory* instance, - void* current_item, - const char* dir_path); - -/** - * @brief generate random value - * - * @param min - min value - * @param max - max value - * @return uint32_t - */ -uint32_t subghz_history_rand_range(uint32_t min, uint32_t max); - -/** - * @brief write random noise signals to file applying to max line value - * - * @param file - Stream* - * @param is_negative_start - first value is negative or positive? - * @param current_position - 0 if started from beginning - * @param empty_line - add RAW_Data to this line - * @return true - * @return false - */ -bool subghz_history_write_file_noise( - Stream* file, - bool is_negative_start, - size_t current_position, - bool empty_line); - -/** - * @brief taken from flipper_format_stream_read_value_line but takes only one int32 value - * - * @param stream - Stream* - * @param _data - int32_t* output data - * @param data_size - size of data - * @return true - * @return false - */ -bool subghz_history_read_int32(Stream* stream, int32_t* _data, const uint16_t data_size); - -/** - * @brief write payload to file spliting by lines - * - * @param src - Stream* of source - * @param file - Stream* of file - * @param is_negative_start - first value is negative or positive? - * @param current_position - by default is 0 but in this value returned last position of payload - * @return true - * @return false - */ -bool subghz_history_write_file_data( - Stream* src, - Stream* file, - bool* is_negative_start, - size_t* current_position); - -/** - * @brief taken from flipper_format_stream_read_valid_key - * - * @param stream - Stream* - * @param key - FuriString* output value - * @return true - * @return false - */ -bool subghz_history_stream_read_valid_key(Stream* stream, FuriString* key); - -/** - * @brief taken from flipper_format_stream_seek_to_key - * - * @param stream - Stream* - * @param key - key - * @param strict_mode - false - * @return true - * @return false - */ -bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode); - -/** - * @brief taken from flipper_format_stream_read_value - * - * @param stream - Stream* - * @param value - FuriString* output value - * @param last - return position is last flag - * @return true - * @return false - */ -bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last); diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index eaad78b91..a82c9cf83 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -244,8 +244,8 @@ void subghz_protocol_decoder_raw_reset(void* context) { void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration) { furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; - - if(!instance->pause && (instance->upload_raw != NULL)) { + // Add check if we got duration higher than 1 second, we skipping it, temp fix + if((!instance->pause && (instance->upload_raw != NULL)) && (duration < ((uint32_t)1000000))) { if(duration > subghz_protocol_raw_const.te_short) { if(instance->last_level != level) { instance->last_level = (level ? true : false); diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 8bc6e8446..1c8b59c63 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -56,6 +56,7 @@ void subghz_file_encoder_worker_add_level_duration( bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) { char* str1; + int32_t temp_ds = 0; bool res = false; // Line sample: "RAW_Data: -1, 2, -2..." @@ -72,7 +73,13 @@ bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, co // Skip space str1 += 1; - subghz_file_encoder_worker_add_level_duration(instance, atoi(str1)); + // + temp_ds = atoi(str1); + if((temp_ds < -1000000) || (temp_ds > 1000000)) { + //FURI_LOG_I("PARSE", "Number overflow - %d", atoi(str1)); + } else { + subghz_file_encoder_worker_add_level_duration(instance, temp_ds); + } } res = true; } From 8a468fccf086d922eb506d2717455f7e532b2130 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 14 Apr 2023 23:44:18 +0300 Subject: [PATCH 032/282] Nero Radio 57bit experimental support + encoder improvements and decoder changes Please report if you find any issues with old 56bit systems that was working before this change! --- lib/subghz/blocks/generic.c | 2 +- lib/subghz/protocols/nero_radio.c | 46 +++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 331606fe5..1827388af 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -152,7 +152,7 @@ SubGhzProtocolStatus subghz_block_generic_deserialize_check_count_bit( break; } if(instance->data_count_bit != count_bit) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); + FURI_LOG_D(TAG, "Wrong number of bits in key"); ret = SubGhzProtocolStatusErrorValueBitCount; break; } diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c index d7731dca6..433b62e74 100644 --- a/lib/subghz/protocols/nero_radio.c +++ b/lib/subghz/protocols/nero_radio.c @@ -142,14 +142,22 @@ static bool //send bit 1 instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37); + if(instance->generic.data_count_bit == 57) { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)1300); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 23); + } } else { //send bit 0 instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37); + if(instance->generic.data_count_bit == 57) { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)1300); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 23); + } } return true; } @@ -164,8 +172,14 @@ SubGhzProtocolStatus &instance->generic, flipper_format, subghz_protocol_nero_radio_const.min_count_bit_for_found); - if(ret != SubGhzProtocolStatusOk) { - break; + + if((ret == SubGhzProtocolStatusErrorValueBitCount) && + (instance->generic.data_count_bit == 57)) { + ret = SubGhzProtocolStatusOk; + } else { + if(ret != SubGhzProtocolStatusOk) { + break; + } } //optional parameter parameter flipper_format_read_uint32( @@ -284,8 +298,7 @@ void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t break; case NeroRadioDecoderStepCheckDuration: if(!level) { - if(duration >= ((uint32_t)subghz_protocol_nero_radio_const.te_short * 10 + - subghz_protocol_nero_radio_const.te_delta * 2)) { + if(duration >= ((uint32_t)1250)) { //Found stop bit if(DURATION_DIFF( instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) < @@ -298,8 +311,10 @@ void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t subghz_protocol_blocks_add_bit(&instance->decoder, 1); } instance->decoder.parser_step = NeroRadioDecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_nero_radio_const.min_count_bit_for_found) { + if((instance->decoder.decode_count_bit == + subghz_protocol_nero_radio_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == + subghz_protocol_nero_radio_const.min_count_bit_for_found + 1)) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; @@ -356,10 +371,19 @@ SubGhzProtocolStatus subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolDecoderNeroRadio* instance = context; - return subghz_block_generic_deserialize_check_count_bit( + SubGhzProtocolStatus stat; + + stat = subghz_block_generic_deserialize_check_count_bit( &instance->generic, flipper_format, subghz_protocol_nero_radio_const.min_count_bit_for_found); + + if((stat == SubGhzProtocolStatusErrorValueBitCount) && + (instance->generic.data_count_bit == 57)) { + return SubGhzProtocolStatusOk; + } else { + return stat; + } } void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output) { From ab5364232f866b93a37ebbc96e629966fe6db189 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 14 Apr 2023 21:50:53 +0100 Subject: [PATCH 033/282] Updated ac.ir New additions --- assets/resources/infrared/assets/ac.ir | 28 ++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index edddec899..6059de7fa 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 21st Mar, 2023 -# Last Checked 29th Mar, 2023 +# Last Updated 14th Apr, 2023 +# Last Checked 14th Apr, 2023 # name: POWER type: raw @@ -1686,3 +1686,27 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1373 348 1310 376 463 1190 1318 400 1286 401 439 1244 442 1244 1288 400 465 1218 468 1218 468 1219 467 7970 1307 404 1281 405 435 1252 1281 406 1280 406 434 1252 434 1252 1281 406 434 1253 434 1252 434 1252 434 8000 1280 406 1281 406 434 1252 1281 406 1280 406 434 1252 434 1252 1281 406 434 1253 433 1253 433 1253 434 8000 1280 406 1280 406 434 1253 1280 406 1280 406 434 1253 433 1253 1280 406 434 1253 433 1253 433 1253 433 8001 1279 406 1280 406 434 1253 1280 407 1279 407 433 1253 434 1253 1280 407 433 1253 433 1253 433 1253 433 8001 1279 407 1279 407 433 1253 1280 407 1280 407 433 1253 433 1253 1280 407 433 1253 433 1253 434 1253 433 +# OFF +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3827 1854 505 433 507 1382 506 434 506 1383 505 434 506 1410 478 433 507 1381 507 433 507 1383 505 435 505 1382 505 1383 505 435 505 1382 506 435 505 1383 505 1382 506 1384 504 1381 507 434 506 434 506 1382 506 1382 506 432 508 434 506 436 504 433 507 1382 506 435 505 434 506 436 504 1381 507 1383 505 434 506 1382 506 1381 507 434 506 1381 507 1381 507 1383 505 435 505 434 506 435 505 433 507 1383 505 435 505 435 505 434 506 1381 507 433 507 435 505 434 506 1382 506 433 507 434 506 433 507 438 502 434 506 433 507 434 506 434 506 432 508 435 505 434 506 433 507 433 507 1387 501 435 505 436 504 433 507 433 507 435 505 433 507 434 506 434 506 435 505 435 505 432 508 1410 478 461 479 432 508 435 505 434 506 433 507 436 504 434 506 434 506 433 507 435 505 1383 505 435 505 1381 507 1381 507 1382 506 1382 506 1381 507 435 505 433 507 433 507 461 478 1383 505 433 507 434 506 +# +name: TEMP- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3778 1905 506 434 506 1381 507 436 504 1381 507 435 505 1382 506 432 508 1380 508 433 507 1381 507 432 508 1380 508 1381 507 432 508 1380 508 432 508 1380 508 1380 508 1380 508 1380 508 433 507 432 508 1380 508 1380 508 432 508 431 509 432 508 433 507 1380 508 432 508 432 508 431 509 433 507 1380 508 434 506 1380 508 1380 508 434 506 1381 507 1380 508 1380 508 433 507 431 509 433 507 1381 507 1381 507 432 508 431 509 431 509 1381 507 431 509 432 508 432 508 1381 507 431 509 432 508 432 508 432 508 432 508 432 508 435 505 432 508 432 508 431 509 432 508 432 508 434 506 1379 509 432 508 432 508 433 507 431 509 433 507 432 508 433 507 432 508 432 508 432 508 431 509 1380 508 432 508 432 508 1380 508 431 509 433 507 432 508 432 508 432 508 431 509 432 508 1381 507 431 509 1382 506 1380 508 1380 508 1380 508 1380 507 434 506 433 507 433 507 431 509 1380 508 1380 508 432 508 +# +name: TEMP+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3828 1855 504 431 508 1382 506 434 506 1382 506 434 506 1381 507 432 508 1381 507 434 506 1381 560 381 506 1381 507 1410 478 433 507 1382 506 436 504 1381 507 1382 506 1409 479 1382 506 433 507 433 507 1381 507 1383 505 435 505 434 506 432 508 433 507 1381 507 433 507 435 505 432 508 1381 507 1382 506 433 507 1382 506 1380 508 433 507 1382 506 1379 509 1381 507 432 508 433 507 433 507 1381 507 1380 508 433 507 433 507 432 508 1380 508 432 508 432 508 434 506 1380 508 432 508 432 508 432 508 432 508 434 506 434 506 433 507 433 507 434 506 432 508 433 507 432 508 435 505 1383 505 433 507 433 507 434 506 433 507 433 507 432 508 433 507 434 506 433 507 433 507 432 508 1382 506 432 508 431 509 1381 507 433 507 434 506 434 506 432 508 433 507 434 506 433 507 1380 508 431 509 1381 507 1409 479 1382 506 1383 505 1381 507 435 505 433 507 433 507 1382 506 1380 508 1382 506 433 507 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3778 1903 508 432 508 1380 508 431 509 1379 509 430 510 1379 509 431 509 1382 506 432 508 1378 510 431 509 1379 509 1381 507 431 509 1379 509 432 508 1379 509 1379 509 1380 508 1381 507 431 509 431 509 1380 508 1379 509 430 510 431 509 431 509 432 508 1379 509 430 510 432 508 431 508 1379 509 430 510 1379 509 1381 507 430 509 432 508 431 509 431 509 1378 510 431 509 431 509 431 509 1380 508 1378 510 432 508 430 510 1379 509 1380 508 431 509 432 508 432 508 1379 509 432 508 431 509 431 509 432 508 431 509 431 509 431 508 431 509 432 508 431 509 431 509 431 509 431 509 1381 507 430 510 431 509 431 509 431 509 431 509 431 509 431 509 430 510 431 509 431 509 432 508 1378 510 431 509 431 509 430 510 431 509 432 508 431 509 431 509 430 510 431 509 431 509 1379 509 431 509 1379 509 1380 508 1379 509 1379 509 1379 509 432 508 431 509 431 509 1379 509 431 509 431 509 1379 509 From 3fc43e8ebd46bb8c7128fbaf47ff2e14fb283968 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 14 Apr 2023 21:51:28 +0100 Subject: [PATCH 034/282] Updated audio.ir Updated last checked --- assets/resources/infrared/assets/audio.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 0adb61efb..4a3fb2d79 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 29th Mar, 2023 -# Last Checked 29th Mar, 2023 +# Last Checked 14th Apr, 2023 # name: POWER type: parsed From c1fc15225184a1b79896f4cd06a335d1824bbd22 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 14 Apr 2023 21:52:07 +0100 Subject: [PATCH 035/282] Updated fans.ir New additions --- assets/resources/infrared/assets/fans.ir | 58 +++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index f451ba483..0aceebb35 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 15th Feb, 2023 -# Last Checked 29th Mar, 2023 +# Last Updated 14th Apr, 2023 +# Last Checked 14th Apr, 2023 # name: POWER type: raw @@ -1419,3 +1419,57 @@ type: parsed protocol: NECext address: 80 DE 00 00 command: 10 EF 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4640 4393 562 1442 562 1443 562 1443 537 1470 535 1468 562 1444 562 2398 562 1444 562 1446 560 2424 535 1470 535 2425 534 1472 533 1473 533 2427 533 1474 531 1473 4577 4456 531 1473 531 1474 532 1474 531 1474 532 1474 531 1474 531 2429 531 1474 531 1474 531 2429 531 1474 531 2429 531 1474 531 1474 531 2429 531 1474 531 14007 9125 2259 530 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4609 4425 563 1441 563 1442 563 1442 538 1469 537 1469 561 1444 562 2400 560 1470 535 1470 536 1470 536 2424 536 1470 535 1471 534 1472 533 2427 533 2427 533 1472 4580 4454 531 1472 533 1474 532 1474 531 1474 532 1474 532 1474 532 2428 532 1474 532 1474 532 1474 532 2428 532 1474 532 1474 532 1474 532 2428 532 2429 532 14008 9127 2258 528 50213 9131 2253 532 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4607 4424 563 1441 563 1443 563 1444 536 1469 537 1469 537 1496 534 2399 561 1470 535 2424 536 1470 536 1470 535 2424 535 1471 534 1472 534 1472 533 2427 533 1472 4579 4455 531 1472 532 1474 532 1474 532 1474 532 1474 532 1474 532 2429 531 1474 532 2428 532 1474 532 1474 532 2429 532 1474 532 1474 532 1474 532 2428 532 14007 9125 2258 530 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4604 4427 561 1442 562 1443 561 1444 537 1469 536 1469 562 1445 561 2399 560 1446 559 2424 534 1472 533 2426 533 1473 532 2428 531 1475 531 1474 531 1474 531 1473 4575 4457 530 1473 531 1475 531 1475 530 1475 531 1475 531 1475 530 2429 531 1475 530 2429 530 1475 530 2430 530 1475 530 2429 531 1475 531 1475 531 1475 530 14008 9122 2260 530 +# +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4582 4424 562 1442 562 1471 534 1471 509 1496 510 1496 534 1472 534 2424 536 1470 536 1470 536 1470 536 1470 535 2424 535 2425 534 2426 534 1472 533 1473 533 1472 4581 4453 532 1472 532 1473 533 1473 533 1473 533 1473 533 1473 533 2428 532 1473 532 1473 532 1474 532 1474 532 2428 533 2428 532 2428 532 1474 532 1474 532 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1369 311 1327 312 498 1162 1286 355 1285 362 457 1221 458 1221 458 1221 458 1222 457 1221 458 1219 1312 6796 1310 330 1282 356 454 1219 1281 360 1280 367 452 1227 452 1227 452 1227 452 1227 452 1227 452 1225 1280 6815 1280 359 1279 359 451 1220 1280 361 1279 368 451 1228 451 1228 451 1228 451 1228 451 1228 451 1226 1279 6827 1279 382 1256 382 428 1245 1255 384 1256 391 427 1251 428 1252 426 1252 427 1252 427 1252 427 1250 1255 6838 1255 383 1255 383 426 1245 1255 385 1255 392 426 1252 427 1252 426 1252 426 1252 427 1252 426 1250 1255 6849 1255 383 1255 383 426 1245 1255 385 1254 392 426 1252 426 1252 426 1252 426 1252 426 1252 426 1250 1254 6835 1254 383 1254 383 426 1245 1254 385 1254 392 426 1252 426 1252 426 1252 426 1252 426 1252 426 1250 1254 6852 1254 383 1254 384 425 1245 1254 386 1253 392 426 1253 425 1252 426 1252 426 1253 425 1253 425 1251 1253 6835 1253 384 1253 384 425 1245 1253 386 1253 393 425 1252 425 1253 425 1253 425 1253 425 1253 425 1251 1253 6852 1252 384 1253 384 425 1246 1252 386 1253 393 425 1253 424 1253 425 1253 425 1254 424 1253 425 1252 1252 6835 1253 385 1252 385 424 1247 1252 387 1252 394 424 1254 424 1254 424 1254 424 1254 424 1254 424 1252 1251 6850 1251 386 1251 386 423 1248 1251 388 1250 395 423 1255 422 1256 422 1279 399 1280 398 1279 399 1277 1227 6862 1226 411 1226 411 398 1273 1226 413 1226 420 397 1280 398 1279 398 1280 397 1280 398 1280 398 1278 1226 6892 1226 411 1225 411 397 1273 1225 413 1225 420 397 1280 397 1280 398 1280 397 1280 397 1280 397 1279 1224 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1308 332 1305 332 479 1190 1309 333 1307 364 455 1199 479 1198 481 1198 480 1198 480 1197 1331 339 480 7657 1306 332 1305 332 479 1194 1304 335 1305 342 478 1201 477 1201 478 1201 477 1201 478 1199 1305 342 478 7647 1305 333 1304 333 478 1194 1304 335 1304 342 477 1201 477 1201 478 1201 477 1201 477 1199 1305 342 478 7660 1303 334 1304 333 478 1194 1304 336 1303 343 477 1202 477 1202 476 1202 477 1202 476 1200 1304 343 477 7647 1303 334 1304 334 476 1195 1303 336 1303 343 476 1202 476 1202 476 1202 476 1202 476 1200 1303 343 476 7659 1302 335 1302 335 476 1196 1302 337 1302 344 475 1203 475 1203 475 1203 475 1203 475 1201 1302 344 476 7646 1302 335 1300 338 474 1197 1300 338 1301 345 474 1204 474 1204 474 1204 449 1229 449 1227 1275 370 449 7690 1275 362 1275 361 449 1222 1275 364 1275 371 448 1230 448 1230 448 1231 447 1232 446 1229 1274 372 447 +# +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1338 312 1380 310 446 1133 1313 356 1284 362 457 1221 457 1219 1285 363 456 1221 457 1222 482 1196 482 7655 1282 356 1281 357 453 1219 1279 360 1279 367 452 1226 452 1224 1280 367 452 1226 452 1226 452 1226 452 7671 1279 358 1279 358 452 1219 1279 360 1279 367 452 1227 451 1225 1279 367 451 1227 451 1227 451 1227 451 7682 1279 359 1278 359 451 1220 1278 361 1278 368 451 1227 450 1225 1278 368 451 1227 451 1228 450 1228 450 7691 1277 360 1277 360 450 1221 1277 362 1277 368 450 1228 449 1226 1277 369 449 1229 449 1252 426 1252 426 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1338 311 1326 312 497 1134 1364 311 1276 361 457 1221 457 1220 457 1218 1283 361 456 1221 456 1221 457 7676 1285 351 1285 351 457 1213 1286 353 1285 360 457 1220 457 1220 457 1219 1285 361 456 1221 456 1222 481 7639 1283 353 1284 353 455 1216 1283 356 1282 363 454 1223 455 1223 454 1221 1283 363 454 1223 454 1223 454 7676 1282 354 1282 354 454 1217 1282 356 1282 364 453 1223 454 1223 454 1221 1282 363 454 1224 453 1224 454 7681 1281 355 1281 355 453 1217 1281 357 1281 364 453 1225 452 1225 452 1222 1281 365 452 1225 452 1225 452 From 3ac478dfda55981213eafe6dac1987299549d747 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 14 Apr 2023 21:52:38 +0100 Subject: [PATCH 036/282] Updated projectors.ir new additions --- assets/resources/infrared/assets/projectors.ir | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index 92f5a8eeb..c039f2389 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 07th Mar, 2023 -# Last Checked 29th Mar, 2023 +# Last Updated 14th Apr, 2023 +# Last Checked 14th Apr, 2023 # # ON name: POWER @@ -874,3 +874,15 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 9035 4437 563 548 563 548 563 522 594 1645 591 1639 592 518 593 548 563 552 563 1640 592 548 563 553 562 1668 564 524 592 1642 594 1674 562 1673 563 1639 593 548 563 552 564 1669 562 548 563 520 615 529 586 1645 587 529 587 1650 586 1646 586 529 586 1650 586 1649 587 1646 586 524 587 524 587 524 587 524 587 525 643 467 644 440 671 467 644 472 643 1592 644 1593 643 1593 642 1594 641 1594 587 1649 585 1651 563 1682 562 14430 9008 2205 562 +# +name: VOL+ +type: parsed +protocol: NECext +address: 84 F4 00 00 +command: 2C D3 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 84 F4 00 00 +command: 2F D0 00 00 From a81ba240bec5ce2a6bc5b825c991bf7da5ae9fc3 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 14 Apr 2023 21:53:12 +0100 Subject: [PATCH 037/282] Updated tv.ir Updated last checked --- assets/resources/infrared/assets/tv.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index fe2108537..4d9c38f98 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 07th Mar, 2023 -# Last Checked 29th Mar, 2023 +# Last Checked 14th Apr, 2023 # name: POWER type: parsed From 0f48a14181389632ee91144ccf6e1fd013311cb6 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 14 Apr 2023 22:06:55 +0100 Subject: [PATCH 038/282] Updated ac.ir new additions --- assets/resources/infrared/assets/ac.ir | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 6059de7fa..bf047c5e7 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1710,3 +1710,33 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 3778 1903 508 432 508 1380 508 431 509 1379 509 430 510 1379 509 431 509 1382 506 432 508 1378 510 431 509 1379 509 1381 507 431 509 1379 509 432 508 1379 509 1379 509 1380 508 1381 507 431 509 431 509 1380 508 1379 509 430 510 431 509 431 509 432 508 1379 509 430 510 432 508 431 508 1379 509 430 510 1379 509 1381 507 430 509 432 508 431 509 431 509 1378 510 431 509 431 509 431 509 1380 508 1378 510 432 508 430 510 1379 509 1380 508 431 509 432 508 432 508 1379 509 432 508 431 509 431 509 432 508 431 509 431 509 431 508 431 509 432 508 431 509 431 509 431 509 431 509 1381 507 430 510 431 509 431 509 431 509 431 509 431 509 431 509 430 510 431 509 431 509 432 508 1378 510 431 509 431 509 430 510 431 509 432 508 431 509 431 509 430 510 431 509 431 509 1379 509 431 509 1379 509 1380 508 1379 509 1379 509 1379 509 432 508 431 509 431 509 1379 509 431 509 431 509 1379 509 +# +name: POWER +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 04 00 00 00 +# +name: TEMP+ +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 05 00 00 00 +# +name: TEMP- +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 01 00 00 00 +# +name: MODE +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 0D 00 00 00 +# +name: TIMER +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 00 00 00 00 From 0717061212ef8905e9f5f61e3a37b57d2d20493d Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 14 Apr 2023 22:07:16 +0100 Subject: [PATCH 039/282] Updated audio.ir New additions --- assets/resources/infrared/assets/audio.ir | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 4a3fb2d79..c39ade434 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,6 +1,6 @@ Filetype: IR library file Version: 1 -# Last Updated 29th Mar, 2023 +# Last Updated 14th Apr, 2023 # Last Checked 14th Apr, 2023 # name: POWER @@ -2134,3 +2134,15 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 2701 861 496 420 445 444 444 885 446 871 892 441 444 441 444 441 471 415 471 415 470 417 467 444 440 446 883 891 436 449 437 449 437 450 436 450 436 449 883 448 437 451 436 894 436 450 436 450 436 450 436 450 436 450 882 449 436 894 883 116552 2698 862 469 448 438 449 438 894 437 880 884 448 437 449 437 448 438 449 437 449 437 449 437 449 437 449 884 890 437 449 437 449 437 449 437 449 437 449 884 448 437 450 437 893 437 449 437 449 437 449 437 449 437 449 884 448 436 894 883 +# +name: VOL+ +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 3C C3 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 4D B2 00 00 From 59c87c819ac6a7ae6be6a7cd312b5cf9bcc95c98 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 14 Apr 2023 22:07:40 +0100 Subject: [PATCH 040/282] Updated fans.ir New additions --- assets/resources/infrared/assets/fans.ir | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index 0aceebb35..134133e17 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1473,3 +1473,27 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1338 311 1326 312 497 1134 1364 311 1276 361 457 1221 457 1220 457 1218 1283 361 456 1221 456 1221 457 7676 1285 351 1285 351 457 1213 1286 353 1285 360 457 1220 457 1220 457 1219 1285 361 456 1221 456 1222 481 7639 1283 353 1284 353 455 1216 1283 356 1282 363 454 1223 455 1223 454 1221 1283 363 454 1223 454 1223 454 7676 1282 354 1282 354 454 1217 1282 356 1282 364 453 1223 454 1223 454 1221 1282 363 454 1224 453 1224 454 7681 1281 355 1281 355 453 1217 1281 357 1281 364 453 1225 452 1225 452 1222 1281 365 452 1225 452 1225 452 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3565 3379 984 2506 984 2505 985 797 900 2590 900 2590 926 2572 926 826 925 829 922 858 894 859 894 858 895 2604 894 859 894 859 894 2596 894 859 894 859 894 867 894 2596 894 2596 894 2596 894 2596 894 2596 894 867 894 40343 3530 3468 895 2596 894 2596 894 859 894 2596 894 2596 894 2604 894 859 894 859 894 859 894 859 894 859 894 2604 894 859 894 859 894 2596 894 859 894 859 894 867 894 2596 894 2596 894 2596 894 2596 895 2596 894 866 895 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3595 3377 930 2561 929 2561 929 853 900 2591 899 2590 900 2597 901 2590 925 2565 924 2567 922 2570 920 2596 894 866 895 858 895 858 895 2596 894 858 895 859 894 866 895 858 895 858 895 858 895 859 894 858 895 2604 894 40343 3533 3467 895 2595 895 2596 894 858 895 2596 894 2596 894 2604 895 2596 894 2596 894 2596 894 2596 894 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 859 894 859 894 859 894 859 894 2605 893 40319 3533 3442 920 2595 895 2596 894 858 895 2596 894 2596 894 2604 894 2596 894 2596 894 2596 894 2596 894 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 859 894 859 894 859 894 859 894 2604 894 +# +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3594 3380 981 2505 930 2560 930 853 900 2590 900 2590 900 2598 900 2589 900 853 924 2568 921 2571 919 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 2596 894 859 894 859 894 859 894 2604 894 40335 3532 3467 895 2596 894 2596 895 859 894 2596 894 2596 894 2605 894 2596 894 859 894 2596 894 2597 894 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 2597 894 859 894 859 894 859 894 2605 893 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3534 3439 954 2505 985 2535 955 798 954 2535 899 2591 899 2599 899 853 925 2566 924 2567 922 2596 894 2596 894 867 894 859 894 860 893 2597 894 859 894 859 894 867 894 2597 893 859 894 859 894 860 893 859 894 2605 894 40336 3531 3469 894 2597 893 2597 893 859 894 2597 893 2597 893 2605 893 860 893 2597 893 2597 893 2597 894 2597 893 868 893 860 893 860 893 2597 893 860 893 860 893 868 893 2597 893 860 893 860 893 860 893 860 893 2605 893 From 591a36246f96a52397d47c1a6d2d80bc9c3d2bfd Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 14 Apr 2023 22:32:32 +0100 Subject: [PATCH 041/282] Updated audio.ir New additions --- assets/resources/infrared/assets/audio.ir | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index c39ade434..7e93cb3b8 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -2146,3 +2146,33 @@ type: parsed protocol: NECext address: 10 E7 00 00 command: 4D B2 00 00 +# +name: POWER +type: parsed +protocol: Kaseikyo +address: 52 54 32 00 +command: 83 00 00 00 +# +name: MUTE +type: parsed +protocol: Kaseikyo +address: 52 54 32 00 +command: 86 00 00 00 +# +name: VOL+ +type: parsed +protocol: Kaseikyo +address: 52 54 32 00 +command: 84 00 00 00 +# +name: VOL- +type: parsed +protocol: Kaseikyo +address: 52 54 32 00 +command: 85 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 46 00 00 00 From 1a17699356f2fc6b08da86dd1c0c58c9b81d60a5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 16 Apr 2023 05:02:36 +0300 Subject: [PATCH 042/282] Improve nero radio encoder timings --- lib/subghz/protocols/nero_radio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c index 433b62e74..7e787ffd0 100644 --- a/lib/subghz/protocols/nero_radio.c +++ b/lib/subghz/protocols/nero_radio.c @@ -117,8 +117,7 @@ static bool } //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short * 4); + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)830); instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short); From de02a0a25ad1d9b2637bf16bdb78b97b2a38f6ab Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sun, 16 Apr 2023 22:36:15 -0700 Subject: [PATCH 043/282] [#2589] Correctly aborts when correct key is found (#2590) --- applications/external/picopass/picopass_worker.c | 5 ++++- .../picopass/scenes/picopass_scene_elite_dict_attack.c | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/applications/external/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c index 174413bae..e671552c5 100644 --- a/applications/external/picopass/picopass_worker.c +++ b/applications/external/picopass/picopass_worker.c @@ -570,7 +570,7 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); break; } - picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context); + picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); break; } @@ -596,6 +596,9 @@ int32_t picopass_worker_task(void* context) { picopass_worker_write_key(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) { picopass_worker_elite_dict_attack(picopass_worker); + } else if(picopass_worker->state == PicopassWorkerStateStop) { + FURI_LOG_D(TAG, "Worker state stop"); + // no-op } else { FURI_LOG_W(TAG, "Unknown state %d", picopass_worker->state); } diff --git a/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c b/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c index c76a8ffae..e6191d5ba 100644 --- a/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c +++ b/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c @@ -116,8 +116,7 @@ bool picopass_scene_elite_dict_attack_on_event(void* context, SceneManagerEvent uint32_t state = scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack); if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassWorkerEventSuccess || - event.event == PicopassWorkerEventAborted) { + if(event.event == PicopassWorkerEventSuccess) { if(state == DictAttackStateUserDictInProgress || state == DictAttackStateStandardDictInProgress) { picopass_worker_stop(picopass->worker); @@ -127,6 +126,9 @@ bool picopass_scene_elite_dict_attack_on_event(void* context, SceneManagerEvent scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); consumed = true; } + } else if(event.event == PicopassWorkerEventAborted) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); + consumed = true; } else if(event.event == PicopassWorkerEventCardDetected) { dict_attack_set_card_detected(picopass->dict_attack); consumed = true; From 62da755431515004b0b8389a5692e7df25914155 Mon Sep 17 00:00:00 2001 From: Jacob Witt Date: Mon, 17 Apr 2023 20:38:18 -0500 Subject: [PATCH 044/282] Flip slashes for Windows builds (Backslash -> Forward Slash) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tried building this myself using these directions, kept getting errors. Flipped the backslash to a forward slash, and got a successful build. 🤷‍♂️ --- documentation/HowToBuild.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/HowToBuild.md b/documentation/HowToBuild.md index bc0084e9a..e6e85d886 100644 --- a/documentation/HowToBuild.md +++ b/documentation/HowToBuild.md @@ -53,13 +53,13 @@ Check out `documentation/fbt.md` for details on building and flashing firmware. ### Compile everything for development ```sh -.\fbt.cmd FIRMWARE_APP_SET=debug_pack updater_package +./fbt.cmd FIRMWARE_APP_SET=debug_pack updater_package ``` ### Compile everything for release + get updater package to update from microSD card ```sh -.\fbt.cmd COMPACT=1 DEBUG=0 updater_package +./fbt.cmd COMPACT=1 DEBUG=0 updater_package ``` Check `dist/` for build outputs. From d1df16a2bfe810b3bb752c6927d968bd9901afa2 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 18 Apr 2023 14:15:04 +0300 Subject: [PATCH 045/282] Improve wifi marauder keyboard Port uart terminal keyboard into wifi marauder --- .../wifi_marauder_companion/application.fam | 1 + .../assets/KeyBackspaceSelected_16x9.png | Bin 0 -> 1812 bytes .../assets/KeyBackspace_16x9.png | Bin 0 -> 1829 bytes .../assets/KeySaveSelected_24x11.png | Bin 0 -> 1853 bytes .../assets/KeySave_24x11.png | Bin 0 -> 1863 bytes .../assets/WarningDolphin_45x42.png | Bin 0 -> 1139 bytes .../scenes/wifi_marauder_scene_text_input.c | 26 +- .../wifi_marauder_app.c | 8 +- .../wifi_marauder_app_i.h | 4 +- .../wifi_marauder_text_input.c | 625 ++++++++++++++++++ .../wifi_marauder_text_input.h | 82 +++ .../wifi_marauder_validators.c | 57 ++ .../wifi_marauder_validators.h | 21 + 13 files changed, 806 insertions(+), 18 deletions(-) create mode 100644 applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png create mode 100644 applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png create mode 100644 applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png create mode 100644 applications/external/wifi_marauder_companion/assets/KeySave_24x11.png create mode 100644 applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png create mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_text_input.c create mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_text_input.h create mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_validators.c create mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_validators.h diff --git a/applications/external/wifi_marauder_companion/application.fam b/applications/external/wifi_marauder_companion/application.fam index 66255706a..d7bde185e 100644 --- a/applications/external/wifi_marauder_companion/application.fam +++ b/applications/external/wifi_marauder_companion/application.fam @@ -8,4 +8,5 @@ App( order=90, fap_icon="wifi_10px.png", fap_category="GPIO", + fap_icon_assets="assets", ) diff --git a/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png b/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png new file mode 100644 index 0000000000000000000000000000000000000000..7cc0759a8ca6acdb9b9c2e3dc00edde2f0e93a67 GIT binary patch literal 1812 zcmcIl&x_()N{W z-JN@vFI~T+Y1-v}FWiIo6?k5J;aT|qw)dv2CwcE-scA1=t)FMK&%d~)Y0rO}4EC%2 z=aK4R$F?2(hE6fX7H(UFBH{$t4v4EaKLer_Vi@d&Z#A)C)-lFal?RqJo6XEw%T&e4 zBEIiim|Bz~ut4QeR$2KDgeVQ)Gl9#&Q7)}LS*mHl<@TY>s++4|`B+t|9IGdATYvr+ zL&4Vp^Jy_zlt*w&PGkz$CD@V$zdYy`l2xi0C^cC%YIhY;r^KZCtp`aa)U15HX4E*y zkX5o{K-UPu6k#$T&@w-;?b`$g7%xpD(1BnTyO^;O$?)hRrco61v$A3tm;JC~04Xy` zM9{K5&YYn{cI-;z+O~({*abcDHnS;pNXu(4c!7VY__VG>?Z1?*P#iGU)eM+zGa?B_ zvgI?>CU%T`*JH?wZ6SP@IW~7zXzvsW>>M^Zjasu3fJoi8ARJ`vf+uoa+eOUrBobcB zw>cJ!4w<1pj@wleRYXcabz7&```zwtp@zu>K9qa+w)FmX*CD>+AZijr7d#lMB4r@7 zBxNIM<=Lo~JFR)Z8qvz(k!=8Gk?gq@8g zfS#k0rCF(l)r=K#a|A8Qr4h!%R?w1c= z{pq*skHW8}pZxgsP>68$^3NZ9>0PQ& Cw>kg- literal 0 HcmV?d00001 diff --git a/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png b/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png new file mode 100644 index 0000000000000000000000000000000000000000..9946232d953ef1cbfbf0e6754be6645e5ea2747b GIT binary patch literal 1829 zcmcIl&u`pB6gEEvMG+BPN`-{wNT>+Lo*8@XwOcpZ?1t{5I;81J4Y!WR<6SFjkFlNX zCgRjnK|!3jpo$Y0E}Xb=;LeTzpnm{T)f-4i;dy_hpfu#tmAsxAzxTcGz4y(`m)l!6 zS1w(-q$tWtuiM#y_bNQEzxE>h|J=PM>Pg=HtW=aY-mae)lh*~S0I8^$I!Q-a=}mlXitE9+UN$s!YEtd_TB{DI?graxTNXmKb&NR1RCQdP z*p_AEk5q~&HgLlr6cO9QmPZ_Q{?i~@5yjq4=i_-SnEBeUs&daT#^bR*Hg#DH4C1=3 zfvG_$0t-|gW)+*DtXx|lbVSLEB(D;gsWl=C<$mRBz;u>EnlE9qa$Y7Vm@#3wL3CWF zv@i^U^G(xqX_e|ijf0zqnN0f5E;9~PYWYyXtSU!}MEQj(L+?JpJ#W3Q_ zfcbtgnwBTxh8T$yuuHHdQ+~PEE(EJ&(U)?xXw>#1qDqNQ)vI@tERy5$gPPIYL3CIp zd=0ur5T*!|K7p3G9x*>8*u!{c8h{QWRntB?yEl08lWCYa({L}SbyS-h=I2pl*a_8oT+S_c~#IXiq#zs}!z^4spCcOR_0+*o_q`N6;X{rjK1`QsBs`Gcx|z4v#k QTVG_o&8^N)8~5)21Ktij=l}o! literal 0 HcmV?d00001 diff --git a/applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png b/applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb3569d3accc5a5c56829b12c85079172b56729 GIT binary patch literal 1853 zcmcIlPiW*+7#}^zw%YZuf+B)3d$6*;fxiXAXK-X$#&ks`?Z1FR>QX z26aVbT~%`&N5w=X1OWo&yGcQZD9KMx7+O3JvM4Pgkw_&Y^~HA4kU?pcLYz)%lYCqz zD405=sj4ZsOlbo2yrZFUJVocl(hfu!>phe>@9d^rUFW&j&H}!)!;|9lBv{%Lg~)s2 z4%()#|Dlit(}3xA)*qFJ1uF0J7`Su5Y9oEA+srsEMAi|aKWWt3B%(w#g-G)oQNqL^ zf1*@0Ucg(l;0+nNrXfra);gN*63r#X84bG_S5Oapz-U2_2No;}caH=0Jhz?X1x*6p zZZ%{Or9=^P?a(oNj2+}NPLO5lb>xS(hK#yzQ(Fto(z;~|u)ZaN?XnW(`pULU1i&$^ zrW-ON3~kFtm|7MJL)}ES1tUU3N-T@l>$)*vdoGLM%c1>)tfeXjj8tQ`U&jXGx~+pC zJw&zve}0HDBg6^Jz?Y@lahswqGEXq5ZvEhVyV+dJL>TqqMZUhgD7BZGrskL?B8nzU zEO0}S#T1Md#k9-SH0hSMuhLzKa_I5y_(QtDUmB14ku-9rOM~*GXvjh71`cJarlUj3 ze7uCJ^@AP<(j#0_!EzB61Df%LF0|x7U8vqkd`@?cmVP{k{EyPdWes{X>2la%Rk=(? zE%&0TDeAxbb=w#db1i`F%Wmf5GAz>Wv>@jW_p)ho-#0CeC9cGbyZ*s9Cn^o)Rq=_$h#NIZix%+wt GFa8a7)lmol literal 0 HcmV?d00001 diff --git a/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png b/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png new file mode 100644 index 0000000000000000000000000000000000000000..e7dba987a04dad7dd96001913c55566dbde96c8e GIT binary patch literal 1863 zcmcIlO^6&t6dpxnjjSF75f9pQJZy|LUDdzSZ6$n-*6-2D5s-9_fx~uK( zotfQBVDX?Q(UXLzXF6aX)%U*l z-d9y`w^q+Do_PF3p-@5qOL5h2N9RU z^i-~BIziNECdw*wjUcQeOxncsbnKa>(*%1MPoPck0jC)~9$50g-#!ks+4LGwn$d`f zMy;%ZsA3Rsk2!`#ELuW>2#g3fF>;CFBHMCo-El0(@X1&g%&$qdl~*F4Kd~*B3^?Z1 z^bEmDf}0)Wn??sYC6l9$X;6esLO7#_ZCmDy?ZqU3l|%anS#wn!7%f39-Qp(l9fyJ- z(?=x}n~2%2PcX9#VmYdECvH{tWzv)!s%sn^Z&a(TMEXG=KBQ~smzBm!)h4cOBfSV| zapw6l2`LyY2x(Vnan#Li4>BO#dXPeox2Fr~f_P*4)DM)gJ3Y$sMNw8+?gqit>2PpJ znU9yygm%~yKzf8rCa_fc*^nlp(uJ1%rwg^aiBIX^Xz9mu$p0vPT2|JhQCGkYtEqW1 zTD})enxg%?Uw4c#Ggk#{pLa8zmSLH8=LI=?xR>pc=yYsHAgcQUxz^arx`9fR>e$sw zj@}Uy75!kQXF{tT9e=F+z^*!*3|n>nI6oucWq!(t2og`=40-O!tAE1z^ID@;X)nEd zVK7xqj`wg%Ed2Op{k=a*YZpKHX787$ zzhKuN9(?M5ojmn%xcU1}OP>$^`ZD?cW@lIaTn=x3xBtP#z16o~{qVMZ>hecsD?jQQ ME3387mS5lf8`39Il>h($ literal 0 HcmV?d00001 diff --git a/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png b/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png new file mode 100644 index 0000000000000000000000000000000000000000..d766ffbb444db1739f2ccd030e506e8bada11ee8 GIT binary patch literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& literal 0 HcmV?d00001 diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c index b721e868d..e6091a410 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c @@ -44,24 +44,24 @@ void wifi_marauder_scene_text_input_on_enter(void* context) { } // Setup view - TextInput* text_input = app->text_input; + WIFI_TextInput* text_input = app->text_input; // Add help message to header if(app->special_case_input_step == 1) { - text_input_set_header_text(text_input, "Enter source MAC"); + wifi_text_input_set_header_text(text_input, "Enter source MAC"); } else if(0 == strncmp("ssid -a -g", app->selected_tx_string, strlen("ssid -a -g"))) { - text_input_set_header_text(text_input, "Enter # SSIDs to generate"); + wifi_text_input_set_header_text(text_input, "Enter # SSIDs to generate"); } else if(0 == strncmp("ssid -a -n", app->selected_tx_string, strlen("ssid -a -n"))) { - text_input_set_header_text(text_input, "Enter SSID name to add"); + wifi_text_input_set_header_text(text_input, "Enter SSID name to add"); } else if(0 == strncmp("ssid -r", app->selected_tx_string, strlen("ssid -r"))) { - text_input_set_header_text(text_input, "Remove target from SSID list"); + wifi_text_input_set_header_text(text_input, "Remove target from SSID list"); } else if(0 == strncmp("select -a", app->selected_tx_string, strlen("select -a"))) { - text_input_set_header_text(text_input, "Add target from AP list"); + wifi_text_input_set_header_text(text_input, "Add target from AP list"); } else if(0 == strncmp("select -s", app->selected_tx_string, strlen("select -s"))) { - text_input_set_header_text(text_input, "Add target from SSID list"); + wifi_text_input_set_header_text(text_input, "Add target from SSID list"); } else { - text_input_set_header_text(text_input, "Add command arguments"); + wifi_text_input_set_header_text(text_input, "Add command arguments"); } - text_input_set_result_callback( + wifi_text_input_set_result_callback( text_input, wifi_marauder_scene_text_input_callback, app, @@ -84,7 +84,7 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev consumed = true; } else if(event.event == WifiMarauderEventSaveSourceMac) { if(12 != strlen(app->text_input_store)) { - text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); + wifi_text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); } else { snprintf( app->special_case_input_src_addr, @@ -106,12 +106,12 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev // Advance scene to input destination MAC, clear text input app->special_case_input_step = 2; bzero(app->text_input_store, WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE); - text_input_set_header_text(app->text_input, "Enter destination MAC"); + wifi_text_input_set_header_text(app->text_input, "Enter destination MAC"); } consumed = true; } else if(event.event == WifiMarauderEventSaveDestinationMac) { if(12 != strlen(app->text_input_store)) { - text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); + wifi_text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); } else { snprintf( app->special_case_input_dst_addr, @@ -150,5 +150,5 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev void wifi_marauder_scene_text_input_on_exit(void* context) { WifiMarauderApp* app = context; - text_input_reset(app->text_input); + wifi_text_input_reset(app->text_input); } diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c index a5521d49a..9958b09eb 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.c @@ -64,9 +64,11 @@ WifiMarauderApp* wifi_marauder_app_alloc() { app->text_box_store = furi_string_alloc(); furi_string_reserve(app->text_box_store, WIFI_MARAUDER_TEXT_BOX_STORE_SIZE); - app->text_input = text_input_alloc(); + app->text_input = wifi_text_input_alloc(); view_dispatcher_add_view( - app->view_dispatcher, WifiMarauderAppViewTextInput, text_input_get_view(app->text_input)); + app->view_dispatcher, + WifiMarauderAppViewTextInput, + wifi_text_input_get_view(app->text_input)); app->widget = widget_alloc(); view_dispatcher_add_view( @@ -135,7 +137,7 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { widget_free(app->widget); text_box_free(app->text_box); furi_string_free(app->text_box_store); - text_input_free(app->text_input); + wifi_text_input_free(app->text_input); storage_file_free(app->capture_file); storage_file_free(app->log_file); storage_file_free(app->save_pcap_setting_file); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h index 39b48fae3..1bc8f78fe 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h @@ -12,9 +12,9 @@ #include #include #include -#include #include #include +#include "wifi_marauder_text_input.h" #include #include @@ -42,7 +42,7 @@ struct WifiMarauderApp { FuriString* text_box_store; size_t text_box_store_strlen; TextBox* text_box; - TextInput* text_input; + WIFI_TextInput* text_input; Storage* storage; File* capture_file; File* log_file; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c new file mode 100644 index 000000000..d9eee83c9 --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c @@ -0,0 +1,625 @@ +#include "wifi_marauder_text_input.h" +#include +#include "ESP32_WiFi_Marauder_icons.h" +#include "wifi_marauder_app_i.h" +#include + +struct WIFI_TextInput { + View* view; + FuriTimer* timer; +}; + +typedef struct { + const char text; + const uint8_t x; + const uint8_t y; +} WIFI_TextInputKey; + +typedef struct { + const char* header; + char* text_buffer; + size_t text_buffer_size; + bool clear_default_text; + + WIFI_TextInputCallback callback; + void* callback_context; + + uint8_t selected_row; + uint8_t selected_column; + + WIFI_TextInputValidatorCallback validator_callback; + void* validator_callback_context; + FuriString* validator_text; + bool valadator_message_visible; +} WIFI_TextInputModel; + +static const uint8_t keyboard_origin_x = 1; +static const uint8_t keyboard_origin_y = 29; +static const uint8_t keyboard_row_count = 4; + +#define ENTER_KEY '\r' +#define BACKSPACE_KEY '\b' + +static const WIFI_TextInputKey keyboard_keys_row_1[] = { + {'{', 1, 0}, + {'(', 9, 0}, + {'[', 17, 0}, + {'|', 25, 0}, + {'@', 33, 0}, + {'&', 41, 0}, + {'#', 49, 0}, + {';', 57, 0}, + {'^', 65, 0}, + {'*', 73, 0}, + {'`', 81, 0}, + {'"', 89, 0}, + {'~', 97, 0}, + {'\'', 105, 0}, + {'.', 113, 0}, + {'/', 120, 0}, +}; + +static const WIFI_TextInputKey keyboard_keys_row_2[] = { + {'q', 1, 10}, + {'w', 9, 10}, + {'e', 17, 10}, + {'r', 25, 10}, + {'t', 33, 10}, + {'y', 41, 10}, + {'u', 49, 10}, + {'i', 57, 10}, + {'o', 65, 10}, + {'p', 73, 10}, + {'0', 81, 10}, + {'1', 89, 10}, + {'2', 97, 10}, + {'3', 105, 10}, + {'=', 113, 10}, + {'-', 120, 10}, +}; + +static const WIFI_TextInputKey keyboard_keys_row_3[] = { + {'a', 1, 21}, + {'s', 9, 21}, + {'d', 18, 21}, + {'f', 25, 21}, + {'g', 33, 21}, + {'h', 41, 21}, + {'j', 49, 21}, + {'k', 57, 21}, + {'l', 65, 21}, + {BACKSPACE_KEY, 72, 13}, + {'4', 89, 21}, + {'5', 97, 21}, + {'6', 105, 21}, + {'$', 113, 21}, + {'%', 120, 21}, + +}; + +static const WIFI_TextInputKey keyboard_keys_row_4[] = { + {'z', 1, 33}, + {'x', 9, 33}, + {'c', 18, 33}, + {'v', 25, 33}, + {'b', 33, 33}, + {'n', 41, 33}, + {'m', 49, 33}, + {'_', 57, 33}, + {ENTER_KEY, 64, 24}, + {'7', 89, 33}, + {'8', 97, 33}, + {'9', 105, 33}, + {'!', 113, 33}, + {'+', 120, 33}, +}; + +static uint8_t get_row_size(uint8_t row_index) { + uint8_t row_size = 0; + + switch(row_index + 1) { + case 1: + row_size = sizeof(keyboard_keys_row_1) / sizeof(WIFI_TextInputKey); + break; + case 2: + row_size = sizeof(keyboard_keys_row_2) / sizeof(WIFI_TextInputKey); + break; + case 3: + row_size = sizeof(keyboard_keys_row_3) / sizeof(WIFI_TextInputKey); + break; + case 4: + row_size = sizeof(keyboard_keys_row_4) / sizeof(WIFI_TextInputKey); + break; + } + + return row_size; +} + +static const WIFI_TextInputKey* get_row(uint8_t row_index) { + const WIFI_TextInputKey* row = NULL; + + switch(row_index + 1) { + case 1: + row = keyboard_keys_row_1; + break; + case 2: + row = keyboard_keys_row_2; + break; + case 3: + row = keyboard_keys_row_3; + break; + case 4: + row = keyboard_keys_row_4; + break; + } + + return row; +} + +static char get_selected_char(WIFI_TextInputModel* model) { + return get_row(model->selected_row)[model->selected_column].text; +} + +static bool char_is_lowercase(char letter) { + return (letter >= 0x61 && letter <= 0x7A); +} + +static char char_to_uppercase(const char letter) { + switch(letter) { + case '_': + return 0x20; + break; + case '(': + return 0x29; + break; + case '{': + return 0x7d; + break; + case '[': + return 0x5d; + break; + case '/': + return 0x5c; + break; + case ';': + return 0x3a; + break; + case '.': + return 0x2c; + break; + case '!': + return 0x3f; + break; + case '<': + return 0x3e; + break; + } + if(char_is_lowercase(letter)) { + return (letter - 0x20); + } else { + return letter; + } +} + +static void wifi_text_input_backspace_cb(WIFI_TextInputModel* model) { + uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer); + if(text_length > 0) { + model->text_buffer[text_length - 1] = 0; + } +} + +static void wifi_text_input_view_draw_callback(Canvas* canvas, void* _model) { + WIFI_TextInputModel* model = _model; + //uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0; + uint8_t needed_string_width = canvas_width(canvas) - 8; + uint8_t start_pos = 4; + + const char* text = model->text_buffer; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_str(canvas, 2, 7, model->header); + elements_slightly_rounded_frame(canvas, 1, 8, 126, 12); + + if(canvas_string_width(canvas, text) > needed_string_width) { + canvas_draw_str(canvas, start_pos, 17, "..."); + start_pos += 6; + needed_string_width -= 8; + } + + while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) { + text++; + } + + if(model->clear_default_text) { + elements_slightly_rounded_box( + canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 18, "|"); + canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 18, "|"); + } + canvas_draw_str(canvas, start_pos, 17, text); + + canvas_set_font(canvas, FontKeyboard); + + for(uint8_t row = 0; row <= keyboard_row_count; row++) { + const uint8_t column_count = get_row_size(row); + const WIFI_TextInputKey* keys = get_row(row); + + for(size_t column = 0; column < column_count; column++) { + if(keys[column].text == ENTER_KEY) { + canvas_set_color(canvas, ColorBlack); + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeySaveSelected_24x11); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeySave_24x11); + } + } else if(keys[column].text == BACKSPACE_KEY) { + canvas_set_color(canvas, ColorBlack); + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspaceSelected_16x9); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspace_16x9); + } + } else { + if(model->selected_row == row && model->selected_column == column) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, + keyboard_origin_x + keys[column].x - 1, + keyboard_origin_y + keys[column].y - 8, + 7, + 10); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + + canvas_draw_glyph( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + keys[column].text); + } + } + } + if(model->valadator_message_visible) { + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 8, 10, 110, 48); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42); + canvas_draw_rframe(canvas, 8, 8, 112, 50, 3); + canvas_draw_rframe(canvas, 9, 9, 110, 48, 2); + elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text)); + canvas_set_font(canvas, FontKeyboard); + } +} + +static void + wifi_text_input_handle_up(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_row > 0) { + model->selected_row--; + if(model->selected_column > get_row_size(model->selected_row) - 6) { + model->selected_column = model->selected_column + 1; + } + } +} + +static void + wifi_text_input_handle_down(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_row < keyboard_row_count - 1) { + model->selected_row++; + if(model->selected_column > get_row_size(model->selected_row) - 4) { + model->selected_column = model->selected_column - 1; + } + } +} + +static void + wifi_text_input_handle_left(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_column > 0) { + model->selected_column--; + } else { + model->selected_column = get_row_size(model->selected_row) - 1; + } +} + +static void + wifi_text_input_handle_right(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_column < get_row_size(model->selected_row) - 1) { + model->selected_column++; + } else { + model->selected_column = 0; + } +} + +static void wifi_text_input_handle_ok( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputModel* model, + bool shift) { + char selected = get_selected_char(model); + uint8_t text_length = strlen(model->text_buffer); + + if(shift) { + selected = char_to_uppercase(selected); + } + + if(selected == ENTER_KEY) { + if(model->validator_callback && + (!model->validator_callback( + model->text_buffer, model->validator_text, model->validator_callback_context))) { + model->valadator_message_visible = true; + furi_timer_start(wifi_text_input->timer, furi_kernel_get_tick_frequency() * 4); + } else if(model->callback != 0 && text_length > 0) { + model->callback(model->callback_context); + } + } else if(selected == BACKSPACE_KEY) { + wifi_text_input_backspace_cb(model); + } else { + if(model->clear_default_text) { + text_length = 0; + } + if(text_length < (model->text_buffer_size - 1)) { + model->text_buffer[text_length] = selected; + model->text_buffer[text_length + 1] = 0; + } + } + model->clear_default_text = false; +} + +static bool wifi_text_input_view_input_callback(InputEvent* event, void* context) { + WIFI_TextInput* wifi_text_input = context; + furi_assert(wifi_text_input); + + bool consumed = false; + + // Acquire model + WIFI_TextInputModel* model = view_get_model(wifi_text_input->view); + + if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && + model->valadator_message_visible) { + model->valadator_message_visible = false; + consumed = true; + } else if(event->type == InputTypeShort) { + consumed = true; + switch(event->key) { + case InputKeyUp: + wifi_text_input_handle_up(wifi_text_input, model); + break; + case InputKeyDown: + wifi_text_input_handle_down(wifi_text_input, model); + break; + case InputKeyLeft: + wifi_text_input_handle_left(wifi_text_input, model); + break; + case InputKeyRight: + wifi_text_input_handle_right(wifi_text_input, model); + break; + case InputKeyOk: + wifi_text_input_handle_ok(wifi_text_input, model, false); + break; + default: + consumed = false; + break; + } + } else if(event->type == InputTypeLong) { + consumed = true; + switch(event->key) { + case InputKeyUp: + wifi_text_input_handle_up(wifi_text_input, model); + break; + case InputKeyDown: + wifi_text_input_handle_down(wifi_text_input, model); + break; + case InputKeyLeft: + wifi_text_input_handle_left(wifi_text_input, model); + break; + case InputKeyRight: + wifi_text_input_handle_right(wifi_text_input, model); + break; + case InputKeyOk: + wifi_text_input_handle_ok(wifi_text_input, model, true); + break; + case InputKeyBack: + wifi_text_input_backspace_cb(model); + break; + default: + consumed = false; + break; + } + } else if(event->type == InputTypeRepeat) { + consumed = true; + switch(event->key) { + case InputKeyUp: + wifi_text_input_handle_up(wifi_text_input, model); + break; + case InputKeyDown: + wifi_text_input_handle_down(wifi_text_input, model); + break; + case InputKeyLeft: + wifi_text_input_handle_left(wifi_text_input, model); + break; + case InputKeyRight: + wifi_text_input_handle_right(wifi_text_input, model); + break; + case InputKeyBack: + wifi_text_input_backspace_cb(model); + break; + default: + consumed = false; + break; + } + } + + // Commit model + view_commit_model(wifi_text_input->view, consumed); + + return consumed; +} + +void wifi_text_input_timer_callback(void* context) { + furi_assert(context); + WIFI_TextInput* wifi_text_input = context; + + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { model->valadator_message_visible = false; }, + true); +} + +WIFI_TextInput* wifi_text_input_alloc() { + WIFI_TextInput* wifi_text_input = malloc(sizeof(WIFI_TextInput)); + wifi_text_input->view = view_alloc(); + view_set_context(wifi_text_input->view, wifi_text_input); + view_allocate_model(wifi_text_input->view, ViewModelTypeLocking, sizeof(WIFI_TextInputModel)); + view_set_draw_callback(wifi_text_input->view, wifi_text_input_view_draw_callback); + view_set_input_callback(wifi_text_input->view, wifi_text_input_view_input_callback); + + wifi_text_input->timer = + furi_timer_alloc(wifi_text_input_timer_callback, FuriTimerTypeOnce, wifi_text_input); + + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { model->validator_text = furi_string_alloc(); }, + false); + + wifi_text_input_reset(wifi_text_input); + + return wifi_text_input; +} + +void wifi_text_input_free(WIFI_TextInput* wifi_text_input) { + furi_assert(wifi_text_input); + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { furi_string_free(model->validator_text); }, + false); + + // Send stop command + furi_timer_stop(wifi_text_input->timer); + // Release allocated memory + furi_timer_free(wifi_text_input->timer); + + view_free(wifi_text_input->view); + + free(wifi_text_input); +} + +void wifi_text_input_reset(WIFI_TextInput* wifi_text_input) { + furi_assert(wifi_text_input); + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { + model->text_buffer_size = 0; + model->header = ""; + model->selected_row = 0; + model->selected_column = 0; + model->clear_default_text = false; + model->text_buffer = NULL; + model->text_buffer_size = 0; + model->callback = NULL; + model->callback_context = NULL; + model->validator_callback = NULL; + model->validator_callback_context = NULL; + furi_string_reset(model->validator_text); + model->valadator_message_visible = false; + }, + true); +} + +View* wifi_text_input_get_view(WIFI_TextInput* wifi_text_input) { + furi_assert(wifi_text_input); + return wifi_text_input->view; +} + +void wifi_text_input_set_result_callback( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputCallback callback, + void* callback_context, + char* text_buffer, + size_t text_buffer_size, + bool clear_default_text) { + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { + model->callback = callback; + model->callback_context = callback_context; + model->text_buffer = text_buffer; + model->text_buffer_size = text_buffer_size; + model->clear_default_text = clear_default_text; + if(text_buffer && text_buffer[0] != '\0') { + // Set focus on Save + model->selected_row = 2; + model->selected_column = 8; + } + }, + true); +} + +void wifi_text_input_set_validator( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputValidatorCallback callback, + void* callback_context) { + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { + model->validator_callback = callback; + model->validator_callback_context = callback_context; + }, + true); +} + +WIFI_TextInputValidatorCallback + wifi_text_input_get_validator_callback(WIFI_TextInput* wifi_text_input) { + WIFI_TextInputValidatorCallback validator_callback = NULL; + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { validator_callback = model->validator_callback; }, + false); + return validator_callback; +} + +void* wifi_text_input_get_validator_callback_context(WIFI_TextInput* wifi_text_input) { + void* validator_callback_context = NULL; + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { validator_callback_context = model->validator_callback_context; }, + false); + return validator_callback_context; +} + +void wifi_text_input_set_header_text(WIFI_TextInput* wifi_text_input, const char* text) { + with_view_model( + wifi_text_input->view, WIFI_TextInputModel * model, { model->header = text; }, true); +} diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h new file mode 100644 index 000000000..b6b1f7bdf --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include "wifi_marauder_validators.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Text input anonymous structure */ +typedef struct WIFI_TextInput WIFI_TextInput; +typedef void (*WIFI_TextInputCallback)(void* context); +typedef bool (*WIFI_TextInputValidatorCallback)(const char* text, FuriString* error, void* context); + +/** Allocate and initialize text input + * + * This text input is used to enter string + * + * @return WIFI_TextInput instance + */ +WIFI_TextInput* wifi_text_input_alloc(); + +/** Deinitialize and free text input + * + * @param wifi_text_input WIFI_TextInput instance + */ +void wifi_text_input_free(WIFI_TextInput* wifi_text_input); + +/** Clean text input view Note: this function does not free memory + * + * @param wifi_text_input Text input instance + */ +void wifi_text_input_reset(WIFI_TextInput* wifi_text_input); + +/** Get text input view + * + * @param wifi_text_input WIFI_TextInput instance + * + * @return View instance that can be used for embedding + */ +View* wifi_text_input_get_view(WIFI_TextInput* wifi_text_input); + +/** Set text input result callback + * + * @param wifi_text_input WIFI_TextInput instance + * @param callback callback fn + * @param callback_context callback context + * @param text_buffer pointer to YOUR text buffer, that we going + * to modify + * @param text_buffer_size YOUR text buffer size in bytes. Max string + * length will be text_buffer_size-1. + * @param clear_default_text clear text from text_buffer on first OK + * event + */ +void wifi_text_input_set_result_callback( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputCallback callback, + void* callback_context, + char* text_buffer, + size_t text_buffer_size, + bool clear_default_text); + +void wifi_text_input_set_validator( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputValidatorCallback callback, + void* callback_context); + +WIFI_TextInputValidatorCallback + wifi_text_input_get_validator_callback(WIFI_TextInput* wifi_text_input); + +void* wifi_text_input_get_validator_callback_context(WIFI_TextInput* wifi_text_input); + +/** Set text input header text + * + * @param wifi_text_input WIFI_TextInput instance + * @param text text to be shown + */ +void wifi_text_input_set_header_text(WIFI_TextInput* wifi_text_input, const char* text); + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_validators.c b/applications/external/wifi_marauder_companion/wifi_marauder_validators.c new file mode 100644 index 000000000..5bec88269 --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_validators.c @@ -0,0 +1,57 @@ +#include +#include "wifi_marauder_validators.h" +#include + +struct ValidatorIsFile { + char* app_path_folder; + const char* app_extension; + char* current_name; +}; + +bool validator_is_file_callback(const char* text, FuriString* error, void* context) { + furi_assert(context); + ValidatorIsFile* instance = context; + + if(instance->current_name != NULL) { + if(strcmp(instance->current_name, text) == 0) { + return true; + } + } + + bool ret = true; + FuriString* path = furi_string_alloc_printf( + "%s/%s%s", instance->app_path_folder, text, instance->app_extension); + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_common_stat(storage, furi_string_get_cstr(path), NULL) == FSE_OK) { + ret = false; + furi_string_printf(error, "This name\nexists!\nChoose\nanother one."); + } else { + ret = true; + } + furi_string_free(path); + furi_record_close(RECORD_STORAGE); + + return ret; +} + +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name) { + ValidatorIsFile* instance = malloc(sizeof(ValidatorIsFile)); + + instance->app_path_folder = strdup(app_path_folder); + instance->app_extension = app_extension; + if(current_name != NULL) { + instance->current_name = strdup(current_name); + } + + return instance; +} + +void validator_is_file_free(ValidatorIsFile* instance) { + furi_assert(instance); + free(instance->app_path_folder); + free(instance->current_name); + free(instance); +} diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_validators.h b/applications/external/wifi_marauder_companion/wifi_marauder_validators.h new file mode 100644 index 000000000..d9200b6db --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_validators.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ValidatorIsFile ValidatorIsFile; + +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name); + +void validator_is_file_free(ValidatorIsFile* instance); + +bool validator_is_file_callback(const char* text, FuriString* error, void* context); + +#ifdef __cplusplus +} +#endif From a304e9c3faf68ff6bea092fc7fe8fc566a8a0f1a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 14 Apr 2023 14:45:23 +0300 Subject: [PATCH 046/282] Revert "Revert Deep Sleep due to BLE issues and other random freezes" This reverts commit addf9092876e3b344386863cab6af6975d3ebb00. --- applications/main/fap_loader/fap_loader_app.c | 5 +- .../power_settings_app/views/battery_info.c | 51 +++++++------- .../settings/system/system_settings.c | 21 ++++++ debug/flipperapps.py | 3 +- documentation/FuriHalDebuging.md | 26 +++++++ documentation/fbt.md | 1 + firmware/targets/f18/api_symbols.csv | 11 +-- .../targets/f18/furi_hal/furi_hal_resources.c | 3 + .../targets/f18/furi_hal/furi_hal_resources.h | 3 + firmware/targets/f7/api_symbols.csv | 11 +-- firmware/targets/f7/ble_glue/ble_glue.c | 6 -- firmware/targets/f7/furi_hal/furi_hal_clock.c | 9 ++- firmware/targets/f7/furi_hal/furi_hal_debug.c | 21 ++++++ firmware/targets/f7/furi_hal/furi_hal_os.c | 30 ++++++-- firmware/targets/f7/furi_hal/furi_hal_power.c | 69 +++++++++---------- .../targets/f7/furi_hal/furi_hal_random.c | 42 ++++++----- .../targets/f7/furi_hal/furi_hal_resources.c | 3 + .../targets/f7/furi_hal/furi_hal_resources.h | 3 + firmware/targets/f7/furi_hal/furi_hal_rtc.c | 15 ++-- .../targets/furi_hal_include/furi_hal_debug.h | 3 + .../targets/furi_hal_include/furi_hal_power.h | 6 -- .../targets/furi_hal_include/furi_hal_rtc.h | 4 ++ furi/core/check.c | 3 + furi/core/core_defines.h | 4 ++ site_scons/cc.scons | 1 + site_scons/commandline.scons | 8 +++ 26 files changed, 244 insertions(+), 118 deletions(-) create mode 100644 documentation/FuriHalDebuging.md diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index f4f2550da..47dd9cf5e 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -1,6 +1,7 @@ #include "fap_loader_app.h" #include +#include #include #include @@ -23,8 +24,6 @@ struct FapLoader { Loading* loading; }; -volatile bool fap_loader_debug_active = false; - bool fap_loader_load_name_and_icon( FuriString* path, Storage* storage, @@ -134,7 +133,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader, bool ignore_mismatch) FuriThread* thread = flipper_application_spawn(loader->app, NULL); /* This flag is set by the debugger - to break on app start */ - if(fap_loader_debug_active) { + if(furi_hal_debug_is_gdb_session_active()) { FURI_LOG_W(TAG, "Triggering BP for debugger"); /* After hitting this, you can set breakpoints in your .fap's code * Note that you have to toggle breakpoints that were set before */ diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index 7394fd3c5..0956cae4f 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -4,8 +4,8 @@ #include #include -#define LOW_CHARGE_THRESHOLD 10 -#define HIGH_DRAIN_CURRENT_THRESHOLD 100 +#define LOW_CHARGE_THRESHOLD (10) +#define HIGH_DRAIN_CURRENT_THRESHOLD (-100) struct BatteryInfo { View* view; @@ -25,14 +25,13 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { char header[20] = {}; char value[20] = {}; - int32_t drain_current = data->gauge_current * (-1000); - uint32_t charge_current = data->gauge_current * 1000; + int32_t current = 1000.0f * data->gauge_current; // Draw battery canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); - if(charge_current > 0) { + if(current > 0) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); - } else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) { + } else if(current < HIGH_DRAIN_CURRENT_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); } else if(data->charge < LOW_CHARGE_THRESHOLD) { canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); @@ -44,7 +43,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { elements_bubble(canvas, 53, 0, 71, 39); // Set text - if(charge_current > 0) { + if(current > 0) { snprintf(emote, sizeof(emote), "%s", "Yummy!"); snprintf(header, sizeof(header), "%s", "Charging at"); snprintf( @@ -53,34 +52,36 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { "%lu.%luV %lumA", (uint32_t)(data->vbus_voltage), (uint32_t)(data->vbus_voltage * 10) % 10, - charge_current); - } else if(drain_current > 0) { + current); + } else if(current < 0) { snprintf( emote, sizeof(emote), "%s", - drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); + current < HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); snprintf(header, sizeof(header), "%s", "Consumption is"); snprintf( value, sizeof(value), "%ld %s", - drain_current, - drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); - } else if(drain_current != 0) { - snprintf(header, 20, "..."); - } else if(data->charge_voltage_limit < 4.2) { - // Non-default battery charging limit, mention it - snprintf(emote, sizeof(emote), "Charged!"); - snprintf(header, sizeof(header), "Limited to"); - snprintf( - value, - sizeof(value), - "%lu.%luV", - (uint32_t)(data->charge_voltage_limit), - (uint32_t)(data->charge_voltage_limit * 10) % 10); + ABS(current), + current < HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); + } else if(data->vbus_voltage > 0) { + if(data->charge_voltage_limit < 4.2) { + // Non-default battery charging limit, mention it + snprintf(emote, sizeof(emote), "Charged!"); + snprintf(header, sizeof(header), "Limited to"); + snprintf( + value, + sizeof(value), + "%lu.%luV", + (uint32_t)(data->charge_voltage_limit), + (uint32_t)(data->charge_voltage_limit * 10) % 10); + } else { + snprintf(header, sizeof(header), "Charged!"); + } } else { - snprintf(header, sizeof(header), "Charged!"); + snprintf(header, sizeof(header), "Napping..."); } canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index 868f87bb3..8cbe62da8 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -141,6 +141,21 @@ static void hand_orient_changed(VariableItem* item) { loader_update_menu(); } +const char* const sleep_method[] = { + "Default", + "Legacy", +}; + +static void sleep_method_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, sleep_method[index]); + if(index) { + furi_hal_rtc_set_flag(FuriHalRtcFlagLegacySleep); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagLegacySleep); + } +} + static uint32_t system_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; @@ -218,6 +233,12 @@ SystemSettings* system_settings_alloc() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, heap_trace_mode_text[value_index]); + item = variable_item_list_add( + app->var_item_list, "Sleep Method", COUNT_OF(sleep_method), sleep_method_changed, app); + value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) ? 1 : 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, sleep_method[value_index]); + view_set_previous_callback( variable_item_list_get_view(app->var_item_list), system_settings_exit); view_dispatcher_add_view( diff --git a/debug/flipperapps.py b/debug/flipperapps.py index 1dc5ebd04..90582c1e4 100644 --- a/debug/flipperapps.py +++ b/debug/flipperapps.py @@ -135,6 +135,7 @@ class FlipperAppStateHelper: self.app_list_ptr = None self.app_list_entry_type = None self._current_apps: list[AppState] = [] + self.set_debug_mode(True) def _walk_app_list(self, list_head): while list_head: @@ -195,7 +196,7 @@ class FlipperAppStateHelper: self.set_debug_mode(False) def set_debug_mode(self, mode: bool) -> None: - gdb.execute(f"set variable fap_loader_debug_active = {int(mode)}") + gdb.execute(f"set variable furi_hal_debug_gdb_session_active = {int(mode)}") # Init additional 'fap-set-debug-elf-root' command and set up hooks diff --git a/documentation/FuriHalDebuging.md b/documentation/FuriHalDebuging.md new file mode 100644 index 000000000..8ff770163 --- /dev/null +++ b/documentation/FuriHalDebuging.md @@ -0,0 +1,26 @@ +# Furi HAL Debugging + +Some Furi subsystem got additional debugging features that can be enabled by adding additional defines to firmware compilation. +Usually they are used for low level tracing and profiling or signal redirection/duplication. + + +## FuriHalOs + +`--extra-define=FURI_HAL_OS_DEBUG` enables tick, tick suppression, idle and time flow. + +There are 3 signals that will be exposed to external GPIO pins: + +- `AWAKE` - `PA7` - High when system is busy with computations, low when sleeping. Can be used to track transitions to sleep mode. +- `TICK` - `PA6` - Flipped on system tick, only flips when no tick suppression in progress. Can be used to track tick skew and abnormal task scheduling. +- `SECOND` - `PA4` - Flipped each second. Can be used for tracing RT issue: time flow disturbance means system doesn't conforms Hard RT. + + + +## FuriHalPower + +`--extra-define=FURI_HAL_POWER_DEBUG` enables power subsystem mode transitions tracing. + +There are 2 signals that will be exposed to external GPIO pins: + +- `WFI` - `PB2` - Light sleep (wait for interrupt) used. Basically this is lightest and most non-breaking things power save mode. All function and debug should work correctly in this mode. +- `STOP` - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible. \ No newline at end of file diff --git a/documentation/fbt.md b/documentation/fbt.md index 14d63e9ce..23b2e2b55 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -105,6 +105,7 @@ To run cleanup (think of `make clean`) for specified targets, add the `-c` optio - `--options optionfile.py` (default value `fbt_options.py`) - load a file with multiple configuration values - `--extra-int-apps=app1,app2,appN` - force listed apps to be built as internal with the `firmware` target - `--extra-ext-apps=app1,app2,appN` - force listed apps to be built as external with the `firmware_extapps` target +- `--extra-define=A --extra-define=B=C ` - extra global defines that will be passed to the C/C++ compiler, can be specified multiple times - `--proxy-env=VAR1,VAR2` - additional environment variables to expose to subprocesses spawned by `fbt`. By default, `fbt` sanitizes the execution environment and doesn't forward all inherited environment variables. You can find the list of variables that are always forwarded in the `environ.scons` file. ## Configuration diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 468c90c53..493f59634 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -901,6 +901,7 @@ Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, +Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,-,furi_hal_deinit_early,void, Function,-,furi_hal_flash_erase,void,uint8_t Function,-,furi_hal_flash_get_base,size_t, @@ -983,7 +984,6 @@ Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, Function,+,furi_hal_power_check_otg_status,void, Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" -Function,+,furi_hal_power_deep_sleep_available,_Bool, Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_otg,void, Function,+,furi_hal_power_enable_external_3_3v,void, @@ -1059,6 +1059,7 @@ Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" +Function,+,furi_hal_rtc_sync_shadow,void, Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, @@ -2149,6 +2150,8 @@ Variable,+,gpio_ext_pd0,const GpioPin, Variable,+,gpio_ext_pe4,const GpioPin, Variable,+,gpio_i2c_power_scl,const GpioPin, Variable,+,gpio_i2c_power_sda,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_periph_power,const GpioPin, Variable,+,gpio_pins,const GpioPinRecord[], Variable,+,gpio_pins_count,const size_t, Variable,+,gpio_sdcard_cd,const GpioPin, @@ -2157,11 +2160,13 @@ Variable,+,gpio_speaker,const GpioPin, Variable,+,gpio_spi_d_miso,const GpioPin, Variable,+,gpio_spi_d_mosi,const GpioPin, Variable,+,gpio_spi_d_sck,const GpioPin, +Variable,+,gpio_swclk,const GpioPin, +Variable,+,gpio_swdio,const GpioPin, Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, Variable,+,gpio_usb_dp,const GpioPin, -Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_vibro,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, Variable,+,message_blink_set_color_blue,const NotificationMessage, @@ -2309,7 +2314,6 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, -Variable,+,gpio_periph_power,const GpioPin, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -2364,4 +2368,3 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, -Variable,+,gpio_vibro,const GpioPin, diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index 4a7015d13..6db483dbc 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -6,6 +6,9 @@ #define TAG "FuriHalResources" +const GpioPin gpio_swdio = {.port = GPIOA, .pin = LL_GPIO_PIN_13}; +const GpioPin gpio_swclk = {.port = GPIOA, .pin = LL_GPIO_PIN_14}; + const GpioPin gpio_vibro = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; const GpioPin gpio_ibutton = {.port = GPIOB, .pin = LL_GPIO_PIN_14}; diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.h b/firmware/targets/f18/furi_hal/furi_hal_resources.h index 3c4708d15..7d2caab36 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.h @@ -50,6 +50,9 @@ extern const size_t input_pins_count; extern const GpioPinRecord gpio_pins[]; extern const size_t gpio_pins_count; +extern const GpioPin gpio_swdio; +extern const GpioPin gpio_swclk; + extern const GpioPin gpio_vibro; extern const GpioPin gpio_ibutton; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 53985249f..59b8c2e8d 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1106,6 +1106,7 @@ Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, +Function,+,furi_hal_debug_is_gdb_session_active,_Bool, Function,-,furi_hal_deinit_early,void, Function,-,furi_hal_flash_erase,void,uint8_t Function,-,furi_hal_flash_get_base,size_t, @@ -1238,7 +1239,6 @@ Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, Function,+,furi_hal_power_check_otg_status,void, Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" -Function,+,furi_hal_power_deep_sleep_available,_Bool, Function,+,furi_hal_power_disable_external_3_3v,void, Function,+,furi_hal_power_disable_otg,void, Function,+,furi_hal_power_enable_external_3_3v,void, @@ -1338,6 +1338,7 @@ Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" +Function,+,furi_hal_rtc_sync_shadow,void, Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, @@ -4736,10 +4737,12 @@ Variable,+,gpio_ext_pc1,const GpioPin, Variable,+,gpio_ext_pc3,const GpioPin, Variable,+,gpio_i2c_power_scl,const GpioPin, Variable,+,gpio_i2c_power_sda,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, Variable,+,gpio_infrared_rx,const GpioPin, Variable,+,gpio_infrared_tx,const GpioPin, Variable,+,gpio_nfc_cs,const GpioPin, Variable,+,gpio_nfc_irq_rfid_pull,const GpioPin, +Variable,+,gpio_periph_power,const GpioPin, Variable,+,gpio_pins,const GpioPinRecord[], Variable,+,gpio_pins_count,const size_t, Variable,+,gpio_rf_sw_0,const GpioPin, @@ -4760,11 +4763,13 @@ Variable,+,gpio_spi_r_sck,const GpioPin, Variable,+,gpio_spi_r_sck_ext,const GpioPin, Variable,+,gpio_subghz_cs,const GpioPin, Variable,+,gpio_subghz_cs_ext,const GpioPin, +Variable,+,gpio_swclk,const GpioPin, +Variable,+,gpio_swdio,const GpioPin, Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, Variable,+,gpio_usb_dp,const GpioPin, -Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_vibro,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, Variable,+,lfrfid_protocols,const ProtocolBase*[], @@ -4913,7 +4918,6 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, -Variable,+,gpio_periph_power,const GpioPin, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -6920,4 +6924,3 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, -Variable,+,gpio_vibro,const GpioPin, diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index 83562c73e..a2f2f1a94 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -58,12 +58,6 @@ void ble_glue_init() { ble_glue = malloc(sizeof(BleGlue)); ble_glue->status = BleGlueStatusStartup; - // Configure the system Power Mode - // Select HSI as system clock source after Wake Up from Stop mode - LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); - /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */ - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - #ifdef BLE_GLUE_DEBUG APPD_Init(); #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index cf19451ec..d85524ce4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -63,6 +63,10 @@ void furi_hal_clock_init() { LL_RCC_HSI_Enable(); while(!HS_CLOCK_IS_READY()) ; + /* Select HSI as system clock source after Wake Up from Stop mode + * Must be set before enabling CSS */ + LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); + LL_RCC_HSE_EnableCSS(); /* LSE and LSI1 configuration and activation */ @@ -215,11 +219,14 @@ void furi_hal_clock_switch_to_hsi() { void furi_hal_clock_switch_to_pll() { LL_RCC_HSE_Enable(); LL_RCC_PLL_Enable(); + LL_RCC_PLLSAI1_Enable(); while(!LL_RCC_HSE_IsReady()) ; while(!LL_RCC_PLL_IsReady()) ; + while(!LL_RCC_PLLSAI1_IsReady()) + ; LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); @@ -296,4 +303,4 @@ void furi_hal_clock_mco_disable() { LL_RCC_MSI_Disable(); while(LL_RCC_MSI_IsReady() != 0) ; -} \ No newline at end of file +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_debug.c b/firmware/targets/f7/furi_hal/furi_hal_debug.c index 3b5dfe622..3dc03ea69 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_debug.c +++ b/firmware/targets/f7/furi_hal/furi_hal_debug.c @@ -3,12 +3,26 @@ #include #include +#include +#include + +volatile bool furi_hal_debug_gdb_session_active = false; + void furi_hal_debug_enable() { // Low power mode debug LL_DBGMCU_EnableDBGSleepMode(); LL_DBGMCU_EnableDBGStopMode(); LL_DBGMCU_EnableDBGStandbyMode(); LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); + // SWD GPIO + furi_hal_gpio_init_ex( + &gpio_swdio, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn0JTMS_SWDIO); + furi_hal_gpio_init_ex( + &gpio_swclk, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn0JTCK_SWCLK); } void furi_hal_debug_disable() { @@ -17,4 +31,11 @@ void furi_hal_debug_disable() { LL_DBGMCU_DisableDBGStopMode(); LL_DBGMCU_DisableDBGStandbyMode(); LL_EXTI_DisableIT_32_63(LL_EXTI_LINE_48); + // SWD GPIO + furi_hal_gpio_init_simple(&gpio_swdio, GpioModeAnalog); + furi_hal_gpio_init_simple(&gpio_swclk, GpioModeAnalog); } + +bool furi_hal_debug_is_gdb_session_active() { + return furi_hal_debug_gdb_session_active; +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.c b/firmware/targets/f7/furi_hal/furi_hal_os.c index ee9743e62..3fc1fbea8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_os.c +++ b/firmware/targets/f7/furi_hal/furi_hal_os.c @@ -28,11 +28,24 @@ // Arbitrary (but small) number for better tick consistency #define FURI_HAL_OS_EXTRA_CNT 3 +#ifndef FURI_HAL_OS_DEBUG_AWAKE_GPIO +#define FURI_HAL_OS_DEBUG_AWAKE_GPIO (&gpio_ext_pa7) +#endif + +#ifndef FURI_HAL_OS_DEBUG_TICK_GPIO +#define FURI_HAL_OS_DEBUG_TICK_GPIO (&gpio_ext_pa6) +#endif + +#ifndef FURI_HAL_OS_DEBUG_SECOND_GPIO +#define FURI_HAL_OS_DEBUG_SECOND_GPIO (&gpio_ext_pa4) +#endif + #ifdef FURI_HAL_OS_DEBUG #include void furi_hal_os_timer_callback() { - furi_hal_gpio_write(&gpio_ext_pa4, !furi_hal_gpio_read(&gpio_ext_pa4)); + furi_hal_gpio_write( + FURI_HAL_OS_DEBUG_SECOND_GPIO, !furi_hal_gpio_read(FURI_HAL_OS_DEBUG_SECOND_GPIO)); } #endif @@ -44,9 +57,11 @@ void furi_hal_os_init() { furi_hal_idle_timer_init(); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_AWAKE_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_TICK_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_SECOND_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 1); + FuriTimer* second_timer = furi_timer_alloc(furi_hal_os_timer_callback, FuriTimerTypePeriodic, NULL); furi_timer_start(second_timer, FURI_HAL_OS_TICK_HZ); @@ -58,7 +73,8 @@ void furi_hal_os_init() { void furi_hal_os_tick() { if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa6, !furi_hal_gpio_read(&gpio_ext_pa6)); + furi_hal_gpio_write( + FURI_HAL_OS_DEBUG_TICK_GPIO, !furi_hal_gpio_read(FURI_HAL_OS_DEBUG_TICK_GPIO)); #endif xPortSysTickHandler(); } @@ -121,14 +137,14 @@ static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) { furi_hal_idle_timer_start(FURI_HAL_OS_TICKS_TO_IDLE_CNT(expected_idle_ticks)); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa7, 0); + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 0); #endif // Go to sleep mode furi_hal_power_sleep(); #ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa7, 1); + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 1); #endif // Calculate how much time we spent in the sleep diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index fd601ec7e..9a87cef15 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -19,15 +21,16 @@ #define TAG "FuriHalPower" -#ifdef FURI_HAL_POWER_DEEP_SLEEP_ENABLED -#define FURI_HAL_POWER_DEEP_INSOMNIA 0 -#else -#define FURI_HAL_POWER_DEEP_INSOMNIA 1 +#ifndef FURI_HAL_POWER_DEBUG_WFI_GPIO +#define FURI_HAL_POWER_DEBUG_WFI_GPIO (&gpio_ext_pb2) +#endif + +#ifndef FURI_HAL_POWER_DEBUG_STOP_GPIO +#define FURI_HAL_POWER_DEBUG_STOP_GPIO (&gpio_ext_pc3) #endif typedef struct { volatile uint8_t insomnia; - volatile uint8_t deep_insomnia; volatile uint8_t suppress_charge; uint8_t gauge_initialized; @@ -36,7 +39,6 @@ typedef struct { static volatile FuriHalPower furi_hal_power = { .insomnia = 0, - .deep_insomnia = FURI_HAL_POWER_DEEP_INSOMNIA, .suppress_charge = 0, }; @@ -79,19 +81,23 @@ const ParamCEDV cedv = { }; void furi_hal_power_init() { +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_WFI_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_STOP_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); +#endif + LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN); + LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2); + LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2); furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); bq27220_init(&furi_hal_i2c_handle_power, &cedv); bq25896_init(&furi_hal_i2c_handle_power); furi_hal_i2c_release(&furi_hal_i2c_handle_power); -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_init_simple(&gpio_ext_pb2, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); -#endif - FURI_LOG_I(TAG, "Init OK"); } @@ -140,11 +146,12 @@ bool furi_hal_power_sleep_available() { return furi_hal_power.insomnia == 0; } -bool furi_hal_power_deep_sleep_available() { - return furi_hal_bt_is_alive() && furi_hal_power.deep_insomnia == 0; +static inline bool furi_hal_power_deep_sleep_available() { + return furi_hal_bt_is_alive() && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) && + !furi_hal_debug_is_gdb_session_active(); } -void furi_hal_power_light_sleep() { +static inline void furi_hal_power_light_sleep() { __WFI(); } @@ -152,17 +159,15 @@ static inline void furi_hal_power_suspend_aux_periphs() { // Disable USART furi_hal_uart_suspend(FuriHalUartIdUSART1); furi_hal_uart_suspend(FuriHalUartIdLPUART1); - // TODO: Disable USB } static inline void furi_hal_power_resume_aux_periphs() { // Re-enable USART furi_hal_uart_resume(FuriHalUartIdUSART1); furi_hal_uart_resume(FuriHalUartIdLPUART1); - // TODO: Re-enable USB } -void furi_hal_power_deep_sleep() { +static inline void furi_hal_power_deep_sleep() { furi_hal_power_suspend_aux_periphs(); while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) @@ -187,8 +192,6 @@ void furi_hal_power_deep_sleep() { LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); // Prepare deep sleep - LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2); LL_LPM_EnableDeepSleep(); #if defined(__CC_ARM) @@ -200,13 +203,6 @@ void furi_hal_power_deep_sleep() { LL_LPM_EnableSleep(); - // Make sure that values differ to prevent disaster on wfi - LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - - LL_PWR_ClearFlag_C1STOP_C1STB(); - LL_PWR_ClearFlag_C2STOP_C2STB(); - /* Release ENTRY_STOP_MODE semaphore */ LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); @@ -220,28 +216,25 @@ void furi_hal_power_deep_sleep() { LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); furi_hal_power_resume_aux_periphs(); + furi_hal_rtc_sync_shadow(); } void furi_hal_power_sleep() { if(furi_hal_power_deep_sleep_available()) { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pc3, 1); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 1); #endif - furi_hal_power_deep_sleep(); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pc3, 0); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); #endif } else { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pb2, 1); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 1); #endif - furi_hal_power_light_sleep(); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pb2, 0); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); #endif } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_random.c b/firmware/targets/f7/furi_hal/furi_hal_random.c index f36407cc1..d3461c4d1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_random.c +++ b/firmware/targets/f7/furi_hal/furi_hal_random.c @@ -9,19 +9,35 @@ #define TAG "FuriHalRandom" +static uint32_t furi_hal_random_read_rng() { + while(LL_RNG_IsActiveFlag_CECS(RNG) && LL_RNG_IsActiveFlag_SECS(RNG) && + !LL_RNG_IsActiveFlag_DRDY(RNG)) { + /* Error handling as described in RM0434, pg. 582-583 */ + if(LL_RNG_IsActiveFlag_CECS(RNG)) { + /* Clock error occurred */ + LL_RNG_ClearFlag_CEIS(RNG); + } + + if(LL_RNG_IsActiveFlag_SECS(RNG)) { + /* Noise source error occurred */ + LL_RNG_ClearFlag_SEIS(RNG); + + for(uint32_t i = 0; i < 12; ++i) { + const volatile uint32_t discard = LL_RNG_ReadRandData32(RNG); + UNUSED(discard); + } + } + } + + return LL_RNG_ReadRandData32(RNG); +} + uint32_t furi_hal_random_get() { while(LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID)) ; LL_RNG_Enable(RNG); - while(!LL_RNG_IsActiveFlag_DRDY(RNG)) - ; - - if((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) { - furi_crash("TRNG error"); - } - - uint32_t random_val = LL_RNG_ReadRandData32(RNG); + const uint32_t random_val = furi_hal_random_read_rng(); LL_RNG_Disable(RNG); LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0); @@ -35,15 +51,7 @@ void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len) { LL_RNG_Enable(RNG); for(uint32_t i = 0; i < len; i += 4) { - while(!LL_RNG_IsActiveFlag_DRDY(RNG)) - ; - - if((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) { - furi_crash("TRNG error"); - } - - uint32_t random_val = LL_RNG_ReadRandData32(RNG); - + const uint32_t random_val = furi_hal_random_read_rng(); uint8_t len_cur = ((i + 4) < len) ? (4) : (len - i); memcpy(&buf[i], &random_val, len_cur); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 9c0b482f7..f87f2a31a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -6,6 +6,9 @@ #define TAG "FuriHalResources" +const GpioPin gpio_swdio = {.port = GPIOA, .pin = LL_GPIO_PIN_13}; +const GpioPin gpio_swclk = {.port = GPIOA, .pin = LL_GPIO_PIN_14}; + const GpioPin gpio_vibro = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; const GpioPin gpio_ibutton = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index c0e32c313..391f8f4ff 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -50,6 +50,9 @@ extern const size_t input_pins_count; extern const GpioPinRecord gpio_pins[]; extern const size_t gpio_pins_count; +extern const GpioPin gpio_swdio; +extern const GpioPin gpio_swclk; + extern const GpioPin gpio_vibro; extern const GpioPin gpio_ibutton; diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index 84e7fe395..7bd45c35d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -165,6 +165,14 @@ void furi_hal_rtc_init() { FURI_LOG_I(TAG, "Init OK"); } +void furi_hal_rtc_sync_shadow() { + if(!LL_RTC_IsShadowRegBypassEnabled(RTC)) { + LL_RTC_ClearFlag_RS(RTC); + while(!LL_RTC_IsActiveFlag_RS(RTC)) { + }; + } +} + uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg) { return LL_RTC_BAK_GetRegister(RTC, reg); } @@ -312,12 +320,7 @@ void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime) { /* Exit Initialization mode */ LL_RTC_DisableInitMode(RTC); - /* If RTC_CR_BYPSHAD bit = 0, wait for synchro else this check is not needed */ - if(!LL_RTC_IsShadowRegBypassEnabled(RTC)) { - LL_RTC_ClearFlag_RS(RTC); - while(!LL_RTC_IsActiveFlag_RS(RTC)) { - }; - } + furi_hal_rtc_sync_shadow(); /* Enable write protection */ LL_RTC_EnableWriteProtection(RTC); diff --git a/firmware/targets/furi_hal_include/furi_hal_debug.h b/firmware/targets/furi_hal_include/furi_hal_debug.h index 88397bbba..befbb4f40 100644 --- a/firmware/targets/furi_hal_include/furi_hal_debug.h +++ b/firmware/targets/furi_hal_include/furi_hal_debug.h @@ -18,6 +18,9 @@ void furi_hal_debug_enable(); /** Disable MCU debug */ void furi_hal_debug_disable(); +/** Check if GDB debug session is active */ +bool furi_hal_debug_is_gdb_session_active(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index 462e20e41..00182fa28 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -58,12 +58,6 @@ void furi_hal_power_insomnia_exit(); */ bool furi_hal_power_sleep_available(); -/** Check if deep sleep availble - * - * @return true if available - */ -bool furi_hal_power_deep_sleep_available(); - /** Go to sleep */ void furi_hal_power_sleep(); diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index b16b04a68..0d9f46f01 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -30,6 +30,7 @@ typedef enum { FuriHalRtcFlagLock = (1 << 2), FuriHalRtcFlagC2Update = (1 << 3), FuriHalRtcFlagHandOrient = (1 << 4), + FuriHalRtcFlagLegacySleep = (1 << 5), } FuriHalRtcFlag; typedef enum { @@ -85,6 +86,9 @@ void furi_hal_rtc_deinit_early(); /** Initialize RTC subsystem */ void furi_hal_rtc_init(); +/** Force sync shadow registers */ +void furi_hal_rtc_sync_shadow(); + /** Get RTC register content * * @param[in] reg The register identifier diff --git a/furi/core/check.c b/furi/core/check.c index 910527cee..64f9f72f1 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -117,6 +118,8 @@ FURI_NORETURN void __furi_crash() { if(debug) { furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); furi_hal_console_puts("\033[0m\r\n"); + furi_hal_debug_enable(); + RESTORE_REGISTERS_AND_HALT_MCU(true); } else { furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h index 03a364abd..830bb191c 100644 --- a/furi/core/core_defines.h +++ b/furi/core/core_defines.h @@ -24,6 +24,10 @@ extern "C" { }) #endif +#ifndef ABS +#define ABS(a) ({ (a) < 0 ? -(a) : (a); }) +#endif + #ifndef ROUND_UP_TO #define ROUND_UP_TO(a, b) \ ({ \ diff --git a/site_scons/cc.scons b/site_scons/cc.scons index 1eb6a3376..55ab72ba6 100644 --- a/site_scons/cc.scons +++ b/site_scons/cc.scons @@ -36,6 +36,7 @@ ENV.AppendUnique( ], CPPDEFINES=[ "_GNU_SOURCE", + *GetOption("extra_defines"), ], LINKFLAGS=[ "-mcpu=cortex-m4", diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 5610478cb..84ef6ce19 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -26,6 +26,14 @@ AddOption( help="List of applications to add to firmware's built-ins. Also see FIRMWARE_APP_SET and FIRMWARE_APPS", ) +AddOption( + "--extra-define", + action="append", + dest="extra_defines", + default=[], + help="Extra global define that will be passed to C/C++ compiler, can be specified multiple times", +) + AddOption( "--extra-ext-apps", action="store", From f68c3b2a653a2f5fc35c3bc90c8447f3d5d97398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 18 Apr 2023 20:38:35 +0900 Subject: [PATCH 047/282] [FL-3264] Various stop mode fixes (#2584) * BleGlue: log hci_cmd_resp invocation * BleGlue: increase BleHciDriver stack size * ble hid app: increase stack * ble: comment unnecessary hci reset * BleGlue: stricter checks in communication with core2, cleanup code * Furi: enter insomnia when executing from RAM --------- Co-authored-by: gornekich --- firmware/targets/f7/ble_glue/app_debug.c | 2 +- firmware/targets/f7/ble_glue/ble_app.c | 33 ++++++++++-------------- firmware/targets/f7/ble_glue/gap.c | 2 -- firmware/targets/f7/src/main.c | 3 +++ 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index d84588540..78e789ac3 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -68,7 +68,7 @@ static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] = { {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */ {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */ {GPIOA, LL_GPIO_PIN_4, 1, 0}, /* PES_ACTIVITY - Set on Entry / Reset on Exit */ - {GPIOB, LL_GPIO_PIN_2, 1, 0}, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */ + {GPIOC, LL_GPIO_PIN_0, 1, 0}, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */ /* From v1.3.0 */ {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_NO_DELAY - Set on Entry / Reset on Exit */ {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_STACK_STORE_NVM_CB - Set on Entry / Reset on Exit */ diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 30be3c7ce..4fc4d521b 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -137,38 +137,33 @@ static int32_t ble_app_hci_thread(void* arg) { // Called by WPAN lib void hci_notify_asynch_evt(void* pdata) { UNUSED(pdata); - if(ble_app) { - FuriThreadId thread_id = furi_thread_get_id(ble_app->thread); - furi_assert(thread_id); - furi_thread_flags_set(thread_id, BLE_APP_FLAG_HCI_EVENT); - } + furi_check(ble_app); + FuriThreadId thread_id = furi_thread_get_id(ble_app->thread); + furi_assert(thread_id); + furi_thread_flags_set(thread_id, BLE_APP_FLAG_HCI_EVENT); } void hci_cmd_resp_release(uint32_t flag) { UNUSED(flag); - if(ble_app) { - furi_semaphore_release(ble_app->hci_sem); - } + furi_check(ble_app); + furi_check(furi_semaphore_release(ble_app->hci_sem) == FuriStatusOk); } void hci_cmd_resp_wait(uint32_t timeout) { - UNUSED(timeout); - if(ble_app) { - furi_semaphore_acquire(ble_app->hci_sem, FuriWaitForever); - } + furi_check(ble_app); + furi_check(furi_semaphore_acquire(ble_app->hci_sem, timeout) == FuriStatusOk); } static void ble_app_hci_event_handler(void* pPayload) { SVCCTL_UserEvtFlowStatus_t svctl_return_status; tHCI_UserEvtRxParam* pParam = (tHCI_UserEvtRxParam*)pPayload; - if(ble_app) { - svctl_return_status = SVCCTL_UserEvtRx((void*)&(pParam->pckt->evtserial)); - if(svctl_return_status != SVCCTL_UserEvtFlowDisable) { - pParam->status = HCI_TL_UserEventFlow_Enable; - } else { - pParam->status = HCI_TL_UserEventFlow_Disable; - } + furi_check(ble_app); + svctl_return_status = SVCCTL_UserEvtRx((void*)&(pParam->pckt->evtserial)); + if(svctl_return_status != SVCCTL_UserEvtFlowDisable) { + pParam->status = HCI_TL_UserEventFlow_Enable; + } else { + pParam->status = HCI_TL_UserEventFlow_Disable; } } diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 8ef037d6b..cbb2a203b 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -289,8 +289,6 @@ static void gap_init_svc(Gap* gap) { tBleStatus status; uint32_t srd_bd_addr[2]; - // HCI Reset to synchronise BLE Stack - hci_reset(); // Configure mac address aci_hal_write_config_data( CONFIG_DATA_PUBADDR_OFFSET, CONFIG_DATA_PUBADDR_LEN, gap->config->mac_address); diff --git a/firmware/targets/f7/src/main.c b/firmware/targets/f7/src/main.c index 1f2b5d6e4..2c353f52b 100644 --- a/firmware/targets/f7/src/main.c +++ b/firmware/targets/f7/src/main.c @@ -29,6 +29,8 @@ int main() { FuriThread* main_thread = furi_thread_alloc_ex("Init", 4096, init_task, NULL); #ifdef FURI_RAM_EXEC + // Prevent entering sleep mode when executed from RAM + furi_hal_power_insomnia_enter(); furi_thread_start(main_thread); #else furi_hal_light_sequence("RGB"); @@ -44,6 +46,7 @@ int main() { furi_hal_power_reset(); } else if(boot_mode == FuriHalRtcBootModeUpdate) { furi_hal_light_sequence("rgb BR"); + // Do update flipper_boot_update_exec(); // if things go nice, we shouldn't reach this point. // But if we do, abandon to avoid bootloops From ab921a6f0d31ef7420d40cc7506428a300127f57 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 18 Apr 2023 15:03:09 +0300 Subject: [PATCH 048/282] Update changelog --- CHANGELOG.md | 10 +++++++++- documentation/HowToBuild.md | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f84b0985b..b592469b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ -### New changes +### New changes +* OFW: Various stop mode fixes -> **Fixes all known issues with BLE (Random freezes, menu freeze, BT Remote plugin freeze) and other similar issues** +* OFW: Picopass: Correctly aborts when correct key is found -> Fixes Bug (Picopass app not reading elite keyed cards anymore. #413) +* SubGhz: Nero Radio 57bit **experimental** support + encoder improvements and decoder changes +* SubGhz: Fix RAW recording and reading, (bug where raw file plays endlessly) +* Plugins: Improve wifi marauder keyboard (added extra symbols!) (Port uart terminal keyboard into wifi marauder) +* Infrared: Update universal remote assets (by @amec0e | PR #421) +* Docs: Update build docs (by @PhoenixSheppy | PR #425) +### Previous changes * If you have copied apps into `apps` folder - remove `apps` folder on your microSD before installing this release to avoid issues! * SubGHz: (Bug that I decided to keep as a feature) You can change default button (Ok) for remote by holding custom button and pressing back at same time (same can be used to restore your button if you changed it accidentally) - Be careful, it might be unstable, I will make proper option to change button in next releases * SubGHz: Fixes for custom button bugs in SubGHz Remote app diff --git a/documentation/HowToBuild.md b/documentation/HowToBuild.md index e6e85d886..80b93b1c3 100644 --- a/documentation/HowToBuild.md +++ b/documentation/HowToBuild.md @@ -62,6 +62,8 @@ Check out `documentation/fbt.md` for details on building and flashing firmware. ./fbt.cmd COMPACT=1 DEBUG=0 updater_package ``` +**You may need to change** `/` **to** `\` **in front of fbt command (Only for Windows)!** + Check `dist/` for build outputs. Use **`flipper-z-{target}-update-{suffix}.tgz`** to flash your device. From 059053b9b82b4635045cd2461a116bf79ceebb5e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 18 Apr 2023 22:16:29 +0300 Subject: [PATCH 049/282] SubGHz Remote add Alutech AT4N Support Fix null pointer dereference --- .../main/subghz_remote/subghz_remote_app.c | 74 ++++++++++--------- lib/subghz/protocols/alutech_at_4n.c | 13 ++++ 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index a6225e1b5..0724355a3 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -135,7 +135,7 @@ static void cfg_read_file_path( *is_enabled = 0; } else { *text_file_label = extract_filename(furi_string_get_cstr(text_file_path), 16); - FURI_LOG_D(TAG, "%s file: %s", read_key, furi_string_get_cstr(text_file_path)); + //FURI_LOG_D(TAG, "%s file: %s", read_key, furi_string_get_cstr(text_file_path)); *is_enabled = 1; } flipper_format_rewind(fff_file); @@ -154,7 +154,7 @@ static void cfg_read_file_label( if(is_enabled == 1) { *text_file_label = char_to_str((char*)furi_string_get_cstr(temp_label), 16); } - FURI_LOG_D(TAG, "%s label: %s", read_key, *text_file_label); + //FURI_LOG_D(TAG, "%s label: %s", read_key, *text_file_label); } flipper_format_rewind(fff_file); furi_string_free(temp_label); @@ -387,6 +387,10 @@ bool subghz_remote_key_load( FURI_LOG_E(TAG, "Could not read Protocol."); break; } + if(!flipper_format_rewind(fff_data)) { + FURI_LOG_E(TAG, "Rewind error"); + return false; + } if(!furi_string_cmp_str(preset->protocol, "RAW")) { subghz_protocol_raw_gen_fff_data(fff_data, path); @@ -395,10 +399,7 @@ bool subghz_remote_key_load( FURI_LOG_E(TAG, "Unable to insert or update Repeat"); break; } - if(!flipper_format_rewind(fff_data)) { - FURI_LOG_E(TAG, "Rewind error"); - return false; - } + } else { stream_copy_full( flipper_format_get_raw_stream(fff_file), flipper_format_get_raw_stream(fff_data)); @@ -407,10 +408,6 @@ bool subghz_remote_key_load( FURI_LOG_E(TAG, "Unable to insert or update Repeat"); break; } - if(!flipper_format_rewind(fff_data)) { - FURI_LOG_E(TAG, "Rewind error"); - return false; - } } if(!flipper_format_rewind(fff_file)) { @@ -468,7 +465,7 @@ bool subghz_remote_save_protocol_to_file(FlipperFormat* fff_file, const char* de stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS); saved = true; - FURI_LOG_D(TAG, "(save) OK Save"); + //FURI_LOG_D(TAG, "(save) OK Save"); } while(0); furi_string_free(file_dir); furi_record_close(RECORD_STORAGE); @@ -528,7 +525,8 @@ static bool subghz_remote_send_sub(SubGHzRemote* app, FlipperFormat* fff_data) { bool res = false; do { if(!furi_hal_subghz_is_tx_allowed(app->txpreset->frequency)) { - printf( + FURI_LOG_E( + TAG, "In your settings, only reception on this frequency (%lu) is allowed,\r\n" "the actual operation of the subghz remote app is not possible\r\n ", app->txpreset->frequency); @@ -545,7 +543,11 @@ static bool subghz_remote_send_sub(SubGHzRemote* app, FlipperFormat* fff_data) { break; } - subghz_transmitter_deserialize(app->tx_transmitter, fff_data); + if(subghz_transmitter_deserialize(app->tx_transmitter, fff_data) != + SubGhzProtocolStatusOk) { + FURI_LOG_E(TAG, "Deserialize error!"); + break; + } furi_hal_subghz_reset(); furi_hal_subghz_idle(); @@ -576,7 +578,7 @@ static bool subghz_remote_send_sub(SubGHzRemote* app, FlipperFormat* fff_data) { } static void subghz_remote_send_signal(SubGHzRemote* app, Storage* storage, const char* path) { - FURI_LOG_D(TAG, "Sending: %s", path); + //FURI_LOG_D(TAG, "Sending: %s", path); app->tx_file_path = path; @@ -616,7 +618,7 @@ static void subghz_remote_send_signal(SubGHzRemote* app, Storage* storage, const static void subghz_remote_process_signal(SubGHzRemote* app, FuriString* signal) { view_port_update(app->view_port); - FURI_LOG_D(TAG, "signal = %s", furi_string_get_cstr(signal)); + //FURI_LOG_D(TAG, "signal = %s", furi_string_get_cstr(signal)); if(strlen(furi_string_get_cstr(signal)) > 12) { Storage* storage = furi_record_open(RECORD_STORAGE); @@ -746,6 +748,8 @@ void subghz_remote_subghz_alloc(SubGHzRemote* app) { app->environment, EXT_PATH("subghz/assets/came_atomo")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( app->environment, EXT_PATH("subghz/assets/nice_flor_s")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + app->environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_protocol_registry(app->environment, (void*)&subghz_protocol_registry); app->subghz_receiver = subghz_receiver_alloc_init(app->environment); @@ -868,14 +872,14 @@ int32_t subghz_remote_app(void* p) { bool exit_loop = false; if(app->file_result == 2) { - FURI_LOG_D( - TAG, - "U: %s - D: %s - L: %s - R: %s - O: %s ", - furi_string_get_cstr(app->up_file), - furi_string_get_cstr(app->down_file), - furi_string_get_cstr(app->left_file), - furi_string_get_cstr(app->right_file), - furi_string_get_cstr(app->ok_file)); + //FURI_LOG_D( + //TAG, + //"U: %s - D: %s - L: %s - R: %s - O: %s ", + //furi_string_get_cstr(app->up_file), + //furi_string_get_cstr(app->down_file), + //furi_string_get_cstr(app->left_file), + //furi_string_get_cstr(app->right_file), + //furi_string_get_cstr(app->ok_file)); //variables to control multiple button presses and status updates app->send_status = "Idle"; @@ -893,11 +897,11 @@ int32_t subghz_remote_app(void* p) { while(1) { furi_check( furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk); - FURI_LOG_D( - TAG, - "key: %s type: %s", - input_get_key_name(input.key), - input_get_type_name(input.type)); + //FURI_LOG_D( + //TAG, + //"key: %s type: %s", + //input_get_key_name(input.key), + //input_get_type_name(input.type)); switch(input.key) { case InputKeyUp: @@ -999,12 +1003,12 @@ int32_t subghz_remote_app(void* p) { } if(app->processing == 0) { - FURI_LOG_D(TAG, "processing 0"); + //FURI_LOG_D(TAG, "processing 0"); app->send_status = "Idle"; app->send_status_c = 0; app->button = 0; } else if(app->processing == 1) { - FURI_LOG_D(TAG, "processing 1"); + //FURI_LOG_D(TAG, "processing 1"); app->send_status = "Send"; @@ -1047,11 +1051,11 @@ int32_t subghz_remote_app(void* p) { while(1) { furi_check( furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk); - FURI_LOG_D( - TAG, - "key: %s type: %s", - input_get_key_name(input.key), - input_get_type_name(input.type)); + //FURI_LOG_D( + //TAG, + //"key: %s type: %s", + //input_get_key_name(input.key), + //input_get_type_name(input.type)); switch(input.key) { case InputKeyRight: diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index 04e10adf5..20dae49c1 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -79,6 +79,11 @@ const SubGhzProtocol subghz_protocol_alutech_at_4n = { .encoder = &subghz_protocol_alutech_at_4n_encoder, }; +static void subghz_protocol_alutech_at_4n_remote_controller( + SubGhzBlockGeneric* instance, + uint8_t crc, + const char* file_name); + void* subghz_protocol_encoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolEncoderAlutech_at_4n* instance = @@ -503,10 +508,18 @@ SubGhzProtocolStatus subghz_protocol_encoder_alutech_at_4n_deserialize( break; } + if(!flipper_format_read_uint32(flipper_format, "CRC", (uint32_t*)&instance->crc, 1)) { + FURI_LOG_E(TAG, "Missing CRC"); + break; + } + //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + subghz_protocol_alutech_at_4n_remote_controller( + &instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name); + subghz_protocol_encoder_alutech_at_4n_get_upload(instance, instance->generic.btn); if(!flipper_format_rewind(flipper_format)) { From 2c7eb53caceea63999aed09d691271a3d1ee58bc Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Wed, 19 Apr 2023 11:30:26 +0300 Subject: [PATCH 050/282] [FL-2505] Active RPC session icon (#2583) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Active RPC session icon * Add RpcOwner, don't show the RPC icon when the session was started from BLE * Fix rpc_test and f18 api * Bump API version Co-authored-by: あく --- applications/debug/unit_tests/rpc/rpc_test.c | 4 +-- applications/services/bt/bt_service/bt.c | 2 +- applications/services/rpc/rpc.c | 9 ++++++- applications/services/rpc/rpc.h | 18 ++++++++++++- applications/services/rpc/rpc_cli.c | 2 +- applications/services/rpc/rpc_gui.c | 26 ++++++++++++++++++- assets/icons/StatusBar/Rpc_active_7x8.png | Bin 0 -> 3607 bytes firmware/targets/f18/api_symbols.csv | 5 ++-- firmware/targets/f7/api_symbols.csv | 5 ++-- 9 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 assets/icons/StatusBar/Rpc_active_7x8.png diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c index 329f3b741..167266a84 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -84,7 +84,7 @@ static void test_rpc_setup(void) { rpc = furi_record_open(RECORD_RPC); for(int i = 0; !(rpc_session[0].session) && (i < 10000); ++i) { - rpc_session[0].session = rpc_session_open(rpc); + rpc_session[0].session = rpc_session_open(rpc, RpcOwnerUnknown); furi_delay_tick(1); } furi_check(rpc_session[0].session); @@ -104,7 +104,7 @@ static void test_rpc_setup_second_session(void) { furi_check(!(rpc_session[1].session)); for(int i = 0; !(rpc_session[1].session) && (i < 10000); ++i) { - rpc_session[1].session = rpc_session_open(rpc); + rpc_session[1].session = rpc_session_open(rpc, RpcOwnerUnknown); furi_delay_tick(1); } furi_check(rpc_session[1].session); diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 16b60231b..2dcea3485 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -225,7 +225,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { furi_event_flag_clear(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); if(bt->profile == BtProfileSerial) { // Open RPC session - bt->rpc_session = rpc_session_open(bt->rpc); + bt->rpc_session = rpc_session_open(bt->rpc, RpcOwnerBle); if(bt->rpc_session) { FURI_LOG_I(TAG, "Open RPC connection"); rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index 57a4ec9aa..5b09e9b51 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -76,6 +76,7 @@ struct RpcSession { RpcBufferIsEmptyCallback buffer_is_empty_callback; RpcSessionClosedCallback closed_callback; RpcSessionTerminatedCallback terminated_callback; + RpcOwner owner; void* context; }; @@ -83,6 +84,11 @@ struct Rpc { FuriMutex* busy_mutex; }; +RpcOwner rpc_session_get_owner(RpcSession* session) { + furi_assert(session); + return session->owner; +} + static void rpc_close_session_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(context); @@ -348,7 +354,7 @@ static void rpc_session_free_callback(FuriThreadState thread_state, void* contex } } -RpcSession* rpc_session_open(Rpc* rpc) { +RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) { furi_assert(rpc); RpcSession* session = malloc(sizeof(RpcSession)); @@ -357,6 +363,7 @@ RpcSession* rpc_session_open(Rpc* rpc) { session->rpc = rpc; session->terminate = false; session->decode_error = false; + session->owner = owner; RpcHandlerDict_init(session->handlers); session->decoded_message = malloc(sizeof(PB_Main)); diff --git a/applications/services/rpc/rpc.h b/applications/services/rpc/rpc.h index ec290e083..d11fdc162 100644 --- a/applications/services/rpc/rpc.h +++ b/applications/services/rpc/rpc.h @@ -30,6 +30,21 @@ typedef void (*RpcSessionClosedCallback)(void* context); * and all operations were finished */ typedef void (*RpcSessionTerminatedCallback)(void* context); +/** RPC owner */ +typedef enum { + RpcOwnerUnknown = 0, + RpcOwnerBle, + RpcOwnerUsb, + RpcOwnerCount, +} RpcOwner; + +/** Get RPC session owner + * + * @param session pointer to RpcSession descriptor + * @return session owner + */ +RpcOwner rpc_session_get_owner(RpcSession* session); + /** Open RPC session * * USAGE: @@ -44,10 +59,11 @@ typedef void (*RpcSessionTerminatedCallback)(void* context); * * * @param rpc instance + * @param owner owner of session * @return pointer to RpcSession descriptor, or * NULL if RPC is busy and can't open session now */ -RpcSession* rpc_session_open(Rpc* rpc); +RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner); /** Close RPC session * It is guaranteed that no callbacks will be called diff --git a/applications/services/rpc/rpc_cli.c b/applications/services/rpc/rpc_cli.c index d14b8eee2..f1c139b5f 100644 --- a/applications/services/rpc/rpc_cli.c +++ b/applications/services/rpc/rpc_cli.c @@ -47,7 +47,7 @@ void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context) { FURI_LOG_D(TAG, "Free memory %lu", mem_before); furi_hal_usb_lock(); - RpcSession* rpc_session = rpc_session_open(rpc); + RpcSession* rpc_session = rpc_session_open(rpc, RpcOwnerUsb); if(rpc_session == NULL) { printf("Session start error\r\n"); furi_hal_usb_unlock(); diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index 0c70702cf..9ba20a832 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -2,6 +2,7 @@ #include "rpc_i.h" #include "gui.pb.h" #include +#include #define TAG "RpcGui" @@ -31,6 +32,8 @@ typedef struct { uint32_t input_key_counter[InputKeyMAX]; uint32_t input_counter; + + ViewPort* rpc_session_active_viewport; } RpcGuiSystem; static const PB_Gui_ScreenOrientation rpc_system_gui_screen_orientation_map[] = { @@ -352,6 +355,12 @@ static void rpc_system_gui_virtual_display_frame_process(const PB_Main* request, (void)session; } +static void rpc_active_session_icon_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + furi_assert(canvas); + canvas_draw_icon(canvas, 0, 0, &I_Rpc_active_7x8); +} + void* rpc_system_gui_alloc(RpcSession* session) { furi_assert(session); @@ -359,6 +368,18 @@ void* rpc_system_gui_alloc(RpcSession* session) { rpc_gui->gui = furi_record_open(RECORD_GUI); rpc_gui->session = session; + // Active session icon + rpc_gui->rpc_session_active_viewport = view_port_alloc(); + view_port_set_width(rpc_gui->rpc_session_active_viewport, icon_get_width(&I_Rpc_active_7x8)); + view_port_draw_callback_set( + rpc_gui->rpc_session_active_viewport, rpc_active_session_icon_draw_callback, session); + if(rpc_session_get_owner(rpc_gui->session) != RpcOwnerBle) { + view_port_enabled_set(rpc_gui->rpc_session_active_viewport, true); + } else { + view_port_enabled_set(rpc_gui->rpc_session_active_viewport, false); + } + gui_add_view_port(rpc_gui->gui, rpc_gui->rpc_session_active_viewport, GuiLayerStatusBarLeft); + RpcHandler rpc_handler = { .message_handler = NULL, .decode_submessage = NULL, @@ -399,6 +420,9 @@ void rpc_system_gui_free(void* context) { rpc_gui->virtual_display_not_empty = false; } + gui_remove_view_port(rpc_gui->gui, rpc_gui->rpc_session_active_viewport); + view_port_free(rpc_gui->rpc_session_active_viewport); + if(rpc_gui->is_streaming) { rpc_gui->is_streaming = false; // Remove GUI framebuffer callback @@ -415,4 +439,4 @@ void rpc_system_gui_free(void* context) { } furi_record_close(RECORD_GUI); free(rpc_gui); -} +} \ No newline at end of file diff --git a/assets/icons/StatusBar/Rpc_active_7x8.png b/assets/icons/StatusBar/Rpc_active_7x8.png new file mode 100644 index 0000000000000000000000000000000000000000..f643a82aa1d2efab26d7e8976bce73124c5c64d2 GIT binary patch literal 3607 zcmaJ@c{r3^8-FYn%91rCV?>K(%%GTzZ7ic~jcrt7jKN@*#$ZO0C8cD`mNlWIhBigA zCHq*SNGLlA4GGE8H{S2<{k}iGzUR80=bUq&`}e!=bKk#nUC&ipYjZ&X836zQ1T9b| zXwJBU^YZcWaK7CA5?TNtfFvN1wiZYvh(aZM68!K0K=04C3&JF=Na_!*;DsHH`{o~~ z`Go^uMJ8Xfh;yFE4FG#dMAWb$Dq6r%Tw-??%Ar1@M*x)_J(_#4+{@@%^r;w!Gdf@8 z2MeEF2xiaCt-W8XoXVP7?_hKahwTtguyf0ziqtFj#bICszU*XjZpx76+R5 z0FUgRdI$i?0N}?6F$M$o1%cV`7W&(OzM`Z-DWETJZxJ65%LSw#G~dr{_!4k)2uG`O z*VX~KOhd3bZ*2)znDcX(Id@pqHXtw#lOSy9285*>tF{3k9c}=*Ppq8>WXY4O(OolK zx0@L5+Fz?DV!VAkY_GuWJ*h_0_O8l(K8Y?U z#cyg(?sHp~>M-PV&6t4lsOiPhRF)W3GP}O-tA%EH%%!OQv)m zBJ6oyVb_Vz0W{#kwK!Z@7gWge`UmWp>sL(Ou3}`Anr zx1T#EOl+3#>?M&pzlekcbBrYhc~5Cpu~f8z&xt?s6146BIO(2EsZy}$YCYW@{x|_+ z##H{QuumaJ>Ffa^G1ny2exa5dyK2nK-;5deo9XZ$G*qS@gIz-e7|F&Mla6dhY#`?L|57`0hu; zZ=JFr<_6kA?5-4vX$52`wP#8qSp{nOJ#R7yUW65I$TY2j|6}An)3i5f-M*i9OixWm zeh=Cucv&#A3FUrJ+E@C#bm5*dX-K-|-ED8v(wpry-os?my>1HMBs*XZFCPw(NNg2N zfu}g8gr-d0w|DS&Fz8|2-)aBALHNO0#|wAO9G=>a74g55e9%)Q=kT)VNJ$4e2pw~AKYKv z?>Lq<`$2VZ^KA*Z&%QSaOXd*^pqAFY}6gotcK$;M`D^%`%^<$+fuDkSC}^)^&J_GOOEfE7QJU?RA-32PJXts zPMNzh;hC)G_lh%%>jN{1L*k?2@rSw(E!mO!p|k6=0<#&j+vjwbvCB%!#N|%8w!NZq zy~k+BLa4NWwm?5h%w&#qPNvw51OoYSj8Y#yjTJzT{)?*`XL;D2^Z^XNC~bKeTSb*1`lHFyY7tr*%H znjX4iJ!kFMPu52KkD3w2H^~L-ZEjqzxF)!&!ezpr^7!(|^QQCO*`d8HcH7JCkX`x` z#=H0ho#m-X88{KQ-EvE%){Aj=S8+HzX2DzPoBU-S(U> zdf41Ax?G+hoi4R{LHy8R*nRacCrj;U=V&Xcz07gvHLt3;h4`ZDFOCiPJf{YFF@5~d zez@7p^04yz<>B-zKTH?WL}_CC`RA^V*Z0*Uu8#S0Px)nNW{qdqr_yjG6N7m#q$a03 zSUEv@&f3liAv#evMbn-bOhkf=Z6SMXCJISba$?^uWk%VUR_Dd$oqSwYeq8UyWVU0< zvd+h27mz=_FWZ;}G-~qOpj8-&(l=fxE?PO7^nPM?emM6*O*c6!IV&G6NJuSJd9P7+ zu*}9&MI}jaoH0pkl})c2Q3;TL7um0yZ3u@#cEgxpi1pWbttUXH3loqDG^ zx5j@0I*` zWL7>o>SXQO6SBl0_V<4}Ue-D{gPJLrAu(5+YaSl2@-gapb9r8{Saux?o`wc#Ur zL?4OWhk{ckGQ-IInR6LI&nTx33)LA-ygD#+H{|@7?dRJauLobZ52ar$TjHBRF;u$Y zH33Yud}1!*b`|^c_55tPvvTIuWxqn%&@o&OWDms^Y~cH^vU8A;EjC7l~nfEA~ zDaOWU%gf6O%2a*x=tvqVd{QXZ*&6Eji!tN>U}|V%bnQeZX1#BI=W0x6O$noDk;;Bh z^bp>p*d$-s!9XnBT`%q!xWF!FR}3{($)_J&H{7&c?D~moDtu$JqCXX~xei&mw73?g ztF)W*S1C<1#n9}?p0pq=6%QDBklpbh3nJDFkH%v?80T8>dH}$4kYJCYVXQ3SI5JTk zyXB)!CsH_Q0MIj_Q?NK+JPqWI_acxGVD`%zFo@uR06S@0X;@K^cy9tKn2NUxwzkIw z`{Hyxzy|stJvy92K*ZCqAUe^H(|^RvS^Oh^Jc*-uDo5Oq1JY`oi?Q9- z(%$sfwXHi+u~ebK8TMP47}?YN-Q7GqJOC~;5nh-=Nc0{P@y3W&D6GyjJR}U@0eB1n TX-MlFYYS&#YHd=yAa5I#)A literal 0 HcmV?d00001 diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 493f59634..a37ef02cc 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,22.0,, +Version,+,23.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1596,7 +1596,8 @@ Function,-,rindex,char*,"const char*, int" Function,+,rpc_session_close,void,RpcSession* Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, TickType_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* -Function,+,rpc_session_open,RpcSession*,Rpc* +Function,+,rpc_session_get_owner,RpcOwner,RpcSession* +Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" Function,+,rpc_session_set_buffer_is_empty_callback,void,"RpcSession*, RpcBufferIsEmptyCallback" Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCallback" Function,+,rpc_session_set_context,void,"RpcSession*, void*" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3ef57813b..43ede425e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,22.0,, +Version,+,23.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2373,7 +2373,8 @@ Function,-,roundl,long double,long double Function,+,rpc_session_close,void,RpcSession* Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, TickType_t" Function,+,rpc_session_get_available_size,size_t,RpcSession* -Function,+,rpc_session_open,RpcSession*,Rpc* +Function,+,rpc_session_get_owner,RpcOwner,RpcSession* +Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" Function,+,rpc_session_set_buffer_is_empty_callback,void,"RpcSession*, RpcBufferIsEmptyCallback" Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCallback" Function,+,rpc_session_set_context,void,"RpcSession*, void*" From c6e5ca1fec4976112d2a7ea579e0861e6f5314df Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 19 Apr 2023 11:51:08 +0300 Subject: [PATCH 051/282] Fix keeloq custom buttons bug When you receive couple signals on read screen and open last one it was replacing its button with first one received --- applications/main/subghz/scenes/subghz_scene_receiver_info.c | 3 +++ applications/main/subghz/scenes/subghz_scene_transmitter.c | 4 ++++ lib/subghz/protocols/keeloq.c | 3 --- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 99765ee16..58e428785 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -115,6 +115,9 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) { void subghz_scene_receiver_info_on_enter(void* context) { SubGhz* subghz = context; + keeloq_reset_original_btn(); + subghz_custom_btns_reset(); + subghz_scene_receiver_info_draw_widget(subghz); } diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 4ee3854da..ab9155fb7 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -52,6 +52,10 @@ bool subghz_scene_transmitter_update_data_show(void* context) { void subghz_scene_transmitter_on_enter(void* context) { SubGhz* subghz = context; + + keeloq_reset_original_btn(); + subghz_custom_btns_reset(); + if(!subghz_scene_transmitter_update_data_show(subghz)) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventViewTransmitterError); diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 6698f54fe..4900e7e49 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -393,7 +393,6 @@ static bool uint8_t custom_btn_id = subghz_custom_btn_get(); uint8_t original_btn_num = subghz_custom_btn_get_original(); - // Set custom button if(custom_btn_id == 1) { switch(original_btn_num) { @@ -499,11 +498,9 @@ static bool break; } } - if((custom_btn_id == 0) && (original_btn_num != 0)) { btn = original_btn_num; } - // Generate new key if(subghz_protocol_keeloq_gen_data(instance, btn, true)) { From 7dfae0fb61e6889ea11713f16f1d6fb151af2732 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 19 Apr 2023 12:32:23 +0300 Subject: [PATCH 052/282] Just in case --- applications/main/subghz_remote/subghz_remote_app.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index 0724355a3..64f866b87 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -355,6 +355,8 @@ bool subghz_remote_key_load( bool res = false; subghz_custom_btn_set(0); + keeloq_reset_original_btn(); + subghz_custom_btns_reset(); do { // load frequency from file From 74fe003f8bd6e69230979d02a90853f0ea9f9750 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Wed, 19 Apr 2023 12:33:23 +0300 Subject: [PATCH 053/282] [FL-3171] Introduce stealth mode and auto-selective lock (#2576) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Introduce stealth mode and auto-selective lock * Stealth mode status bar icon * Review fixes * Fix icon disappearing after reboot * Support overriding stealth mode * FuriHal: correct reserved space size in RTC SystemReg Co-authored-by: あく --- applications/services/desktop/desktop.c | 29 +++++++++++++ applications/services/desktop/desktop_i.h | 2 + .../desktop/scenes/desktop_scene_lock_menu.c | 12 ++++++ .../services/desktop/views/desktop_events.h | 2 + .../desktop/views/desktop_view_lock_menu.c | 38 +++++++++++++----- .../desktop/views/desktop_view_lock_menu.h | 2 + .../services/notification/notification_app.c | 27 ++++++++----- .../notification_settings_app.c | 37 ++++++++++++----- assets/icons/StatusBar/Muted_8x8.png | Bin 0 -> 3626 bytes .../targets/furi_hal_include/furi_hal_rtc.h | 1 + 10 files changed, 121 insertions(+), 29 deletions(-) create mode 100644 assets/icons/StatusBar/Muted_8x8.png diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 41470ed3a..bdb730099 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -46,6 +46,12 @@ static void desktop_dummy_mode_icon_draw_callback(Canvas* canvas, void* context) canvas_draw_icon(canvas, 0, 0, &I_GameMode_11x8); } +static void desktop_stealth_mode_icon_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + furi_assert(canvas); + canvas_draw_icon(canvas, 0, 0, &I_Muted_8x8); +} + static bool desktop_custom_event_callback(void* context, uint32_t event) { furi_assert(context); Desktop* desktop = (Desktop*)context; @@ -153,6 +159,17 @@ void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) { desktop->in_transition = false; } +void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled) { + desktop->in_transition = true; + if(enabled) { + furi_hal_rtc_set_flag(FuriHalRtcFlagStealthMode); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagStealthMode); + } + view_port_enabled_set(desktop->stealth_mode_icon_viewport, enabled); + desktop->in_transition = false; +} + Desktop* desktop_alloc() { Desktop* desktop = malloc(sizeof(Desktop)); @@ -244,6 +261,18 @@ Desktop* desktop_alloc() { view_port_enabled_set(desktop->dummy_mode_icon_viewport, false); gui_add_view_port(desktop->gui, desktop->dummy_mode_icon_viewport, GuiLayerStatusBarLeft); + // Stealth mode icon + desktop->stealth_mode_icon_viewport = view_port_alloc(); + view_port_set_width(desktop->stealth_mode_icon_viewport, icon_get_width(&I_Muted_8x8)); + view_port_draw_callback_set( + desktop->stealth_mode_icon_viewport, desktop_stealth_mode_icon_draw_callback, desktop); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) { + view_port_enabled_set(desktop->stealth_mode_icon_viewport, true); + } else { + view_port_enabled_set(desktop->stealth_mode_icon_viewport, false); + } + gui_add_view_port(desktop->gui, desktop->stealth_mode_icon_viewport, GuiLayerStatusBarLeft); + // Special case: autostart application is already running desktop->loader = furi_record_open(RECORD_LOADER); if(loader_is_locked(desktop->loader) && diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index 2f3ec9b51..ede6bbcc3 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -59,6 +59,7 @@ struct Desktop { ViewPort* lock_icon_viewport; ViewPort* dummy_mode_icon_viewport; + ViewPort* stealth_mode_icon_viewport; AnimationManager* animation_manager; @@ -79,3 +80,4 @@ void desktop_free(Desktop* desktop); void desktop_lock(Desktop* desktop); void desktop_unlock(Desktop* desktop); void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled); +void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled); diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index 365fe1702..bfaa8a036 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -27,6 +27,8 @@ void desktop_scene_lock_menu_on_enter(void* context) { desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); desktop_lock_menu_set_pin_state(desktop->lock_menu, desktop->settings.pin_code.length > 0); desktop_lock_menu_set_dummy_mode_state(desktop->lock_menu, desktop->settings.dummy_mode); + desktop_lock_menu_set_stealth_mode_state( + desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); desktop_lock_menu_set_idx(desktop->lock_menu, 0); view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdLockMenu); @@ -78,6 +80,16 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_search_and_switch_to_previous_scene( desktop->scene_manager, DesktopSceneMain); break; + case DesktopLockMenuEventStealthModeOn: + desktop_set_stealth_mode_state(desktop, true); + scene_manager_search_and_switch_to_previous_scene( + desktop->scene_manager, DesktopSceneMain); + break; + case DesktopLockMenuEventStealthModeOff: + desktop_set_stealth_mode_state(desktop, false); + scene_manager_search_and_switch_to_previous_scene( + desktop->scene_manager, DesktopSceneMain); + break; default: break; } diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h index 666d179b8..983e84438 100644 --- a/applications/services/desktop/views/desktop_events.h +++ b/applications/services/desktop/views/desktop_events.h @@ -34,6 +34,8 @@ typedef enum { DesktopLockMenuEventPinLock, DesktopLockMenuEventDummyModeOn, DesktopLockMenuEventDummyModeOff, + DesktopLockMenuEventStealthModeOn, + DesktopLockMenuEventStealthModeOff, DesktopAnimationEventCheckAnimation, DesktopAnimationEventNewIdleAnimation, diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 52570f8ca..8b25a890f 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -7,7 +7,7 @@ typedef enum { DesktopLockMenuIndexLock, - DesktopLockMenuIndexPinLock, + DesktopLockMenuIndexStealth, DesktopLockMenuIndexDummy, DesktopLockMenuIndexTotalCount @@ -39,6 +39,14 @@ void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool true); } +void desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode) { + with_view_model( + lock_menu->view, + DesktopLockMenuViewModel * model, + { model->stealth_mode = stealth_mode; }, + true); +} + void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx) { furi_assert(idx < DesktopLockMenuIndexTotalCount); with_view_model( @@ -58,11 +66,11 @@ void desktop_lock_menu_draw_callback(Canvas* canvas, void* model) { if(i == DesktopLockMenuIndexLock) { str = "Lock"; - } else if(i == DesktopLockMenuIndexPinLock) { - if(m->pin_is_set) { - str = "Lock with PIN"; + } else if(i == DesktopLockMenuIndexStealth) { + if(m->stealth_mode) { + str = "Sound Mode"; } else { - str = "Set PIN"; + str = "Stealth Mode"; } } else if(i == DesktopLockMenuIndexDummy) { //-V547 if(m->dummy_mode) { @@ -93,6 +101,8 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { uint8_t idx = 0; bool consumed = false; bool dummy_mode = false; + bool stealth_mode = false; + bool pin_is_set = false; bool update = false; with_view_model( @@ -120,14 +130,24 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { } idx = model->idx; dummy_mode = model->dummy_mode; + stealth_mode = model->stealth_mode; + pin_is_set = model->pin_is_set; }, update); if(event->key == InputKeyOk) { - if((idx == DesktopLockMenuIndexLock) && (event->type == InputTypeShort)) { - lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); - } else if((idx == DesktopLockMenuIndexPinLock) && (event->type == InputTypeShort)) { - lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context); + if((idx == DesktopLockMenuIndexLock)) { + if((pin_is_set) && (event->type == InputTypeShort)) { + lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context); + } else if((pin_is_set == false) && (event->type == InputTypeShort)) { + lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); + } + } else if(idx == DesktopLockMenuIndexStealth) { + if((stealth_mode == false) && (event->type == InputTypeShort)) { + lock_menu->callback(DesktopLockMenuEventStealthModeOn, lock_menu->context); + } else if((stealth_mode == true) && (event->type == InputTypeShort)) { + lock_menu->callback(DesktopLockMenuEventStealthModeOff, lock_menu->context); + } } else if(idx == DesktopLockMenuIndexDummy) { if((dummy_mode == false) && (event->type == InputTypeShort)) { lock_menu->callback(DesktopLockMenuEventDummyModeOn, lock_menu->context); diff --git a/applications/services/desktop/views/desktop_view_lock_menu.h b/applications/services/desktop/views/desktop_view_lock_menu.h index 812aa9f99..03ce6fa80 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.h +++ b/applications/services/desktop/views/desktop_view_lock_menu.h @@ -19,6 +19,7 @@ typedef struct { uint8_t idx; bool pin_is_set; bool dummy_mode; + bool stealth_mode; } DesktopLockMenuViewModel; void desktop_lock_menu_set_callback( @@ -29,6 +30,7 @@ void desktop_lock_menu_set_callback( View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu); void desktop_lock_menu_set_pin_state(DesktopLockMenuView* lock_menu, bool pin_is_set); void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode); +void desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode); void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx); DesktopLockMenuView* desktop_lock_menu_alloc(); void desktop_lock_menu_free(DesktopLockMenuView* lock_menu); diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index 2e170f547..f91a73f32 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -20,9 +20,9 @@ static const uint8_t reset_sound_mask = 1 << 4; static const uint8_t reset_display_mask = 1 << 5; static const uint8_t reset_blink_mask = 1 << 6; -void notification_vibro_on(); +void notification_vibro_on(bool force); void notification_vibro_off(); -void notification_sound_on(float freq, float volume); +void notification_sound_on(float freq, float volume, bool force); void notification_sound_off(); uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); @@ -141,17 +141,21 @@ uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { } // generics -void notification_vibro_on() { - furi_hal_vibro_on(true); +void notification_vibro_on(bool force) { + if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { + furi_hal_vibro_on(true); + } } void notification_vibro_off() { furi_hal_vibro_on(false); } -void notification_sound_on(float freq, float volume) { - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { - furi_hal_speaker_start(freq, volume); +void notification_sound_on(float freq, float volume, bool force) { + if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(freq, volume); + } } } @@ -174,6 +178,8 @@ void notification_process_notification_message( NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; + bool force_volume = false; + bool force_vibro = false; const NotificationMessage* notification_message; notification_message = (*message->sequence)[notification_message_index]; @@ -269,7 +275,7 @@ void notification_process_notification_message( break; case NotificationMessageTypeVibro: if(notification_message->data.vibro.on) { - if(vibro_setting) notification_vibro_on(); + if(vibro_setting) notification_vibro_on(force_vibro); } else { notification_vibro_off(); } @@ -278,7 +284,8 @@ void notification_process_notification_message( case NotificationMessageTypeSoundOn: notification_sound_on( notification_message->data.sound.frequency, - notification_message->data.sound.volume * speaker_volume_setting); + notification_message->data.sound.volume * speaker_volume_setting, + force_volume); reset_mask |= reset_sound_mask; break; case NotificationMessageTypeSoundOff: @@ -307,9 +314,11 @@ void notification_process_notification_message( break; case NotificationMessageTypeForceSpeakerVolumeSetting: speaker_volume_setting = notification_message->data.forced_settings.speaker_volume; + force_volume = true; break; case NotificationMessageTypeForceVibroSetting: vibro_setting = notification_message->data.forced_settings.vibro; + force_vibro = true; break; case NotificationMessageTypeForceDisplayBrightnessSetting: display_brightness_setting = diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c index d462163ad..8efbc5e08 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -157,18 +157,33 @@ static NotificationAppSettings* alloc_settings() { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, backlight_text[value_index]); - item = variable_item_list_add( - app->variable_item_list, "Volume", VOLUME_COUNT, volume_changed, app); - value_index = - value_index_float(app->notification->settings.speaker_volume, volume_value, VOLUME_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, volume_text[value_index]); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) { + item = variable_item_list_add(app->variable_item_list, "Volume", 1, NULL, app); + value_index = 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, "Stealth"); + } else { + item = variable_item_list_add( + app->variable_item_list, "Volume", VOLUME_COUNT, volume_changed, app); + value_index = value_index_float( + app->notification->settings.speaker_volume, volume_value, VOLUME_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, volume_text[value_index]); + } - item = - variable_item_list_add(app->variable_item_list, "Vibro", VIBRO_COUNT, vibro_changed, app); - value_index = value_index_bool(app->notification->settings.vibro_on, vibro_value, VIBRO_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, vibro_text[value_index]); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) { + item = variable_item_list_add(app->variable_item_list, "Vibro", 1, NULL, app); + value_index = 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, "Stealth"); + } else { + item = variable_item_list_add( + app->variable_item_list, "Vibro", VIBRO_COUNT, vibro_changed, app); + value_index = + value_index_bool(app->notification->settings.vibro_on, vibro_value, VIBRO_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, vibro_text[value_index]); + } app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); diff --git a/assets/icons/StatusBar/Muted_8x8.png b/assets/icons/StatusBar/Muted_8x8.png new file mode 100644 index 0000000000000000000000000000000000000000..fee4e09f5e68ce3fd468d2aae8ffea79a1fcd74a GIT binary patch literal 3626 zcmaJ^c|26@+dsBKS+a&?jCd-`%vg%a*q2eZ#x^Q3#$Yf@V=yD6q?BygvL=+&kV=_k zON4AuBujP@8cWFjj_3FE{N6v_-t#%<+}C~G*Y|r}*L{EgIOm3~wYdPlG(P|U0v1RU z6no^|d3d?l@5K)5R{+3oj5jv6wJLFmL43BAD;oRFhY&)m~g zzsmqvfx+7-?3^on8^Bx@7BQ%gjN*3`6W^DKbf~-1#gEL28p%1#^fJ5|btc;3oc0g7 z{(={w!K~TY_0Q`SlbMqnZS;1b@O>gm2@|fFC`?2n;+D0A_w#=;&hG+J+4W0*7)9k$F~ z-RcXT11_W+q!rcVMQmQc5Ce-*v6Ic*Mlj;aq{3E1hu)=NUUCsGf?ILT2u2!0ifdB{&NqLBrV^ug=Ug-`DlsZ?!9ls7&U^KZ)7WK zBsnp=ObqrOs?ilT+BFt_fdAh96hkTd8h8))ixMvBoPFuT!liFu+5(e9BIhnolO=b# z?a!{=UvZ6(+pv*W6eACh+UFkI18(D$OpE0PW00D+!}CE?QDUdT^^KH#&O#%f1Q(>j zf+|H!C+3{NT6|w4Nd4x<%?fi^(&cBTxju0Q7`%EYCw=o>j(-PLQ^+MeCD*q@y7V9- z{AF%I$Ej}tR3P+bEH=CYCg^$V3+CLd>!xlu(9%i`64-IHmSdb2Ru+9cP+X=a8^hu~ z#2FnfI>USZs&K8}mH2pbv?bd3q2i1}sYp4m6JNwtSnXfn#D_MeioqhQbu@SQ(|EKQ zL+OY7LHTUO^M477x+WqI2{zpxv*wpqj90hVW8sVJL#pQ6Do8FFb=6uz>t`F&WFZ_x(WQtnOHxO~qH1$Jjr|-AjQ zKZEuPToZ_BK)N56@|C(MRj|KI3X*2|fahasTBAMmv${;0*BzldBnR}-<)b0u3GYRc z;mMB4Vabz^>>d0a^tZUkyO{@6cv2s8AY<0#mkY`;c z5OUGd&e;EvC&M$rGi%~PD~I5_r$ci(Uoua&$+ro#T~y^#)mMGm?Nj7g6jpS+H@LT| z*L{{UD=|CW%L*W88DyzsEx`%!l>ol-W96sIBg^{&+P~|4#7@(v_?F%;2~G`km@g52 z<}FEnXLYkWo%d5^_Q`N6cYb1m`MZ@zc%%#OLM!w>g0dzUBFdFORlT9bU!Jp+ z-)v0Pmx{iPn8^F4ne`{5k3~16^rnO~^Q2g%tT664>N(OKmmEJcEV|E4t4tHRo&0QN zoHF)iz%!a(J}k;8ste2@42q42#U1H9vSdf>h0Sct@Xw^r?3&eC#w;r?6PDX$TMmfG zcAuc02%%5|S_1vZ71Q~{nr{p13g1pSdC7R)^Uqq;l6-I~zF8zSFjdlyP;j$=csIB? zd0hHOdFP9dOL-ZuGy03Z5IzmGAnHROuUn~Q$TYdUMX(Hi&aI$FF0_nh6=W3DF2+wn z&Ig`%KEGah(B^rjQg%0#(AZaZcBr!Xq8nTffm>^v;?jGk$9XsS?)9n%Rp3EHl|oo| zSX$WbjqLRkYt5T+zp97n3lj6zJZ>CsIL0|DFH2v3&gIJ`$DPJ~f43G-4A1LbkiB~1 zMtk`LoaHEqq#DTunN_Stzn4mShxmKmEb%ikYtR-Y%Y)tfVz0IH4hn~n9;{kUwY=nw z8Z!5bV#?vA(j-?y#f}_Rf28*Pbg3Qn0(C{Dn=$6R?o}B(A6Izm)rmfzmy|#+hL0c0 z4?B~=3}-%KUZ!3DWjdcKLKQv4H+y5OuBQ%hW7MB>(l1>jb1dCHg^Dd6@6UZDIWg7N zoE@a=tmPaZq8)ihB=wp8cm&AE7P8-FyntvWE4pwsBhtpSDmNzg^wTotN!{BMS&qre z+5;y-jn}-d*_Z{q)8Ml~Dbo|AZog7kv~o1*{>o_oa^Z`rPEux4W*$lapHiy)8CrI@ z%*In$IZ^tFUSw4G#-8q5V%?m@%Pb%r*3H$O|+v>idC z92R`&{R^?Eh|c@>jg_xBX(ksYH8m59f1Y`T6~`odIIw(DyUsdZIx3m$${;fy7XK}msNdH>g}!`2)ei^7$GZu;wvj@ zrs9P%&B?dYG<1pB`Qyq!oV2@4dG$ot0Mk6kran9F;{A)emrYsIK2(xl|C8$KXM6O5azXl(KTpYFicCYPi|J&BVv`b}6ywga!awohx zfT5a4$U)qzWPQ7pmnC~%*1W20JB$Sz#RfEJ#W`i?WF1D#uZ~?w94+uD$j-9XM|?`_ zr>|IUr1`p>cB%9-ZqL@2r2eohb6sg#{5l!uGFL(V30`sUQ!G1GgeXRshmkjjkx$N& zrg1CHvrf9j)Sp(s9PP1i5ak$`FfoS{tFvo5BwEyGD!OfBNQ|;)Dp1D1%(e2g6^uk37>v7=| zc!NTNTtyolG5=trsI4!Q#blKaHdM-`9o{h5vR&+0!!{IruvyWY4B6U%tXy4O57$xL z$Ns96qMD+qb|g<~5Qc&S3_VEhIFJPaeI*=tS^oVa>se$i3l+3bu}1-_dtN1w5*_3WMiB+9vMu**#%qMV}pIM zIv!wsJ&-O9&L$w>s2C89;79a_(-7dldEsn(#|;64{tlt~BEbJC3T!+BuT-8DQhSQU^uR2>F^vG3!mu;Xxb4LD2(^v?xmC!~0I!cit>|Do-kpHD6`v0?2>^+0fFk}c!4Z73S??5Z7|38#K_&3_0io*T3 z-v5)>-=0p!K~OkGDuxR4F)=F zg~8&9JDwwdL#(Xe7DRt4hKR*km>|Gx5;Z*D1FoS1HPJRTGckc4H-*7WO|+pp+NPS? zn%bs1T2Ny{t>0J^5;lNHBVuSo13}C{;vUc*;?A0 zvd8W1?fYNu4zMjwD$3e!8yGKxo@CFCSeO{v(|T2+oA>}=Z|&{PR5hP@%p4#N Date: Wed, 19 Apr 2023 12:47:01 +0300 Subject: [PATCH 054/282] [FL-3089] Raw RFID documentation (#2592) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- documentation/LFRFIDRaw.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 documentation/LFRFIDRaw.md diff --git a/documentation/LFRFIDRaw.md b/documentation/LFRFIDRaw.md new file mode 100644 index 000000000..5a8cbde60 --- /dev/null +++ b/documentation/LFRFIDRaw.md @@ -0,0 +1,23 @@ +# Reading RAW RFID data + +Flipper Zero has the option to read RAW data from 125 kHz cards that allows you to record the card's data and save it, similar to how a dictaphone records sound. + +To use this function, you need to activate the Debug mode on your Flipper Zero by doing the following: + +1. Go to **Main Menu** → **Settings** → **System**. + +2. Set **Debug** to **ON**. + +Once the Debug mode is activated on your Flipper Zero, you can read RAW data from 125 kHz RFID cards: + +1. Go to **Main Menu** → **125 kHz RFID** → **Extra Actions**. + +2. Select **RAW RFID** data and name the raw file. + +3. Read instructions and press **OK**. + +4. Apply the card to Flipper Zero's back. + +5. Once the reading is finished, press **OK**. + +Two files with data (with ASK and PSK modulations) will be saved in the `lfrfid` folder on the microSD card. Now, you can share it and the card's photo with developers by creating an issue on GitHub. From 39325036607d16effddfb5016cb7ffbd5b38ac49 Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 19 Apr 2023 15:08:13 +0400 Subject: [PATCH 055/282] [FL-3243] github: testing SDK with ufbt action (#2581) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * github: testing SDK with ufbt action * github: also build apps with ufbt * github: fixed dir lookup for ufbt * ufbt: checks for compatibility on app discovery * github: Conditional app skip for ufbt * github: fixed app build flow with ufbt * extra debug * github: lint: message capture * github: testing different output capture method for linters * shorter version of status check * github: updated comment actions to suppress warnings * Reverted formatting changes Co-authored-by: あく --- .github/workflows/build.yml | 44 ++++++++++++++++--- .../workflows/lint_and_submodule_check.yml | 34 +++++++++++--- scripts/ufbt/SConstruct | 14 ++++++ 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5f6f50a9d..dfeb8d83f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -139,7 +139,7 @@ jobs: - name: 'Find Previous Comment' if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} - uses: peter-evans/find-comment@v1 + uses: peter-evans/find-comment@v2 id: fc with: issue-number: ${{ github.event.pull_request.number }} @@ -148,7 +148,7 @@ jobs: - name: 'Create or update comment' if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request}} - uses: peter-evans/create-or-update-comment@v1 + uses: peter-evans/create-or-update-comment@v3 with: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} @@ -162,6 +162,9 @@ jobs: compact: if: ${{ !startsWith(github.ref, 'refs/tags') }} runs-on: [self-hosted,FlipperZeroShell] + strategy: + matrix: + target: [f7, f18] steps: - name: 'Wipe workspace' run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; @@ -185,9 +188,40 @@ jobs: python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" || cat "${{ github.event_path }}" - name: 'Build the firmware' + id: build-fw run: | set -e - for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package + TARGET="$(echo '${{ matrix.target }}' | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package + echo "sdk-file=$(ls dist/${{ matrix.target }}-*/flipper-z-${{ matrix.target }}-sdk-*.zip)" >> $GITHUB_OUTPUT + + - name: Deploy uFBT with SDK + uses: flipperdevices/flipperzero-ufbt-action@v0.1.0 + with: + task: setup + sdk-file: ${{ steps.build-fw.outputs.sdk-file }} + + - name: Build test app with SDK + run: | + mkdir testapp + cd testapp + ufbt create APPID=testapp + ufbt + + - name: Build example & external apps with uFBT + run: | + for appdir in 'applications/external' 'applications/examples'; do + for app in $(find "$appdir" -maxdepth 1 -mindepth 1 -type d); do + pushd $app + TARGETS_FAM=$(grep "targets" application.fam || echo "${{ matrix.target }}") + if ! grep -q "${{ matrix.target }}" <<< $TARGETS_FAM ; then + echo Skipping unsupported app: $app + popd + continue + fi + echo Building $app + ufbt + popd + done done + diff --git a/.github/workflows/lint_and_submodule_check.yml b/.github/workflows/lint_and_submodule_check.yml index cecfd1248..999111cc9 100644 --- a/.github/workflows/lint_and_submodule_check.yml +++ b/.github/workflows/lint_and_submodule_check.yml @@ -40,7 +40,7 @@ jobs: COMMITS_IN_BRANCH="$(git rev-list --count dev)"; if [ $COMMITS_IN_BRANCH -lt $SUB_COMMITS_MIN ]; then echo "name=fails::error" >> $GITHUB_OUTPUT; - echo "::error::Error: Too low commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)"; + echo "::error::Error: Too few commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)"; exit 1; fi if ! grep -q "/$SUB_BRANCH" <<< "$BRANCHES"; then @@ -51,12 +51,36 @@ jobs: - name: 'Check Python code formatting' id: syntax_check_py - run: ./fbt lint_py 2>&1 >/dev/null || echo "errors=1" >> $GITHUB_OUTPUT - + run: | + set +e; + ./fbt -s lint_py 2>&1 | tee lint-py.log; + if [ "${PIPESTATUS[0]}" -ne 0 ]; then + # Save multiline output + echo "errors=1" >> $GITHUB_OUTPUT; + printf "Python Lint errors:\n\`\`\`\n" >> $GITHUB_STEP_SUMMARY; + echo "$(cat lint-py.log)" >> $GITHUB_STEP_SUMMARY; + printf "\n\`\`\`\n" >> $GITHUB_STEP_SUMMARY; + exit 1; + else + echo "Python Lint: all good ✨" >> $GITHUB_STEP_SUMMARY; + fi + - name: 'Check C++ code formatting' - if: always() id: syntax_check_cpp - run: ./fbt lint 2>&1 >/dev/null || echo "errors=1" >> $GITHUB_OUTPUT + if: always() + run: | + set +e; + ./fbt -s lint 2>&1 | tee lint-cpp.log; + if [ "${PIPESTATUS[0]}" -ne 0 ]; then + # Save multiline output + echo "errors=1" >> $GITHUB_OUTPUT; + printf "C Lint errors:\n\`\`\`\n" >> $GITHUB_STEP_SUMMARY; + echo "$(cat lint-cpp.log)" >> $GITHUB_STEP_SUMMARY; + printf "\n\`\`\`\n" >> $GITHUB_STEP_SUMMARY; + exit 1; + else + echo "C Lint: all good ✨" >> $GITHUB_STEP_SUMMARY; + fi - name: Report code formatting errors if: ( steps.syntax_check_py.outputs.errors || steps.syntax_check_cpp.outputs.errors ) && github.event.pull_request diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 7228e2f51..ce7c8b978 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -1,6 +1,8 @@ from SCons.Platform import TempFileMunge from SCons.Node import FS from SCons.Errors import UserError +from SCons.Warnings import warn, WarningOnByDefault + import os import multiprocessing @@ -246,7 +248,12 @@ known_extapps = [ for apptype in apps_to_build_as_faps for app in appenv["APPBUILD"].get_apps_of_type(apptype, True) ] +incompatible_apps = [] for app in known_extapps: + if not app.supports_hardware_target(appenv.subst("f${TARGET_HW}")): + incompatible_apps.append(app) + continue + app_artifacts = appenv.BuildAppElf(app) app_src_dir = extract_abs_dir(app_artifacts.app._appdir) app_artifacts.installer = [ @@ -254,6 +261,13 @@ for app in known_extapps: appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug), ] +if len(incompatible_apps): + print( + "WARNING: The following apps are not compatible with the current target hardware and will not be built: {}".format( + ", ".join([app.name for app in incompatible_apps]) + ) + ) + if appenv["FORCE"]: appenv.AlwaysBuild([extapp.compact for extapp in apps_artifacts.values()]) From 40e435a961e3ba3963dce2d28d9b6be10afbb440 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 19 Apr 2023 19:19:26 +0300 Subject: [PATCH 056/282] Kostily and velosipedy !!! Needs to be re-done in proper way after ofw subghz refactoring merge --- .../main/subghz/helpers/subghz_types.h | 6 -- .../subghz/scenes/subghz_scene_receiver.c | 29 +++++++- .../scenes/subghz_scene_receiver_config.c | 71 +++++++++++++++---- applications/main/subghz/subghz_i.h | 4 +- 4 files changed, 90 insertions(+), 20 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 270fd7a21..3c5982427 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -35,12 +35,6 @@ typedef enum { SubGhzSpeakerStateEnable, } SubGhzSpeakerState; -/** SubGhzStarLineIgnore state */ -typedef enum { - SubGhzStarLineIgnoreDisable, - SubGhzStarLineIgnoreEnable, -} SubGhzStarLineIgnoreState; - /** SubGhzRxKeyState state */ typedef enum { SubGhzRxKeyStateIDLE, diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 08fed3aa4..5276bc4a8 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -147,7 +147,9 @@ void subghz_scene_receiver_on_enter(void* context) { subghz_receiver_set_rx_callback( subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); - if(subghz->txrx->starline_state == SubGhzStarLineIgnoreEnable) { + // TODO: Replace with proper solution based on protocol flags, remove kostily and velosipedy from here + // Needs to be done after subghz refactoring merge!!! + if(subghz->txrx->ignore_starline == true) { SubGhzProtocolDecoderBase* protocoldecoderbase = NULL; protocoldecoderbase = subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "Star Line"); @@ -156,6 +158,31 @@ void subghz_scene_receiver_on_enter(void* context) { protocoldecoderbase, NULL, subghz->txrx->receiver); } } + if(subghz->txrx->ignore_auto_alarms == true) { + SubGhzProtocolDecoderBase* protocoldecoderbase = NULL; + protocoldecoderbase = + subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "KIA Seed"); + if(protocoldecoderbase) { + subghz_protocol_decoder_base_set_decoder_callback( + protocoldecoderbase, NULL, subghz->txrx->receiver); + } + protocoldecoderbase = NULL; + protocoldecoderbase = + subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "Scher-Khan"); + if(protocoldecoderbase) { + subghz_protocol_decoder_base_set_decoder_callback( + protocoldecoderbase, NULL, subghz->txrx->receiver); + } + } + if(subghz->txrx->ignore_magellan == true) { + SubGhzProtocolDecoderBase* protocoldecoderbase = NULL; + protocoldecoderbase = + subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, "Magellan"); + if(protocoldecoderbase) { + subghz_protocol_decoder_base_set_decoder_callback( + protocoldecoderbase, NULL, subghz->txrx->receiver); + } + } subghz->state_notifications = SubGhzNotificationStateRx; if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 6a6cf860b..68c5b4b7a 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -7,6 +7,8 @@ enum SubGhzSettingIndex { SubGhzSettingIndexModulation, SubGhzSettingIndexBinRAW, SubGhzSettingIndexIgnoreStarline, + SubGhzSettingIndexIgnoreCars, + SubGhzSettingIndexIgnoreMagellan, SubGhzSettingIndexSound, SubGhzSettingIndexLock, SubGhzSettingIndexRAWThresholdRSSI, @@ -69,14 +71,22 @@ const uint32_t bin_raw_value[BIN_RAW_COUNT] = { SubGhzProtocolFlag_Decodable, SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, }; -#define STAR_LINE_COUNT 2 -const char* const star_line_text[STAR_LINE_COUNT] = { +#define STARLINE_COUNT 2 +const char* const starline_text[STARLINE_COUNT] = { "OFF", "ON", }; -const uint32_t star_line_value[STAR_LINE_COUNT] = { - SubGhzStarLineIgnoreDisable, - SubGhzStarLineIgnoreEnable, + +#define AUTO_ALARMS_COUNT 2 +const char* const auto_alarms_text[AUTO_ALARMS_COUNT] = { + "OFF", + "ON", +}; + +#define MAGELLAN_COUNT 2 +const char* const magellan_text[MAGELLAN_COUNT] = { + "OFF", + "ON", }; uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { @@ -231,8 +241,24 @@ static void subghz_scene_receiver_config_set_starline(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, star_line_text[index]); - subghz->txrx->starline_state = star_line_value[index]; + variable_item_set_current_value_text(item, starline_text[index]); + subghz->txrx->ignore_starline = (index == 1); +} + +static void subghz_scene_receiver_config_set_auto_alarms(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, auto_alarms_text[index]); + subghz->txrx->ignore_auto_alarms = (index == 1); +} + +static void subghz_scene_receiver_config_set_magellan(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, magellan_text[index]); + subghz->txrx->ignore_magellan = (index == 1); } static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { @@ -313,15 +339,36 @@ void subghz_scene_receiver_config_on_enter(void* context) { SubGhzCustomEventManagerSet) { item = variable_item_list_add( subghz->variable_item_list, - "Ignore StarLine:", - STAR_LINE_COUNT, + "Ignore Starline:", + STARLINE_COUNT, subghz_scene_receiver_config_set_starline, subghz); - value_index = - value_index_uint32(subghz->txrx->starline_state, star_line_value, STAR_LINE_COUNT); + value_index = subghz->txrx->ignore_starline; variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, star_line_text[value_index]); + variable_item_set_current_value_text(item, starline_text[value_index]); + + item = variable_item_list_add( + subghz->variable_item_list, + "Ignore Cars:", + AUTO_ALARMS_COUNT, + subghz_scene_receiver_config_set_auto_alarms, + subghz); + + value_index = subghz->txrx->ignore_auto_alarms; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, auto_alarms_text[value_index]); + + item = variable_item_list_add( + subghz->variable_item_list, + "Ignore Magellan:", + MAGELLAN_COUNT, + subghz_scene_receiver_config_set_magellan, + subghz); + + value_index = subghz->txrx->ignore_magellan; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, magellan_text[value_index]); } // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index adabe4aad..faae35fa2 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -73,7 +73,9 @@ struct SubGhzTxRx { SubGhzTxRxState txrx_state; SubGhzHopperState hopper_state; SubGhzSpeakerState speaker_state; - SubGhzStarLineIgnoreState starline_state; + bool ignore_starline; + bool ignore_auto_alarms; + bool ignore_magellan; uint8_t hopper_timeout; uint8_t hopper_idx_frequency; SubGhzRxKeyState rx_key_state; From dd1a92803e9c1c1607134fac56c96a72b1f8f6f5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 20 Apr 2023 01:24:04 +0300 Subject: [PATCH 057/282] Adding fallback Just in case --- lib/subghz/subghz_file_encoder_worker.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 1c8b59c63..fce4e8592 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -76,6 +76,11 @@ bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, co // temp_ds = atoi(str1); if((temp_ds < -1000000) || (temp_ds > 1000000)) { + if(temp_ds > 0) { + subghz_file_encoder_worker_add_level_duration(instance, (int32_t)100); + } else { + subghz_file_encoder_worker_add_level_duration(instance, (int32_t)-100); + } //FURI_LOG_I("PARSE", "Number overflow - %d", atoi(str1)); } else { subghz_file_encoder_worker_add_level_duration(instance, temp_ds); From 4d015a1106f2577b47c66c5dd7edcccc58caa2e5 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 20 Apr 2023 16:57:51 +0400 Subject: [PATCH 058/282] [FL-3271] cubewb: updated to v1.16.0 (#2595) * cubewb: updated project to v1.16.0 * hal: updated api_symbols for f18 * FuriHal: add missing enterprise sleep and insomnia * FuriHal: slightly more paranoic sleep mode Co-authored-by: Aleksandr Kutuzov --- fbt_options.py | 2 +- firmware/targets/f18/api_symbols.csv | 40 +++++++-------- firmware/targets/f7/api_symbols.csv | 38 +++++++------- firmware/targets/f7/ble_glue/app_common.h | 1 + firmware/targets/f7/ble_glue/app_conf.h | 2 + firmware/targets/f7/ble_glue/app_debug.c | 3 +- firmware/targets/f7/ble_glue/ble_app.c | 10 +++- firmware/targets/f7/ble_glue/ble_const.h | 4 ++ firmware/targets/f7/ble_glue/ble_glue.c | 2 + firmware/targets/f7/ble_glue/compiler.h | 10 +++- firmware/targets/f7/ble_glue/gap.c | 51 +++++++++---------- firmware/targets/f7/furi_hal/furi_hal_flash.c | 3 ++ firmware/targets/f7/furi_hal/furi_hal_power.c | 29 +++++++++-- lib/STM32CubeWB | 2 +- scripts/ob.data | 4 +- 15 files changed, 124 insertions(+), 77 deletions(-) diff --git a/fbt_options.py b/fbt_options.py index a10c64b96..25e84afde 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -20,7 +20,7 @@ DIST_SUFFIX = "local" COPRO_OB_DATA = "scripts/ob.data" # Must match lib/STM32CubeWB version -COPRO_CUBE_VERSION = "1.13.3" +COPRO_CUBE_VERSION = "1.16.0" COPRO_CUBE_DIR = "lib/STM32CubeWB" diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index a37ef02cc..4ee3d8636 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -176,17 +176,17 @@ Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* -Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" +Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, const LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* Function,-,LL_ADC_DeInit,ErrorStatus,ADC_TypeDef* -Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_INJ_InitTypeDef*" +Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_INJ_InitTypeDef*" Function,-,LL_ADC_INJ_StructInit,void,LL_ADC_INJ_InitTypeDef* -Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_InitTypeDef*" -Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_REG_InitTypeDef*" +Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_InitTypeDef*" +Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_REG_InitTypeDef*" Function,-,LL_ADC_REG_StructInit,void,LL_ADC_REG_InitTypeDef* Function,-,LL_ADC_StructInit,void,LL_ADC_InitTypeDef* Function,-,LL_COMP_DeInit,ErrorStatus,COMP_TypeDef* -Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, LL_COMP_InitTypeDef*" +Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, const LL_COMP_InitTypeDef*" Function,-,LL_COMP_StructInit,void,LL_COMP_InitTypeDef* Function,-,LL_CRC_DeInit,ErrorStatus,CRC_TypeDef* Function,-,LL_CRS_DeInit,ErrorStatus, @@ -199,16 +199,16 @@ Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef* Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* -Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef* -Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*" +Function,-,LL_I2C_DeInit,ErrorStatus,const I2C_TypeDef* +Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, const LL_I2C_InitTypeDef*" Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* Function,-,LL_Init1msTick,void,uint32_t Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* Function,-,LL_LPTIM_Disable,void,LPTIM_TypeDef* -Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, LL_LPTIM_InitTypeDef*" +Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, const LL_LPTIM_InitTypeDef*" Function,-,LL_LPTIM_StructInit,void,LL_LPTIM_InitTypeDef* -Function,-,LL_LPUART_DeInit,ErrorStatus,USART_TypeDef* -Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, LL_LPUART_InitTypeDef*" +Function,-,LL_LPUART_DeInit,ErrorStatus,const USART_TypeDef* +Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, const LL_LPUART_InitTypeDef*" Function,-,LL_LPUART_StructInit,void,LL_LPUART_InitTypeDef* Function,-,LL_PKA_DeInit,ErrorStatus,PKA_TypeDef* Function,-,LL_PKA_Init,ErrorStatus,"PKA_TypeDef*, LL_PKA_InitTypeDef*" @@ -253,23 +253,23 @@ Function,+,LL_SPI_Init,ErrorStatus,"SPI_TypeDef*, LL_SPI_InitTypeDef*" Function,-,LL_SPI_StructInit,void,LL_SPI_InitTypeDef* Function,-,LL_SetFlashLatency,ErrorStatus,uint32_t Function,+,LL_SetSystemCoreClock,void,uint32_t -Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_BDTR_InitTypeDef*" +Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_BDTR_InitTypeDef*" Function,-,LL_TIM_BDTR_StructInit,void,LL_TIM_BDTR_InitTypeDef* -Function,+,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef* -Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_ENCODER_InitTypeDef*" +Function,-,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef* +Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_ENCODER_InitTypeDef*" Function,-,LL_TIM_ENCODER_StructInit,void,LL_TIM_ENCODER_InitTypeDef* -Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_HALLSENSOR_InitTypeDef*" +Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_HALLSENSOR_InitTypeDef*" Function,-,LL_TIM_HALLSENSOR_StructInit,void,LL_TIM_HALLSENSOR_InitTypeDef* -Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_IC_InitTypeDef*" +Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_IC_InitTypeDef*" Function,-,LL_TIM_IC_StructInit,void,LL_TIM_IC_InitTypeDef* -Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_InitTypeDef*" -Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_OC_InitTypeDef*" +Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_InitTypeDef*" +Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_OC_InitTypeDef*" Function,-,LL_TIM_OC_StructInit,void,LL_TIM_OC_InitTypeDef* Function,-,LL_TIM_StructInit,void,LL_TIM_InitTypeDef* -Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, LL_USART_ClockInitTypeDef*" +Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, const LL_USART_ClockInitTypeDef*" Function,-,LL_USART_ClockStructInit,void,LL_USART_ClockInitTypeDef* -Function,-,LL_USART_DeInit,ErrorStatus,USART_TypeDef* -Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, LL_USART_InitTypeDef*" +Function,-,LL_USART_DeInit,ErrorStatus,const USART_TypeDef* +Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, const LL_USART_InitTypeDef*" Function,-,LL_USART_StructInit,void,LL_USART_InitTypeDef* Function,-,LL_mDelay,void,uint32_t Function,-,SystemCoreClockUpdate,void, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 43ede425e..a2d70e7f8 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -208,17 +208,17 @@ Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* -Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" +Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, const LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* Function,-,LL_ADC_DeInit,ErrorStatus,ADC_TypeDef* -Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_INJ_InitTypeDef*" +Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_INJ_InitTypeDef*" Function,-,LL_ADC_INJ_StructInit,void,LL_ADC_INJ_InitTypeDef* -Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_InitTypeDef*" -Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_REG_InitTypeDef*" +Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_InitTypeDef*" +Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_REG_InitTypeDef*" Function,-,LL_ADC_REG_StructInit,void,LL_ADC_REG_InitTypeDef* Function,-,LL_ADC_StructInit,void,LL_ADC_InitTypeDef* Function,-,LL_COMP_DeInit,ErrorStatus,COMP_TypeDef* -Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, LL_COMP_InitTypeDef*" +Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, const LL_COMP_InitTypeDef*" Function,-,LL_COMP_StructInit,void,LL_COMP_InitTypeDef* Function,-,LL_CRC_DeInit,ErrorStatus,CRC_TypeDef* Function,-,LL_CRS_DeInit,ErrorStatus, @@ -231,16 +231,16 @@ Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef* Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* -Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef* -Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*" +Function,-,LL_I2C_DeInit,ErrorStatus,const I2C_TypeDef* +Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, const LL_I2C_InitTypeDef*" Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* Function,-,LL_Init1msTick,void,uint32_t Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* Function,-,LL_LPTIM_Disable,void,LPTIM_TypeDef* -Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, LL_LPTIM_InitTypeDef*" +Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, const LL_LPTIM_InitTypeDef*" Function,-,LL_LPTIM_StructInit,void,LL_LPTIM_InitTypeDef* -Function,-,LL_LPUART_DeInit,ErrorStatus,USART_TypeDef* -Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, LL_LPUART_InitTypeDef*" +Function,-,LL_LPUART_DeInit,ErrorStatus,const USART_TypeDef* +Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, const LL_LPUART_InitTypeDef*" Function,-,LL_LPUART_StructInit,void,LL_LPUART_InitTypeDef* Function,-,LL_PKA_DeInit,ErrorStatus,PKA_TypeDef* Function,-,LL_PKA_Init,ErrorStatus,"PKA_TypeDef*, LL_PKA_InitTypeDef*" @@ -285,23 +285,23 @@ Function,+,LL_SPI_Init,ErrorStatus,"SPI_TypeDef*, LL_SPI_InitTypeDef*" Function,-,LL_SPI_StructInit,void,LL_SPI_InitTypeDef* Function,-,LL_SetFlashLatency,ErrorStatus,uint32_t Function,+,LL_SetSystemCoreClock,void,uint32_t -Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_BDTR_InitTypeDef*" +Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_BDTR_InitTypeDef*" Function,-,LL_TIM_BDTR_StructInit,void,LL_TIM_BDTR_InitTypeDef* Function,+,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef* -Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_ENCODER_InitTypeDef*" +Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_ENCODER_InitTypeDef*" Function,-,LL_TIM_ENCODER_StructInit,void,LL_TIM_ENCODER_InitTypeDef* -Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_HALLSENSOR_InitTypeDef*" +Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_HALLSENSOR_InitTypeDef*" Function,-,LL_TIM_HALLSENSOR_StructInit,void,LL_TIM_HALLSENSOR_InitTypeDef* -Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_IC_InitTypeDef*" +Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_IC_InitTypeDef*" Function,-,LL_TIM_IC_StructInit,void,LL_TIM_IC_InitTypeDef* -Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_InitTypeDef*" -Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_OC_InitTypeDef*" +Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_InitTypeDef*" +Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_OC_InitTypeDef*" Function,-,LL_TIM_OC_StructInit,void,LL_TIM_OC_InitTypeDef* Function,-,LL_TIM_StructInit,void,LL_TIM_InitTypeDef* -Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, LL_USART_ClockInitTypeDef*" +Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, const LL_USART_ClockInitTypeDef*" Function,-,LL_USART_ClockStructInit,void,LL_USART_ClockInitTypeDef* -Function,-,LL_USART_DeInit,ErrorStatus,USART_TypeDef* -Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, LL_USART_InitTypeDef*" +Function,-,LL_USART_DeInit,ErrorStatus,const USART_TypeDef* +Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, const LL_USART_InitTypeDef*" Function,-,LL_USART_StructInit,void,LL_USART_InitTypeDef* Function,-,LL_mDelay,void,uint32_t Function,-,SystemCoreClockUpdate,void, diff --git a/firmware/targets/f7/ble_glue/app_common.h b/firmware/targets/f7/ble_glue/app_common.h index 214c85acd..8eaf23085 100644 --- a/firmware/targets/f7/ble_glue/app_common.h +++ b/firmware/targets/f7/ble_glue/app_common.h @@ -33,6 +33,7 @@ extern "C" { #include #include +#include #include "app_conf.h" diff --git a/firmware/targets/f7/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h index aaa755a36..ee5115cfe 100644 --- a/firmware/targets/f7/ble_glue/app_conf.h +++ b/firmware/targets/f7/ble_glue/app_conf.h @@ -8,6 +8,8 @@ #define CFG_TX_POWER (0x19) /* +0dBm */ +#define CFG_IDENTITY_ADDRESS GAP_PUBLIC_ADDR + /** * Define Advertising parameters */ diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index 78e789ac3..b443bee21 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -33,7 +33,8 @@ PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0}; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) -static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}}; +static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = + {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}, 0, 0, 0, 0, 0}; /** * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 4fc4d521b..cc6065b97 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; _Static_assert( - sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 49, - "Ble stack config structure size mismatch"); + sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 58, + "Ble stack config structure size mismatch (check new config options - last updated for v.1.16.0)"); typedef struct { FuriMutex* hci_mtx; @@ -88,6 +88,12 @@ bool ble_app_init() { .min_tx_power = 0, .max_tx_power = 0, .rx_model_config = 1, + /* New stack (13.3->16.0)*/ + .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB + .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB + .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) }}; status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); if(status) { diff --git a/firmware/targets/f7/ble_glue/ble_const.h b/firmware/targets/f7/ble_glue/ble_const.h index 0e4c8b398..85f734b62 100644 --- a/firmware/targets/f7/ble_glue/ble_const.h +++ b/firmware/targets/f7/ble_glue/ble_const.h @@ -23,6 +23,7 @@ #include #include #include "osal.h" +#include "compiler.h" /* Default BLE variant */ #ifndef BASIC_FEATURES @@ -34,6 +35,9 @@ #ifndef LL_ONLY #define LL_ONLY 0 #endif +#ifndef LL_ONLY_BASIC +#define LL_ONLY_BASIC 0 +#endif #ifndef BEACON_ONLY #define BEACON_ONLY 0 #endif diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index a2f2f1a94..c73bbd866 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -403,7 +403,9 @@ void shci_cmd_resp_release(uint32_t flag) { void shci_cmd_resp_wait(uint32_t timeout) { UNUSED(timeout); if(ble_glue) { + furi_hal_power_insomnia_enter(); furi_semaphore_acquire(ble_glue->shci_sem, FuriWaitForever); + furi_hal_power_insomnia_exit(); } } diff --git a/firmware/targets/f7/ble_glue/compiler.h b/firmware/targets/f7/ble_glue/compiler.h index 1c3962819..98a93d712 100644 --- a/firmware/targets/f7/ble_glue/compiler.h +++ b/firmware/targets/f7/ble_glue/compiler.h @@ -5,7 +5,7 @@ ***************************************************************************** * @attention * - * Copyright (c) 2018-2022 STMicroelectronics. + * Copyright (c) 2018-2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file @@ -18,6 +18,14 @@ #ifndef COMPILER_H__ #define COMPILER_H__ +#ifndef __PACKED_STRUCT +#define __PACKED_STRUCT PACKED(struct) +#endif + +#ifndef __PACKED_UNION +#define __PACKED_UNION PACKED(union) +#endif + /** * @brief This is the section dedicated to IAR toolchain */ diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index cbb2a203b..f0a9ced3c 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -1,5 +1,6 @@ #include "gap.h" +#include "app_common.h" #include #include @@ -85,7 +86,7 @@ static void gap_verify_connection_parameters(Gap* gap) { SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { hci_event_pckt* event_pckt; evt_le_meta_event* meta_evt; - evt_blue_aci* blue_evt; + evt_blecore_aci* blue_evt; hci_le_phy_update_complete_event_rp0* evt_le_phy_update_complete; uint8_t tx_phy; uint8_t rx_phy; @@ -97,7 +98,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { furi_mutex_acquire(gap->state_mutex, FuriWaitForever); } switch(event_pckt->evt) { - case EVT_DISCONN_COMPLETE: { + case HCI_DISCONNECTION_COMPLETE_EVT_CODE: { hci_disconnection_complete_event_rp0* disconnection_complete_event = (hci_disconnection_complete_event_rp0*)event_pckt->data; if(disconnection_complete_event->Connection_Handle == gap->service.connection_handle) { @@ -106,6 +107,8 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { FURI_LOG_I( TAG, "Disconnect from client. Reason: %02X", disconnection_complete_event->Reason); } + // Enterprise sleep + furi_delay_us(666 + 666); if(gap->enable_adv) { // Restart advertising gap_advertise_start(GapStateAdvFast); @@ -114,10 +117,10 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case EVT_LE_META_EVENT: + case HCI_LE_META_EVT_CODE: meta_evt = (evt_le_meta_event*)event_pckt->data; switch(meta_evt->subevent) { - case EVT_LE_CONN_UPDATE_COMPLETE: { + case HCI_LE_CONNECTION_UPDATE_COMPLETE_SUBEVT_CODE: { hci_le_connection_update_complete_event_rp0* event = (hci_le_connection_update_complete_event_rp0*)meta_evt->data; gap->connection_params.conn_interval = event->Conn_Interval; @@ -128,7 +131,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { break; } - case EVT_LE_PHY_UPDATE_COMPLETE: + case HCI_LE_PHY_UPDATE_COMPLETE_SUBEVT_CODE: evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; if(evt_le_phy_update_complete->Status) { FURI_LOG_E( @@ -144,7 +147,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_LE_CONN_COMPLETE: { + case HCI_LE_CONNECTION_COMPLETE_SUBEVT_CODE: { hci_le_connection_complete_event_rp0* event = (hci_le_connection_complete_event_rp0*)meta_evt->data; gap->connection_params.conn_interval = event->Conn_Interval; @@ -168,16 +171,16 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_VENDOR: - blue_evt = (evt_blue_aci*)event_pckt->data; + case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE: + blue_evt = (evt_blecore_aci*)event_pckt->data; switch(blue_evt->ecode) { aci_gap_pairing_complete_event_rp0* pairing_complete; - case EVT_BLUE_GAP_LIMITED_DISCOVERABLE: + case ACI_GAP_LIMITED_DISCOVERABLE_VSEVT_CODE: FURI_LOG_I(TAG, "Limited discoverable event"); break; - case EVT_BLUE_GAP_PASS_KEY_REQUEST: { + case ACI_GAP_PASS_KEY_REQ_VSEVT_CODE: { // Generate random PIN code uint32_t pin = rand() % 999999; //-V1064 aci_gap_pass_key_resp(gap->service.connection_handle, pin); @@ -190,7 +193,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case EVT_BLUE_ATT_EXCHANGE_MTU_RESP: { + case ACI_ATT_EXCHANGE_MTU_RESP_VSEVT_CODE: { aci_att_exchange_mtu_resp_event_rp0* pr = (void*)blue_evt->data; FURI_LOG_I(TAG, "Rx MTU size: %d", pr->Server_RX_MTU); // Set maximum packet size given header size is 3 bytes @@ -199,32 +202,28 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case EVT_BLUE_GAP_AUTHORIZATION_REQUEST: + case ACI_GAP_AUTHORIZATION_REQ_VSEVT_CODE: FURI_LOG_D(TAG, "Authorization request event"); break; - case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED: + case ACI_GAP_SLAVE_SECURITY_INITIATED_VSEVT_CODE: FURI_LOG_D(TAG, "Slave security initiated"); break; - case EVT_BLUE_GAP_BOND_LOST: + case ACI_GAP_BOND_LOST_VSEVT_CODE: FURI_LOG_D(TAG, "Bond lost event. Start rebonding"); aci_gap_allow_rebond(gap->service.connection_handle); break; - case EVT_BLUE_GAP_DEVICE_FOUND: - FURI_LOG_D(TAG, "Device found event"); - break; - - case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: + case ACI_GAP_ADDR_NOT_RESOLVED_VSEVT_CODE: FURI_LOG_D(TAG, "Address not resolved event"); break; - case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION: + case ACI_GAP_KEYPRESS_NOTIFICATION_VSEVT_CODE: FURI_LOG_D(TAG, "Key press notification event"); break; - case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: { + case ACI_GAP_NUMERIC_COMPARISON_VALUE_VSEVT_CODE: { uint32_t pin = ((aci_gap_numeric_comparison_value_event_rp0*)(blue_evt->data))->Numeric_Value; FURI_LOG_I(TAG, "Verify numeric comparison: %06lu", pin); @@ -234,7 +233,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { break; } - case EVT_BLUE_GAP_PAIRING_CMPLT: + case ACI_GAP_PAIRING_COMPLETE_VSEVT_CODE: pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; if(pairing_complete->Status) { FURI_LOG_E( @@ -249,11 +248,11 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_BLUE_GAP_PROCEDURE_COMPLETE: + case ACI_L2CAP_CONNECTION_UPDATE_RESP_VSEVT_CODE: FURI_LOG_D(TAG, "Procedure complete event"); break; - case EVT_BLUE_L2CAP_CONNECTION_UPDATE_RESP: { + case ACI_L2CAP_CONNECTION_UPDATE_REQ_VSEVT_CODE: { uint16_t result = ((aci_l2cap_connection_update_resp_event_rp0*)(blue_evt->data))->Result; if(result == 0) { @@ -362,7 +361,7 @@ static void gap_init_svc(Gap* gap) { CFG_ENCRYPTION_KEY_SIZE_MAX, CFG_USED_FIXED_PIN, 0, - PUBLIC_ADDR); + CFG_IDENTITY_ADDRESS); // Configure whitelist aci_gap_configure_whitelist(); } @@ -397,7 +396,7 @@ static void gap_advertise_start(GapState new_state) { ADV_IND, min_interval, max_interval, - PUBLIC_ADDR, + CFG_IDENTITY_ADDRESS, 0, strlen(gap->service.adv_name), (uint8_t*)gap->service.adv_name, diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c index fc021d969..d2dbff55f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -114,6 +115,7 @@ static void furi_hal_flash_lock(void) { } static void furi_hal_flash_begin_with_core2(bool erase_flag) { + furi_hal_power_insomnia_enter(); /* Take flash controller ownership */ while(LL_HSEM_1StepLock(HSEM, CFG_HW_FLASH_SEMID) != 0) { furi_thread_yield(); @@ -188,6 +190,7 @@ static void furi_hal_flash_end_with_core2(bool erase_flag) { /* Release flash controller ownership */ LL_HSEM_ReleaseLock(HSEM, CFG_HW_FLASH_SEMID, 0); + furi_hal_power_insomnia_exit(); } static void furi_hal_flash_end(bool erase_flag) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 9a87cef15..3e4e3f48b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -29,6 +29,14 @@ #define FURI_HAL_POWER_DEBUG_STOP_GPIO (&gpio_ext_pc3) #endif +#ifndef FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO +#define FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO (&gpio_ext_pb3) +#endif + +#ifndef FURI_HAL_POWER_STOP_MODE +#define FURI_HAL_POWER_STOP_MODE (LL_PWR_MODE_STOP2) +#endif + typedef struct { volatile uint8_t insomnia; volatile uint8_t suppress_charge; @@ -84,14 +92,16 @@ void furi_hal_power_init() { #ifdef FURI_HAL_POWER_DEBUG furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_WFI_GPIO, GpioModeOutputPushPull); furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_STOP_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, GpioModeOutputPushPull); furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, 0); #endif LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN); - LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2); + LL_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); + LL_C2_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); bq27220_init(&furi_hal_i2c_handle_power, &cedv); @@ -148,7 +158,9 @@ bool furi_hal_power_sleep_available() { static inline bool furi_hal_power_deep_sleep_available() { return furi_hal_bt_is_alive() && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) && - !furi_hal_debug_is_gdb_session_active(); + !furi_hal_debug_is_gdb_session_active() && !LL_PWR_IsActiveFlag_CRPE() && + !LL_PWR_IsActiveFlag_CRP() && !LL_PWR_IsActiveFlag_BLEA() && + !LL_PWR_IsActiveFlag_BLEWU(); } static inline void furi_hal_power_light_sleep() { @@ -199,7 +211,16 @@ static inline void furi_hal_power_deep_sleep() { __force_stores(); #endif - __WFI(); + bool should_abort_sleep = LL_PWR_IsActiveFlag_CRPE() || LL_PWR_IsActiveFlag_CRP() || + LL_PWR_IsActiveFlag_BLEA() || LL_PWR_IsActiveFlag_BLEWU(); + + if(should_abort_sleep) { +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, 1); +#endif + } else { + __WFI(); + } LL_LPM_EnableSleep(); diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB index a9e29b431..06b8133fa 160000 --- a/lib/STM32CubeWB +++ b/lib/STM32CubeWB @@ -1 +1 @@ -Subproject commit a9e29b431f6dac95b6fc860a717834f35b7f78e5 +Subproject commit 06b8133fa295474507b55b1a5695d4b8bf804ed6 diff --git a/scripts/ob.data b/scripts/ob.data index 5276a5103..605faccbf 100644 --- a/scripts/ob.data +++ b/scripts/ob.data @@ -14,7 +14,7 @@ IWDGSTOP:0x1:rw IWDGSW:0x1:rw IPCCDBA:0x0:rw ESE:0x1:r -SFSA:0xD7:r +SFSA:0xD5:r FSD:0x0:r DDS:0x1:r C2OPT:0x1:r @@ -22,7 +22,7 @@ NBRSD:0x0:r SNBRSA:0xD:r BRSD:0x0:r SBRSA:0x12:r -SBRV:0x35C00:r +SBRV:0x35400:r PCROP1A_STRT:0x1FF:r PCROP1A_END:0x0:r PCROP_RDP:0x1:rw From 8cfb37c12f21ed45d943fa7795775cbc8927022c Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 18 Apr 2023 15:56:41 +0300 Subject: [PATCH 059/282] Gui, module menu: fix memleak --- applications/services/gui/modules/menu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index 3e3b6c2e4..afae8b8fa 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -154,6 +154,8 @@ Menu* menu_alloc() { void menu_free(Menu* menu) { furi_assert(menu); menu_reset(menu); + with_view_model( + menu->view, MenuModel * model, { MenuItemArray_clear(model->items); }, false); view_free(menu->view); free(menu); } From 19becd9985aaf82fc17a9079a5538a99bbca7652 Mon Sep 17 00:00:00 2001 From: Dmitry Zinin Date: Thu, 20 Apr 2023 22:45:19 +0300 Subject: [PATCH 060/282] Keynote with vertical layout --- applications/external/hid_app/hid.c | 15 ++ applications/external/hid_app/hid.h | 4 +- applications/external/hid_app/views.h | 3 +- .../hid_app/views/hid_keynote_vertical.c | 216 ++++++++++++++++++ .../hid_app/views/hid_keynote_vertical.h | 14 ++ 5 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 applications/external/hid_app/views/hid_keynote_vertical.c create mode 100644 applications/external/hid_app/views/hid_keynote_vertical.h diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index b679c395c..5b00fdc1a 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -7,6 +7,7 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexKeynote, + HidSubmenuIndexKeynoteVertical, HidSubmenuIndexKeyboard, HidSubmenuIndexMedia, HidSubmenuIndexTikTok, @@ -21,6 +22,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { if(index == HidSubmenuIndexKeynote) { app->view_id = HidViewKeynote; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); + } else if(index == HidSubmenuIndexKeynoteVertical) { + app->view_id = HidViewKeynoteVertical; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynoteVertical); } else if(index == HidSubmenuIndexKeyboard) { app->view_id = HidViewKeyboard; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard); @@ -54,6 +58,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con } } hid_keynote_set_connected_status(hid->hid_keynote, connected); + hid_keynote_vertical_set_connected_status(hid->hid_keynote_vertical, connected); hid_keyboard_set_connected_status(hid->hid_keyboard, connected); hid_media_set_connected_status(hid->hid_media, connected); hid_mouse_set_connected_status(hid->hid_mouse, connected); @@ -105,6 +110,8 @@ Hid* hid_alloc(HidTransport transport) { app->device_type_submenu = submenu_alloc(); submenu_add_item( app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Keynote Vertical", HidSubmenuIndexKeynoteVertical, hid_submenu_callback, app); submenu_add_item( app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app); submenu_add_item( @@ -159,6 +166,12 @@ Hid* hid_app_alloc_view(void* context) { view_dispatcher_add_view( app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote)); + // Keynote Vertical view + app->hid_keynote_vertical = hid_keynote_vertical_alloc(app); + view_set_previous_callback(hid_keynote_vertical_get_view(app->hid_keynote_vertical), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewKeynoteVertical, hid_keynote_vertical_get_view(app->hid_keynote_vertical)); + // Keyboard view app->hid_keyboard = hid_keyboard_alloc(app); view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view); @@ -216,6 +229,8 @@ void hid_free(Hid* app) { dialog_ex_free(app->dialog); view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote); hid_keynote_free(app->hid_keynote); + view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynoteVertical); + hid_keynote_vertical_free(app->hid_keynote_vertical); view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard); hid_keyboard_free(app->hid_keyboard); view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia); diff --git a/applications/external/hid_app/hid.h b/applications/external/hid_app/hid.h index 1ff42c940..be9176a28 100644 --- a/applications/external/hid_app/hid.h +++ b/applications/external/hid_app/hid.h @@ -17,6 +17,7 @@ #include #include #include "views/hid_keynote.h" +#include "views/hid_keynote_vertical.h" #include "views/hid_keyboard.h" #include "views/hid_media.h" #include "views/hid_mouse.h" @@ -41,6 +42,7 @@ struct Hid { Submenu* device_type_submenu; DialogEx* dialog; HidKeynote* hid_keynote; + HidKeynoteVertical* hid_keynote_vertical; HidKeyboard* hid_keyboard; HidMedia* hid_media; HidMouse* hid_mouse; @@ -64,4 +66,4 @@ void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy); void hid_hal_mouse_scroll(Hid* instance, int8_t delta); void hid_hal_mouse_press(Hid* instance, uint16_t event); void hid_hal_mouse_release(Hid* instance, uint16_t event); -void hid_hal_mouse_release_all(Hid* instance); \ No newline at end of file +void hid_hal_mouse_release_all(Hid* instance); diff --git a/applications/external/hid_app/views.h b/applications/external/hid_app/views.h index 14cfa126f..81e8d6dbe 100644 --- a/applications/external/hid_app/views.h +++ b/applications/external/hid_app/views.h @@ -1,6 +1,7 @@ typedef enum { HidViewSubmenu, HidViewKeynote, + HidViewKeynoteVertical, HidViewKeyboard, HidViewMedia, HidViewMouse, @@ -8,4 +9,4 @@ typedef enum { BtHidViewTikTok, BtHidViewYTShorts, HidViewExitConfirm, -} HidView; \ No newline at end of file +} HidView; diff --git a/applications/external/hid_app/views/hid_keynote_vertical.c b/applications/external/hid_app/views/hid_keynote_vertical.c new file mode 100644 index 000000000..fc9b185eb --- /dev/null +++ b/applications/external/hid_app/views/hid_keynote_vertical.c @@ -0,0 +1,216 @@ +#include "hid_keynote_vertical.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidKeynoteVertical" + +struct HidKeynoteVertical { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool back_pressed; + bool connected; + HidTransport transport; +} HidKeynoteVerticalModel; + +static void hid_keynote_vertical_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { + canvas_draw_triangle(canvas, x, y, 5, 3, dir); + if(dir == CanvasDirectionBottomToTop) { + canvas_draw_line(canvas, x, y + 6, x, y - 1); + } else if(dir == CanvasDirectionTopToBottom) { + canvas_draw_line(canvas, x, y - 6, x, y + 1); + } else if(dir == CanvasDirectionRightToLeft) { + canvas_draw_line(canvas, x + 6, y, x - 1, y); + } else if(dir == CanvasDirectionLeftToRight) { + canvas_draw_line(canvas, x - 6, y, x + 1, y); + } +} + +static void hid_keynote_vertical_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidKeynoteVerticalModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote"); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 24, 14, AlignLeft, AlignTop, "Vertical"); + + canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit"); + + // Up + canvas_draw_icon(canvas, 21, 24, &I_Button_18x18); + if(model->up_pressed) { + elements_slightly_rounded_box(canvas, 24, 26, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_vertical_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); + canvas_set_color(canvas, ColorBlack); + + // Down + canvas_draw_icon(canvas, 21, 45, &I_Button_18x18); + if(model->down_pressed) { + elements_slightly_rounded_box(canvas, 24, 47, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_vertical_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); + canvas_set_color(canvas, ColorBlack); + + // Left + canvas_draw_icon(canvas, 0, 45, &I_Button_18x18); + if(model->left_pressed) { + elements_slightly_rounded_box(canvas, 3, 47, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_vertical_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); + canvas_set_color(canvas, ColorBlack); + + // Right + canvas_draw_icon(canvas, 42, 45, &I_Button_18x18); + if(model->right_pressed) { + elements_slightly_rounded_box(canvas, 45, 47, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_vertical_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); + canvas_set_color(canvas, ColorBlack); + + // Ok + canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); + if(model->ok_pressed) { + elements_slightly_rounded_box(canvas, 66, 27, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); + elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space"); + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 63, 45, &I_Space_65x18); + if(model->back_pressed) { + elements_slightly_rounded_box(canvas, 66, 47, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); +} + +static void hid_keynote_vertical_process(HidKeynoteVertical* hid_keynote_vertical, InputEvent* event) { + with_view_model( + hid_keynote_vertical->view, + HidKeynoteVerticalModel * model, + { + if(event->type == InputTypePress) { + if(event->key == InputKeyUp) { + model->up_pressed = true; + hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW); + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW); + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR); + } else if(event->key == InputKeyBack) { + model->back_pressed = true; + } + } else if(event->type == InputTypeRelease) { + if(event->key == InputKeyUp) { + model->up_pressed = false; + hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW); + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW); + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR); + } else if(event->key == InputKeyBack) { + model->back_pressed = false; + } + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyBack) { + hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE); + hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE); + hid_hal_consumer_key_press(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK); + hid_hal_consumer_key_release(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK); + } + } + }, + true); +} + +static bool hid_keynote_vertical_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidKeynoteVertical* hid_keynote_vertical = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_keynote_vertical->hid); + } else { + hid_keynote_vertical_process(hid_keynote_vertical, event); + consumed = true; + } + + return consumed; +} + +HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* hid) { + HidKeynoteVertical* hid_keynote_vertical = malloc(sizeof(HidKeynoteVertical)); + hid_keynote_vertical->view = view_alloc(); + hid_keynote_vertical->hid = hid; + view_set_context(hid_keynote_vertical->view, hid_keynote_vertical); + view_allocate_model(hid_keynote_vertical->view, ViewModelTypeLocking, sizeof(HidKeynoteVerticalModel)); + view_set_draw_callback(hid_keynote_vertical->view, hid_keynote_vertical_draw_callback); + view_set_input_callback(hid_keynote_vertical->view, hid_keynote_vertical_input_callback); + + with_view_model( + hid_keynote_vertical->view, HidKeynoteVerticalModel * model, { model->transport = hid->transport; }, true); + + return hid_keynote_vertical; +} + +void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical) { + furi_assert(hid_keynote_vertical); + view_free(hid_keynote_vertical->view); + free(hid_keynote_vertical); +} + +View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical) { + furi_assert(hid_keynote_vertical); + return hid_keynote_vertical->view; +} + +void hid_keynote_vertical_set_connected_status(HidKeynoteVertical* hid_keynote_vertical, bool connected) { + furi_assert(hid_keynote_vertical); + with_view_model( + hid_keynote_vertical->view, HidKeynoteVerticalModel * model, { model->connected = connected; }, true); +} diff --git a/applications/external/hid_app/views/hid_keynote_vertical.h b/applications/external/hid_app/views/hid_keynote_vertical.h new file mode 100644 index 000000000..16af4dd7e --- /dev/null +++ b/applications/external/hid_app/views/hid_keynote_vertical.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidKeynoteVertical HidKeynoteVertical; + +HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* bt_hid); + +void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical); + +View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical); + +void hid_keynote_vertical_set_connected_status(HidKeynoteVertical* hid_keynote_vertical, bool connected); From fd40a770894436faa4cb0eaae5500b1b443a3f1b Mon Sep 17 00:00:00 2001 From: Nikita Vostokov Date: Fri, 21 Apr 2023 03:08:17 +0300 Subject: [PATCH 061/282] Add time of Rx SubGHz signal --- applications/main/subghz/subghz_history.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index f6530238e..bb6d00a17 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -12,6 +12,7 @@ typedef struct { FlipperFormat* flipper_string; uint8_t type; SubGhzRadioPreset* preset; + FuriHalRtcDateTime datetime; } SubGhzHistoryItem; ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) @@ -161,7 +162,9 @@ uint16_t subghz_history_get_last_index(SubGhzHistory* instance) { } void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx) { SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); - furi_string_set(output, item->item_str); + FuriHalRtcDateTime* t = &item->datetime; + furi_string_printf(output, "%.2d:%.2d:%.2d ", t->hour, t->minute, t->second); + furi_string_cat(output, item->item_str); } bool subghz_history_add_to_history( @@ -195,6 +198,7 @@ bool subghz_history_add_to_history( furi_string_set(item->preset->name, preset->name); item->preset->data = preset->data; item->preset->data_size = preset->data_size; + furi_hal_rtc_get_datetime(&item->datetime); item->item_str = furi_string_alloc(); item->flipper_string = flipper_format_string_alloc(); From 95e3b7d42bb6e29e7aa90d2b8706671f3d5c5406 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov Date: Fri, 21 Apr 2023 03:08:38 +0300 Subject: [PATCH 062/282] [SubGHz] Show receiving time of signal --- .../subghz/scenes/subghz_scene_decode_raw.c | 41 +++++++++------ .../subghz/scenes/subghz_scene_receiver.c | 41 +++++++++------ applications/main/subghz/subghz_history.c | 6 ++- applications/main/subghz/subghz_history.h | 8 +++ applications/main/subghz/views/receiver.c | 52 ++++++++++++++++++- applications/main/subghz/views/receiver.h | 1 + 6 files changed, 116 insertions(+), 33 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 5746efbed..5ac4fcccd 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -62,26 +62,31 @@ static void subghz_scene_add_to_history_callback( void* context) { furi_assert(context); SubGhz* subghz = context; - FuriString* str_buff; - str_buff = furi_string_alloc(); + FuriString* item_name; + FuriString* item_time; + uint16_t idx = subghz_history_get_item(subghz->txrx->history); + item_name = furi_string_alloc(); + item_time = furi_string_alloc(); if(subghz_history_add_to_history(subghz->txrx->history, decoder_base, subghz->txrx->preset)) { - furi_string_reset(str_buff); + furi_string_reset(item_name); + furi_string_reset(item_time); subghz->state_notifications = SubGhzNotificationStateRxDone; - subghz_history_get_text_item_menu( - subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); + subghz_history_get_text_item_menu(subghz->txrx->history, item_name, idx); + subghz_history_get_time_item_menu(subghz->txrx->history, item_time, idx); subghz_view_receiver_add_item_to_menu( subghz->subghz_receiver, - furi_string_get_cstr(str_buff), - subghz_history_get_type_protocol( - subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1)); + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), + subghz_history_get_type_protocol(subghz->txrx->history, idx)); subghz_scene_receiver_update_statusbar(subghz); } subghz_receiver_reset(receiver); - furi_string_free(str_buff); + furi_string_free(item_name); + furi_string_free(item_time); } bool subghz_scene_decode_raw_start(SubGhz* subghz) { @@ -159,8 +164,10 @@ bool subghz_scene_decode_raw_next(SubGhz* subghz) { void subghz_scene_decode_raw_on_enter(void* context) { SubGhz* subghz = context; - FuriString* str_buff; - str_buff = furi_string_alloc(); + FuriString* item_name; + FuriString* item_time; + item_name = furi_string_alloc(); + item_time = furi_string_alloc(); subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz->lock); subghz_view_receiver_set_mode(subghz->subghz_receiver, SubGhzViewReceiverModeFile); @@ -183,14 +190,18 @@ void subghz_scene_decode_raw_on_enter(void* context) { //Load history to receiver subghz_view_receiver_exit(subghz->subghz_receiver); for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) { - furi_string_reset(str_buff); - subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i); + furi_string_reset(item_name); + furi_string_reset(item_time); + subghz_history_get_text_item_menu(subghz->txrx->history, item_name, i); + subghz_history_get_time_item_menu(subghz->txrx->history, item_time, i); subghz_view_receiver_add_item_to_menu( subghz->subghz_receiver, - furi_string_get_cstr(str_buff), + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), subghz_history_get_type_protocol(subghz->txrx->history, i)); } - furi_string_free(str_buff); + furi_string_free(item_name); + furi_string_free(item_time); subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 5276bc4a8..b17a0700b 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -91,34 +91,41 @@ static void subghz_scene_add_to_history_callback( void* context) { furi_assert(context); SubGhz* subghz = context; - FuriString* str_buff; - str_buff = furi_string_alloc(); + FuriString* item_name; + FuriString* item_time; + uint16_t idx = subghz_history_get_item(subghz->txrx->history); + item_name = furi_string_alloc(); + item_time = furi_string_alloc(); if(subghz_history_add_to_history(subghz->txrx->history, decoder_base, subghz->txrx->preset)) { - furi_string_reset(str_buff); + furi_string_reset(item_name); + furi_string_reset(item_time); subghz->state_notifications = SubGhzNotificationStateRxDone; - subghz_history_get_text_item_menu( - subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); + subghz_history_get_text_item_menu(subghz->txrx->history, item_name, idx); + subghz_history_get_time_item_menu(subghz->txrx->history, item_time, idx); subghz_view_receiver_add_item_to_menu( subghz->subghz_receiver, - furi_string_get_cstr(str_buff), - subghz_history_get_type_protocol( - subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1)); + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), + subghz_history_get_type_protocol(subghz->txrx->history, idx)); subghz_scene_receiver_update_statusbar(subghz); } subghz_receiver_reset(receiver); - furi_string_free(str_buff); + furi_string_free(item_name); + furi_string_free(item_time); subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; } void subghz_scene_receiver_on_enter(void* context) { SubGhz* subghz = context; - FuriString* str_buff; - str_buff = furi_string_alloc(); + FuriString* item_name; + FuriString* item_time; + item_name = furi_string_alloc(); + item_time = furi_string_alloc(); if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) { subghz_preset_init(subghz, "AM650", subghz->last_settings->frequency, NULL, 0); @@ -132,15 +139,19 @@ void subghz_scene_receiver_on_enter(void* context) { //Load history to receiver subghz_view_receiver_exit(subghz->subghz_receiver); for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) { - furi_string_reset(str_buff); - subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i); + furi_string_reset(item_name); + furi_string_reset(item_time); + subghz_history_get_text_item_menu(subghz->txrx->history, item_name, i); + subghz_history_get_time_item_menu(subghz->txrx->history, item_time, i); subghz_view_receiver_add_item_to_menu( subghz->subghz_receiver, - furi_string_get_cstr(str_buff), + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), subghz_history_get_type_protocol(subghz->txrx->history, i)); subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; } - furi_string_free(str_buff); + furi_string_free(item_name); + furi_string_free(item_time); subghz_scene_receiver_update_statusbar(subghz); subghz_view_receiver_set_callback( subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index bb6d00a17..51d5ada7d 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -161,10 +161,14 @@ uint16_t subghz_history_get_last_index(SubGhzHistory* instance) { return instance->last_index_write; } void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx) { + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + furi_string_set(output, item->item_str); +} + +void subghz_history_get_time_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx) { SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); FuriHalRtcDateTime* t = &item->datetime; furi_string_printf(output, "%.2d:%.2d:%.2d ", t->hour, t->minute, t->second); - furi_string_cat(output, item->item_str); } bool subghz_history_add_to_history( diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index 1f2f8d246..1b27d52ad 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -78,6 +78,14 @@ const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t i */ void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx); +/** Get time item menu to history[idx] + * + * @param instance - SubGhzHistory instance + * @param output - FuriString* output + * @param idx - record index + */ +void subghz_history_get_time_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx); + /** Get string the remaining number of records to history * * @param instance - SubGhzHistory instance diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 8dbb22b86..33a3c79d1 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -14,8 +14,12 @@ #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f +#define SCROLL_INTERVAL (333) +#define SCROLL_DELAY (2) + typedef struct { FuriString* item_str; + FuriString* time; uint8_t type; } SubGhzReceiverMenuItem; @@ -51,6 +55,7 @@ struct SubGhzViewReceiver { View* view; SubGhzViewReceiverCallback callback; void* context; + FuriTimer* scroll_timer; }; typedef struct { @@ -65,6 +70,7 @@ typedef struct { SubGhzViewReceiverBarShow bar_show; SubGhzViewReceiverMode mode; uint8_t u_rssi; + size_t scroll_counter; } SubGhzViewReceiverModel; void subghz_view_receiver_set_mode( @@ -144,6 +150,7 @@ static void subghz_view_receiver_update_offset(SubGhzViewReceiver* subghz_receiv void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, + const char* time, uint8_t type) { furi_assert(subghz_receiver); with_view_model( @@ -152,6 +159,7 @@ void subghz_view_receiver_add_item_to_menu( { SubGhzReceiverMenuItem* item_menu = SubGhzReceiverMenuItemArray_push_raw(model->history->data); + item_menu->time = furi_string_alloc_set(time); item_menu->item_str = furi_string_alloc_set(name); item_menu->type = type; if((model->idx == model->history_item - 1)) { @@ -240,14 +248,29 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0); item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx); furi_string_set(str_buff, item_menu->item_str); - elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 7 : MAX_LEN_PX); + size_t scroll_counter = model->scroll_counter; if(model->idx == idx) { subghz_view_receiver_draw_frame(canvas, i, scrollbar); + if(scroll_counter < SCROLL_DELAY) { + // Show time of signal one moment + furi_string_set(str_buff, item_menu->time); + scroll_counter = 0; + } else { + scroll_counter -= SCROLL_DELAY; + } } else { canvas_set_color(canvas, ColorBlack); + scroll_counter = 0; } canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); - canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); + elements_scrollable_text_line( + canvas, + 15, + 9 + i * FRAME_HEIGHT, + (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX), + str_buff, + scroll_counter, + (model->idx != idx)); furi_string_reset(str_buff); } if(scrollbar) { @@ -353,6 +376,13 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { } } +static void subghz_view_receiver_scroll_timer_callback(void* context) { + furi_assert(context); + SubGhzViewReceiver* subghz_receiver = context; + with_view_model( + subghz_receiver->view, SubGhzViewReceiverModel * model, { model->scroll_counter++; }, true); +} + static void subghz_view_receiver_timer_callback(void* context) { furi_assert(context); SubGhzViewReceiver* subghz_receiver = context; @@ -412,6 +442,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { SubGhzViewReceiverModel * model, { if(model->idx != 0) model->idx--; + model->scroll_counter = 0; }, true); } else if( @@ -423,6 +454,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { { if((model->history_item != 0) && (model->idx != model->history_item - 1)) model->idx++; + model->scroll_counter = 0; }, true); } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { @@ -442,6 +474,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { if(it->index == (size_t)(model->idx)) { furi_string_free(item->item_str); + furi_string_free(item->time); item->type = 0; SubGhzReceiverMenuItemArray_remove(model->history->data, it); } @@ -475,6 +508,13 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { void subghz_view_receiver_enter(void* context) { furi_assert(context); + SubGhzViewReceiver* subghz_receiver = context; + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->scroll_counter = 0; }, + true); + furi_timer_start(subghz_receiver->scroll_timer, SCROLL_INTERVAL); } void subghz_view_receiver_exit(void* context) { @@ -491,6 +531,7 @@ void subghz_view_receiver_exit(void* context) { for M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) { furi_string_free(item_menu->item_str); + furi_string_free(item_menu->time); item_menu->type = 0; } SubGhzReceiverMenuItemArray_reset(model->history->data); @@ -500,6 +541,7 @@ void subghz_view_receiver_exit(void* context) { }, false); furi_timer_stop(subghz_receiver->timer); + furi_timer_stop(subghz_receiver->scroll_timer); } SubGhzViewReceiver* subghz_view_receiver_alloc() { @@ -518,6 +560,9 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { view_set_enter_callback(subghz_receiver->view, subghz_view_receiver_enter); view_set_exit_callback(subghz_receiver->view, subghz_view_receiver_exit); + subghz_receiver->scroll_timer = furi_timer_alloc( + subghz_view_receiver_scroll_timer_callback, FuriTimerTypePeriodic, subghz_receiver); + with_view_model( subghz_receiver->view, SubGhzViewReceiverModel * model, @@ -539,6 +584,8 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { furi_assert(subghz_receiver); + furi_timer_free(subghz_receiver->scroll_timer); + with_view_model( subghz_receiver->view, SubGhzViewReceiverModel * model, @@ -550,6 +597,7 @@ void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { for M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) { furi_string_free(item_menu->item_str); + furi_string_free(item_menu->time); item_menu->type = 0; } SubGhzReceiverMenuItemArray_clear(model->history->data); diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index ad8c31eda..f5ec86b4b 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -40,6 +40,7 @@ void subghz_view_receiver_add_data_progress( void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, + const char* time, uint8_t type); uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver); From 67a457dd1fabbeb0312e6336f20c88217b90a76c Mon Sep 17 00:00:00 2001 From: Nikita Vostokov Date: Fri, 21 Apr 2023 04:10:32 +0300 Subject: [PATCH 063/282] SubGhz - Add date of signal to *.sub files header --- applications/external/protoview/signal_file.c | 16 +++++++++++++++ lib/subghz/blocks/generic.c | 20 +++++++++++++++++++ lib/subghz/protocols/bin_raw.c | 19 ++++++++++++++++++ lib/subghz/protocols/raw.c | 13 ++++++++++++ 4 files changed, 68 insertions(+) diff --git a/applications/external/protoview/signal_file.c b/applications/external/protoview/signal_file.c index c60a6a181..c16423f69 100644 --- a/applications/external/protoview/signal_file.c +++ b/applications/external/protoview/signal_file.c @@ -28,12 +28,28 @@ bool save_signal(ProtoViewApp* app, const char* filename) { FuriString* file_content = furi_string_alloc(); const char* preset_id = ProtoViewModulations[app->modulation].id; + char str_date[30]; + FuriHalRtcDateTime now; + furi_hal_rtc_get_datetime(&now); + snprintf( + str_date, + sizeof(str_date), + "%.4d/%.2d/%.2d %.2d:%.2d:%.2d", + now.year, + now.month, + now.day, + now.hour, + now.minute, + now.second); + furi_string_printf( file_content, "Filetype: Flipper SubGhz RAW File\n" "Version: 1\n" + "Date: %s\n" "Frequency: %ld\n" "Preset: %s\n", + str_date, app->frequency, preset_id ? preset_id : "FuriHalSubGhzPresetCustom"); diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 1827388af..696a97e04 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -37,6 +37,25 @@ SubGhzProtocolStatus subghz_block_generic_serialize( break; } + FuriHalRtcDateTime now; + furi_hal_rtc_get_datetime(&now); + // Format should be locale independent for transfer across devices with different locales + furi_string_printf( + temp_str, + "%.4d/%.2d/%.2d %.2d:%.2d:%.2d", + now.year, + now.month, + now.day, + now.hour, + now.minute, + now.second); + if(!flipper_format_write_string_cstr( + flipper_format, "Date", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Date"); + res = SubGhzProtocolStatusError; + break; + } + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); res = SubGhzProtocolStatusErrorParserFrequency; @@ -93,6 +112,7 @@ SubGhzProtocolStatus subghz_block_generic_serialize( uint32_t temp = (instance->data_2 >> 4) & 0xFFFFF; if(!flipper_format_write_uint32(flipper_format, "Data", &temp, 1)) { FURI_LOG_E(TAG, "Unable to add Data"); + res = SubGhzProtocolStatusError; break; } } diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c index 003cc5edd..702bb8fd7 100644 --- a/lib/subghz/protocols/bin_raw.c +++ b/lib/subghz/protocols/bin_raw.c @@ -987,6 +987,25 @@ SubGhzProtocolStatus subghz_protocol_decoder_bin_raw_serialize( break; } + FuriHalRtcDateTime now; + furi_hal_rtc_get_datetime(&now); + // Format should be locale independent for transfer across devices with different locales + furi_string_printf( + temp_str, + "%.4d/%.2d/%.2d %.2d:%.2d:%.2d", + now.year, + now.month, + now.day, + now.hour, + now.minute, + now.second); + if(!flipper_format_write_string_cstr( + flipper_format, "Date", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Date"); + res = SubGhzProtocolStatusError; + break; + } + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); res = SubGhzProtocolStatusErrorParserFrequency; diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index a82c9cf83..bbd777aa5 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -126,6 +126,19 @@ bool subghz_protocol_raw_save_to_file_init( break; } + FuriHalRtcDateTime now; + furi_hal_rtc_get_datetime(&now); + // Format should be locale independent for transfer across devices with different locales + furi_string_printf( + temp_str, + "%.4d/%.2d/%.2d %.2d:%.2d:%.2d", + now.year, + now.month, + now.day, + now.hour, + now.minute, + now.second); + if(!flipper_format_write_uint32( instance->flipper_file, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); From 2b6eee1848fb3655a7d750a69f4e7d43932e8193 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 21 Apr 2023 05:50:30 +0300 Subject: [PATCH 064/282] move icons a bit, apply fbt format add arrow showing upper side, its not clearly how you need to hold it, arrow helps a bit --- applications/external/hid_app/hid.c | 13 +++++-- .../hid_app/views/hid_keynote_vertical.c | 38 ++++++++++++------- .../hid_app/views/hid_keynote_vertical.h | 4 +- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index 5b00fdc1a..f29a3b22a 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -111,7 +111,11 @@ Hid* hid_alloc(HidTransport transport) { submenu_add_item( app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app); submenu_add_item( - app->device_type_submenu, "Keynote Vertical", HidSubmenuIndexKeynoteVertical, hid_submenu_callback, app); + app->device_type_submenu, + "Keynote Vertical", + HidSubmenuIndexKeynoteVertical, + hid_submenu_callback, + app); submenu_add_item( app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app); submenu_add_item( @@ -168,9 +172,12 @@ Hid* hid_app_alloc_view(void* context) { // Keynote Vertical view app->hid_keynote_vertical = hid_keynote_vertical_alloc(app); - view_set_previous_callback(hid_keynote_vertical_get_view(app->hid_keynote_vertical), hid_exit_confirm_view); + view_set_previous_callback( + hid_keynote_vertical_get_view(app->hid_keynote_vertical), hid_exit_confirm_view); view_dispatcher_add_view( - app->view_dispatcher, HidViewKeynoteVertical, hid_keynote_vertical_get_view(app->hid_keynote_vertical)); + app->view_dispatcher, + HidViewKeynoteVertical, + hid_keynote_vertical_get_view(app->hid_keynote_vertical)); // Keyboard view app->hid_keyboard = hid_keyboard_alloc(app); diff --git a/applications/external/hid_app/views/hid_keynote_vertical.c b/applications/external/hid_app/views/hid_keynote_vertical.c index fc9b185eb..7d2303813 100644 --- a/applications/external/hid_app/views/hid_keynote_vertical.c +++ b/applications/external/hid_app/views/hid_keynote_vertical.c @@ -22,7 +22,8 @@ typedef struct { HidTransport transport; } HidKeynoteVerticalModel; -static void hid_keynote_vertical_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { +static void + hid_keynote_vertical_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { canvas_draw_triangle(canvas, x, y, 5, 3, dir); if(dir == CanvasDirectionBottomToTop) { canvas_draw_line(canvas, x, y + 6, x, y - 1); @@ -51,7 +52,8 @@ static void hid_keynote_vertical_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote"); canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 24, 14, AlignLeft, AlignTop, "Vertical"); + elements_multiline_text_aligned( + canvas, 24, 14, AlignLeft, AlignTop, "Vertical Up --->"); canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8); canvas_set_font(canvas, FontSecondary); @@ -76,21 +78,21 @@ static void hid_keynote_vertical_draw_callback(Canvas* canvas, void* context) { canvas_set_color(canvas, ColorBlack); // Left - canvas_draw_icon(canvas, 0, 45, &I_Button_18x18); + canvas_draw_icon(canvas, 0, 35, &I_Button_18x18); if(model->left_pressed) { - elements_slightly_rounded_box(canvas, 3, 47, 13, 13); + elements_slightly_rounded_box(canvas, 3, 37, 13, 13); canvas_set_color(canvas, ColorWhite); } - hid_keynote_vertical_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); + hid_keynote_vertical_draw_arrow(canvas, 7, 43, CanvasDirectionRightToLeft); canvas_set_color(canvas, ColorBlack); // Right - canvas_draw_icon(canvas, 42, 45, &I_Button_18x18); + canvas_draw_icon(canvas, 42, 35, &I_Button_18x18); if(model->right_pressed) { - elements_slightly_rounded_box(canvas, 45, 47, 13, 13); + elements_slightly_rounded_box(canvas, 45, 37, 13, 13); canvas_set_color(canvas, ColorWhite); } - hid_keynote_vertical_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); + hid_keynote_vertical_draw_arrow(canvas, 53, 43, CanvasDirectionLeftToRight); canvas_set_color(canvas, ColorBlack); // Ok @@ -113,7 +115,8 @@ static void hid_keynote_vertical_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); } -static void hid_keynote_vertical_process(HidKeynoteVertical* hid_keynote_vertical, InputEvent* event) { +static void + hid_keynote_vertical_process(HidKeynoteVertical* hid_keynote_vertical, InputEvent* event) { with_view_model( hid_keynote_vertical->view, HidKeynoteVerticalModel * model, @@ -188,12 +191,16 @@ HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* hid) { hid_keynote_vertical->view = view_alloc(); hid_keynote_vertical->hid = hid; view_set_context(hid_keynote_vertical->view, hid_keynote_vertical); - view_allocate_model(hid_keynote_vertical->view, ViewModelTypeLocking, sizeof(HidKeynoteVerticalModel)); + view_allocate_model( + hid_keynote_vertical->view, ViewModelTypeLocking, sizeof(HidKeynoteVerticalModel)); view_set_draw_callback(hid_keynote_vertical->view, hid_keynote_vertical_draw_callback); view_set_input_callback(hid_keynote_vertical->view, hid_keynote_vertical_input_callback); with_view_model( - hid_keynote_vertical->view, HidKeynoteVerticalModel * model, { model->transport = hid->transport; }, true); + hid_keynote_vertical->view, + HidKeynoteVerticalModel * model, + { model->transport = hid->transport; }, + true); return hid_keynote_vertical; } @@ -209,8 +216,13 @@ View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical) { return hid_keynote_vertical->view; } -void hid_keynote_vertical_set_connected_status(HidKeynoteVertical* hid_keynote_vertical, bool connected) { +void hid_keynote_vertical_set_connected_status( + HidKeynoteVertical* hid_keynote_vertical, + bool connected) { furi_assert(hid_keynote_vertical); with_view_model( - hid_keynote_vertical->view, HidKeynoteVerticalModel * model, { model->connected = connected; }, true); + hid_keynote_vertical->view, + HidKeynoteVerticalModel * model, + { model->connected = connected; }, + true); } diff --git a/applications/external/hid_app/views/hid_keynote_vertical.h b/applications/external/hid_app/views/hid_keynote_vertical.h index 16af4dd7e..bb7134adb 100644 --- a/applications/external/hid_app/views/hid_keynote_vertical.h +++ b/applications/external/hid_app/views/hid_keynote_vertical.h @@ -11,4 +11,6 @@ void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical); View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical); -void hid_keynote_vertical_set_connected_status(HidKeynoteVertical* hid_keynote_vertical, bool connected); +void hid_keynote_vertical_set_connected_status( + HidKeynoteVertical* hid_keynote_vertical, + bool connected); From 15f204bbc5cfa440e92462e8deb2c1a16c20e6ad Mon Sep 17 00:00:00 2001 From: Nikita Vostokov Date: Fri, 21 Apr 2023 04:10:32 +0300 Subject: [PATCH 065/282] Revert "SubGhz - Add date of signal to *.sub files header" This reverts commit 67a457dd1fabbeb0312e6336f20c88217b90a76c. --- applications/external/protoview/signal_file.c | 16 --------------- lib/subghz/blocks/generic.c | 20 ------------------- lib/subghz/protocols/bin_raw.c | 19 ------------------ lib/subghz/protocols/raw.c | 13 ------------ 4 files changed, 68 deletions(-) diff --git a/applications/external/protoview/signal_file.c b/applications/external/protoview/signal_file.c index c16423f69..c60a6a181 100644 --- a/applications/external/protoview/signal_file.c +++ b/applications/external/protoview/signal_file.c @@ -28,28 +28,12 @@ bool save_signal(ProtoViewApp* app, const char* filename) { FuriString* file_content = furi_string_alloc(); const char* preset_id = ProtoViewModulations[app->modulation].id; - char str_date[30]; - FuriHalRtcDateTime now; - furi_hal_rtc_get_datetime(&now); - snprintf( - str_date, - sizeof(str_date), - "%.4d/%.2d/%.2d %.2d:%.2d:%.2d", - now.year, - now.month, - now.day, - now.hour, - now.minute, - now.second); - furi_string_printf( file_content, "Filetype: Flipper SubGhz RAW File\n" "Version: 1\n" - "Date: %s\n" "Frequency: %ld\n" "Preset: %s\n", - str_date, app->frequency, preset_id ? preset_id : "FuriHalSubGhzPresetCustom"); diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 696a97e04..1827388af 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -37,25 +37,6 @@ SubGhzProtocolStatus subghz_block_generic_serialize( break; } - FuriHalRtcDateTime now; - furi_hal_rtc_get_datetime(&now); - // Format should be locale independent for transfer across devices with different locales - furi_string_printf( - temp_str, - "%.4d/%.2d/%.2d %.2d:%.2d:%.2d", - now.year, - now.month, - now.day, - now.hour, - now.minute, - now.second); - if(!flipper_format_write_string_cstr( - flipper_format, "Date", furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Unable to add Date"); - res = SubGhzProtocolStatusError; - break; - } - if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); res = SubGhzProtocolStatusErrorParserFrequency; @@ -112,7 +93,6 @@ SubGhzProtocolStatus subghz_block_generic_serialize( uint32_t temp = (instance->data_2 >> 4) & 0xFFFFF; if(!flipper_format_write_uint32(flipper_format, "Data", &temp, 1)) { FURI_LOG_E(TAG, "Unable to add Data"); - res = SubGhzProtocolStatusError; break; } } diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c index 702bb8fd7..003cc5edd 100644 --- a/lib/subghz/protocols/bin_raw.c +++ b/lib/subghz/protocols/bin_raw.c @@ -987,25 +987,6 @@ SubGhzProtocolStatus subghz_protocol_decoder_bin_raw_serialize( break; } - FuriHalRtcDateTime now; - furi_hal_rtc_get_datetime(&now); - // Format should be locale independent for transfer across devices with different locales - furi_string_printf( - temp_str, - "%.4d/%.2d/%.2d %.2d:%.2d:%.2d", - now.year, - now.month, - now.day, - now.hour, - now.minute, - now.second); - if(!flipper_format_write_string_cstr( - flipper_format, "Date", furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Unable to add Date"); - res = SubGhzProtocolStatusError; - break; - } - if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); res = SubGhzProtocolStatusErrorParserFrequency; diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index bbd777aa5..a82c9cf83 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -126,19 +126,6 @@ bool subghz_protocol_raw_save_to_file_init( break; } - FuriHalRtcDateTime now; - furi_hal_rtc_get_datetime(&now); - // Format should be locale independent for transfer across devices with different locales - furi_string_printf( - temp_str, - "%.4d/%.2d/%.2d %.2d:%.2d:%.2d", - now.year, - now.month, - now.day, - now.hour, - now.minute, - now.second); - if(!flipper_format_write_uint32( instance->flipper_file, "Frequency", &preset->frequency, 1)) { FURI_LOG_E(TAG, "Unable to add Frequency"); From 1bb3ab87156335013f487ee6b0d9f3e72f0c1b4d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 21 Apr 2023 06:46:10 +0300 Subject: [PATCH 066/282] New option to save signal names with timestamps --- .../scenes/subghz_scene_ext_module_settings.c | 36 +++++++++-- .../subghz/scenes/subghz_scene_save_name.c | 59 +++++++++++++++---- .../main/subghz/subghz_last_settings.c | 20 +++++++ .../main/subghz/subghz_last_settings.h | 1 + firmware/targets/f7/api_symbols.csv | 2 + .../targets/f7/furi_hal/furi_hal_subghz.c | 9 +++ .../targets/f7/furi_hal/furi_hal_subghz.h | 9 +++ 7 files changed, 119 insertions(+), 17 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c index a96208074..75a2215fc 100644 --- a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c @@ -1,10 +1,11 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -uint8_t value_index_exm; -uint8_t value_index_dpin; -uint8_t value_index_cnt; -uint8_t value_index_pwr; +static uint8_t value_index_exm; +static uint8_t value_index_dpin; +static uint8_t value_index_cnt; +static uint8_t value_index_pwr; +static uint8_t value_index_time; #define EXT_MODULES_COUNT (sizeof(radio_modules_variables_text) / sizeof(char* const)) const char* const radio_modules_variables_text[] = { @@ -18,6 +19,12 @@ const char* const ext_mod_power_text[EXT_MOD_POWER_COUNT] = { "OFF", }; +#define TIMESTAMP_NAMES_COUNT 2 +const char* const timestamp_names_text[TIMESTAMP_NAMES_COUNT] = { + "OFF", + "ON", +}; + #define DEBUG_P_COUNT 2 const char* const debug_pin_text[DEBUG_P_COUNT] = { "OFF", @@ -104,6 +111,17 @@ static void subghz_scene_receiver_config_set_ext_mod_power(VariableItem* item) { subghz_last_settings_save(subghz->last_settings); } +static void subghz_scene_receiver_config_set_timestamp_file_names(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, timestamp_names_text[index]); + + furi_hal_subghz_set_timestamp_file_names((index == 1)); + subghz->last_settings->timestamp_file_names = (index == 1); + subghz_last_settings_save(subghz->last_settings); +} + void subghz_scene_ext_module_settings_on_enter(void* context) { SubGhz* subghz = context; @@ -129,6 +147,16 @@ void subghz_scene_ext_module_settings_on_enter(void* context) { variable_item_set_current_value_index(item, value_index_pwr); variable_item_set_current_value_text(item, ext_mod_power_text[value_index_pwr]); + item = variable_item_list_add( + subghz->variable_item_list, + "Time in names", + TIMESTAMP_NAMES_COUNT, + subghz_scene_receiver_config_set_timestamp_file_names, + subghz); + value_index_time = furi_hal_subghz_get_timestamp_file_names(); + variable_item_set_current_value_index(item, value_index_time); + variable_item_set_current_value_text(item, timestamp_names_text[value_index_time]); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { item = variable_item_list_add( subghz->variable_item_list, diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 4f37ebc0b..282e22201 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -14,18 +14,34 @@ void subghz_scene_save_name_text_input_callback(void* context) { view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneSaveName); } -void subghz_scene_save_name_get_timefilename(FuriString* name) { +void subghz_scene_save_name_get_timefilename( + FuriString* name, + const char* proto_name, + bool fulldate) { FuriHalRtcDateTime datetime = {0}; furi_hal_rtc_get_datetime(&datetime); - furi_string_printf( - name, - "RAW_%.4d%.2d%.2d-%.2d%.2d%.2d", - datetime.year, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second); + if(fulldate) { + furi_string_printf( + name, + "%s_%.4d%.2d%.2d-%.2d%.2d%.2d", + proto_name, + datetime.year, + datetime.month, + datetime.day, + datetime.hour, + datetime.minute, + datetime.second); + } else { + furi_string_printf( + name, + "%s_%.2d%.2d-%.2d%.2d%.2d", + proto_name, + datetime.month, + datetime.day, + datetime.hour, + datetime.minute, + datetime.second); + } } void subghz_scene_save_name_on_enter(void* context) { @@ -42,8 +58,25 @@ void subghz_scene_save_name_on_enter(void* context) { if(!subghz_path_is_file(subghz->file_path)) { char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; - set_random_name(file_name_buf, SUBGHZ_MAX_LEN_NAME); - furi_string_set(file_name, file_name_buf); + if(furi_hal_subghz_get_timestamp_file_names()) { + if(subghz->txrx->decoder_result->protocol->name != 0x0) { + if(subghz->txrx->decoder_result->protocol->name != NULL) { + if(strlen(subghz->txrx->decoder_result->protocol->name) != 0) { + subghz_scene_save_name_get_timefilename( + file_name, subghz->txrx->decoder_result->protocol->name, false); + } else { + subghz_scene_save_name_get_timefilename(file_name, "S", true); + } + } else { + subghz_scene_save_name_get_timefilename(file_name, "S", true); + } + } else { + subghz_scene_save_name_get_timefilename(file_name, "S", true); + } + } else { + set_random_name(file_name_buf, SUBGHZ_MAX_LEN_NAME); + furi_string_set(file_name, file_name_buf); + } furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); //highlighting the entire filename by default dev_name_empty = true; @@ -57,7 +90,7 @@ void subghz_scene_save_name_on_enter(void* context) { if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == SubGhzCustomEventManagerSetRAW) { dev_name_empty = true; - subghz_scene_save_name_get_timefilename(file_name); + subghz_scene_save_name_get_timefilename(file_name, "RAW", true); } } furi_string_set(subghz->file_path, dir_name); diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 6fc51554d..2f4445a42 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -19,6 +19,7 @@ #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED "External" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER "ExtPower" +#define SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES "TimestampNames" SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); @@ -45,6 +46,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count float temp_frequency_analyzer_trigger = 0; bool temp_external_module_enabled = false; bool temp_external_module_power_5v_disable = false; + bool temp_timestamp_file_names = false; //int32_t temp_preset = 0; bool frequency_analyzer_feedback_level_was_read = false; bool frequency_analyzer_trigger_was_read = false; @@ -76,6 +78,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER, (bool*)&temp_external_module_power_5v_disable, 1); + flipper_format_read_bool( + fff_data_file, + SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, + (bool*)&temp_timestamp_file_names, + 1); } else { FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); @@ -89,6 +96,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL; instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; instance->external_module_enabled = false; + instance->timestamp_file_names = false; } else { instance->frequency = temp_frequency; @@ -109,6 +117,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->external_module_power_5v_disable = temp_external_module_power_5v_disable; + instance->timestamp_file_names = temp_timestamp_file_names; + + // Set globally + furi_hal_subghz_set_timestamp_file_names(instance->timestamp_file_names); + if(instance->external_module_power_5v_disable) { furi_hal_subghz_set_external_power_disable(true); furi_hal_subghz_disable_ext_power(); @@ -189,6 +202,13 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { 1)) { break; } + if(!flipper_format_insert_or_update_bool( + file, + SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, + &instance->timestamp_file_names, + 1)) { + break; + } saved = true; } while(0); diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index 5e3630468..260c879f4 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -12,6 +12,7 @@ typedef struct { float frequency_analyzer_trigger; bool external_module_enabled; bool external_module_power_5v_disable; + bool timestamp_file_names; } SubGhzLastSettings; SubGhzLastSettings* subghz_last_settings_alloc(void); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index f19310349..6bbb94a6a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1373,6 +1373,7 @@ Function,+,furi_hal_subghz_get_lqi,uint8_t, Function,+,furi_hal_subghz_get_radio_type,SubGhzRadioType, Function,+,furi_hal_subghz_get_rolling_counter_mult,uint8_t, Function,+,furi_hal_subghz_get_rssi,float, +Function,+,furi_hal_subghz_get_timestamp_file_names,_Bool, Function,+,furi_hal_subghz_idle,void, Function,-,furi_hal_subghz_init,void, Function,+,furi_hal_subghz_init_check,_Bool, @@ -1395,6 +1396,7 @@ Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath Function,+,furi_hal_subghz_set_radio_type,_Bool,SubGhzRadioType Function,+,furi_hal_subghz_set_rolling_counter_mult,void,uint8_t +Function,+,furi_hal_subghz_set_timestamp_file_names,void,_Bool Function,-,furi_hal_subghz_shutdown,void, Function,+,furi_hal_subghz_sleep,void, Function,+,furi_hal_subghz_start_async_rx,void,"FuriHalSubGhzCaptureCallback, void*" diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index ed9864ae9..1a4e39965 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -40,6 +40,7 @@ volatile FuriHalSubGhz furi_hal_subghz = { .cc1101_g0_pin = &gpio_cc1101_g0, .rolling_counter_mult = 1, .ext_module_power_disabled = false, + .timestamp_file_names = false, }; bool furi_hal_subghz_set_radio_type(SubGhzRadioType state) { @@ -79,6 +80,14 @@ bool furi_hal_subghz_get_external_power_disable(void) { return furi_hal_subghz.ext_module_power_disabled; } +void furi_hal_subghz_set_timestamp_file_names(bool state) { + furi_hal_subghz.timestamp_file_names = state; +} + +bool furi_hal_subghz_get_timestamp_file_names(void) { + return furi_hal_subghz.timestamp_file_names; +} + void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin) { furi_hal_subghz.async_mirror_pin = pin; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index ec886f693..b366e8578 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -79,6 +79,7 @@ typedef struct { const GpioPin* cc1101_g0_pin; uint8_t rolling_counter_mult; bool ext_module_power_disabled; + bool timestamp_file_names; } FuriHalSubGhz; extern volatile FuriHalSubGhz furi_hal_subghz; @@ -330,6 +331,14 @@ void furi_hal_subghz_set_external_power_disable(bool state); */ bool furi_hal_subghz_get_external_power_disable(void); +/** If true - disable generation of random name and add timestamp to filenames instead + */ +void furi_hal_subghz_set_timestamp_file_names(bool state); + +/** Get the current state of the timestamp instead of random name flag + */ +bool furi_hal_subghz_get_timestamp_file_names(void); + #ifdef __cplusplus } #endif From d92833284e40d5e84e17d2fb937012816d262e2d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 21 Apr 2023 07:25:34 +0300 Subject: [PATCH 067/282] Scroll improvements --- applications/main/subghz/views/receiver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 33a3c79d1..d48ff8b96 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -14,7 +14,7 @@ #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f -#define SCROLL_INTERVAL (333) +#define SCROLL_INTERVAL (606) #define SCROLL_DELAY (2) typedef struct { From f0e866cb56a6e4e0e615f6780a3ce6224778ad52 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 21 Apr 2023 07:38:01 +0300 Subject: [PATCH 068/282] Fix add manually naming --- applications/main/subghz/scenes/subghz_scene_save_name.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 282e22201..6a8c68aeb 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -62,8 +62,13 @@ void subghz_scene_save_name_on_enter(void* context) { if(subghz->txrx->decoder_result->protocol->name != 0x0) { if(subghz->txrx->decoder_result->protocol->name != NULL) { if(strlen(subghz->txrx->decoder_result->protocol->name) != 0) { - subghz_scene_save_name_get_timefilename( - file_name, subghz->txrx->decoder_result->protocol->name, false); + if(strcmp(subghz->txrx->decoder_result->protocol->name, "BinRAW") == 0) { + subghz_scene_save_name_get_timefilename(file_name, "S", true); + } else { + subghz_scene_save_name_get_timefilename( + file_name, subghz->txrx->decoder_result->protocol->name, false); + } + } else { subghz_scene_save_name_get_timefilename(file_name, "S", true); } From 7747f45be3c75cee7965c15ac4d5b49788598e4f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 21 Apr 2023 07:51:37 +0300 Subject: [PATCH 069/282] Fix null pointer dereference --- applications/main/subghz/scenes/subghz_scene_save_name.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 6a8c68aeb..3f7658201 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -59,8 +59,8 @@ void subghz_scene_save_name_on_enter(void* context) { if(!subghz_path_is_file(subghz->file_path)) { char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; if(furi_hal_subghz_get_timestamp_file_names()) { - if(subghz->txrx->decoder_result->protocol->name != 0x0) { - if(subghz->txrx->decoder_result->protocol->name != NULL) { + if(subghz->txrx->decoder_result != 0x0) { + if(subghz->txrx->decoder_result != NULL) { if(strlen(subghz->txrx->decoder_result->protocol->name) != 0) { if(strcmp(subghz->txrx->decoder_result->protocol->name, "BinRAW") == 0) { subghz_scene_save_name_get_timefilename(file_name, "S", true); From e8bd435fe3a80c655bda5d6fa9ddf0c1f8ede78a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 21 Apr 2023 09:46:48 +0300 Subject: [PATCH 070/282] fw version in json --- scripts/version.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/version.py b/scripts/version.py index 2faef119c..df56f8479 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -154,6 +154,7 @@ class Main(App): "firmware_commit": current_info["GIT_COMMIT"], "firmware_branch": current_info["GIT_BRANCH"], "firmware_target": current_info["TARGET"], + "firmware_version": current_info["VERSION"], } with open(version_json_name, "w", newline="\n") as file: json.dump(version_json, file, indent=4) From 1c65ec9ef2a3acd7c3fa5bef0d0d18391d4f4061 Mon Sep 17 00:00:00 2001 From: ushastoe Date: Fri, 21 Apr 2023 11:36:17 +0300 Subject: [PATCH 071/282] some change - add [beta] for YT Shorts - fix pause click on like button - fix slide screen --- applications/external/hid_app/hid.c | 2 +- applications/external/hid_app/views/hid_tiktok.c | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index b679c395c..49002cfa2 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -120,7 +120,7 @@ Hid* hid_alloc(HidTransport transport) { app); submenu_add_item( app->device_type_submenu, - "YT Shorts Controller", + "[Beta]YT Shorts Controller", HidSubmenuIndexYTShorts, hid_submenu_callback, app); diff --git a/applications/external/hid_app/views/hid_tiktok.c b/applications/external/hid_app/views/hid_tiktok.c index 3b48d32cb..9b15d812b 100644 --- a/applications/external/hid_app/views/hid_tiktok.c +++ b/applications/external/hid_app/views/hid_tiktok.c @@ -178,32 +178,25 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) { } else if(event->type == InputTypeShort) { if(event->key == InputKeyOk) { hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); + furi_delay_ms(25); hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); + furi_delay_ms(100); hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); + furi_delay_ms(25); hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); consumed = true; } else if(event->key == InputKeyDown) { // Swipe to new video - hid_hal_mouse_scroll(hid_tiktok->hid, 6); - hid_hal_mouse_scroll(hid_tiktok->hid, 12); hid_hal_mouse_scroll(hid_tiktok->hid, 19); - hid_hal_mouse_scroll(hid_tiktok->hid, 12); - hid_hal_mouse_scroll(hid_tiktok->hid, 6); consumed = true; } else if(event->key == InputKeyUp) { // Swipe to previous video - hid_hal_mouse_scroll(hid_tiktok->hid, -6); - hid_hal_mouse_scroll(hid_tiktok->hid, -12); hid_hal_mouse_scroll(hid_tiktok->hid, -19); - hid_hal_mouse_scroll(hid_tiktok->hid, -12); - hid_hal_mouse_scroll(hid_tiktok->hid, -6); consumed = true; } else if(event->key == InputKeyBack) { // Pause hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_RIGHT); + furi_delay_ms(25); hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_RIGHT); consumed = true; } From 634c37fb2309ef95ad02f3564725f96128c6d3fb Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 21 Apr 2023 13:59:49 +0300 Subject: [PATCH 072/282] Update readme and changelog --- CHANGELOG.md | 22 +++++++++++++++++----- ReadMe.md | 3 +++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b592469b8..81dd2a402 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,31 @@ ### New changes -* OFW: Various stop mode fixes -> **Fixes all known issues with BLE (Random freezes, menu freeze, BT Remote plugin freeze) and other similar issues** -* OFW: Picopass: Correctly aborts when correct key is found -> Fixes Bug (Picopass app not reading elite keyed cards anymore. #413) +* Note: Due to latest official changes this release was delayed - release was tested by group of users, and some of them reported getting one crash on "furi_check failed", but this can not be reproduced in any way we tried, so, please if you found any issues with BLE (+ Mobile app) that results in crash, create issue with all details how you got it and how many times, and detailed steps on repeating such issue, if you got one crash and can't get it again, collect information how it happened and create issue with as much details as possible -> Thanks! +* SubGHz: New option to use timestamps + protocol name when you saving file, instead of random name - Enable in `Radio Settings -> Time in names = ON` +* SubGHz: Read mode UI improvements (scrolling text, + shows time when signal was received) (by @wosk | PR #429) +* SubGHz: New options to ignore Magellan, Cars(ScherKhan, Kia)(no you can't send that signals) +* SubGHz: Fix keeloq custom buttons bugs * SubGhz: Nero Radio 57bit **experimental** support + encoder improvements and decoder changes -* SubGhz: Fix RAW recording and reading, (bug where raw file plays endlessly) +* SubGhz: Fix RAW recording and reading, (bug where raw file plays endlessly) (Fixes issue #431) +* SubGHz Remote: Add Alutech AT4N Support, fix some issues +* Plugins: BLE Remote -> Keynote with vertical layout (by @Kami-no | PR #428) * Plugins: Improve wifi marauder keyboard (added extra symbols!) (Port uart terminal keyboard into wifi marauder) * Infrared: Update universal remote assets (by @amec0e | PR #421) * Docs: Update build docs (by @PhoenixSheppy | PR #425) -### Previous changes +* OFW: cubewb: updated to v1.16.0 -> **Part 2 of "Various stop mode fixes"** +* OFW: github: testing SDK with ufbt action +* OFW: Raw RFID documentation +* OFW: Introduce stealth mode and auto-selective lock +* OFW: Active RPC session icon -> **Breaking API change, api was changed from 22.x to 23.x** +* OFW: Various stop mode fixes -> **Should fix known issues with BLE (Random freezes, menu freeze, BT Remote plugin freeze) and other similar issues** +* OFW: Picopass: Correctly aborts when correct key is found -> Fixes Bug (Picopass app not reading elite keyed cards anymore. #413) +### Pre-release changes * If you have copied apps into `apps` folder - remove `apps` folder on your microSD before installing this release to avoid issues! * SubGHz: (Bug that I decided to keep as a feature) You can change default button (Ok) for remote by holding custom button and pressing back at same time (same can be used to restore your button if you changed it accidentally) - Be careful, it might be unstable, I will make proper option to change button in next releases * SubGHz: Fixes for custom button bugs in SubGHz Remote app * SubGHz: Add alutech table to enviroment alloc and free * Docs: Fix and update docs - thanks to @lesterrry * Plugins: Bluetooth Remote - implemented YouTube Shorts Remote (may be unstable) -* Plugins: Bluetooth Remote - improvements and fixes for TikTok remote (by @krolchonok | PR #420) +* Plugins: Bluetooth Remote - improvements and fixes for TikTok remote (by @krolchonok | PR #420 and #432) * Plugins: Implement an array for baudrates on GPS UART app (+ add 19200 baud) (by @p0ns | PR #416) * Plugins: Remove UART Echo from releases since it is locked on 115200 baud, and we have **UART Terminal** with ability to set baudrate * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) diff --git a/ReadMe.md b/ReadMe.md index ec188293d..fea644f92 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -54,6 +54,8 @@ Our Discord Community: - Lock device with pin(or regular lock if pin not set) by holding UP button on main screen [(by an4tur0r)](https://github.com/DarkFlippers/unleashed-firmware/pull/107) * Sub-GHz -> Short press OK in frequency analyzer to save detected frequency for usage in Read modes * Sub-GHz -> Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu and automatically use selected frequency +* SubGHz -> New option to use timestamps + protocol name when you saving file, instead of random name - Enable in `Radio Settings -> Time in names = ON` +* SubGHz -> Read mode UI improvements (scrolling text, + shows time when signal was received) (by @wosk) * Sub-GHz -> External CC1101 module support (Hardware SPI used) * SubGHz -> **Hold right in received signal list to delete selected signal** * SubGHz -> **Custom buttons for Keeloq / Alutech AT4N / Nice Flor S / Somfy Telis / Security+ 2.0** - now you can use arrow buttons to send signal with different button code @@ -72,6 +74,7 @@ Thanks to Official team (to their SubGHz Developer, Skorp) for implementing deco Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported manufacturers list](https://0bin.net/paste/VwR2lNJY#WH9vnPgvcp7w6zVKucFCuNREKAcOij8KsJ6vqLfMn3b) Encoders or sending made by @xMasterX: +- Nero Radio 57bit (experimental) (+ 56bit encoder improvements) - Keeloq: HCS101 - Keeloq: AN-Motors - Keeloq: JCM Tech From 7514bc8b49c3dd90fae35d0f20b2d21ebb89b113 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 21 Apr 2023 15:55:06 +0300 Subject: [PATCH 073/282] Final updates --- CHANGELOG.md | 1 + .../services/power/power_service/power.c | 16 ++++++++++++---- .../services/power/power_service/power.h | 6 ++++++ .../scenes/desktop_settings_scene_start.c | 6 ++++++ firmware/targets/f7/api_symbols.csv | 1 + 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81dd2a402..dbc2bdc13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * SubGhz: Nero Radio 57bit **experimental** support + encoder improvements and decoder changes * SubGhz: Fix RAW recording and reading, (bug where raw file plays endlessly) (Fixes issue #431) * SubGHz Remote: Add Alutech AT4N Support, fix some issues +* Power GUI: Changing battery style doesnt require reboot (Added API to trigger UI change from different place) (Inspired by @ESurge work) * Plugins: BLE Remote -> Keynote with vertical layout (by @Kami-no | PR #428) * Plugins: Improve wifi marauder keyboard (added extra symbols!) (Port uart terminal keyboard into wifi marauder) * Infrared: Update universal remote assets (by @amec0e | PR #421) diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 2d890d2ee..e61776060 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -399,6 +399,18 @@ static void power_check_battery_level_change(Power* power) { } } +void power_trigger_ui_update(Power* power) { + DesktopSettings* settings = malloc(sizeof(DesktopSettings)); + bool is_loaded = DESKTOP_SETTINGS_LOAD(settings); + if(is_loaded) { + power->displayBatteryPercentage = settings->displayBatteryPercentage; + } else { + power->displayBatteryPercentage = DISPLAY_BATTERY_BAR; + } + free(settings); + view_port_update(power->battery_view_port); +} + int32_t power_srv(void* p) { UNUSED(p); @@ -431,10 +443,6 @@ int32_t power_srv(void* p) { // Update battery view port if(need_refresh) { - DesktopSettings* settings = malloc(sizeof(DesktopSettings)); - DESKTOP_SETTINGS_LOAD(settings); - power->displayBatteryPercentage = settings->displayBatteryPercentage; - free(settings); view_port_update(power->battery_view_port); } diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index c7f5d7e35..d83127c0b 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -94,6 +94,12 @@ bool power_is_battery_healthy(Power* power); */ void power_enable_low_battery_level_notification(Power* power, bool enable); +/** Trigger UI update for changing battery layout + * + * @param power Power instance + */ +void power_trigger_ui_update(Power* power); + #ifdef __cplusplus } #endif diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 49c2e7422..b28d95d16 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -3,6 +3,7 @@ #include "../desktop_settings_app.h" #include "desktop_settings_scene.h" +#include #define SCENE_EVENT_SELECT_FAVORITE_PRIMARY 0 #define SCENE_EVENT_SELECT_FAVORITE_SECONDARY 1 @@ -169,4 +170,9 @@ void desktop_settings_scene_start_on_exit(void* context) { DesktopSettingsApp* app = context; variable_item_list_reset(app->variable_item_list); DESKTOP_SETTINGS_SAVE(&app->settings); + + // Trigger UI update in case we changed battery layout + Power* power = furi_record_open(RECORD_POWER); + power_trigger_ui_update(power); + furi_record_close(RECORD_POWER); } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 6bbb94a6a..5bc0106b6 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2173,6 +2173,7 @@ Function,+,power_get_pubsub,FuriPubSub*,Power* Function,+,power_is_battery_healthy,_Bool,Power* Function,+,power_off,void,Power* Function,+,power_reboot,void,PowerBootMode +Function,+,power_trigger_ui_update,void,Power* Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t" From 40a5306ed44c883772886052c4777338529a04bf Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Fri, 21 Apr 2023 16:10:08 +0300 Subject: [PATCH 074/282] Combining BFT/FAAC scenes --- .../main/subghz/scenes/subghz_scene_config.h | 9 +- .../main/subghz/scenes/subghz_scene_set_cnt.c | 66 +++++++ .../subghz/scenes/subghz_scene_set_cnt_bft.c | 46 ----- .../subghz/scenes/subghz_scene_set_cnt_faac.c | 46 ----- ...e_set_fix_bft.c => subghz_scene_set_fix.c} | 14 +- .../subghz/scenes/subghz_scene_set_fix_faac.c | 46 ----- .../subghz/scenes/subghz_scene_set_seed.c | 170 ++++++++++++++++++ .../subghz/scenes/subghz_scene_set_seed_bft.c | 105 ----------- .../scenes/subghz_scene_set_seed_faac.c | 110 ------------ .../subghz/scenes/subghz_scene_set_type.c | 6 +- 10 files changed, 249 insertions(+), 369 deletions(-) create mode 100644 applications/main/subghz/scenes/subghz_scene_set_cnt.c delete mode 100644 applications/main/subghz/scenes/subghz_scene_set_cnt_bft.c delete mode 100644 applications/main/subghz/scenes/subghz_scene_set_cnt_faac.c rename applications/main/subghz/scenes/{subghz_scene_set_fix_bft.c => subghz_scene_set_fix.c} (74%) delete mode 100644 applications/main/subghz/scenes/subghz_scene_set_fix_faac.c create mode 100644 applications/main/subghz/scenes/subghz_scene_set_seed.c delete mode 100644 applications/main/subghz/scenes/subghz_scene_set_seed_bft.c delete mode 100644 applications/main/subghz/scenes/subghz_scene_set_seed_faac.c diff --git a/applications/main/subghz/scenes/subghz_scene_config.h b/applications/main/subghz/scenes/subghz_scene_config.h index 5acd534dc..6fb183d60 100644 --- a/applications/main/subghz/scenes/subghz_scene_config.h +++ b/applications/main/subghz/scenes/subghz_scene_config.h @@ -19,12 +19,9 @@ ADD_SCENE(subghz, test_static, TestStatic) ADD_SCENE(subghz, test_packet, TestPacket) #endif ADD_SCENE(subghz, set_type, SetType) -ADD_SCENE(subghz, set_fix_faac, SetFixFaac) -ADD_SCENE(subghz, set_cnt_faac, SetCntFaac) -ADD_SCENE(subghz, set_seed_faac, SetSeedFaac) -ADD_SCENE(subghz, set_fix_bft, SetFixBft) -ADD_SCENE(subghz, set_cnt_bft, SetCntBft) -ADD_SCENE(subghz, set_seed_bft, SetSeedBft) +ADD_SCENE(subghz, set_fix, SetFix) +ADD_SCENE(subghz, set_cnt, SetCnt) +ADD_SCENE(subghz, set_seed, SetSeed) ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer) ADD_SCENE(subghz, ext_module_settings, ExtModuleSettings) ADD_SCENE(subghz, read_raw, ReadRAW) diff --git a/applications/main/subghz/scenes/subghz_scene_set_cnt.c b/applications/main/subghz/scenes/subghz_scene_set_cnt.c new file mode 100644 index 000000000..62ef581ff --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_set_cnt.c @@ -0,0 +1,66 @@ +#include "../subghz_i.h" + +#define TAG "SubGhzSetCnt" + +void subghz_scene_set_cnt_byte_input_callback(void* context) { + SubGhz* subghz = context; + + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); +} + +void subghz_scene_set_cnt_on_enter(void* context) { + SubGhz* subghz = context; + + // Setup view + ByteInput* byte_input = subghz->byte_input; + SubGhzCustomEvent state = + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType); + + switch(state) { + case SubmenuIndexBFTClone: + byte_input_set_header_text(byte_input, "Enter COUNTER in hex"); + byte_input_set_result_callback( + byte_input, + subghz_scene_set_cnt_byte_input_callback, + NULL, + subghz, + subghz->txrx->secure_data->cnt, + 2); + break; + case SubmenuIndexFaacSLH_433: + case SubmenuIndexFaacSLH_868: + byte_input_set_header_text(byte_input, "Enter COUNTER in hex, 20bits"); + byte_input_set_result_callback( + byte_input, + subghz_scene_set_cnt_byte_input_callback, + NULL, + subghz, + subghz->txrx->secure_data->cnt, + 3); + break; + default: + break; + } + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); +} + +bool subghz_scene_set_cnt_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzCustomEventByteInputDone) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetSeed); + consumed = true; + } + } + return consumed; +} + +void subghz_scene_set_cnt_on_exit(void* context) { + SubGhz* subghz = context; + + // Clear view + byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(subghz->byte_input, ""); +} diff --git a/applications/main/subghz/scenes/subghz_scene_set_cnt_bft.c b/applications/main/subghz/scenes/subghz_scene_set_cnt_bft.c deleted file mode 100644 index d58409a7e..000000000 --- a/applications/main/subghz/scenes/subghz_scene_set_cnt_bft.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../subghz_i.h" - -#define TAG "SubGhzSetCntBft" - -void subghz_scene_set_cnt_bft_byte_input_callback(void* context) { - SubGhz* subghz = context; - - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); -} - -void subghz_scene_set_cnt_bft_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - ByteInput* byte_input = subghz->byte_input; - byte_input_set_header_text(byte_input, "Enter COUNTER in hex"); - byte_input_set_result_callback( - byte_input, - subghz_scene_set_cnt_bft_byte_input_callback, - NULL, - subghz, - subghz->txrx->secure_data->cnt, - 2); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); -} - -bool subghz_scene_set_cnt_bft_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventByteInputDone) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetSeedBft); - consumed = true; - } - } - return consumed; -} - -void subghz_scene_set_cnt_bft_on_exit(void* context) { - SubGhz* subghz = context; - - // Clear view - byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(subghz->byte_input, ""); -} diff --git a/applications/main/subghz/scenes/subghz_scene_set_cnt_faac.c b/applications/main/subghz/scenes/subghz_scene_set_cnt_faac.c deleted file mode 100644 index 6002dea74..000000000 --- a/applications/main/subghz/scenes/subghz_scene_set_cnt_faac.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../subghz_i.h" - -#define TAG "SubGhzSetCntFaac" - -void subghz_scene_set_cnt_faac_byte_input_callback(void* context) { - SubGhz* subghz = context; - - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); -} - -void subghz_scene_set_cnt_faac_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - ByteInput* byte_input = subghz->byte_input; - byte_input_set_header_text(byte_input, "Enter COUNTER in hex, 20bits"); - byte_input_set_result_callback( - byte_input, - subghz_scene_set_cnt_faac_byte_input_callback, - NULL, - subghz, - subghz->txrx->secure_data->cnt, - 3); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); -} - -bool subghz_scene_set_cnt_faac_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventByteInputDone) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetSeedFaac); - consumed = true; - } - } - return consumed; -} - -void subghz_scene_set_cnt_faac_on_exit(void* context) { - SubGhz* subghz = context; - - // Clear view - byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(subghz->byte_input, ""); -} diff --git a/applications/main/subghz/scenes/subghz_scene_set_fix_bft.c b/applications/main/subghz/scenes/subghz_scene_set_fix.c similarity index 74% rename from applications/main/subghz/scenes/subghz_scene_set_fix_bft.c rename to applications/main/subghz/scenes/subghz_scene_set_fix.c index 8153fa2a0..01431dac6 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_fix_bft.c +++ b/applications/main/subghz/scenes/subghz_scene_set_fix.c @@ -1,14 +1,14 @@ #include "../subghz_i.h" -#define TAG "SubGhzSetFixBft" +#define TAG "SubGhzSetFix" -void subghz_scene_set_fix_bft_byte_input_callback(void* context) { +void subghz_scene_set_fix_byte_input_callback(void* context) { SubGhz* subghz = context; view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); } -void subghz_scene_set_fix_bft_on_enter(void* context) { +void subghz_scene_set_fix_on_enter(void* context) { SubGhz* subghz = context; // Setup view @@ -16,7 +16,7 @@ void subghz_scene_set_fix_bft_on_enter(void* context) { byte_input_set_header_text(byte_input, "Enter FIX in hex"); byte_input_set_result_callback( byte_input, - subghz_scene_set_fix_bft_byte_input_callback, + subghz_scene_set_fix_byte_input_callback, NULL, subghz, subghz->txrx->secure_data->fix, @@ -24,20 +24,20 @@ void subghz_scene_set_fix_bft_on_enter(void* context) { view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); } -bool subghz_scene_set_fix_bft_on_event(void* context, SceneManagerEvent event) { +bool subghz_scene_set_fix_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventByteInputDone) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetCntBft); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetCnt); consumed = true; } } return consumed; } -void subghz_scene_set_fix_bft_on_exit(void* context) { +void subghz_scene_set_fix_on_exit(void* context) { SubGhz* subghz = context; // Clear view diff --git a/applications/main/subghz/scenes/subghz_scene_set_fix_faac.c b/applications/main/subghz/scenes/subghz_scene_set_fix_faac.c deleted file mode 100644 index 333062c38..000000000 --- a/applications/main/subghz/scenes/subghz_scene_set_fix_faac.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../subghz_i.h" - -#define TAG "SubGhzSetFixFaac" - -void subghz_scene_set_fix_faac_byte_input_callback(void* context) { - SubGhz* subghz = context; - - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); -} - -void subghz_scene_set_fix_faac_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - ByteInput* byte_input = subghz->byte_input; - byte_input_set_header_text(byte_input, "Enter FIX in hex"); - byte_input_set_result_callback( - byte_input, - subghz_scene_set_fix_faac_byte_input_callback, - NULL, - subghz, - subghz->txrx->secure_data->fix, - 4); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); -} - -bool subghz_scene_set_fix_faac_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventByteInputDone) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetCntFaac); - consumed = true; - } - } - return consumed; -} - -void subghz_scene_set_fix_faac_on_exit(void* context) { - SubGhz* subghz = context; - - // Clear view - byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(subghz->byte_input, ""); -} diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed.c b/applications/main/subghz/scenes/subghz_scene_set_seed.c new file mode 100644 index 000000000..a4f19fa40 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_set_seed.c @@ -0,0 +1,170 @@ +#include "../subghz_i.h" +#include +#include +#include + +#define TAG "SubGhzSetSeed" + +void subghz_scene_set_seed_byte_input_callback(void* context) { + SubGhz* subghz = context; + + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); +} + +void subghz_scene_set_seed_on_enter(void* context) { + SubGhz* subghz = context; + + // Setup view + ByteInput* byte_input = subghz->byte_input; + byte_input_set_header_text(byte_input, "Enter SEED in hex"); + byte_input_set_result_callback( + byte_input, + subghz_scene_set_seed_byte_input_callback, + NULL, + subghz, + subghz->txrx->secure_data->seed, + 4); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); +} + +bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + bool consumed = false; + bool generated_protocol = false; + uint32_t fix_part, cnt, seed; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzCustomEventByteInputDone) { + SubGhzCustomEvent state = + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType); + + switch(state) { + case SubmenuIndexBFTClone: + fix_part = subghz->txrx->secure_data->fix[0] << 24 | + subghz->txrx->secure_data->fix[1] << 16 | + subghz->txrx->secure_data->fix[2] << 8 | + subghz->txrx->secure_data->fix[3]; + + cnt = subghz->txrx->secure_data->cnt[0] << 8 | subghz->txrx->secure_data->cnt[1]; + + seed = subghz->txrx->secure_data->seed[0] << 24 | + subghz->txrx->secure_data->seed[1] << 16 | + subghz->txrx->secure_data->seed[2] << 8 | + subghz->txrx->secure_data->seed[3]; + + subghz->txrx->transmitter = + subghz_transmitter_alloc_init(subghz->txrx->environment, "KeeLoq"); + if(subghz->txrx->transmitter) { + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + subghz_protocol_keeloq_bft_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + fix_part & 0x0FFFFFFF, + fix_part >> 28, + cnt, + seed, + "BFT", + subghz->txrx->preset); + + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF; + } + + flipper_format_write_hex( + subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t)); + + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "BFT"); + + generated_protocol = true; + } + + subghz_transmitter_free(subghz->txrx->transmitter); + + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + consumed = true; + break; + case SubmenuIndexFaacSLH_433: + case SubmenuIndexFaacSLH_868: + fix_part = subghz->txrx->secure_data->fix[0] << 24 | + subghz->txrx->secure_data->fix[1] << 16 | + subghz->txrx->secure_data->fix[2] << 8 | + subghz->txrx->secure_data->fix[3]; + + cnt = subghz->txrx->secure_data->cnt[0] << 16 | + subghz->txrx->secure_data->cnt[1] << 8 | subghz->txrx->secure_data->cnt[2]; + + seed = subghz->txrx->secure_data->seed[0] << 24 | + subghz->txrx->secure_data->seed[1] << 16 | + subghz->txrx->secure_data->seed[2] << 8 | + subghz->txrx->secure_data->seed[3]; + + subghz->txrx->transmitter = + subghz_transmitter_alloc_init(subghz->txrx->environment, "Faac SLH"); + if(subghz->txrx->transmitter) { + SubGhzCustomEvent state = + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType); + + if(state == SubmenuIndexFaacSLH_433) { + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + } else if(state == SubmenuIndexFaacSLH_868) { + subghz_preset_init(subghz, "AM650", 868350000, NULL, 0); + } + subghz_protocol_faac_slh_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + fix_part >> 4, + fix_part & 0xf, + (cnt & 0xFFFFF), + seed, + "FAAC_SLH", + subghz->txrx->preset); + // RogueMaster dont steal! + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF; + } + + flipper_format_write_hex( + subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t)); + + generated_protocol = true; + } + + subghz_transmitter_free(subghz->txrx->transmitter); + + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + consumed = true; + break; + + default: + break; + } + } + + if(generated_protocol) { + subghz_file_name_clear(subghz); + DOLPHIN_DEED(DolphinDeedSubGhzAddManually); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneSetType, SubGhzCustomEventManagerSet); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + return true; + } + } + return consumed; +} + +void subghz_scene_set_seed_on_exit(void* context) { + SubGhz* subghz = context; + + // Clear view + byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(subghz->byte_input, ""); +} diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c b/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c deleted file mode 100644 index 2c48462ef..000000000 --- a/applications/main/subghz/scenes/subghz_scene_set_seed_bft.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "../subghz_i.h" -#include -#include - -#define TAG "SubGhzSetSeedBft" - -void subghz_scene_set_seed_bft_byte_input_callback(void* context) { - SubGhz* subghz = context; - - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); -} - -void subghz_scene_set_seed_bft_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - // RogueMaster don't steal!!! - ByteInput* byte_input = subghz->byte_input; - byte_input_set_header_text(byte_input, "Enter SEED in hex"); - byte_input_set_result_callback( - byte_input, - subghz_scene_set_seed_bft_byte_input_callback, - NULL, - subghz, - subghz->txrx->secure_data->seed, - 4); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); -} - -bool subghz_scene_set_seed_bft_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - bool generated_protocol = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventByteInputDone) { - uint32_t fix_part = - subghz->txrx->secure_data->fix[0] << 24 | subghz->txrx->secure_data->fix[1] << 16 | - subghz->txrx->secure_data->fix[2] << 8 | subghz->txrx->secure_data->fix[3]; - - uint16_t cnt = subghz->txrx->secure_data->cnt[0] << 8 | - subghz->txrx->secure_data->cnt[1]; - - uint32_t seed = subghz->txrx->secure_data->seed[0] << 24 | - subghz->txrx->secure_data->seed[1] << 16 | - subghz->txrx->secure_data->seed[2] << 8 | - subghz->txrx->secure_data->seed[3]; - - subghz->txrx->transmitter = - subghz_transmitter_alloc_init(subghz->txrx->environment, "KeeLoq"); - if(subghz->txrx->transmitter) { - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - subghz_protocol_keeloq_bft_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - fix_part & 0x0FFFFFFF, - fix_part >> 28, - cnt, - seed, - "BFT", - subghz->txrx->preset); - - uint8_t seed_data[sizeof(uint32_t)] = {0}; - for(size_t i = 0; i < sizeof(uint32_t); i++) { - seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF; - } - - flipper_format_write_hex( - subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t)); - - flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "BFT"); - - generated_protocol = true; - } else { - generated_protocol = false; - } - - subghz_transmitter_free(subghz->txrx->transmitter); - - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); - } - consumed = true; - } - - if(generated_protocol) { - subghz_file_name_clear(subghz); - DOLPHIN_DEED(DolphinDeedSubGhzAddManually); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneSetType, SubGhzCustomEventManagerSet); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); - return true; - } - } - return consumed; -} - -void subghz_scene_set_seed_bft_on_exit(void* context) { - SubGhz* subghz = context; - - // Clear view - byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(subghz->byte_input, ""); -} diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c b/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c deleted file mode 100644 index 55387a0a5..000000000 --- a/applications/main/subghz/scenes/subghz_scene_set_seed_faac.c +++ /dev/null @@ -1,110 +0,0 @@ -#include "../subghz_i.h" -#include -#include - -#define TAG "SubGhzSetSeedFaac" - -void subghz_scene_set_seed_faac_byte_input_callback(void* context) { - SubGhz* subghz = context; - - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventByteInputDone); -} - -void subghz_scene_set_seed_faac_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - ByteInput* byte_input = subghz->byte_input; - byte_input_set_header_text(byte_input, "Enter SEED in hex"); - byte_input_set_result_callback( - byte_input, - subghz_scene_set_seed_faac_byte_input_callback, - NULL, - subghz, - subghz->txrx->secure_data->seed, - 4); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdByteInput); -} - -bool subghz_scene_set_seed_faac_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - bool generated_protocol = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventByteInputDone) { - uint32_t fix_part = - subghz->txrx->secure_data->fix[0] << 24 | subghz->txrx->secure_data->fix[1] << 16 | - subghz->txrx->secure_data->fix[2] << 8 | subghz->txrx->secure_data->fix[3]; - - uint32_t cnt = subghz->txrx->secure_data->cnt[0] << 16 | - subghz->txrx->secure_data->cnt[1] << 8 | - subghz->txrx->secure_data->cnt[2]; - - uint32_t seed = subghz->txrx->secure_data->seed[0] << 24 | - subghz->txrx->secure_data->seed[1] << 16 | - subghz->txrx->secure_data->seed[2] << 8 | - subghz->txrx->secure_data->seed[3]; - - subghz->txrx->transmitter = - subghz_transmitter_alloc_init(subghz->txrx->environment, "Faac SLH"); - if(subghz->txrx->transmitter) { - SubGhzCustomEvent state = - scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType); - - if(state == SubmenuIndexFaacSLH_433) { - subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); - } else if(state == SubmenuIndexFaacSLH_868) { - subghz_preset_init(subghz, "AM650", 868350000, NULL, 0); - } - subghz_protocol_faac_slh_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - fix_part >> 4, - fix_part & 0xf, - (cnt & 0xFFFFF), - seed, - "FAAC_SLH", - subghz->txrx->preset); - // RogueMaster dont steal! - uint8_t seed_data[sizeof(uint32_t)] = {0}; - for(size_t i = 0; i < sizeof(uint32_t); i++) { - seed_data[sizeof(uint32_t) - i - 1] = (seed >> i * 8) & 0xFF; - } - - flipper_format_write_hex( - subghz->txrx->fff_data, "Seed", seed_data, sizeof(uint32_t)); - - generated_protocol = true; - } else { - generated_protocol = false; - } - - subghz_transmitter_free(subghz->txrx->transmitter); - - if(!generated_protocol) { - furi_string_set( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); - } - consumed = true; - } - - if(generated_protocol) { - subghz_file_name_clear(subghz); - DOLPHIN_DEED(DolphinDeedSubGhzAddManually); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneSetType, SubGhzCustomEventManagerSet); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); - return true; - } - } - return consumed; -} - -void subghz_scene_set_seed_faac_on_exit(void* context) { - SubGhz* subghz = context; - - // Clear view - byte_input_set_result_callback(subghz->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(subghz->byte_input, ""); -} diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 431d5d2d7..e871a6439 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -389,13 +389,13 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { uint32_t key = subghz_random_serial(); switch(event.event) { case SubmenuIndexFaacSLH_868: - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixFaac); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFix); break; case SubmenuIndexFaacSLH_433: - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixFaac); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFix); break; case SubmenuIndexBFTClone: - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFixBft); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetFix); break; case SubmenuIndexPricenton: key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 From f2a7172a8486c9e089d3a373eec87d2d30218df1 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Fri, 21 Apr 2023 16:17:59 +0300 Subject: [PATCH 075/282] Drop excess DOLPHIN_DEED --- applications/main/subghz/scenes/subghz_scene_set_seed.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed.c b/applications/main/subghz/scenes/subghz_scene_set_seed.c index a4f19fa40..aa2a270f3 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_seed.c +++ b/applications/main/subghz/scenes/subghz_scene_set_seed.c @@ -1,5 +1,4 @@ #include "../subghz_i.h" -#include #include #include @@ -151,7 +150,6 @@ bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) { if(generated_protocol) { subghz_file_name_clear(subghz); - DOLPHIN_DEED(DolphinDeedSubGhzAddManually); scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneSetType, SubGhzCustomEventManagerSet); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); From cbbdf0f6def49ed1d44f25f2ea9545835e637d9f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 22 Apr 2023 04:25:05 +0300 Subject: [PATCH 076/282] Update pictures --- documentation/HowToInstall.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/documentation/HowToInstall.md b/documentation/HowToInstall.md index 29dcd3f38..2f27ad9cf 100644 --- a/documentation/HowToInstall.md +++ b/documentation/HowToInstall.md @@ -23,7 +23,7 @@ after that on web updater page - press `Connect` button - And wait, if all flashed successfully - you will have all needed assets pre installed - Done -![web_instr](https://user-images.githubusercontent.com/10697207/190942577-9d137e01-468b-4c74-8587-c2a17c3c7534.jpg) +![web](https://user-images.githubusercontent.com/10697207/233754320-1c952aa7-f73f-43a9-adf2-9eaf95e97249.jpg)
@@ -41,7 +41,8 @@ after that on web updater page - press `Connect` button - And if all flashed successfully - you will have all needed assets pre installed - Done -![ios](https://user-images.githubusercontent.com/10697207/192114863-75693972-31fb-4b5f-bcc4-4122abb352c2.jpg) +![ios](https://user-images.githubusercontent.com/10697207/233754364-a8474a48-2628-4be4-bb6e-a1c99fe99912.jpg) +

@@ -74,7 +75,8 @@ after that on web updater page - press `Connect` button - And if all flashed successfully - you will have all needed assets pre installed - Done -![andro_web](https://user-images.githubusercontent.com/10697207/197042413-cfc93f31-8b84-4cdb-967b-276f46224e50.jpg) +![androweb](https://user-images.githubusercontent.com/10697207/233754394-abbbd446-9b16-4a92-9a57-b9b9d0d1b45b.jpg) +

@@ -92,7 +94,8 @@ after that on web updater page - press `Connect` button - And wait, if all flashed successfully - you will have all needed assets pre installed - Done -![qflip](https://user-images.githubusercontent.com/10697207/192114874-4edae5f5-6bff-4674-8e3b-030ceaf17abc.png) +![qflip](https://user-images.githubusercontent.com/10697207/233754408-089f9f3e-9da7-4e40-840c-9b118361538e.jpg) +

@@ -111,7 +114,8 @@ after that on web updater page - press `Connect` button - Update will start, wait for all stages - Done -![manual](https://user-images.githubusercontent.com/10697207/192114890-b9220265-1fe3-4837-8e98-ed267282e11e.png) +![manual](https://user-images.githubusercontent.com/10697207/233754434-0d219490-634c-4dad-ad74-f0ae5f68ccb7.jpg) +

From eb3a4bfa6e4a00625028872e3c34007f708af993 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 22 Apr 2023 04:38:14 +0300 Subject: [PATCH 077/282] Fix add manually naming --- applications/main/subghz/scenes/subghz_scene_save_name.c | 4 +++- applications/main/subghz/scenes/subghz_scene_set_type.c | 2 ++ applications/main/subghz/subghz.c | 1 + applications/main/subghz/subghz_i.h | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 3f7658201..9c58c61ad 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -62,7 +62,7 @@ void subghz_scene_save_name_on_enter(void* context) { if(subghz->txrx->decoder_result != 0x0) { if(subghz->txrx->decoder_result != NULL) { if(strlen(subghz->txrx->decoder_result->protocol->name) != 0) { - if(strcmp(subghz->txrx->decoder_result->protocol->name, "BinRAW") == 0) { + if(subghz->from_add_manually) { subghz_scene_save_name_get_timefilename(file_name, "S", true); } else { subghz_scene_save_name_get_timefilename( @@ -198,6 +198,8 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { void subghz_scene_save_name_on_exit(void* context) { SubGhz* subghz = context; + subghz->from_add_manually = false; + // Clear validator void* validator_context = text_input_get_validator_callback_context(subghz->text_input); text_input_set_validator(subghz->text_input, NULL, NULL); diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index e871a6439..95cf96e9c 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -105,6 +105,8 @@ void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) { void subghz_scene_set_type_on_enter(void* context) { SubGhz* subghz = context; + subghz->from_add_manually = true; + submenu_add_item( subghz->submenu, "Faac SLH 868MHz", diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 75c6e627a..3bd3ef2cc 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -72,6 +72,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->in_decoder_scene = false; subghz->in_decoder_scene_skip = false; + subghz->from_add_manually = false; // View Dispatcher subghz->view_dispatcher = view_dispatcher_alloc(); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index faae35fa2..eeab89791 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -127,6 +127,7 @@ struct SubGhz { bool in_decoder_scene; bool in_decoder_scene_skip; + bool from_add_manually; SubGhzDecodeRawState decode_raw_state; SubGhzFileEncoderWorker* decode_raw_file_worker_encoder; From c07c06aba8851b6d0a2c988c7e16c1ad9758969a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 22 Apr 2023 09:06:33 +0100 Subject: [PATCH 078/282] Workflow updates --- .github/workflow_data/discord.py | 45 +++++++++++++++++++++++++++++--- .github/workflows/discord.yml | 4 +-- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/.github/workflow_data/discord.py b/.github/workflow_data/discord.py index 17659f8f7..2b9e29da9 100644 --- a/.github/workflow_data/discord.py +++ b/.github/workflow_data/discord.py @@ -9,7 +9,7 @@ if __name__ == "__main__": with open(os.environ["GITHUB_EVENT_PATH"], "r") as f: event = json.load(f) - webhook = "DEV_DISCORD_WEBHOOK" + webhook = "DEV_WEBHOOK" title = desc = url = "" color = 0 fields = [] @@ -45,7 +45,7 @@ if __name__ == "__main__": title = f"Pull Request {event['action'].title()} ({branch}): {name}" match event["action"]: case "opened": - desc = (event["body"][:2045] + "...") if len(event["body"]) > 2048 else event["body"] + desc = (pr["body"][:2045] + "...") if len(pr["body"]) > 2048 else pr["body"] color = 3669797 fields.append( @@ -80,7 +80,7 @@ if __name__ == "__main__": case "release": match event["action"]: case "published": - webhook = "DEV_DISCORD_WEBHOOK" + webhook = "RELEASE_WEBHOOK" color = 13845998 title = f"New Release published: {event['name']}" desc += f"Changelog:" @@ -123,6 +123,45 @@ if __name__ == "__main__": case _: sys.exit(1) + case "workflow_run": + run = event["workflow_run"] + url = run["html_url"] + title = "Workflow " + match run["conclusion"]: + case "action_required": + title += "Requires Attention" + color = 16751872 + case "failure": + title += "Failed" + color = 16723712 + case _: + sys.exit(0) + title += f": {run['name']}" + + case "issues": + issue = event["issue"] + url = issue["html_url"] + name = issue["title"][:50] + ("..." if len(issue["title"]) > 50 else "") + title = f"Issue {event['action'].title()}: {name}" + match event["action"]: + case "opened": + desc = (issue["body"][:2045] + "...") if len(issue["body"]) > 2048 else issue["body"] + color = 3669797 + case "closed": + color = 16723712 + case "reopened": + color = 16751872 + case _: + sys.exit(1) + + case "issue_comment": + comment = event["comment"] + issue = event["issue"] + url = comment["html_url"] + title = f"New Comment on Issue: {issue['title']}" + color = 3669797 + desc = (comment["body"][:2045] + "...") if len(comment["body"]) > 2048 else comment["body"] + case _: sys.exit(1) diff --git a/.github/workflows/discord.yml b/.github/workflows/discord.yml index 8ff4775ac..840f01d67 100644 --- a/.github/workflows/discord.yml +++ b/.github/workflows/discord.yml @@ -10,7 +10,7 @@ on: release: types: - "published" - check_suite: + workflow_run: types: - "completed" issues: @@ -32,5 +32,5 @@ jobs: - name: Send webhook env: - DEV_DISCORD_WEBHOOK: "https://discord.com/api/webhooks/${{ secrets.DEV_WEBHOOK_ID }}/${{ secrets.DEV_WEBHOOK_TOKEN }}" + DEV_WEBHOOK: "https://discord.com/api/webhooks/${{ secrets.DEV_WEBHOOK_ID }}/${{ secrets.DEV_WEBHOOK_TOKEN }}" run: python .github/workflow_data/discord.py From 4a6d5c4f9d497e50394c2880c34e516c95dfae5f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 22 Apr 2023 09:25:53 +0100 Subject: [PATCH 079/282] Cleanup workflows --- .github/workflow_data/{discord.py => webhook.py} | 0 .github/workflows/build.yml | 4 ++-- .github/workflows/hotfix.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/release.yml | 2 +- .../workflows/{sonarqube.yaml => sonarcloud.yaml} | 4 ++-- .../{check_submodules.yml => submodules.yml} | 4 ++-- .github/workflows/{discord.yml => webhook.yml} | 12 ++++++++++-- 8 files changed, 19 insertions(+), 11 deletions(-) rename .github/workflow_data/{discord.py => webhook.py} (100%) rename .github/workflows/{sonarqube.yaml => sonarcloud.yaml} (98%) rename .github/workflows/{check_submodules.yml => submodules.yml} (96%) rename .github/workflows/{discord.yml => webhook.yml} (73%) diff --git a/.github/workflow_data/discord.py b/.github/workflow_data/webhook.py similarity index 100% rename from .github/workflow_data/discord.py rename to .github/workflow_data/webhook.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 715070b83..37ec991e6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: 'Build artifact' +name: 'Build' on: push: @@ -41,7 +41,7 @@ jobs: with: name: updater path: | - dist/${{ env.DEFAULT_TARGET }}-* + dist/${{ env.DEFAULT_TARGET }}-*/${{ env.DEFAULT_TARGET }}-update-*/ # - name: 'Find Previous Comment' # if: ${{ github.event.pull_request }} diff --git a/.github/workflows/hotfix.yml b/.github/workflows/hotfix.yml index 82fb6fa30..505f9c676 100644 --- a/.github/workflows/hotfix.yml +++ b/.github/workflows/hotfix.yml @@ -1,4 +1,4 @@ -name: "Hotfix integration" +name: "Hotfix" on: pull_request_review: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 818aa7d84..da9ca2a87 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,4 +1,4 @@ -name: 'Lint formatting' +name: 'Lint' on: push: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1c4c39f8f..61bc01fcf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: "Release integration" +name: 'Release' on: pull_request_review: diff --git a/.github/workflows/sonarqube.yaml b/.github/workflows/sonarcloud.yaml similarity index 98% rename from .github/workflows/sonarqube.yaml rename to .github/workflows/sonarcloud.yaml index 7f3d311b0..e763aaad7 100644 --- a/.github/workflows/sonarqube.yaml +++ b/.github/workflows/sonarcloud.yaml @@ -1,4 +1,4 @@ -name: 'SonarCloud analysis' +name: 'SonarCloud' on: workflow_dispatch: @@ -10,7 +10,7 @@ env: DEFAULT_TARGET: f7 jobs: - analyze: + sonarcloud: runs-on: ubuntu-latest env: SONAR_SCANNER_VERSION: 4.7.0.2747 diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/submodules.yml similarity index 96% rename from .github/workflows/check_submodules.yml rename to .github/workflows/submodules.yml index ad63f711b..52342fd04 100644 --- a/.github/workflows/check_submodules.yml +++ b/.github/workflows/submodules.yml @@ -1,4 +1,4 @@ -name: 'Check submodules' +name: 'Submodules' on: push: @@ -10,7 +10,7 @@ on: pull_request: jobs: - check_protobuf: + submodules: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/discord.yml b/.github/workflows/webhook.yml similarity index 73% rename from .github/workflows/discord.yml rename to .github/workflows/webhook.yml index 840f01d67..c3f4d9432 100644 --- a/.github/workflows/discord.yml +++ b/.github/workflows/webhook.yml @@ -1,4 +1,4 @@ -name: Discord webhook +name: 'Webhook' on: push: @@ -11,6 +11,14 @@ on: types: - "published" workflow_run: + workflows: + - "Build" + - "Hotfix" + - "Lint" + - "Release" + - "SonarCloud" + - "Submodules" + - "Webhook" types: - "completed" issues: @@ -33,4 +41,4 @@ jobs: - name: Send webhook env: DEV_WEBHOOK: "https://discord.com/api/webhooks/${{ secrets.DEV_WEBHOOK_ID }}/${{ secrets.DEV_WEBHOOK_TOKEN }}" - run: python .github/workflow_data/discord.py + run: python .github/workflow_data/webhook.py From 852b051e2685de8478db1092820d70cede6e039d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 22 Apr 2023 09:26:37 +0100 Subject: [PATCH 080/282] Fix webhook workflow --- .github/workflows/webhook.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/webhook.yml b/.github/workflows/webhook.yml index c3f4d9432..a4e380467 100644 --- a/.github/workflows/webhook.yml +++ b/.github/workflows/webhook.yml @@ -18,7 +18,6 @@ on: - "Release" - "SonarCloud" - "Submodules" - - "Webhook" types: - "completed" issues: From 3a2240f2e17076e33b4c63f23b959c49a895c670 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 22 Apr 2023 14:54:20 +0300 Subject: [PATCH 081/282] Merge pull request #436 from krolchonok/HowToInstall45 Update HowToInstall.md --- documentation/HowToInstall.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/documentation/HowToInstall.md b/documentation/HowToInstall.md index 2f27ad9cf..a650d97ee 100644 --- a/documentation/HowToInstall.md +++ b/documentation/HowToInstall.md @@ -20,10 +20,10 @@ after that on web updater page - press `Connect` button - Press `Install` button -- And wait, if all flashed successfully - you will have all needed assets pre installed +- And wait, if all flashed + successfully - you will have all needed assets pre installed - Done - -![web](https://user-images.githubusercontent.com/10697207/233754320-1c952aa7-f73f-43a9-adf2-9eaf95e97249.jpg) +![web](https://user-images.githubusercontent.com/40743392/233779040-596ad9a9-87cd-4b19-a831-16fdc678ba35.png)
@@ -40,9 +40,7 @@ after that on web updater page - press `Connect` button - Error in ios app will show up, but flipper will be updated successfully - And if all flashed successfully - you will have all needed assets pre installed - Done - -![ios](https://user-images.githubusercontent.com/10697207/233754364-a8474a48-2628-4be4-bb6e-a1c99fe99912.jpg) - +![ios](https://user-images.githubusercontent.com/40743392/233779071-10e37af5-a7d9-41d6-81cd-9bb660352b4d.png)

@@ -58,8 +56,7 @@ after that on web updater page - press `Connect` button - Wait until update is finished - And if all flashed successfully - you will have all needed assets pre installed - Done - -![andro_tgz](https://user-images.githubusercontent.com/10697207/197042029-a5824787-08bc-4fd8-93ee-b7faff082c54.jpg) +![andro_tgz](https://user-images.githubusercontent.com/40743392/233782806-e81c9634-4694-4faf-88ce-08b89a8b6fa0.png)

@@ -74,8 +71,7 @@ after that on web updater page - press `Connect` button - Wait until update is finished - And if all flashed successfully - you will have all needed assets pre installed - Done - -![androweb](https://user-images.githubusercontent.com/10697207/233754394-abbbd446-9b16-4a92-9a57-b9b9d0d1b45b.jpg) +![androweb](https://user-images.githubusercontent.com/40743392/233782906-1f8f1ebf-c488-4d9f-9a6f-67a4e693cbda.png)
@@ -94,7 +90,8 @@ after that on web updater page - press `Connect` button - And wait, if all flashed successfully - you will have all needed assets pre installed - Done -![qflip](https://user-images.githubusercontent.com/10697207/233754408-089f9f3e-9da7-4e40-840c-9b118361538e.jpg) +![qflip](https://user-images.githubusercontent.com/40743392/233779331-3f21c662-6e77-42e5-a928-f5441bd85bd4.png) +
@@ -114,7 +111,8 @@ after that on web updater page - press `Connect` button - Update will start, wait for all stages - Done -![manual](https://user-images.githubusercontent.com/10697207/233754434-0d219490-634c-4dad-ad74-f0ae5f68ccb7.jpg) +![manual](https://user-images.githubusercontent.com/40743392/233779363-faf5b35b-e136-4dca-b1fb-15188b26eb6a.png) +
From c1292d08de390a706f3574869403d5f4a9e34efb Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 22 Apr 2023 17:28:55 +0300 Subject: [PATCH 082/282] Apply RFAL changes from OFW PR 2549 --- lib/ST25RFAL002/platform.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c index 754e25650..5fe65ef88 100644 --- a/lib/ST25RFAL002/platform.c +++ b/lib/ST25RFAL002/platform.c @@ -45,16 +45,19 @@ void platformDisableIrqCallback() { void platformSetIrqCallback(PlatformIrqCallback callback) { rfal_platform.callback = callback; - rfal_platform.thread = - furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); - furi_thread_mark_as_service(rfal_platform.thread); - furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); - furi_thread_start(rfal_platform.thread); - furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); - // Disable interrupt callback as the pin is shared between 2 apps - // It is enabled in rfalLowPowerModeStop() - furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); + if(!rfal_platform.thread) { + rfal_platform.thread = + furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); + furi_thread_mark_as_service(rfal_platform.thread); + furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); + furi_thread_start(rfal_platform.thread); + + furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); + // Disable interrupt callback as the pin is shared between 2 apps + // It is enabled in rfalLowPowerModeStop() + furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); + } } bool platformSpiTxRx(const uint8_t* txBuf, uint8_t* rxBuf, uint16_t len) { From b46d9e09eb70ac456e7b455dc45b469a17622363 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 22 Apr 2023 17:42:41 +0300 Subject: [PATCH 083/282] Revert "Fix add manually naming" This reverts commit eb3a4bfa6e4a00625028872e3c34007f708af993. --- applications/main/subghz/scenes/subghz_scene_save_name.c | 4 +--- applications/main/subghz/scenes/subghz_scene_set_type.c | 2 -- applications/main/subghz/subghz.c | 1 - applications/main/subghz/subghz_i.h | 1 - 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 9c58c61ad..3f7658201 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -62,7 +62,7 @@ void subghz_scene_save_name_on_enter(void* context) { if(subghz->txrx->decoder_result != 0x0) { if(subghz->txrx->decoder_result != NULL) { if(strlen(subghz->txrx->decoder_result->protocol->name) != 0) { - if(subghz->from_add_manually) { + if(strcmp(subghz->txrx->decoder_result->protocol->name, "BinRAW") == 0) { subghz_scene_save_name_get_timefilename(file_name, "S", true); } else { subghz_scene_save_name_get_timefilename( @@ -198,8 +198,6 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { void subghz_scene_save_name_on_exit(void* context) { SubGhz* subghz = context; - subghz->from_add_manually = false; - // Clear validator void* validator_context = text_input_get_validator_callback_context(subghz->text_input); text_input_set_validator(subghz->text_input, NULL, NULL); diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 95cf96e9c..e871a6439 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -105,8 +105,6 @@ void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) { void subghz_scene_set_type_on_enter(void* context) { SubGhz* subghz = context; - subghz->from_add_manually = true; - submenu_add_item( subghz->submenu, "Faac SLH 868MHz", diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 3bd3ef2cc..75c6e627a 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -72,7 +72,6 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->in_decoder_scene = false; subghz->in_decoder_scene_skip = false; - subghz->from_add_manually = false; // View Dispatcher subghz->view_dispatcher = view_dispatcher_alloc(); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index eeab89791..faae35fa2 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -127,7 +127,6 @@ struct SubGhz { bool in_decoder_scene; bool in_decoder_scene_skip; - bool from_add_manually; SubGhzDecodeRawState decode_raw_state; SubGhzFileEncoderWorker* decode_raw_file_worker_encoder; From 47ece055925c1dde9e236ae385b8f6f74698c8fe Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 22 Apr 2023 19:47:33 +0300 Subject: [PATCH 084/282] using scene_manager`s function --- applications/main/subghz/scenes/subghz_scene_save_name.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 3f7658201..d3f5474be 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -62,7 +62,8 @@ void subghz_scene_save_name_on_enter(void* context) { if(subghz->txrx->decoder_result != 0x0) { if(subghz->txrx->decoder_result != NULL) { if(strlen(subghz->txrx->decoder_result->protocol->name) != 0) { - if(strcmp(subghz->txrx->decoder_result->protocol->name, "BinRAW") == 0) { + if(scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneSetType)) { subghz_scene_save_name_get_timefilename(file_name, "S", true); } else { subghz_scene_save_name_get_timefilename( From 8a785993ed9729dac65f77539d3e0e839f02bff7 Mon Sep 17 00:00:00 2001 From: SkorP Date: Sat, 22 Apr 2023 21:03:44 +0400 Subject: [PATCH 085/282] WS: add protocol "Wendox" --- .../protocols/protocol_items.c | 1 + .../protocols/protocol_items.h | 1 + .../weather_station/protocols/wendox.c | 248 ++++++++++++++++++ .../weather_station/protocols/wendox.h | 80 ++++++ 4 files changed, 330 insertions(+) create mode 100644 applications/external/weather_station/protocols/wendox.c create mode 100644 applications/external/weather_station/protocols/wendox.h diff --git a/applications/external/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c index 2c9d751c7..098014703 100644 --- a/applications/external/weather_station/protocols/protocol_items.c +++ b/applications/external/weather_station/protocols/protocol_items.c @@ -16,6 +16,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_auriol_th, &ws_protocol_oregon_v1, &ws_protocol_tx_8300, + &ws_protocol_wendox, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/external/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h index f9e443abc..2268150f2 100644 --- a/applications/external/weather_station/protocols/protocol_items.h +++ b/applications/external/weather_station/protocols/protocol_items.h @@ -16,5 +16,6 @@ #include "auriol_hg0601a.h" #include "oregon_v1.h" #include "tx_8300.h" +#include "wendox.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/applications/external/weather_station/protocols/wendox.c b/applications/external/weather_station/protocols/wendox.c new file mode 100644 index 000000000..fec1a8bf1 --- /dev/null +++ b/applications/external/weather_station/protocols/wendox.c @@ -0,0 +1,248 @@ +#include "wendox.h" + +#define TAG "WSProtocolWendox" + +static const SubGhzBlockConst ws_protocol_wendox_const = { + .te_short = 1955, + .te_long = 5865, + .te_delta = 300, + .min_count_bit_for_found = 28, +}; + +struct WSProtocolDecoderWendox { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderWendox { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + WendoxDecoderStepReset = 0, + WendoxDecoderStepCheckPreambule, + WendoxDecoderStepSaveDuration, + WendoxDecoderStepCheckDuration, +} WendoxDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_wendox_decoder = { + .alloc = ws_protocol_decoder_wendox_alloc, + .free = ws_protocol_decoder_wendox_free, + + .feed = ws_protocol_decoder_wendox_feed, + .reset = ws_protocol_decoder_wendox_reset, + + .get_hash_data = ws_protocol_decoder_wendox_get_hash_data, + .serialize = ws_protocol_decoder_wendox_serialize, + .deserialize = ws_protocol_decoder_wendox_deserialize, + .get_string = ws_protocol_decoder_wendox_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_wendox_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_wendox = { + .name = WS_PROTOCOL_WENDOX_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_wendox_decoder, + .encoder = &ws_protocol_wendox_encoder, +}; + +void* ws_protocol_decoder_wendox_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderWendox* instance = malloc(sizeof(WSProtocolDecoderWendox)); + instance->base.protocol = &ws_protocol_wendox; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_wendox_free(void* context) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + free(instance); +} + +void ws_protocol_decoder_wendox_reset(void* context) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + instance->decoder.parser_step = WendoxDecoderStepReset; +} + +// static bool ws_protocol_wendox_check(WSProtocolDecoderWendox* instance) { +// if(!instance->decoder.decode_data) return false; +// uint8_t msg[] = { +// instance->decoder.decode_data >> 24, +// instance->decoder.decode_data >> 16, +// instance->decoder.decode_data >> 8}; + +// uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 3, 0x98, 0xF1); +// return (crc == (instance->decoder.decode_data & 0xFF)); +// } + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_wendox_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 4) & 0xFF; + instance->battery_low = WS_NO_BATT; + instance->channel = WS_NO_CHANNEL; + + if(((instance->data >> 22) & 1)) { + instance->temp = (float)(((instance->data >> 13) & 0x1FF) + 12) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 13) & 0x1FF) + 1 - 12) / -10.0f; + } + FURI_LOG_E("TAG", "%llX %f", instance->data, (double)instance->temp); + + instance->btn = WS_NO_BTN; + instance->humidity = WS_NO_HUMIDITY; +} + +void ws_protocol_decoder_wendox_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + + switch(instance->decoder.parser_step) { + case WendoxDecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < + ws_protocol_wendox_const.te_delta)) { + instance->decoder.parser_step = WendoxDecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case WendoxDecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_short) < + ws_protocol_wendox_const.te_delta * 1) && + (DURATION_DIFF(duration, ws_protocol_wendox_const.te_long) < + ws_protocol_wendox_const.te_delta * 2)) { + instance->header_count++; + } else if((instance->header_count > 4) && (instance->header_count < 12)) { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_long) < + ws_protocol_wendox_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < + ws_protocol_wendox_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxDecoderStepReset; + } + + } else { + instance->decoder.parser_step = WendoxDecoderStepReset; + } + } + break; + + case WendoxDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = WendoxDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = WendoxDecoderStepReset; + } + break; + + case WendoxDecoderStepCheckDuration: + if(!level) { + if(duration > ws_protocol_wendox_const.te_short + ws_protocol_wendox_const.te_long) { + if(instance->decoder.decode_count_bit == + ws_protocol_wendox_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_wendox_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + + instance->decoder.parser_step = WendoxDecoderStepReset; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_short) < + ws_protocol_wendox_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_wendox_const.te_long) < + ws_protocol_wendox_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = WendoxDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_long) < + ws_protocol_wendox_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < + ws_protocol_wendox_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxDecoderStepReset; + } + } else { + instance->decoder.parser_step = WendoxDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_wendox_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_wendox_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + ws_protocol_decoder_wendox_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_wendox_const.min_count_bit_for_found); +} + +void ws_protocol_decoder_wendox_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderWendox* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/external/weather_station/protocols/wendox.h b/applications/external/weather_station/protocols/wendox.h new file mode 100644 index 000000000..698ac0bc0 --- /dev/null +++ b/applications/external/weather_station/protocols/wendox.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_WENDOX_NAME "Wendox" + +typedef struct WSProtocolDecoderWendox WSProtocolDecoderWendox; +typedef struct WSProtocolEncoderWendox WSProtocolEncoderWendox; + +extern const SubGhzProtocolDecoder ws_protocol_wendox_decoder; +extern const SubGhzProtocolEncoder ws_protocol_wendox_encoder; +extern const SubGhzProtocol ws_protocol_wendox; + +/** + * Allocate WSProtocolDecoderWendox. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderWendox* pointer to a WSProtocolDecoderWendox instance + */ +void* ws_protocol_decoder_wendox_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderWendox. + * @param context Pointer to a WSProtocolDecoderWendox instance + */ +void ws_protocol_decoder_wendox_free(void* context); + +/** + * Reset decoder WSProtocolDecoderWendox. + * @param context Pointer to a WSProtocolDecoderWendox instance + */ +void ws_protocol_decoder_wendox_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderWendox instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_wendox_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderWendox instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_wendox_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderWendox. + * @param context Pointer to a WSProtocolDecoderWendox instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus ws_protocol_decoder_wendox_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderWendox. + * @param context Pointer to a WSProtocolDecoderWendox instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + ws_protocol_decoder_wendox_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderWendox instance + * @param output Resulting text + */ +void ws_protocol_decoder_wendox_get_string(void* context, FuriString* output); From 4671f8ca64414de530e1719cf3f60814f7f3f876 Mon Sep 17 00:00:00 2001 From: hedger Date: Sat, 22 Apr 2023 21:32:41 +0400 Subject: [PATCH 086/282] cubewb: downgraded to v1.15.0 --- fbt_options.py | 2 +- firmware/targets/f7/api_symbols.csv | 4 ++-- firmware/targets/f7/ble_glue/ble_app.c | 4 ++-- lib/STM32CubeWB | 2 +- scripts/fbt_tools/fbt_dist.py | 4 +++- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/fbt_options.py b/fbt_options.py index 25e84afde..4fd7ef496 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -20,7 +20,7 @@ DIST_SUFFIX = "local" COPRO_OB_DATA = "scripts/ob.data" # Must match lib/STM32CubeWB version -COPRO_CUBE_VERSION = "1.16.0" +COPRO_CUBE_VERSION = "1.15.0" COPRO_CUBE_DIR = "lib/STM32CubeWB" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a2d70e7f8..d0c6b36ad 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -231,8 +231,8 @@ Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef* Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* -Function,-,LL_I2C_DeInit,ErrorStatus,const I2C_TypeDef* -Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, const LL_I2C_InitTypeDef*" +Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef* +Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*" Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* Function,-,LL_Init1msTick,void,uint32_t Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index cc6065b97..a325830cf 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; _Static_assert( - sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 58, - "Ble stack config structure size mismatch (check new config options - last updated for v.1.16.0)"); + sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 57, + "Ble stack config structure size mismatch (check new config options - last updated for v.1.15.0)"); typedef struct { FuriMutex* hci_mtx; diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB index 06b8133fa..c4cec8ae5 160000 --- a/lib/STM32CubeWB +++ b/lib/STM32CubeWB @@ -1 +1 @@ -Subproject commit 06b8133fa295474507b55b1a5695d4b8bf804ed6 +Subproject commit c4cec8ae57a79e949a184cd0b4117a008a0a25a7 diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index f0b443486..d2808419c 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -112,6 +112,8 @@ def DistCommand(env, name, source, **kw): def generate(env): + if not env["VERBOSE"]: + env.SetDefault(COPROCOMSTR="\tCOPRO\t${TARGET}") env.AddMethod(AddFwProject) env.AddMethod(DistCommand) env.AddMethod(AddOpenOCDFlashTarget) @@ -147,7 +149,7 @@ def generate(env): '--stack_file="${COPRO_STACK_BIN}" ' "--stack_addr=${COPRO_STACK_ADDR} ", ], - "\tCOPRO\t${TARGET}", + "$COPROCOMSTR", ) ), } From ad0db706c04dfa1baf2779e5b52bec5a6aff2de8 Mon Sep 17 00:00:00 2001 From: hedger Date: Sat, 22 Apr 2023 21:38:40 +0400 Subject: [PATCH 087/282] hal: updated f18 symbols to match LL --- firmware/targets/f18/api_symbols.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 4ee3d8636..eb2d6f43f 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -199,8 +199,8 @@ Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef* Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* -Function,-,LL_I2C_DeInit,ErrorStatus,const I2C_TypeDef* -Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, const LL_I2C_InitTypeDef*" +Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef* +Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*" Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* Function,-,LL_Init1msTick,void,uint32_t Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* From 3561f79cf4ba474cb835ea12ea458f8e6d51786d Mon Sep 17 00:00:00 2001 From: hedger Date: Sat, 22 Apr 2023 21:32:41 +0400 Subject: [PATCH 088/282] cubewb: downgraded to v1.15.0 --- fbt_options.py | 2 +- firmware/targets/f7/api_symbols.csv | 4 ++-- firmware/targets/f7/ble_glue/ble_app.c | 4 ++-- lib/STM32CubeWB | 2 +- scripts/fbt_tools/fbt_dist.py | 4 +++- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/fbt_options.py b/fbt_options.py index 70de4f9a4..02379fc4e 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -20,7 +20,7 @@ DIST_SUFFIX = "local" COPRO_OB_DATA = "scripts/ob.data" # Must match lib/STM32CubeWB version -COPRO_CUBE_VERSION = "1.16.0" +COPRO_CUBE_VERSION = "1.15.0" COPRO_CUBE_DIR = "lib/STM32CubeWB" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 5bc0106b6..d9cc6d21f 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -236,8 +236,8 @@ Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef* Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* -Function,-,LL_I2C_DeInit,ErrorStatus,const I2C_TypeDef* -Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, const LL_I2C_InitTypeDef*" +Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef* +Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*" Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* Function,-,LL_Init1msTick,void,uint32_t Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index cc6065b97..a325830cf 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; _Static_assert( - sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 58, - "Ble stack config structure size mismatch (check new config options - last updated for v.1.16.0)"); + sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 57, + "Ble stack config structure size mismatch (check new config options - last updated for v.1.15.0)"); typedef struct { FuriMutex* hci_mtx; diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB index 06b8133fa..c4cec8ae5 160000 --- a/lib/STM32CubeWB +++ b/lib/STM32CubeWB @@ -1 +1 @@ -Subproject commit 06b8133fa295474507b55b1a5695d4b8bf804ed6 +Subproject commit c4cec8ae57a79e949a184cd0b4117a008a0a25a7 diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index c0eca0d2a..a5669c488 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -112,6 +112,8 @@ def DistCommand(env, name, source, **kw): def generate(env): + if not env["VERBOSE"]: + env.SetDefault(COPROCOMSTR="\tCOPRO\t${TARGET}") env.AddMethod(AddFwProject) env.AddMethod(DistCommand) env.AddMethod(AddOpenOCDFlashTarget) @@ -147,7 +149,7 @@ def generate(env): '--stack_file="${COPRO_STACK_BIN}" ' "--stack_addr=${COPRO_STACK_ADDR} ", ], - "\tCOPRO\t${TARGET}", + "$COPROCOMSTR", ) ), } From b0fa913fa2c7a046c4957793bd467ed270bf0b83 Mon Sep 17 00:00:00 2001 From: hedger Date: Sat, 22 Apr 2023 21:49:00 +0400 Subject: [PATCH 089/282] hal: flash: use furi_hal_cortex_timer for timeouts --- firmware/targets/f7/furi_hal/furi_hal_flash.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c index d2dbff55f..a880a3e49 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -231,17 +232,14 @@ static void furi_hal_flush_cache(void) { bool furi_hal_flash_wait_last_operation(uint32_t timeout) { uint32_t error = 0; - uint32_t countdown = 0; /* Wait for the FLASH operation to complete by polling on BUSY flag to be reset. Even if the FLASH operation fails, the BUSY flag will be reset and an error flag will be set */ - countdown = timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); + while(READ_BIT(FLASH->SR, FLASH_SR_BSY)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } @@ -264,12 +262,10 @@ bool furi_hal_flash_wait_last_operation(uint32_t timeout) { CLEAR_BIT(FLASH->SR, error); /* Wait for control register to be written */ - countdown = timeout; + timer = furi_hal_cortex_timer_get(timeout * 1000); + while(READ_BIT(FLASH->SR, FLASH_SR_CFGBSY)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } From d537e21c658999be34ae9237510bcdd5beed5ddf Mon Sep 17 00:00:00 2001 From: hedger Date: Sat, 22 Apr 2023 21:49:00 +0400 Subject: [PATCH 090/282] hal: flash: use furi_hal_cortex_timer for timeouts --- firmware/targets/f7/furi_hal/furi_hal_flash.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c index d2dbff55f..a880a3e49 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -231,17 +232,14 @@ static void furi_hal_flush_cache(void) { bool furi_hal_flash_wait_last_operation(uint32_t timeout) { uint32_t error = 0; - uint32_t countdown = 0; /* Wait for the FLASH operation to complete by polling on BUSY flag to be reset. Even if the FLASH operation fails, the BUSY flag will be reset and an error flag will be set */ - countdown = timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); + while(READ_BIT(FLASH->SR, FLASH_SR_BSY)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } @@ -264,12 +262,10 @@ bool furi_hal_flash_wait_last_operation(uint32_t timeout) { CLEAR_BIT(FLASH->SR, error); /* Wait for control register to be written */ - countdown = timeout; + timer = furi_hal_cortex_timer_get(timeout * 1000); + while(READ_BIT(FLASH->SR, FLASH_SR_CFGBSY)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } From 7aa671689209e434daac403e6d8528f59f9466ac Mon Sep 17 00:00:00 2001 From: SkorP Date: Sat, 22 Apr 2023 21:57:31 +0400 Subject: [PATCH 091/282] WS: add bat status --- applications/external/weather_station/protocols/wendox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/weather_station/protocols/wendox.c b/applications/external/weather_station/protocols/wendox.c index fec1a8bf1..94e09f795 100644 --- a/applications/external/weather_station/protocols/wendox.c +++ b/applications/external/weather_station/protocols/wendox.c @@ -101,7 +101,7 @@ void ws_protocol_decoder_wendox_reset(void* context) { */ static void ws_protocol_wendox_remote_controller(WSBlockGeneric* instance) { instance->id = (instance->data >> 4) & 0xFF; - instance->battery_low = WS_NO_BATT; + instance->battery_low = (instance->data >> 5) & 1; instance->channel = WS_NO_CHANNEL; if(((instance->data >> 22) & 1)) { From a782a5ad297862ad9c49ddce7df47e9cbb6eab63 Mon Sep 17 00:00:00 2001 From: hedger Date: Sat, 22 Apr 2023 22:04:03 +0400 Subject: [PATCH 092/282] scripts: fixed cube version validation from config file --- scripts/flipper/assets/copro.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index b61ac0329..e0375b51f 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -34,7 +34,7 @@ class Copro: self.mcu_copro = None self.logger = logging.getLogger(self.__class__.__name__) - def loadCubeInfo(self, cube_dir, cube_version): + def loadCubeInfo(self, cube_dir, reference_cube_version): if not os.path.isdir(cube_dir): raise Exception(f'"{cube_dir}" doesn\'t exists') self.cube_dir = cube_dir @@ -50,7 +50,7 @@ class Copro: if not cube_version or not cube_version.startswith("FW.WB"): raise Exception(f"Incorrect Cube package or version info") cube_version = cube_version.replace("FW.WB.", "", 1) - if cube_version != cube_version: + if cube_version != reference_cube_version: raise Exception(f"Unsupported cube version") self.version = cube_version From ffae861bc2dbf1952a7ba67e05b8bb0b6581c16a Mon Sep 17 00:00:00 2001 From: hedger Date: Sun, 23 Apr 2023 00:48:47 +0400 Subject: [PATCH 093/282] hal: flash: added 3 seconds timeout when waiting for C2 to unlock flash controller. On timeout, triggers furi_check --- firmware/targets/f7/furi_hal/furi_hal_flash.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c index a880a3e49..464d88d95 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c @@ -27,6 +27,16 @@ #define FURI_HAL_FLASH_OPT_KEY2 0x4C5D6E7F #define FURI_HAL_FLASH_OB_TOTAL_WORDS (0x80 / (sizeof(uint32_t) * 2)) +/* lib/STM32CubeWB/Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_RfWithFlash/Core/Src/flash_driver.c + * ProcessSingleFlashOperation, quote: + > In most BLE application, the flash should not be blocked by the CPU2 longer than FLASH_TIMEOUT_VALUE (1000ms) + > However, it could be that for some marginal application, this time is longer. + > ... there is no other way than waiting the operation to be completed. + > If for any reason this test is never passed, this means there is a failure in the system and there is no other + > way to recover than applying a device reset. + */ +#define FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS 3000u /* 3 seconds */ + #define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__)&0x7U) == (0x00UL)) #define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \ (((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \ @@ -132,9 +142,11 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) { for(volatile uint32_t i = 0; i < 35; i++) ; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS * 1000); while(true) { /* Wait till flash controller become usable */ while(LL_FLASH_IsActiveFlag_OperationSuspended()) { + furi_check(!furi_hal_cortex_timer_is_expired(timer)); furi_thread_yield(); }; @@ -144,6 +156,7 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) { /* Actually we already have mutex for it, but specification is specification */ if(LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) { taskEXIT_CRITICAL(); + furi_check(!furi_hal_cortex_timer_is_expired(timer)); furi_thread_yield(); continue; } @@ -151,6 +164,7 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) { /* Take sempahopre and prevent core2 from anything funky */ if(LL_HSEM_1StepLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != 0) { taskEXIT_CRITICAL(); + furi_check(!furi_hal_cortex_timer_is_expired(timer)); furi_thread_yield(); continue; } @@ -237,7 +251,6 @@ bool furi_hal_flash_wait_last_operation(uint32_t timeout) { Even if the FLASH operation fails, the BUSY flag will be reset and an error flag will be set */ FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); - while(READ_BIT(FLASH->SR, FLASH_SR_BSY)) { if(furi_hal_cortex_timer_is_expired(timer)) { return false; @@ -263,7 +276,6 @@ bool furi_hal_flash_wait_last_operation(uint32_t timeout) { /* Wait for control register to be written */ timer = furi_hal_cortex_timer_get(timeout * 1000); - while(READ_BIT(FLASH->SR, FLASH_SR_CFGBSY)) { if(furi_hal_cortex_timer_is_expired(timer)) { return false; From 1092cc92be9284c591b320f45cea6422a76fde33 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 23 Apr 2023 00:12:39 +0300 Subject: [PATCH 094/282] why? --- lib/ST25RFAL002/platform.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c index 5fe65ef88..754e25650 100644 --- a/lib/ST25RFAL002/platform.c +++ b/lib/ST25RFAL002/platform.c @@ -45,19 +45,16 @@ void platformDisableIrqCallback() { void platformSetIrqCallback(PlatformIrqCallback callback) { rfal_platform.callback = callback; + rfal_platform.thread = + furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); + furi_thread_mark_as_service(rfal_platform.thread); + furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); + furi_thread_start(rfal_platform.thread); - if(!rfal_platform.thread) { - rfal_platform.thread = - furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); - furi_thread_mark_as_service(rfal_platform.thread); - furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); - furi_thread_start(rfal_platform.thread); - - furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); - // Disable interrupt callback as the pin is shared between 2 apps - // It is enabled in rfalLowPowerModeStop() - furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); - } + furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); + // Disable interrupt callback as the pin is shared between 2 apps + // It is enabled in rfalLowPowerModeStop() + furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); } bool platformSpiTxRx(const uint8_t* txBuf, uint8_t* rxBuf, uint16_t len) { From 10efb9590ad0ef789fdeb56a9c285f57185c1121 Mon Sep 17 00:00:00 2001 From: hedger Date: Sat, 22 Apr 2023 22:04:03 +0400 Subject: [PATCH 095/282] scripts: fixed cube version validation from config file --- scripts/flipper/assets/copro.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index b61ac0329..e0375b51f 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -34,7 +34,7 @@ class Copro: self.mcu_copro = None self.logger = logging.getLogger(self.__class__.__name__) - def loadCubeInfo(self, cube_dir, cube_version): + def loadCubeInfo(self, cube_dir, reference_cube_version): if not os.path.isdir(cube_dir): raise Exception(f'"{cube_dir}" doesn\'t exists') self.cube_dir = cube_dir @@ -50,7 +50,7 @@ class Copro: if not cube_version or not cube_version.startswith("FW.WB"): raise Exception(f"Incorrect Cube package or version info") cube_version = cube_version.replace("FW.WB.", "", 1) - if cube_version != cube_version: + if cube_version != reference_cube_version: raise Exception(f"Unsupported cube version") self.version = cube_version From 963b19e2e0eb224d47686a8873366992f6f02646 Mon Sep 17 00:00:00 2001 From: hedger Date: Sun, 23 Apr 2023 01:31:07 +0400 Subject: [PATCH 096/282] nfc: fixed missing interrupt setup on multiple platformSetIrqCallback() invocations --- lib/ST25RFAL002/platform.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c index 5fe65ef88..a97be1c75 100644 --- a/lib/ST25RFAL002/platform.c +++ b/lib/ST25RFAL002/platform.c @@ -52,12 +52,12 @@ void platformSetIrqCallback(PlatformIrqCallback callback) { furi_thread_mark_as_service(rfal_platform.thread); furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); furi_thread_start(rfal_platform.thread); - - furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); - // Disable interrupt callback as the pin is shared between 2 apps - // It is enabled in rfalLowPowerModeStop() - furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); } + + furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); + // Disable interrupt callback as the pin is shared between 2 apps + // It is enabled in rfalLowPowerModeStop() + furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); } bool platformSpiTxRx(const uint8_t* txBuf, uint8_t* rxBuf, uint16_t len) { From 082db6737332a2e5cd75c370183544b7c034eb8a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 23 Apr 2023 00:12:39 +0300 Subject: [PATCH 097/282] Revert "why?" This reverts commit 1092cc92be9284c591b320f45cea6422a76fde33. --- lib/ST25RFAL002/platform.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c index 754e25650..5fe65ef88 100644 --- a/lib/ST25RFAL002/platform.c +++ b/lib/ST25RFAL002/platform.c @@ -45,16 +45,19 @@ void platformDisableIrqCallback() { void platformSetIrqCallback(PlatformIrqCallback callback) { rfal_platform.callback = callback; - rfal_platform.thread = - furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); - furi_thread_mark_as_service(rfal_platform.thread); - furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); - furi_thread_start(rfal_platform.thread); - furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); - // Disable interrupt callback as the pin is shared between 2 apps - // It is enabled in rfalLowPowerModeStop() - furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); + if(!rfal_platform.thread) { + rfal_platform.thread = + furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL); + furi_thread_mark_as_service(rfal_platform.thread); + furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); + furi_thread_start(rfal_platform.thread); + + furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); + // Disable interrupt callback as the pin is shared between 2 apps + // It is enabled in rfalLowPowerModeStop() + furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); + } } bool platformSpiTxRx(const uint8_t* txBuf, uint8_t* rxBuf, uint16_t len) { From ba0efda2aaa0b0f1956194d1f776d50ae8b8f848 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 23 Apr 2023 01:57:07 +0300 Subject: [PATCH 098/282] hal: gpio: don't trigger furi_check on furi_hal_gpio_add_int_callback() with same parameters --- firmware/targets/f7/furi_hal/furi_hal_gpio.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_gpio.c b/firmware/targets/f7/furi_hal/furi_hal_gpio.c index e8318afcf..c0e409775 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_gpio.c +++ b/firmware/targets/f7/furi_hal/furi_hal_gpio.c @@ -178,10 +178,13 @@ void furi_hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, vo FURI_CRITICAL_ENTER(); uint8_t pin_num = furi_hal_gpio_get_pin_num(gpio); - furi_assert(gpio_interrupt[pin_num].callback == NULL); - gpio_interrupt[pin_num].callback = cb; - gpio_interrupt[pin_num].context = ctx; - gpio_interrupt[pin_num].ready = true; + volatile GpioInterrupt* interrupt = &gpio_interrupt[pin_num]; + furi_check( + (interrupt->callback == NULL) || + ((interrupt->callback == cb) && (interrupt->context == ctx))); + interrupt->callback = cb; + interrupt->context = ctx; + interrupt->ready = true; FURI_CRITICAL_EXIT(); } From a0597a9e6ee41e2e61050ba7e33ae6fdb76df848 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 23 Apr 2023 01:57:38 +0300 Subject: [PATCH 099/282] Fix RFID Fuzzer and iButton Fuzzer Bugs --- .../external/flipfrid/application.fam | 2 +- applications/external/flipfrid/flipfrid.c | 18 +++ applications/external/flipfrid/flipfrid.h | 2 + .../scene/flipfrid_scene_entrypoint.c | 130 +++++++--------- .../external/ibtn_fuzzer/ibtnfuzzer.c | 16 ++ .../external/ibtn_fuzzer/ibtnfuzzer.h | 2 + .../scene/ibtnfuzzer_scene_entrypoint.c | 140 ++++++++---------- 7 files changed, 159 insertions(+), 151 deletions(-) diff --git a/applications/external/flipfrid/application.fam b/applications/external/flipfrid/application.fam index 8f620f4ac..343d4f62d 100644 --- a/applications/external/flipfrid/application.fam +++ b/applications/external/flipfrid/application.fam @@ -4,7 +4,7 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="flipfrid_start", requires=["gui", "storage", "dialogs", "input", "notification"], - stack_size=1 * 1024, + stack_size=2 * 1024, order=15, fap_icon="rfid_10px.png", fap_category="Tools", diff --git a/applications/external/flipfrid/flipfrid.c b/applications/external/flipfrid/flipfrid.c index 9ddbdd42e..4608bab16 100644 --- a/applications/external/flipfrid/flipfrid.c +++ b/applications/external/flipfrid/flipfrid.c @@ -58,6 +58,16 @@ FlipFridState* flipfrid_alloc() { flipfrid->proto_name = furi_string_alloc(); flipfrid->data_str = furi_string_alloc(); + flipfrid->main_menu_items[0] = furi_string_alloc_set("Default Values"); + flipfrid->main_menu_items[1] = furi_string_alloc_set("BF Customer ID"); + flipfrid->main_menu_items[2] = furi_string_alloc_set("Load File"); + flipfrid->main_menu_items[3] = furi_string_alloc_set("Load UIDs from file"); + + flipfrid->main_menu_proto_items[0] = furi_string_alloc_set("EM4100"); + flipfrid->main_menu_proto_items[1] = furi_string_alloc_set("HIDProx"); + flipfrid->main_menu_proto_items[2] = furi_string_alloc_set("PAC/Stanley"); + flipfrid->main_menu_proto_items[3] = furi_string_alloc_set("H10301"); + flipfrid->previous_scene = NoneScene; flipfrid->current_scene = SceneEntryPoint; flipfrid->is_running = true; @@ -100,6 +110,14 @@ void flipfrid_free(FlipFridState* flipfrid) { furi_string_free(flipfrid->proto_name); furi_string_free(flipfrid->data_str); + for(uint32_t i = 0; i < 4; i++) { + furi_string_free(flipfrid->main_menu_items[i]); + } + + for(uint32_t i = 0; i < 4; i++) { + furi_string_free(flipfrid->main_menu_proto_items[i]); + } + free(flipfrid->data); free(flipfrid->payload); diff --git a/applications/external/flipfrid/flipfrid.h b/applications/external/flipfrid/flipfrid.h index e4122054b..0ee8aa320 100644 --- a/applications/external/flipfrid/flipfrid.h +++ b/applications/external/flipfrid/flipfrid.h @@ -75,6 +75,8 @@ typedef struct { FlipFridProtos proto; FuriString* attack_name; FuriString* proto_name; + FuriString* main_menu_items[4]; + FuriString* main_menu_proto_items[4]; DialogsApp* dialogs; FuriString* notification_msg; diff --git a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c index 24c19dc4c..f4b39aa66 100644 --- a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c +++ b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c @@ -1,8 +1,5 @@ #include "flipfrid_scene_entrypoint.h" -FuriString* main_menu_items[4]; -FuriString* main_menu_proto_items[4]; - void flipfrid_scene_entrypoint_menu_callback( FlipFridState* context, uint32_t index, @@ -68,31 +65,14 @@ void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) { menu_items[i] = furi_string_alloc(); }*/ - main_menu_items[0] = furi_string_alloc_set("Default Values"); - main_menu_items[1] = furi_string_alloc_set("BF Customer ID"); - main_menu_items[2] = furi_string_alloc_set("Load File"); - main_menu_items[3] = furi_string_alloc_set("Load UIDs from file"); - context->menu_proto_index = 0; /*for(uint32_t i = 0; i < 4; i++) { menu_proto_items[i] = furi_string_alloc(); }*/ - - main_menu_proto_items[0] = furi_string_alloc_set("EM4100"); - main_menu_proto_items[1] = furi_string_alloc_set("HIDProx"); - main_menu_proto_items[2] = furi_string_alloc_set("PAC/Stanley"); - main_menu_proto_items[3] = furi_string_alloc_set("H10301"); } void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) { UNUSED(context); - for(uint32_t i = 0; i < 4; i++) { - furi_string_free(main_menu_items[i]); - } - - for(uint32_t i = 0; i < 4; i++) { - furi_string_free(main_menu_proto_items[i]); - } } void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) { @@ -145,73 +125,77 @@ void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); - if(main_menu_items[context->menu_index] != NULL) { - if(context->menu_index > FlipFridAttackDefaultValues) { - canvas_set_font(canvas, FontSecondary); + if(context->main_menu_items != NULL) { + if(context->main_menu_items[context->menu_index] != NULL) { + if(context->menu_index > FlipFridAttackDefaultValues) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 24, + AlignCenter, + AlignTop, + furi_string_get_cstr(context->main_menu_items[context->menu_index - 1])); + } + + canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned( canvas, 64, - 24, + 36, AlignCenter, AlignTop, - furi_string_get_cstr(main_menu_items[context->menu_index - 1])); - } + furi_string_get_cstr(context->main_menu_items[context->menu_index])); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 36, - AlignCenter, - AlignTop, - furi_string_get_cstr(main_menu_items[context->menu_index])); + if(context->menu_index < FlipFridAttackLoadFileCustomUids) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 48, + AlignCenter, + AlignTop, + furi_string_get_cstr(context->main_menu_items[context->menu_index + 1])); + } - if(context->menu_index < FlipFridAttackLoadFileCustomUids) { - canvas_set_font(canvas, FontSecondary); + if(context->menu_proto_index > EM4100) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + -12, + AlignCenter, + AlignTop, + furi_string_get_cstr( + context->main_menu_proto_items[context->menu_proto_index - 1])); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); + + canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned( canvas, 64, - 48, + 4, AlignCenter, AlignTop, - furi_string_get_cstr(main_menu_items[context->menu_index + 1])); - } + furi_string_get_cstr(context->main_menu_proto_items[context->menu_proto_index])); - if(context->menu_proto_index > EM4100) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index - 1])); - } + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 4, - AlignCenter, - AlignTop, - furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index])); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); - - if(context->menu_proto_index < H10301) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index + 1])); + if(context->menu_proto_index < H10301) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + -12, + AlignCenter, + AlignTop, + furi_string_get_cstr( + context->main_menu_proto_items[context->menu_proto_index + 1])); + } } } } \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/ibtnfuzzer.c b/applications/external/ibtn_fuzzer/ibtnfuzzer.c index c5f2a5f7c..d385a22d2 100644 --- a/applications/external/ibtn_fuzzer/ibtnfuzzer.c +++ b/applications/external/ibtn_fuzzer/ibtnfuzzer.c @@ -58,6 +58,14 @@ iBtnFuzzerState* ibtnfuzzer_alloc() { ibtnfuzzer->proto_name = furi_string_alloc(); ibtnfuzzer->data_str = furi_string_alloc(); + ibtnfuzzer->main_menu_items[0] = furi_string_alloc_set("Default Values"); + ibtnfuzzer->main_menu_items[1] = furi_string_alloc_set("Load File"); + ibtnfuzzer->main_menu_items[2] = furi_string_alloc_set("Load UIDs from file"); + + ibtnfuzzer->main_menu_proto_items[0] = furi_string_alloc_set("DS1990"); + ibtnfuzzer->main_menu_proto_items[1] = furi_string_alloc_set("Metakom"); + ibtnfuzzer->main_menu_proto_items[2] = furi_string_alloc_set("Cyfral"); + ibtnfuzzer->previous_scene = NoneScene; ibtnfuzzer->current_scene = SceneEntryPoint; ibtnfuzzer->is_running = true; @@ -104,6 +112,14 @@ void ibtnfuzzer_free(iBtnFuzzerState* ibtnfuzzer) { furi_string_free(ibtnfuzzer->proto_name); furi_string_free(ibtnfuzzer->data_str); + for(uint32_t i = 0; i < 3; i++) { + furi_string_free(ibtnfuzzer->main_menu_items[i]); + } + + for(uint32_t i = 0; i < 3; i++) { + furi_string_free(ibtnfuzzer->main_menu_proto_items[i]); + } + free(ibtnfuzzer->data); free(ibtnfuzzer->payload); diff --git a/applications/external/ibtn_fuzzer/ibtnfuzzer.h b/applications/external/ibtn_fuzzer/ibtnfuzzer.h index ed42cc541..3a3a1d21f 100644 --- a/applications/external/ibtn_fuzzer/ibtnfuzzer.h +++ b/applications/external/ibtn_fuzzer/ibtnfuzzer.h @@ -73,6 +73,8 @@ typedef struct { iBtnFuzzerProtos proto; FuriString* attack_name; FuriString* proto_name; + FuriString* main_menu_items[3]; + FuriString* main_menu_proto_items[3]; DialogsApp* dialogs; FuriString* notification_msg; diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c index 3ea7e49e6..1dd239c3b 100644 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c +++ b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c @@ -1,8 +1,5 @@ #include "ibtnfuzzer_scene_entrypoint.h" -FuriString* main_menu_items[3]; -FuriString* main_menu_proto_items[3]; - void ibtnfuzzer_scene_entrypoint_menu_callback( iBtnFuzzerState* context, uint32_t index, @@ -61,30 +58,14 @@ void ibtnfuzzer_scene_entrypoint_on_enter(iBtnFuzzerState* context) { menu_items[i] = furi_string_alloc(); }*/ - main_menu_items[0] = furi_string_alloc_set("Default Values"); - main_menu_items[1] = furi_string_alloc_set("Load File"); - main_menu_items[2] = furi_string_alloc_set("Load UIDs from file"); - context->menu_proto_index = 0; /*for(uint32_t i = 0; i < 4; i++) { menu_proto_items[i] = furi_string_alloc(); }*/ - - main_menu_proto_items[0] = furi_string_alloc_set("DS1990"); - main_menu_proto_items[1] = furi_string_alloc_set("Metakom"); - main_menu_proto_items[2] = furi_string_alloc_set("Cyfral"); } void ibtnfuzzer_scene_entrypoint_on_exit(iBtnFuzzerState* context) { context->enter_rerun = false; - - for(uint32_t i = 0; i < 3; i++) { - furi_string_free(main_menu_items[i]); - } - - for(uint32_t i = 0; i < 3; i++) { - furi_string_free(main_menu_proto_items[i]); - } } void ibtnfuzzer_scene_entrypoint_on_tick(iBtnFuzzerState* context) { @@ -142,74 +123,79 @@ void ibtnfuzzer_scene_entrypoint_on_draw(Canvas* canvas, iBtnFuzzerState* contex canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); - if(main_menu_items[context->menu_index] != NULL) { - if(context->menu_index > iBtnFuzzerAttackDefaultValues) { - canvas_set_font(canvas, FontSecondary); + if(context->main_menu_items != NULL) { + if(context->main_menu_items[context->menu_index] != NULL) { + if(context->menu_index > iBtnFuzzerAttackDefaultValues) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 24, + AlignCenter, + AlignTop, + furi_string_get_cstr(context->main_menu_items[context->menu_index - 1])); + } + + canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned( canvas, 64, - 24, + 36, AlignCenter, AlignTop, - furi_string_get_cstr(main_menu_items[context->menu_index - 1])); - } + furi_string_get_cstr(context->main_menu_items[context->menu_index])); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 36, - AlignCenter, - AlignTop, - furi_string_get_cstr(main_menu_items[context->menu_index])); + if(context->menu_index < iBtnFuzzerAttackLoadFileCustomUids) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 48, + AlignCenter, + AlignTop, + furi_string_get_cstr(context->main_menu_items[context->menu_index + 1])); + } - if(context->menu_index < iBtnFuzzerAttackLoadFileCustomUids) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 48, - AlignCenter, - AlignTop, - furi_string_get_cstr(main_menu_items[context->menu_index + 1])); - } + if(context->menu_proto_index > DS1990) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + -12, + AlignCenter, + AlignTop, + furi_string_get_cstr( + context->main_menu_proto_items[context->menu_proto_index - 1])); + } - if(context->menu_proto_index > DS1990) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index - 1])); - } + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); + canvas_set_font(canvas, FontPrimary); + if(context->main_menu_proto_items[context->menu_proto_index] != NULL) { + canvas_draw_str_aligned( + canvas, + 64, + 4, + AlignCenter, + AlignTop, + furi_string_get_cstr( + context->main_menu_proto_items[context->menu_proto_index])); + } + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); - canvas_set_font(canvas, FontPrimary); - if(main_menu_proto_items[context->menu_proto_index] != NULL) { - canvas_draw_str_aligned( - canvas, - 64, - 4, - AlignCenter, - AlignTop, - furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index])); - } - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); - - if(context->menu_proto_index < Cyfral) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr(main_menu_proto_items[context->menu_proto_index + 1])); + if(context->menu_proto_index < Cyfral) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + -12, + AlignCenter, + AlignTop, + furi_string_get_cstr( + context->main_menu_proto_items[context->menu_proto_index + 1])); + } } } } \ No newline at end of file From 5d98d2703bc33e873610fad376b8973297c3caaa Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 23 Apr 2023 02:21:10 +0300 Subject: [PATCH 100/282] Fix external subghz module --- firmware/targets/f7/furi_hal/furi_hal_subghz.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 1a4e39965..8667f2c5f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -645,11 +645,12 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_disable_int_callback(furi_hal_subghz.cc1101_g0_pin); + furi_hal_gpio_remove_int_callback(furi_hal_subghz.cc1101_g0_pin); furi_hal_gpio_add_int_callback( furi_hal_subghz.cc1101_g0_pin, furi_hal_subghz_capture_ext_ISR, furi_hal_subghz_capture_callback); - furi_hal_gpio_enable_int_callback(furi_hal_subghz.cc1101_g0_pin); } // Start timer From 02c4b6d7e237d9f1eb9d7b432037e3fe1a1f121c Mon Sep 17 00:00:00 2001 From: hedger Date: Sun, 23 Apr 2023 04:49:03 +0400 Subject: [PATCH 101/282] scripts: storage: fixed exception handler for paths --- scripts/flipper/storage.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 7b56ee0d0..cff32ceb1 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -335,7 +335,9 @@ class FlipperStorage: def _check_no_error(self, response, path=None): if self.has_error(response): - raise FlipperStorageException.from_error_code(self.get_error(response)) + raise FlipperStorageException.from_error_code( + path, self.get_error(response) + ) def size(self, path: str): """file size on Flipper""" From ca2884548a44a4350e1bf8f77ed3a57f25c0e32d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:02:55 +0100 Subject: [PATCH 102/282] Sanitize name changer input --- .../scenes/xtreme_app_scene_misc_rename.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c index a5f5dd8c5..30684da65 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c @@ -12,12 +12,28 @@ static void xtreme_app_scene_misc_rename_text_input_callback(void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); } +static bool xtreme_app_scene_misc_rename_validator(const char* text, FuriString* error, void* context) { + UNUSED(context); + + for(; *text; ++text) { + const char c = *text; + if((c < '0' || c > '9') && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) { + furi_string_printf(error, "Please only\nenter letters\nand numbers!"); + return false; + } + } + + return true; +} + void xtreme_app_scene_misc_rename_on_enter(void* context) { XtremeApp* app = context; TextInput* text_input = app->text_input; text_input_set_header_text(text_input, "Leave empty for default"); + text_input_set_validator(text_input, xtreme_app_scene_misc_rename_validator, NULL); + text_input_set_result_callback( text_input, xtreme_app_scene_misc_rename_text_input_callback, From 2d794b32f54509dee74b0ae443ec377b9cc69dc7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:04:16 +0100 Subject: [PATCH 103/282] Add minimum input length functionality to keyboard --- applications/services/gui/modules/text_input.c | 16 +++++++++++++++- applications/services/gui/modules/text_input.h | 4 ++++ firmware/targets/f7/api_symbols.csv | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 35ab30c16..63c93d3c1 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -23,6 +23,7 @@ typedef struct { const char* header; char* text_buffer; size_t text_buffer_size; + size_t minimum_length; bool clear_default_text; FuriString* temp_str; @@ -441,7 +442,7 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, I model->text_buffer, model->validator_text, model->validator_callback_context))) { model->validator_message_visible = true; furi_timer_start(text_input->timer, furi_kernel_get_tick_frequency() * 4); - } else if(model->callback != 0 && text_length > 0) { + } else if(model->callback != 0 && text_length >= model->minimum_length) { model->callback(model->callback_context); } } else if(selected == SWITCH_KEYBOARD_KEY) { @@ -631,6 +632,7 @@ void text_input_reset(TextInput* text_input) { model->selected_row = 0; model->selected_column = 0; model->selected_keyboard = 0; + model->minimum_length = 1; model->clear_default_text = false; model->cursor_pos = 0; model->cursor_select = false; @@ -681,6 +683,18 @@ void text_input_set_result_callback( true); } +void text_input_set_minimum_length( + TextInput* text_input, + size_t minimum_length) { + with_view_model( + text_input->view, + TextInputModel * model, + { + model->minimum_length = minimum_length; + }, + true); +} + void text_input_set_validator( TextInput* text_input, TextInputValidatorCallback callback, diff --git a/applications/services/gui/modules/text_input.h b/applications/services/gui/modules/text_input.h index 218df3141..5c5d8365b 100644 --- a/applications/services/gui/modules/text_input.h +++ b/applications/services/gui/modules/text_input.h @@ -70,6 +70,10 @@ void text_input_set_validator( TextInputValidatorCallback callback, void* callback_context); +void text_input_set_minimum_length( + TextInput* text_input, + size_t minimum_length); + TextInputValidatorCallback text_input_get_validator_callback(TextInput* text_input); void* text_input_get_validator_callback_context(TextInput* text_input); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index be5934b82..de959aa4d 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -3454,6 +3454,7 @@ Function,+,text_input_get_validator_callback_context,void*,TextInput* Function,+,text_input_get_view,View*,TextInput* Function,+,text_input_reset,void,TextInput* Function,+,text_input_set_header_text,void,"TextInput*, const char*" +Function,+,text_input_set_minimum_length,void,"TextInput*, size_t" Function,+,text_input_set_result_callback,void,"TextInput*, TextInputCallback, void*, char*, size_t, _Bool" Function,+,text_input_set_validator,void,"TextInput*, TextInputValidatorCallback, void*" Function,-,tga_save,void,const char* From 4f4895d08b32bf9ae0b899fec27caa348d5caaf6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:04:54 +0100 Subject: [PATCH 104/282] Allow name changer input to be empty (use default) --- .../main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c index 30684da65..9f034ea56 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c @@ -34,6 +34,8 @@ void xtreme_app_scene_misc_rename_on_enter(void* context) { text_input_set_validator(text_input, xtreme_app_scene_misc_rename_validator, NULL); + text_input_set_minimum_length(text_input, 0); + text_input_set_result_callback( text_input, xtreme_app_scene_misc_rename_text_input_callback, From f351a1e7e8238ff0cbf090f53fd2139a6def4a88 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 23 Apr 2023 14:05:28 +0100 Subject: [PATCH 105/282] Update API symbols --- firmware/targets/f7/api_symbols.csv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index de959aa4d..62c3516be 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -4850,10 +4850,12 @@ Variable,+,gpio_ext_pc1,const GpioPin, Variable,+,gpio_ext_pc3,const GpioPin, Variable,+,gpio_i2c_power_scl,const GpioPin, Variable,+,gpio_i2c_power_sda,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, Variable,+,gpio_infrared_rx,const GpioPin, Variable,+,gpio_infrared_tx,const GpioPin, Variable,+,gpio_nfc_cs,const GpioPin, Variable,+,gpio_nfc_irq_rfid_pull,const GpioPin, +Variable,+,gpio_periph_power,const GpioPin, Variable,+,gpio_pins,const GpioPinRecord[], Variable,+,gpio_pins_count,const size_t, Variable,+,gpio_rf_sw_0,const GpioPin, @@ -4878,7 +4880,7 @@ Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, Variable,+,gpio_usb_dp,const GpioPin, -Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_vibro,const GpioPin, Variable,+,input_pins,const InputPin[], Variable,+,input_pins_count,const size_t, Variable,+,lfrfid_protocols,const ProtocolBase*[], @@ -5027,7 +5029,6 @@ Variable,+,message_red_255,const NotificationMessage, Variable,+,message_sound_off,const NotificationMessage, Variable,+,message_vibro_off,const NotificationMessage, Variable,+,message_vibro_on,const NotificationMessage, -Variable,+,gpio_periph_power,const GpioPin, Variable,+,sequence_audiovisual_alert,const NotificationSequence, Variable,+,sequence_blink_blue_10,const NotificationSequence, Variable,+,sequence_blink_blue_100,const NotificationSequence, @@ -7034,4 +7035,3 @@ Variable,+,usb_cdc_single,FuriHalUsbInterface, Variable,+,usb_hid,FuriHalUsbInterface, Variable,+,usb_hid_u2f,FuriHalUsbInterface, Variable,+,usbd_devfs,const usbd_driver, -Variable,+,gpio_vibro,const GpioPin, From c2791b459ba728143dd62e10cbca0b1d919fe14e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 23 Apr 2023 15:32:03 +0100 Subject: [PATCH 106/282] Rework symbol keyboard (fix illegal characters) --- .../services/gui/modules/text_input.c | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 63c93d3c1..569213fa2 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -102,16 +102,15 @@ static const TextInputKey keyboard_keys_row_3[] = { }; static const TextInputKey symbol_keyboard_keys_row_1[] = { - {'!', 1, 8}, - {'"', 10, 8}, - {'#', 19, 8}, - {'$', 28, 8}, - {'%', 37, 8}, - {'&', 46, 8}, - {'/', 55, 8}, - {'(', 64, 8}, - {')', 73, 8}, - {'=', 82, 8}, + {'!', 2, 8}, + {'@', 12, 8}, + {'#', 22, 8}, + {'$', 32, 8}, + {'%', 42, 8}, + {'^', 52, 8}, + {'&', 62, 8}, + {'(', 71, 8}, + {')', 81, 8}, {'0', 91, 8}, {'1', 100, 8}, {'2', 110, 8}, @@ -119,15 +118,14 @@ static const TextInputKey symbol_keyboard_keys_row_1[] = { }; static const TextInputKey symbol_keyboard_keys_row_2[] = { - {'{', 1, 20}, - {'}', 10, 20}, - {'[', 19, 20}, - {']', 28, 20}, - {'<', 37, 20}, - {'>', 46, 20}, - {'\\', 55, 20}, - {'@', 64, 20}, - {'?', 73, 20}, + {'~', 2, 20}, + {'+', 12, 20}, + {'-', 22, 20}, + {'=', 32, 20}, + {'[', 42, 20}, + {']', 52, 20}, + {'{', 62, 20}, + {'}', 72, 20}, {BACKSPACE_KEY, 82, 12}, {'4', 100, 20}, {'5', 110, 20}, @@ -136,14 +134,11 @@ static const TextInputKey symbol_keyboard_keys_row_2[] = { static const TextInputKey symbol_keyboard_keys_row_3[] = { {SWITCH_KEYBOARD_KEY, 1, 23}, - {'+', 13, 32}, - {'`', 21, 32}, - {'\'', 28, 32}, - {'^', 36, 32}, - {'*', 44, 32}, - {',', 52, 32}, - {'.', 59, 32}, - {'-', 67, 32}, + {'.', 15, 32}, + {',', 29, 32}, + {';', 41, 32}, + {'`', 53, 32}, + {'\'', 65, 32}, {ENTER_KEY, 74, 23}, {'7', 100, 32}, {'8', 110, 32}, @@ -291,7 +286,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str(canvas, start_pos, 22, "..."); start_pos += 6; needed_string_width -= 8; - for(uint off = 0; + for(uint32_t off = 0; !furi_string_empty(str) && canvas_string_width(canvas, cstr) > needed_string_width && off < model->cursor_pos; off++) { @@ -377,11 +372,16 @@ static void text_input_handle_up(TextInput* text_input, TextInputModel* model) { UNUSED(text_input); if(model->selected_row > 0) { model->selected_row--; - if(model->selected_column > - get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 6 && - model->selected_row == 0) { + if(model->selected_row == 0 && + model->selected_column > get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 6) { model->selected_column = model->selected_column + 1; } + if(model->selected_row == 1 && model->selected_keyboard == symbol_keyboard.keyboard_index) { + if(model->selected_column > 5) + model->selected_column += 2; + else if(model->selected_column > 1) + model->selected_column += 1; + } } else { model->cursor_select = true; model->clear_default_text = false; @@ -394,11 +394,16 @@ static void text_input_handle_down(TextInput* text_input, TextInputModel* model) model->cursor_select = false; } else if(model->selected_row < keyboard_row_count - 1) { model->selected_row++; - if(model->selected_column > - get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 4 && - model->selected_row == 1) { + if(model->selected_row == 1 && + model->selected_column > get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 4) { model->selected_column = model->selected_column - 1; } + if(model->selected_row == 2 && model->selected_keyboard == symbol_keyboard.keyboard_index) { + if(model->selected_column > 7) + model->selected_column -= 2; + else if(model->selected_column > 1) + model->selected_column -= 1; + } } } From 69530cd50f43dfefc7623188214641d703fe6ec2 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 23 Apr 2023 19:03:23 +0300 Subject: [PATCH 107/282] RFID: More user-friendly RAW emulation Made by Dan Caprita https://forum.flipperzero.one/t/electra-intercom/6368/43 --- applications/main/lfrfid/lfrfid.c | 21 +++++ applications/main/lfrfid/lfrfid_i.h | 2 + .../main/lfrfid/scenes/lfrfid_scene_config.h | 2 + .../scenes/lfrfid_scene_extra_actions.c | 10 +++ .../lfrfid/scenes/lfrfid_scene_raw_emulate.c | 86 +++++++++++++++++++ .../scenes/lfrfid_scene_select_raw_key.c | 23 +++++ 6 files changed, 144 insertions(+) create mode 100644 applications/main/lfrfid/scenes/lfrfid_scene_raw_emulate.c create mode 100644 applications/main/lfrfid/scenes/lfrfid_scene_select_raw_key.c diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index 85a00eea0..a2bcdcf52 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -243,6 +243,27 @@ bool lfrfid_load_key_from_file_select(LfRfid* app) { return result; } +bool lfrfid_load_raw_key_from_file_select(LfRfid* app) { + furi_assert(app); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, LFRFID_APP_RAW_ASK_EXTENSION, &I_125_10px); + browser_options.base_path = LFRFID_APP_FOLDER; + + // Input events and views are managed by file_browser + bool result = + dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); + + if(result) { + // Extract .raw and then .ask + path_extract_filename(app->file_path, app->file_name, true); + path_extract_filename(app->file_name, app->file_name, true); + } + + return result; +} + bool lfrfid_delete_key(LfRfid* app) { furi_assert(app); diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index 72b061930..86929a14a 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -125,6 +125,8 @@ bool lfrfid_save_key(LfRfid* app); bool lfrfid_load_key_from_file_select(LfRfid* app); +bool lfrfid_load_raw_key_from_file_select(LfRfid* app); + bool lfrfid_delete_key(LfRfid* app); bool lfrfid_load_key_data(LfRfid* app, FuriString* path, bool show_dialog); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_config.h b/applications/main/lfrfid/scenes/lfrfid_scene_config.h index 10dc46fe6..54b840844 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_config.h +++ b/applications/main/lfrfid/scenes/lfrfid_scene_config.h @@ -22,4 +22,6 @@ ADD_SCENE(lfrfid, raw_info, RawInfo) ADD_SCENE(lfrfid, raw_name, RawName) ADD_SCENE(lfrfid, raw_read, RawRead) ADD_SCENE(lfrfid, raw_success, RawSuccess) +ADD_SCENE(lfrfid, raw_emulate, RawEmulate) +ADD_SCENE(lfrfid, select_raw_key, SelectRawKey) ADD_SCENE(lfrfid, rpc, Rpc) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index efa319c1e..bf73eccd3 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -6,6 +6,7 @@ typedef enum { SubmenuIndexPSK, SubmenuIndexClearT5577, SubmenuIndexRAW, + SubmenuIndexRAWEmulate, } SubmenuIndex; static void lfrfid_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -44,6 +45,12 @@ void lfrfid_scene_extra_actions_on_enter(void* context) { SubmenuIndexRAW, lfrfid_scene_extra_actions_submenu_callback, app); + submenu_add_item( + submenu, + "Emulate RAW RFID data", + SubmenuIndexRAWEmulate, + lfrfid_scene_extra_actions_submenu_callback, + app); } submenu_set_selected_item( @@ -78,6 +85,9 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); consumed = true; + } else if(event.event == SubmenuIndexRAWEmulate) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectRawKey); + consumed = true; } scene_manager_set_scene_state(app->scene_manager, LfRfidSceneExtraActions, event.event); } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_emulate.c new file mode 100644 index 000000000..c06a5eebf --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_emulate.c @@ -0,0 +1,86 @@ +#include "../lfrfid_i.h" + +#define TAG "ADC" + +typedef struct { + FuriString* string_file_name; + bool error; +} LfRfidEmulateRawState; + +static void lfrfid_raw_emulate_callback(LFRFIDWorkerEmulateRawResult result, void* context) { + LfRfid* app = context; + + if(result == LFRFIDWorkerEmulateRawFileError) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadError); + } else if(result == LFRFIDWorkerEmulateRawOverrun) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadOverrun); + } +} + +void lfrfid_scene_raw_emulate_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + LfRfidEmulateRawState* state = malloc(sizeof(LfRfidEmulateRawState)); + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneRawEmulate, (uint32_t)state); + state->string_file_name = furi_string_alloc(); + + popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + lfrfid_worker_start_thread(app->lfworker); + lfrfid_make_app_folder(app); + + furi_string_printf( + state->string_file_name, + "%s/%s%s", + LFRFID_SD_FOLDER, + furi_string_get_cstr(app->file_name), + LFRFID_APP_RAW_ASK_EXTENSION); + FURI_LOG_D(TAG, "raw_emulate->file_name=%s", furi_string_get_cstr(state->string_file_name)); + popup_set_header(popup, "Emulating\nRAW RFID\nASK", 89, 30, AlignCenter, AlignTop); + lfrfid_worker_emulate_raw_start( + app->lfworker, + furi_string_get_cstr(state->string_file_name), + lfrfid_raw_emulate_callback, + app); + + notification_message(app->notifications, &sequence_blink_start_cyan); + + state->error = false; +} + +bool lfrfid_scene_raw_emulate_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + Popup* popup = app->popup; + LfRfidEmulateRawState* state = (LfRfidEmulateRawState*)scene_manager_get_scene_state( + app->scene_manager, LfRfidSceneRawEmulate); + bool consumed = false; + + furi_assert(state); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventReadError) { + consumed = true; + state->error = true; + popup_set_header( + popup, "Reading\nRAW RFID\nFile error", 89, 30, AlignCenter, AlignTop); + notification_message(app->notifications, &sequence_blink_start_red); + } + } + + return consumed; +} + +void lfrfid_scene_raw_emulate_on_exit(void* context) { + LfRfid* app = context; + LfRfidEmulateRawState* state = (LfRfidEmulateRawState*)scene_manager_get_scene_state( + app->scene_manager, LfRfidSceneRawEmulate); + + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + + furi_string_free(state->string_file_name); + free(state); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_select_raw_key.c b/applications/main/lfrfid/scenes/lfrfid_scene_select_raw_key.c new file mode 100644 index 000000000..f12d280aa --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_select_raw_key.c @@ -0,0 +1,23 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_select_raw_key_on_enter(void* context) { + LfRfid* app = context; + + if(lfrfid_load_raw_key_from_file_select(app)) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneRawEmulate); + + } else { + scene_manager_previous_scene(app->scene_manager); + } +} + +bool lfrfid_scene_select_raw_key_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + return consumed; +} + +void lfrfid_scene_select_raw_key_on_exit(void* context) { + UNUSED(context); +} From 78f993c9bb1caae6802f57a112331739be9e0442 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 23 Apr 2023 17:57:08 +0100 Subject: [PATCH 108/282] Dev webhook fixes --- .github/workflow_data/webhook.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflow_data/webhook.py b/.github/workflow_data/webhook.py index 2b9e29da9..ee97e254b 100644 --- a/.github/workflow_data/webhook.py +++ b/.github/workflow_data/webhook.py @@ -24,12 +24,13 @@ if __name__ == "__main__": else f"{count} New Commit{'' if count == 1 else 's'}" ) desc = f"[**{change}**]({event['compare']}) | [{branch}]({event['repository']['html_url']}/tree/{branch})\n" - for commit in event["commits"][:10]: - msg = commit['message'].splitlines()[0] + for i, commit in enumerate(event["commits"]): + msg = commit['message'].splitlines()[0].replace("`", "") msg = msg[:50] + ("..." if len(msg) > 50 else "") desc += f"\n[`{commit['id'][:7]}`]({commit['url']}): {msg} - [__{commit['author']['username']}__](https://github.com/{commit['author']['username']})" - if count > 10: - desc += f"\n+ {count - 10} more commits" + if len(desc) > 2020: + desc = desc.rsplit("\n", 1)[0] + f"\n+ {count - i} more commits" + break url = event["compare"] color = 16723712 if event["forced"] else 3669797 From a0d6c6e63c3d2a4346cade5dea5ffe5c5fa674de Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 23 Apr 2023 18:43:58 +0100 Subject: [PATCH 109/282] Allow setting folders as favorites in archive --- .../main/archive/helpers/archive_browser.h | 2 +- .../main/archive/helpers/archive_favorites.c | 2 +- .../archive/scenes/archive_scene_browser.c | 7 ++- .../main/archive/views/archive_browser_view.c | 52 ++++++++++--------- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 36da022da..a96fa9679 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -62,7 +62,7 @@ static inline const char* archive_get_default_path(ArchiveTabEnum tab) { } inline bool archive_is_known_app(ArchiveFileTypeEnum type) { - return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); + return (type != ArchiveFileTypeUnknown); } bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx); diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c index 7304be6b4..9d4b963f5 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -164,7 +164,7 @@ bool archive_favorites_read(void* context) { need_refresh = true; } } else { - if(storage_file_exists(storage, furi_string_get_cstr(buffer))) { + if(storage_common_exists(storage, furi_string_get_cstr(buffer))) { storage_common_stat(storage, furi_string_get_cstr(buffer), &file_info); archive_add_file_item( browser, file_info_is_dir(&file_info), furi_string_get_cstr(buffer)); diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index 8d606cf09..3154fbec6 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -21,6 +21,7 @@ static const char* flipper_app_name[] = { [ArchiveFileTypeU2f] = "U2F", [ArchiveFileTypeApplication] = "Apps", [ArchiveFileTypeUpdateManifest] = "UpdaterApp", + [ArchiveFileTypeFolder] = "Archive", }; static void archive_loader_callback(const void* message, void* context) { @@ -107,7 +108,11 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventFileMenuRun: - if(archive_is_known_app(selected->type)) { + if(selected->type == ArchiveFileTypeFolder) { + archive_switch_tab(browser, TAB_LEFT); + archive_show_file_menu(browser, false); + archive_enter_dir(browser, selected->path); + } else if(archive_is_known_app(selected->type)) { archive_run_in_app(browser, selected); archive_show_file_menu(browser, false); } diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 994290a3c..6e42cb40b 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -66,8 +66,35 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { furi_string_set(item_pin, "Unpin"); } - if(selected->type == ArchiveFileTypeFolder) { + if(model->tab_idx == ArchiveTabFavorites) { + //FURI_LOG_D(TAG, "ArchiveTabFavorites"); + + furi_string_set(item_rename, "Move"); + + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_run, + ArchiveBrowserEventFileMenuRun); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_pin, + ArchiveBrowserEventFileMenuPin); + if(selected->type <= ArchiveFileTypeBadKb) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_show, + ArchiveBrowserEventFileMenuShow); + } + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_rename, + ArchiveBrowserEventFileMenuRename); + } else if(selected->type == ArchiveFileTypeFolder) { //FURI_LOG_D(TAG, "Directory type"); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_pin, + ArchiveBrowserEventFileMenuPin); archive_menu_add_item( menu_array_push_raw(model->context_menu), item_rename, @@ -97,29 +124,6 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { menu_array_push_raw(model->context_menu), item_delete, ArchiveBrowserEventFileMenuDelete); - } else if(model->tab_idx == ArchiveTabFavorites) { - //FURI_LOG_D(TAG, "ArchiveTabFavorites"); - - furi_string_set(item_rename, "Move"); - - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - if(selected->type <= ArchiveFileTypeBadKb) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); - } - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_rename, - ArchiveBrowserEventFileMenuRename); } else if(selected->is_app) { //FURI_LOG_D(TAG, "3 types"); archive_menu_add_item( From 8cb3b67295dfbe5b638c2bbd75e600d6ced077b6 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 24 Apr 2023 01:56:43 +0300 Subject: [PATCH 110/282] Fix external radio SPI handlers --- applications/external/playlist/playlist.c | 5 ++++- applications/external/pocsag_pager/pocsag_pager_app.c | 5 ++++- applications/external/protoview/app.c | 5 ++++- .../external/spectrum_analyzer/spectrum_analyzer.c | 5 ++++- applications/external/subbrute | 2 +- .../external/weather_station/weather_station_app.c | 5 ++++- .../subghz/scenes/subghz_scene_ext_module_settings.c | 6 ++++-- applications/main/subghz/scenes/subghz_scene_start.c | 3 ++- applications/main/subghz/subghz.c | 5 ++++- applications/main/subghz/subghz_last_settings.c | 3 ++- applications/main/subghz_remote/subghz_remote_app.c | 5 ++++- firmware/targets/f7/api_symbols.csv | 3 ++- firmware/targets/f7/furi_hal/furi_hal_subghz.c | 9 +++++++-- firmware/targets/f7/furi_hal/furi_hal_subghz.h | 6 +++++- 14 files changed, 51 insertions(+), 16 deletions(-) diff --git a/applications/external/playlist/playlist.c b/applications/external/playlist/playlist.c index 263b296e5..1ecfda02a 100644 --- a/applications/external/playlist/playlist.c +++ b/applications/external/playlist/playlist.c @@ -716,7 +716,8 @@ int32_t playlist_app(void* p) { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -809,6 +810,8 @@ exit_cleanup: furi_hal_power_suppress_charge_exit(); // Disable power for External CC1101 if it was enabled and module is connected furi_hal_subghz_disable_ext_power(); + // Reinit SPI handles for internal radio / nfc + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); if(app->worker != NULL) { if(playlist_worker_running(app->worker)) { diff --git a/applications/external/pocsag_pager/pocsag_pager_app.c b/applications/external/pocsag_pager/pocsag_pager_app.c index f5e989fba..1013164f2 100644 --- a/applications/external/pocsag_pager/pocsag_pager_app.c +++ b/applications/external/pocsag_pager/pocsag_pager_app.c @@ -127,7 +127,8 @@ POCSAGPagerApp* pocsag_pager_app_alloc() { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -145,6 +146,8 @@ void pocsag_pager_app_free(POCSAGPagerApp* app) { // Disable power for External CC1101 if it was enabled and module is connected furi_hal_subghz_disable_ext_power(); + // Reinit SPI handles for internal radio / nfc + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); // Submenu view_dispatcher_remove_view(app->view_dispatcher, POCSAGPagerViewSubmenu); diff --git a/applications/external/protoview/app.c b/applications/external/protoview/app.c index 678c9d75f..3e22b3781 100644 --- a/applications/external/protoview/app.c +++ b/applications/external/protoview/app.c @@ -172,7 +172,8 @@ ProtoViewApp* protoview_app_alloc() { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -192,6 +193,8 @@ void protoview_app_free(ProtoViewApp* app) { // Disable power for External CC1101 if it was enabled and module is connected furi_hal_subghz_disable_ext_power(); + // Reinit SPI handles for internal radio / nfc + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); // View related. view_port_enabled_set(app->view_port, false); diff --git a/applications/external/spectrum_analyzer/spectrum_analyzer.c b/applications/external/spectrum_analyzer/spectrum_analyzer.c index d38c6bf23..26e41f0ce 100644 --- a/applications/external/spectrum_analyzer/spectrum_analyzer.c +++ b/applications/external/spectrum_analyzer/spectrum_analyzer.c @@ -395,6 +395,8 @@ void spectrum_analyzer_free(SpectrumAnalyzer* instance) { // Disable power for External CC1101 if it was enabled and module is connected furi_hal_subghz_disable_ext_power(); + // Reinit SPI handles for internal radio / nfc + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } int32_t spectrum_analyzer_app(void* p) { @@ -408,7 +410,8 @@ int32_t spectrum_analyzer_app(void* p) { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); diff --git a/applications/external/subbrute b/applications/external/subbrute index df12fcd72..c94efdf88 160000 --- a/applications/external/subbrute +++ b/applications/external/subbrute @@ -1 +1 @@ -Subproject commit df12fcd72e168fd1b3007ca767fe8ba13c024ba7 +Subproject commit c94efdf88ac2ed51b88caf288ab3c50afaaec9bb diff --git a/applications/external/weather_station/weather_station_app.c b/applications/external/weather_station/weather_station_app.c index a3135a6b0..8bea4961d 100644 --- a/applications/external/weather_station/weather_station_app.c +++ b/applications/external/weather_station/weather_station_app.c @@ -110,7 +110,8 @@ WeatherStationApp* weather_station_app_alloc() { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -128,6 +129,8 @@ void weather_station_app_free(WeatherStationApp* app) { // Disable power for External CC1101 if it was enabled and module is connected furi_hal_subghz_disable_ext_power(); + // Reinit SPI handles for internal radio / nfc + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); // Submenu view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewSubmenu); diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c index 75a2215fc..508ca7dcc 100644 --- a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c @@ -209,14 +209,16 @@ bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent UNUSED(event); // Set selected radio module - furi_hal_subghz_set_radio_type(value_index_exm); + furi_hal_subghz_select_radio_type(value_index_exm); + furi_hal_subghz_init_radio_type(value_index_exm); furi_hal_subghz_enable_ext_power(); // Check if module is present, if no -> show error if(!furi_hal_subghz_check_radio()) { value_index_exm = 0; - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); furi_string_set(subghz->error_str, "Please connect\nexternal radio"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); } diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index eb14315a8..8777d2461 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -84,7 +84,8 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { furi_hal_subghz_enable_ext_power(); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); subghz->last_settings->external_module_enabled = false; furi_string_set(subghz->error_str, "Please connect\nexternal radio"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 75c6e627a..47da7ae40 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -451,7 +451,8 @@ int32_t subghz_app(void* p) { // Auto switch to internal radio if external radio is not available if(!furi_hal_subghz_check_radio()) { subghz->last_settings->external_module_enabled = false; - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } // Check argument and run corresponding scene if(p && strlen(p)) { @@ -507,6 +508,8 @@ int32_t subghz_app(void* p) { furi_hal_power_suppress_charge_exit(); // Disable power for External CC1101 if it was enabled and module is connected furi_hal_subghz_disable_ext_power(); + // Reinit SPI handles for internal radio / nfc + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); subghz_free(subghz, alloc_for_tx); diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 2f4445a42..7ee2554b0 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -129,7 +129,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count // Set selected radio module if(instance->external_module_enabled) { - furi_hal_subghz_set_radio_type(SubGhzRadioExternal); + furi_hal_subghz_select_radio_type(SubGhzRadioExternal); + furi_hal_subghz_init_radio_type(SubGhzRadioExternal); } /*/} else { diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index 64f866b87..d215a1d0e 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -765,7 +765,8 @@ SubGHzRemote* subghz_remote_alloc(void) { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -792,6 +793,8 @@ void subghz_remote_free(SubGHzRemote* app, bool with_subghz) { // Disable power for External CC1101 if it was enabled and module is connected furi_hal_subghz_disable_ext_power(); + // Reinit SPI handles for internal radio / nfc + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); furi_string_free(app->up_file); furi_string_free(app->down_file); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d9cc6d21f..a61e236c8 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1377,6 +1377,7 @@ Function,+,furi_hal_subghz_get_timestamp_file_names,_Bool, Function,+,furi_hal_subghz_idle,void, Function,-,furi_hal_subghz_init,void, Function,+,furi_hal_subghz_init_check,_Bool, +Function,+,furi_hal_subghz_init_radio_type,_Bool,SubGhzRadioType Function,+,furi_hal_subghz_is_async_tx_complete,_Bool, Function,+,furi_hal_subghz_is_frequency_valid,_Bool,uint32_t Function,+,furi_hal_subghz_is_rx_data_crc_valid,_Bool, @@ -1389,12 +1390,12 @@ Function,+,furi_hal_subghz_read_packet,void,"uint8_t*, uint8_t*" Function,+,furi_hal_subghz_reset,void, Function,+,furi_hal_subghz_rx,void, Function,+,furi_hal_subghz_rx_pipe_not_empty,_Bool, +Function,+,furi_hal_subghz_select_radio_type,void,SubGhzRadioType Function,+,furi_hal_subghz_set_async_mirror_pin,void,const GpioPin* Function,+,furi_hal_subghz_set_external_power_disable,void,_Bool Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath -Function,+,furi_hal_subghz_set_radio_type,_Bool,SubGhzRadioType Function,+,furi_hal_subghz_set_rolling_counter_mult,void,uint8_t Function,+,furi_hal_subghz_set_timestamp_file_names,void,_Bool Function,-,furi_hal_subghz_shutdown,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 8667f2c5f..952e840fe 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -43,11 +43,14 @@ volatile FuriHalSubGhz furi_hal_subghz = { .timestamp_file_names = false, }; -bool furi_hal_subghz_set_radio_type(SubGhzRadioType state) { +void furi_hal_subghz_select_radio_type(SubGhzRadioType state) { furi_hal_subghz.radio_type = state; +} + +bool furi_hal_subghz_init_radio_type(SubGhzRadioType state) { furi_hal_spi_bus_handle_deinit(furi_hal_subghz.spi_bus_handle); - if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { + if(state == SubGhzRadioInternal) { furi_hal_subghz.spi_bus_handle = &furi_hal_spi_bus_handle_subghz; furi_hal_subghz.cc1101_g0_pin = &gpio_cc1101_g0; } else { @@ -120,6 +123,8 @@ void furi_hal_subghz_disable_ext_power(void) { bool furi_hal_subghz_check_radio(void) { bool result = true; + furi_hal_subghz_init_radio_type(furi_hal_subghz.radio_type); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); uint8_t ver = cc1101_get_version(furi_hal_subghz.spi_bus_handle); diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index b366e8578..35eab4faf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -292,7 +292,7 @@ void furi_hal_subghz_stop_async_tx(); * @param state SubGhzRadioInternal or SubGhzRadioExternal * @return true if switching is successful */ -bool furi_hal_subghz_set_radio_type(SubGhzRadioType state); +bool furi_hal_subghz_init_radio_type(SubGhzRadioType state); /** Get current radio * @return SubGhzRadioInternal or SubGhzRadioExternal @@ -339,6 +339,10 @@ void furi_hal_subghz_set_timestamp_file_names(bool state); */ bool furi_hal_subghz_get_timestamp_file_names(void); +/** Set what radio module we will be using + */ +void furi_hal_subghz_select_radio_type(SubGhzRadioType state); + #ifdef __cplusplus } #endif From 1ef70c0bb4db8c4b7f537b39195fcba419af023b Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 24 Apr 2023 11:19:36 +0400 Subject: [PATCH 111/282] [FL-3280] cubewb: downgraded to v1.15.0 (#2605) * cubewb: downgraded to v1.15.0 * hal: updated f18 symbols to match LL * hal: flash: use furi_hal_cortex_timer for timeouts * scripts: fixed cube version validation from config file * hal: flash: added 3 seconds timeout when waiting for C2 to unlock flash controller. On timeout, triggers furi_check * nfc: fixed missing interrupt setup on multiple platformSetIrqCallback() invocations * hal: gpio: don't trigger furi_check on furi_hal_gpio_add_int_callback() with same parameters * Reverted NFC fixes - will be in a separate PR * scripts: storage: fixed exception handler for paths --- fbt_options.py | 2 +- firmware/targets/f18/api_symbols.csv | 4 +-- firmware/targets/f7/api_symbols.csv | 4 +-- firmware/targets/f7/ble_glue/ble_app.c | 4 +-- firmware/targets/f7/furi_hal/furi_hal_flash.c | 30 ++++++++++++------- lib/STM32CubeWB | 2 +- scripts/fbt_tools/fbt_dist.py | 4 ++- scripts/flipper/assets/copro.py | 4 +-- scripts/flipper/storage.py | 4 ++- 9 files changed, 35 insertions(+), 23 deletions(-) diff --git a/fbt_options.py b/fbt_options.py index 25e84afde..4fd7ef496 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -20,7 +20,7 @@ DIST_SUFFIX = "local" COPRO_OB_DATA = "scripts/ob.data" # Must match lib/STM32CubeWB version -COPRO_CUBE_VERSION = "1.16.0" +COPRO_CUBE_VERSION = "1.15.0" COPRO_CUBE_DIR = "lib/STM32CubeWB" diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 4ee3d8636..eb2d6f43f 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -199,8 +199,8 @@ Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef* Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* -Function,-,LL_I2C_DeInit,ErrorStatus,const I2C_TypeDef* -Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, const LL_I2C_InitTypeDef*" +Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef* +Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*" Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* Function,-,LL_Init1msTick,void,uint32_t Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a2d70e7f8..d0c6b36ad 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -231,8 +231,8 @@ Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef* Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* -Function,-,LL_I2C_DeInit,ErrorStatus,const I2C_TypeDef* -Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, const LL_I2C_InitTypeDef*" +Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef* +Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*" Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* Function,-,LL_Init1msTick,void,uint32_t Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index cc6065b97..a325830cf 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; _Static_assert( - sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 58, - "Ble stack config structure size mismatch (check new config options - last updated for v.1.16.0)"); + sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 57, + "Ble stack config structure size mismatch (check new config options - last updated for v.1.15.0)"); typedef struct { FuriMutex* hci_mtx; diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c index d2dbff55f..464d88d95 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ b/firmware/targets/f7/furi_hal/furi_hal_flash.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,16 @@ #define FURI_HAL_FLASH_OPT_KEY2 0x4C5D6E7F #define FURI_HAL_FLASH_OB_TOTAL_WORDS (0x80 / (sizeof(uint32_t) * 2)) +/* lib/STM32CubeWB/Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_RfWithFlash/Core/Src/flash_driver.c + * ProcessSingleFlashOperation, quote: + > In most BLE application, the flash should not be blocked by the CPU2 longer than FLASH_TIMEOUT_VALUE (1000ms) + > However, it could be that for some marginal application, this time is longer. + > ... there is no other way than waiting the operation to be completed. + > If for any reason this test is never passed, this means there is a failure in the system and there is no other + > way to recover than applying a device reset. + */ +#define FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS 3000u /* 3 seconds */ + #define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__)&0x7U) == (0x00UL)) #define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \ (((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \ @@ -131,9 +142,11 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) { for(volatile uint32_t i = 0; i < 35; i++) ; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS * 1000); while(true) { /* Wait till flash controller become usable */ while(LL_FLASH_IsActiveFlag_OperationSuspended()) { + furi_check(!furi_hal_cortex_timer_is_expired(timer)); furi_thread_yield(); }; @@ -143,6 +156,7 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) { /* Actually we already have mutex for it, but specification is specification */ if(LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) { taskEXIT_CRITICAL(); + furi_check(!furi_hal_cortex_timer_is_expired(timer)); furi_thread_yield(); continue; } @@ -150,6 +164,7 @@ static void furi_hal_flash_begin_with_core2(bool erase_flag) { /* Take sempahopre and prevent core2 from anything funky */ if(LL_HSEM_1StepLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != 0) { taskEXIT_CRITICAL(); + furi_check(!furi_hal_cortex_timer_is_expired(timer)); furi_thread_yield(); continue; } @@ -231,17 +246,13 @@ static void furi_hal_flush_cache(void) { bool furi_hal_flash_wait_last_operation(uint32_t timeout) { uint32_t error = 0; - uint32_t countdown = 0; /* Wait for the FLASH operation to complete by polling on BUSY flag to be reset. Even if the FLASH operation fails, the BUSY flag will be reset and an error flag will be set */ - countdown = timeout; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); while(READ_BIT(FLASH->SR, FLASH_SR_BSY)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } @@ -264,12 +275,9 @@ bool furi_hal_flash_wait_last_operation(uint32_t timeout) { CLEAR_BIT(FLASH->SR, error); /* Wait for control register to be written */ - countdown = timeout; + timer = furi_hal_cortex_timer_get(timeout * 1000); while(READ_BIT(FLASH->SR, FLASH_SR_CFGBSY)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { + if(furi_hal_cortex_timer_is_expired(timer)) { return false; } } diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB index 06b8133fa..c4cec8ae5 160000 --- a/lib/STM32CubeWB +++ b/lib/STM32CubeWB @@ -1 +1 @@ -Subproject commit 06b8133fa295474507b55b1a5695d4b8bf804ed6 +Subproject commit c4cec8ae57a79e949a184cd0b4117a008a0a25a7 diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index f0b443486..d2808419c 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -112,6 +112,8 @@ def DistCommand(env, name, source, **kw): def generate(env): + if not env["VERBOSE"]: + env.SetDefault(COPROCOMSTR="\tCOPRO\t${TARGET}") env.AddMethod(AddFwProject) env.AddMethod(DistCommand) env.AddMethod(AddOpenOCDFlashTarget) @@ -147,7 +149,7 @@ def generate(env): '--stack_file="${COPRO_STACK_BIN}" ' "--stack_addr=${COPRO_STACK_ADDR} ", ], - "\tCOPRO\t${TARGET}", + "$COPROCOMSTR", ) ), } diff --git a/scripts/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index b61ac0329..e0375b51f 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -34,7 +34,7 @@ class Copro: self.mcu_copro = None self.logger = logging.getLogger(self.__class__.__name__) - def loadCubeInfo(self, cube_dir, cube_version): + def loadCubeInfo(self, cube_dir, reference_cube_version): if not os.path.isdir(cube_dir): raise Exception(f'"{cube_dir}" doesn\'t exists') self.cube_dir = cube_dir @@ -50,7 +50,7 @@ class Copro: if not cube_version or not cube_version.startswith("FW.WB"): raise Exception(f"Incorrect Cube package or version info") cube_version = cube_version.replace("FW.WB.", "", 1) - if cube_version != cube_version: + if cube_version != reference_cube_version: raise Exception(f"Unsupported cube version") self.version = cube_version diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 7b56ee0d0..cff32ceb1 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -335,7 +335,9 @@ class FlipperStorage: def _check_no_error(self, response, path=None): if self.has_error(response): - raise FlipperStorageException.from_error_code(self.get_error(response)) + raise FlipperStorageException.from_error_code( + path, self.get_error(response) + ) def size(self, path: str): """file size on Flipper""" From 87a023c75d4652e7c45c06143ba6c3c2c3411966 Mon Sep 17 00:00:00 2001 From: nminaylov Date: Mon, 24 Apr 2023 18:00:49 +0300 Subject: [PATCH 112/282] BadUSB: command parser fix --- .../bad_usb/helpers/ducky_script_commands.c | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/applications/main/bad_usb/helpers/ducky_script_commands.c b/applications/main/bad_usb/helpers/ducky_script_commands.c index 1498c9a73..d073b4c8d 100644 --- a/applications/main/bad_usb/helpers/ducky_script_commands.c +++ b/applications/main/bad_usb/helpers/ducky_script_commands.c @@ -152,22 +152,22 @@ static int32_t ducky_fnc_waitforbutton(BadUsbScript* bad_usb, const char* line, } static const DuckyCmd ducky_commands[] = { - {"REM ", NULL, -1}, - {"ID ", NULL, -1}, - {"DELAY ", ducky_fnc_delay, -1}, - {"STRING ", ducky_fnc_string, 0}, - {"STRINGLN ", ducky_fnc_string, 1}, - {"DEFAULT_DELAY ", ducky_fnc_defdelay, -1}, - {"DEFAULTDELAY ", ducky_fnc_defdelay, -1}, - {"STRINGDELAY ", ducky_fnc_strdelay, -1}, - {"STRING_DELAY ", ducky_fnc_strdelay, -1}, - {"REPEAT ", ducky_fnc_repeat, -1}, - {"SYSRQ ", ducky_fnc_sysrq, -1}, - {"ALTCHAR ", ducky_fnc_altchar, -1}, - {"ALTSTRING ", ducky_fnc_altstring, -1}, - {"ALTCODE ", ducky_fnc_altstring, -1}, - {"HOLD ", ducky_fnc_hold, -1}, - {"RELEASE ", ducky_fnc_release, -1}, + {"REM", NULL, -1}, + {"ID", NULL, -1}, + {"DELAY", ducky_fnc_delay, -1}, + {"STRING", ducky_fnc_string, 0}, + {"STRINGLN", ducky_fnc_string, 1}, + {"DEFAULT_DELAY", ducky_fnc_defdelay, -1}, + {"DEFAULTDELAY", ducky_fnc_defdelay, -1}, + {"STRINGDELAY", ducky_fnc_strdelay, -1}, + {"STRING_DELAY", ducky_fnc_strdelay, -1}, + {"REPEAT", ducky_fnc_repeat, -1}, + {"SYSRQ", ducky_fnc_sysrq, -1}, + {"ALTCHAR", ducky_fnc_altchar, -1}, + {"ALTSTRING", ducky_fnc_altstring, -1}, + {"ALTCODE", ducky_fnc_altstring, -1}, + {"HOLD", ducky_fnc_hold, -1}, + {"RELEASE", ducky_fnc_release, -1}, {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, }; @@ -175,8 +175,15 @@ static const DuckyCmd ducky_commands[] = { #define WORKER_TAG TAG "Worker" int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) { + size_t cmd_word_len = strcspn(line, " "); for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { - if(strncmp(line, ducky_commands[i].name, strlen(ducky_commands[i].name)) == 0) { + size_t cmd_compare_len = strlen(ducky_commands[i].name); + + if(cmd_compare_len != cmd_word_len) { + continue; + } + + if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) { if(ducky_commands[i].callback == NULL) { return 0; } else { From ab669b26dab688067682e77e0c3789955783790d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 25 Apr 2023 01:42:20 +0300 Subject: [PATCH 113/282] Fix very old and funny subghz bugs Fixed Frequency Ananyzer issues Fixed read mode issues Fixed re-inits in HAL --- applications/main/subghz/helpers/subghz_custom_event.h | 3 +++ .../main/subghz/scenes/subghz_scene_frequency_analyzer.c | 5 +++-- applications/main/subghz/subghz_history.c | 5 +++++ applications/main/subghz/views/subghz_frequency_analyzer.c | 4 ++-- firmware/targets/f7/furi_hal/furi_hal_subghz.c | 5 +++++ 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 9d2be7ee5..ad49bf09b 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -103,5 +103,8 @@ typedef enum { SubGhzCustomEventViewTransmitterSendStop, SubGhzCustomEventViewTransmitterError, + SubGhzCustomEventViewFreqAnalOkShort, + SubGhzCustomEventViewFreqAnalOkLong, + SubGhzCustomEventByteInputDone, } SubGhzCustomEvent; diff --git a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c index bed044244..b1071d836 100644 --- a/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -54,7 +54,7 @@ bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent e } else if(event.event == SubGhzCustomEventSceneAnalyzerUnlock) { notification_message(subghz->notifications, &sequence_reset_rgb); return true; - } else if(event.event == SubGhzCustomEventViewReceiverOK) { + } else if(event.event == SubGhzCustomEventViewFreqAnalOkShort) { notification_message(subghz->notifications, &sequence_saved); uint32_t frequency = subghz_frequency_analyzer_get_frequency_to_save(subghz->subghz_frequency_analyzer); @@ -64,11 +64,12 @@ bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent e } return true; - } else if(event.event == SubGhzCustomEventViewReceiverUnlock) { + } else if(event.event == SubGhzCustomEventViewFreqAnalOkLong) { // Don't need to save, we already saved on short event #ifdef FURI_DEBUG FURI_LOG_W(TAG, "Goto next scene!"); #endif + //scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneStart, 10); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); return true; } diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index 51d5ada7d..3c018ec8b 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -125,6 +125,11 @@ uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx) const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + if(!item || !item->flipper_string) { + FURI_LOG_E(TAG, "Missing Item"); + furi_string_reset(instance->tmp_string); + return furi_string_get_cstr(instance->tmp_string); + } flipper_format_rewind(item->flipper_string); if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { FURI_LOG_E(TAG, "Missing Protocol"); diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 3b8c37d73..23c05e4c5 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -372,7 +372,7 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { #endif if(updated) { - instance->callback(SubGhzCustomEventViewReceiverOK, instance->context); + instance->callback(SubGhzCustomEventViewFreqAnalOkShort, instance->context); } // First device receive short, then when user release button we get long @@ -385,7 +385,7 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { subghz_frequency_analyzer_worker_stop(instance->worker); } - instance->callback(SubGhzCustomEventViewReceiverUnlock, instance->context); + instance->callback(SubGhzCustomEventViewFreqAnalOkLong, instance->context); } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 952e840fe..6a566d471 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -48,6 +48,11 @@ void furi_hal_subghz_select_radio_type(SubGhzRadioType state) { } bool furi_hal_subghz_init_radio_type(SubGhzRadioType state) { + if(state == SubGhzRadioInternal && furi_hal_subghz.cc1101_g0_pin == &gpio_cc1101_g0) { + return true; + } else if(state == SubGhzRadioExternal && furi_hal_subghz.cc1101_g0_pin == &gpio_cc1101_g0_ext) { + return true; + } furi_hal_spi_bus_handle_deinit(furi_hal_subghz.spi_bus_handle); if(state == SubGhzRadioInternal) { From 6271409e5a8405d50f86c70d7529c01ac91b1d5e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 25 Apr 2023 02:09:29 +0300 Subject: [PATCH 114/282] Allow locking without pin using Up menu on desktop --- applications/services/desktop/views/desktop_view_lock_menu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 8b25a890f..8bf868ebf 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -137,9 +137,9 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { if(event->key == InputKeyOk) { if((idx == DesktopLockMenuIndexLock)) { - if((pin_is_set) && (event->type == InputTypeShort)) { + if((pin_is_set) && (event->type == InputTypeLong)) { lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context); - } else if((pin_is_set == false) && (event->type == InputTypeShort)) { + } else if(event->type == InputTypeShort) { lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); } } else if(idx == DesktopLockMenuIndexStealth) { From d7f9cbf9ca65cd2d7e5426110e686cc18918cd53 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 25 Apr 2023 02:40:33 +0300 Subject: [PATCH 115/282] CI update args --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 11592861f..7776bce6b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -11,7 +11,7 @@ steps: image: alpine/git commands: - git submodule sync - - git -c protocol.version=2 submodule update --init --force --recursive + - git -c protocol.version=2 submodule update --init --force --recursive --jobs 4 - git submodule foreach git config --local gc.auto 0 - git log -1 --format='%H' @@ -258,7 +258,7 @@ steps: image: alpine/git commands: - git submodule sync - - git -c protocol.version=2 submodule update --init --force --recursive + - git -c protocol.version=2 submodule update --init --force --recursive --jobs 4 - git submodule foreach git config --local gc.auto 0 - git log -1 --format='%H' From 30447d76607df5ec8a6cb891807a37d9fd593cbb Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 25 Apr 2023 03:39:16 +0300 Subject: [PATCH 116/282] Rename to make it fit on screen --- applications/external/hid_app/hid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index c86def18f..f29a3b22a 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -131,7 +131,7 @@ Hid* hid_alloc(HidTransport transport) { app); submenu_add_item( app->device_type_submenu, - "[Beta]YT Shorts Controller", + "YT Shorts Controller", HidSubmenuIndexYTShorts, hid_submenu_callback, app); From 6a9bdeae3e51e8629bd777a7d39f205a3e727f8d Mon Sep 17 00:00:00 2001 From: SkorP Date: Tue, 25 Apr 2023 11:22:35 +0400 Subject: [PATCH 117/282] WS: add CRC, refactoring --- .../protocols/protocol_items.c | 2 +- .../protocols/protocol_items.h | 2 +- .../weather_station/protocols/wendox._w6726.c | 291 ++++++++++++++++++ .../weather_station/protocols/wendox.c | 248 --------------- .../weather_station/protocols/wendox.h | 80 ----- .../weather_station/protocols/wendox_w6726.h | 80 +++++ 6 files changed, 373 insertions(+), 330 deletions(-) create mode 100644 applications/external/weather_station/protocols/wendox._w6726.c delete mode 100644 applications/external/weather_station/protocols/wendox.c delete mode 100644 applications/external/weather_station/protocols/wendox.h create mode 100644 applications/external/weather_station/protocols/wendox_w6726.h diff --git a/applications/external/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c index 098014703..cd4bae76d 100644 --- a/applications/external/weather_station/protocols/protocol_items.c +++ b/applications/external/weather_station/protocols/protocol_items.c @@ -16,7 +16,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_auriol_th, &ws_protocol_oregon_v1, &ws_protocol_tx_8300, - &ws_protocol_wendox, + &ws_protocol_wendox_w6726, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/external/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h index 2268150f2..0398c11f2 100644 --- a/applications/external/weather_station/protocols/protocol_items.h +++ b/applications/external/weather_station/protocols/protocol_items.h @@ -16,6 +16,6 @@ #include "auriol_hg0601a.h" #include "oregon_v1.h" #include "tx_8300.h" -#include "wendox.h" +#include "wendox_w6726.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/applications/external/weather_station/protocols/wendox._w6726.c b/applications/external/weather_station/protocols/wendox._w6726.c new file mode 100644 index 000000000..3976f6a37 --- /dev/null +++ b/applications/external/weather_station/protocols/wendox._w6726.c @@ -0,0 +1,291 @@ +#include "wendox_w6726.h" + +#define TAG "WSProtocolWendoxW6726" + +/* + * Wendox W6726 + * + * Temperature -50 to +70 + * _ _ _ __ _ + * _| |___| |___| |___ ... | |_| |__...._______________ + * preamble data guard time + * + * 3 reps every 3 minutes + * in the first message 11 bytes of the preamble in the rest by 7 + * + * bit 0: 1955-hi, 5865-lo + * bit 1: 5865-hi, 1955-lo + * + * guard time: 12*1955+(lo last bit) + * data: 29 bit + */ + +static const SubGhzBlockConst ws_protocol_wendox_w6726_const = { + .te_short = 1955, + .te_long = 5865, + .te_delta = 300, + .min_count_bit_for_found = 29, +}; + +struct WSProtocolDecoderWendoxW6726 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderWendoxW6726 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + WendoxW6726DecoderStepReset = 0, + WendoxW6726DecoderStepCheckPreambule, + WendoxW6726DecoderStepSaveDuration, + WendoxW6726DecoderStepCheckDuration, +} WendoxW6726DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder = { + .alloc = ws_protocol_decoder_wendox_w6726_alloc, + .free = ws_protocol_decoder_wendox_w6726_free, + + .feed = ws_protocol_decoder_wendox_w6726_feed, + .reset = ws_protocol_decoder_wendox_w6726_reset, + + .get_hash_data = ws_protocol_decoder_wendox_w6726_get_hash_data, + .serialize = ws_protocol_decoder_wendox_w6726_serialize, + .deserialize = ws_protocol_decoder_wendox_w6726_deserialize, + .get_string = ws_protocol_decoder_wendox_w6726_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_wendox_w6726 = { + .name = WS_PROTOCOL_WENDOX_W6726_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_wendox_w6726_decoder, + .encoder = &ws_protocol_wendox_w6726_encoder, +}; + +void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderWendoxW6726* instance = malloc(sizeof(WSProtocolDecoderWendoxW6726)); + instance->base.protocol = &ws_protocol_wendox_w6726; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_wendox_w6726_free(void* context) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + free(instance); +} + +void ws_protocol_decoder_wendox_w6726_reset(void* context) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + instance->decoder.parser_step = WendoxW6726DecoderStepReset; +} + +static bool ws_protocol_wendox_w6726_check(WSProtocolDecoderWendoxW6726* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t msg[] = { + instance->decoder.decode_data >> 28, + instance->decoder.decode_data >> 20, + instance->decoder.decode_data >> 12, + instance->decoder.decode_data >> 4}; + + uint8_t crc = subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD); + return (crc == (instance->decoder.decode_data & 0x0F)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_wendox_w6726_remote_controller(WSBlockGeneric* instance) { + instance->id = (instance->data >> 24) & 0xFF; + instance->battery_low = (instance->data >> 6) & 1; + instance->channel = WS_NO_CHANNEL; + + if(((instance->data >> 23) & 1)) { + instance->temp = (float)(((instance->data >> 14) & 0x1FF) + 12) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 14) & 0x1FF) + 1 - 12) / -10.0f; + } + + if(instance->temp < -50.0f) { + instance->temp = -50.0f; + } else if(instance->temp > 70.0f) { + instance->temp = 70.0f; + } + + instance->btn = WS_NO_BTN; + instance->humidity = WS_NO_HUMIDITY; +} + +void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + + switch(instance->decoder.parser_step) { + case WendoxW6726DecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta)) { + instance->decoder.parser_step = WendoxW6726DecoderStepCheckPreambule; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case WendoxW6726DecoderStepCheckPreambule: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta * 1) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2)) { + instance->header_count++; + } else if((instance->header_count > 4) && (instance->header_count < 12)) { + if((DURATION_DIFF( + instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta)) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + } + break; + + case WendoxW6726DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = WendoxW6726DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + break; + + case WendoxW6726DecoderStepCheckDuration: + if(!level) { + if(duration > + ws_protocol_wendox_w6726_const.te_short + ws_protocol_wendox_w6726_const.te_long) { + if(DURATION_DIFF( + instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else if( + DURATION_DIFF( + instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + if((instance->decoder.decode_count_bit == + ws_protocol_wendox_w6726_const.min_count_bit_for_found) && + ws_protocol_wendox_w6726_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_wendox_w6726_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < + ws_protocol_wendox_w6726_const.te_delta * 2) && + (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < + ws_protocol_wendox_w6726_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + } else { + instance->decoder.parser_step = WendoxW6726DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + ws_protocol_wendox_w6726_const.min_count_bit_for_found); +} + +void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderWendoxW6726* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/external/weather_station/protocols/wendox.c b/applications/external/weather_station/protocols/wendox.c deleted file mode 100644 index 94e09f795..000000000 --- a/applications/external/weather_station/protocols/wendox.c +++ /dev/null @@ -1,248 +0,0 @@ -#include "wendox.h" - -#define TAG "WSProtocolWendox" - -static const SubGhzBlockConst ws_protocol_wendox_const = { - .te_short = 1955, - .te_long = 5865, - .te_delta = 300, - .min_count_bit_for_found = 28, -}; - -struct WSProtocolDecoderWendox { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - - uint16_t header_count; -}; - -struct WSProtocolEncoderWendox { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - WendoxDecoderStepReset = 0, - WendoxDecoderStepCheckPreambule, - WendoxDecoderStepSaveDuration, - WendoxDecoderStepCheckDuration, -} WendoxDecoderStep; - -const SubGhzProtocolDecoder ws_protocol_wendox_decoder = { - .alloc = ws_protocol_decoder_wendox_alloc, - .free = ws_protocol_decoder_wendox_free, - - .feed = ws_protocol_decoder_wendox_feed, - .reset = ws_protocol_decoder_wendox_reset, - - .get_hash_data = ws_protocol_decoder_wendox_get_hash_data, - .serialize = ws_protocol_decoder_wendox_serialize, - .deserialize = ws_protocol_decoder_wendox_deserialize, - .get_string = ws_protocol_decoder_wendox_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_wendox_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_wendox = { - .name = WS_PROTOCOL_WENDOX_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_wendox_decoder, - .encoder = &ws_protocol_wendox_encoder, -}; - -void* ws_protocol_decoder_wendox_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderWendox* instance = malloc(sizeof(WSProtocolDecoderWendox)); - instance->base.protocol = &ws_protocol_wendox; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_wendox_free(void* context) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - free(instance); -} - -void ws_protocol_decoder_wendox_reset(void* context) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - instance->decoder.parser_step = WendoxDecoderStepReset; -} - -// static bool ws_protocol_wendox_check(WSProtocolDecoderWendox* instance) { -// if(!instance->decoder.decode_data) return false; -// uint8_t msg[] = { -// instance->decoder.decode_data >> 24, -// instance->decoder.decode_data >> 16, -// instance->decoder.decode_data >> 8}; - -// uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 3, 0x98, 0xF1); -// return (crc == (instance->decoder.decode_data & 0xFF)); -// } - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_wendox_remote_controller(WSBlockGeneric* instance) { - instance->id = (instance->data >> 4) & 0xFF; - instance->battery_low = (instance->data >> 5) & 1; - instance->channel = WS_NO_CHANNEL; - - if(((instance->data >> 22) & 1)) { - instance->temp = (float)(((instance->data >> 13) & 0x1FF) + 12) / 10.0f; - } else { - instance->temp = (float)((~(instance->data >> 13) & 0x1FF) + 1 - 12) / -10.0f; - } - FURI_LOG_E("TAG", "%llX %f", instance->data, (double)instance->temp); - - instance->btn = WS_NO_BTN; - instance->humidity = WS_NO_HUMIDITY; -} - -void ws_protocol_decoder_wendox_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - - switch(instance->decoder.parser_step) { - case WendoxDecoderStepReset: - if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < - ws_protocol_wendox_const.te_delta)) { - instance->decoder.parser_step = WendoxDecoderStepCheckPreambule; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - - case WendoxDecoderStepCheckPreambule: - if(level) { - instance->decoder.te_last = duration; - } else { - if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_short) < - ws_protocol_wendox_const.te_delta * 1) && - (DURATION_DIFF(duration, ws_protocol_wendox_const.te_long) < - ws_protocol_wendox_const.te_delta * 2)) { - instance->header_count++; - } else if((instance->header_count > 4) && (instance->header_count < 12)) { - if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_long) < - ws_protocol_wendox_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < - ws_protocol_wendox_const.te_delta)) { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = WendoxDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = WendoxDecoderStepReset; - } - - } else { - instance->decoder.parser_step = WendoxDecoderStepReset; - } - } - break; - - case WendoxDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = WendoxDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = WendoxDecoderStepReset; - } - break; - - case WendoxDecoderStepCheckDuration: - if(!level) { - if(duration > ws_protocol_wendox_const.te_short + ws_protocol_wendox_const.te_long) { - if(instance->decoder.decode_count_bit == - ws_protocol_wendox_const.min_count_bit_for_found) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_wendox_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - - instance->decoder.parser_step = WendoxDecoderStepReset; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_short) < - ws_protocol_wendox_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_wendox_const.te_long) < - ws_protocol_wendox_const.te_delta * 3)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = WendoxDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_const.te_long) < - ws_protocol_wendox_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_wendox_const.te_short) < - ws_protocol_wendox_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = WendoxDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = WendoxDecoderStepReset; - } - } else { - instance->decoder.parser_step = WendoxDecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_wendox_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_wendox_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_wendox_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, flipper_format, ws_protocol_wendox_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_wendox_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderWendox* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/wendox.h b/applications/external/weather_station/protocols/wendox.h deleted file mode 100644 index 698ac0bc0..000000000 --- a/applications/external/weather_station/protocols/wendox.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_WENDOX_NAME "Wendox" - -typedef struct WSProtocolDecoderWendox WSProtocolDecoderWendox; -typedef struct WSProtocolEncoderWendox WSProtocolEncoderWendox; - -extern const SubGhzProtocolDecoder ws_protocol_wendox_decoder; -extern const SubGhzProtocolEncoder ws_protocol_wendox_encoder; -extern const SubGhzProtocol ws_protocol_wendox; - -/** - * Allocate WSProtocolDecoderWendox. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderWendox* pointer to a WSProtocolDecoderWendox instance - */ -void* ws_protocol_decoder_wendox_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderWendox. - * @param context Pointer to a WSProtocolDecoderWendox instance - */ -void ws_protocol_decoder_wendox_free(void* context); - -/** - * Reset decoder WSProtocolDecoderWendox. - * @param context Pointer to a WSProtocolDecoderWendox instance - */ -void ws_protocol_decoder_wendox_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderWendox instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_wendox_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderWendox instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_wendox_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderWendox. - * @param context Pointer to a WSProtocolDecoderWendox instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_wendox_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderWendox. - * @param context Pointer to a WSProtocolDecoderWendox instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_wendox_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderWendox instance - * @param output Resulting text - */ -void ws_protocol_decoder_wendox_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/wendox_w6726.h b/applications/external/weather_station/protocols/wendox_w6726.h new file mode 100644 index 000000000..236777a1c --- /dev/null +++ b/applications/external/weather_station/protocols/wendox_w6726.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_WENDOX_W6726_NAME "Wendox W6726" + +typedef struct WSProtocolDecoderWendoxW6726 WSProtocolDecoderWendoxW6726; +typedef struct WSProtocolEncoderWendoxW6726 WSProtocolEncoderWendoxW6726; + +extern const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder; +extern const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder; +extern const SubGhzProtocol ws_protocol_wendox_w6726; + +/** + * Allocate WSProtocolDecoderWendoxW6726. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderWendoxW6726* pointer to a WSProtocolDecoderWendoxW6726 instance + */ +void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + */ +void ws_protocol_decoder_wendox_w6726_free(void* context); + +/** + * Reset decoder WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + */ +void ws_protocol_decoder_wendox_w6726_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderWendoxW6726. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance + * @param output Resulting text + */ +void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output); From 9caedb422dd945e074471c6a8acc95f60f1fab60 Mon Sep 17 00:00:00 2001 From: SkorP Date: Tue, 25 Apr 2023 11:36:27 +0400 Subject: [PATCH 118/282] WS: description added --- .../external/weather_station/protocols/wendox._w6726.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/applications/external/weather_station/protocols/wendox._w6726.c b/applications/external/weather_station/protocols/wendox._w6726.c index 3976f6a37..265c77f70 100644 --- a/applications/external/weather_station/protocols/wendox._w6726.c +++ b/applications/external/weather_station/protocols/wendox._w6726.c @@ -15,9 +15,17 @@ * * bit 0: 1955-hi, 5865-lo * bit 1: 5865-hi, 1955-lo - * * guard time: 12*1955+(lo last bit) * data: 29 bit + * + * IIIII | ZTTTTTTTTT | uuuuuuuBuu | CCCC + * + * I: identification; + * Z: temperature sign; + * T: temperature sign dependent +12 Ѱ; + * B: battery low; flag to indicate low battery voltage; + * C: CRC4 (polynomial = 0x9, start_data = 0xD); + * u: unknown; */ static const SubGhzBlockConst ws_protocol_wendox_w6726_const = { From 6b09dfca766b9337d08628b47744ee4c4e98e17d Mon Sep 17 00:00:00 2001 From: SkorP Date: Tue, 25 Apr 2023 11:40:50 +0400 Subject: [PATCH 119/282] WS: fix name file --- .../weather_station/protocols/{wendox._w6726.c => wendox_w6726.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename applications/external/weather_station/protocols/{wendox._w6726.c => wendox_w6726.c} (100%) diff --git a/applications/external/weather_station/protocols/wendox._w6726.c b/applications/external/weather_station/protocols/wendox_w6726.c similarity index 100% rename from applications/external/weather_station/protocols/wendox._w6726.c rename to applications/external/weather_station/protocols/wendox_w6726.c From 83afd1674658a5434c1e8f9e6fe33ff64b876864 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 25 Apr 2023 13:00:40 +0100 Subject: [PATCH 120/282] Revert cubewb update --- fbt_options.py | 2 +- firmware/targets/f7/ble_glue/app_common.h | 1 - firmware/targets/f7/ble_glue/app_conf.h | 2 - firmware/targets/f7/ble_glue/app_debug.c | 3 +- firmware/targets/f7/ble_glue/ble_app.c | 10 +---- firmware/targets/f7/ble_glue/ble_const.h | 4 -- firmware/targets/f7/ble_glue/compiler.h | 10 +---- firmware/targets/f7/ble_glue/gap.c | 49 ++++++++++++----------- lib/STM32CubeWB | 2 +- scripts/ob.data | 4 +- 10 files changed, 34 insertions(+), 53 deletions(-) diff --git a/fbt_options.py b/fbt_options.py index 8edd30758..0e87521a1 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -20,7 +20,7 @@ DIST_SUFFIX = "XFW-0044_09042023" COPRO_OB_DATA = "scripts/ob.data" # Must match lib/STM32CubeWB version -COPRO_CUBE_VERSION = "1.15.0" +COPRO_CUBE_VERSION = "1.13.3" COPRO_CUBE_DIR = "lib/STM32CubeWB" diff --git a/firmware/targets/f7/ble_glue/app_common.h b/firmware/targets/f7/ble_glue/app_common.h index 8eaf23085..214c85acd 100644 --- a/firmware/targets/f7/ble_glue/app_common.h +++ b/firmware/targets/f7/ble_glue/app_common.h @@ -33,7 +33,6 @@ extern "C" { #include #include -#include #include "app_conf.h" diff --git a/firmware/targets/f7/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h index ee5115cfe..aaa755a36 100644 --- a/firmware/targets/f7/ble_glue/app_conf.h +++ b/firmware/targets/f7/ble_glue/app_conf.h @@ -8,8 +8,6 @@ #define CFG_TX_POWER (0x19) /* +0dBm */ -#define CFG_IDENTITY_ADDRESS GAP_PUBLIC_ADDR - /** * Define Advertising parameters */ diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index b443bee21..78e789ac3 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -33,8 +33,7 @@ PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0}; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) -static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = - {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}, 0, 0, 0, 0, 0}; +static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}}; /** * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index a325830cf..4fc4d521b 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; _Static_assert( - sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 57, - "Ble stack config structure size mismatch (check new config options - last updated for v.1.15.0)"); + sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 49, + "Ble stack config structure size mismatch"); typedef struct { FuriMutex* hci_mtx; @@ -88,12 +88,6 @@ bool ble_app_init() { .min_tx_power = 0, .max_tx_power = 0, .rx_model_config = 1, - /* New stack (13.3->16.0)*/ - .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set - .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set - .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB - .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB - .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) }}; status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); if(status) { diff --git a/firmware/targets/f7/ble_glue/ble_const.h b/firmware/targets/f7/ble_glue/ble_const.h index 85f734b62..0e4c8b398 100644 --- a/firmware/targets/f7/ble_glue/ble_const.h +++ b/firmware/targets/f7/ble_glue/ble_const.h @@ -23,7 +23,6 @@ #include #include #include "osal.h" -#include "compiler.h" /* Default BLE variant */ #ifndef BASIC_FEATURES @@ -35,9 +34,6 @@ #ifndef LL_ONLY #define LL_ONLY 0 #endif -#ifndef LL_ONLY_BASIC -#define LL_ONLY_BASIC 0 -#endif #ifndef BEACON_ONLY #define BEACON_ONLY 0 #endif diff --git a/firmware/targets/f7/ble_glue/compiler.h b/firmware/targets/f7/ble_glue/compiler.h index 98a93d712..1c3962819 100644 --- a/firmware/targets/f7/ble_glue/compiler.h +++ b/firmware/targets/f7/ble_glue/compiler.h @@ -5,7 +5,7 @@ ***************************************************************************** * @attention * - * Copyright (c) 2018-2023 STMicroelectronics. + * Copyright (c) 2018-2022 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file @@ -18,14 +18,6 @@ #ifndef COMPILER_H__ #define COMPILER_H__ -#ifndef __PACKED_STRUCT -#define __PACKED_STRUCT PACKED(struct) -#endif - -#ifndef __PACKED_UNION -#define __PACKED_UNION PACKED(union) -#endif - /** * @brief This is the section dedicated to IAR toolchain */ diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 2eaedc28f..7ad9db3ae 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -1,6 +1,5 @@ #include "gap.h" -#include "app_common.h" #include #include @@ -101,7 +100,7 @@ static void gap_verify_connection_parameters(Gap* gap) { SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { hci_event_pckt* event_pckt; evt_le_meta_event* meta_evt; - evt_blecore_aci* blue_evt; + evt_blue_aci* blue_evt; hci_le_phy_update_complete_event_rp0* evt_le_phy_update_complete; uint8_t tx_phy; uint8_t rx_phy; @@ -113,7 +112,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { furi_mutex_acquire(gap->state_mutex, FuriWaitForever); } switch(event_pckt->evt) { - case HCI_DISCONNECTION_COMPLETE_EVT_CODE: { + case EVT_DISCONN_COMPLETE: { hci_disconnection_complete_event_rp0* disconnection_complete_event = (hci_disconnection_complete_event_rp0*)event_pckt->data; if(disconnection_complete_event->Connection_Handle == gap->service.connection_handle) { @@ -132,10 +131,10 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case HCI_LE_META_EVT_CODE: + case EVT_LE_META_EVENT: meta_evt = (evt_le_meta_event*)event_pckt->data; switch(meta_evt->subevent) { - case HCI_LE_CONNECTION_UPDATE_COMPLETE_SUBEVT_CODE: { + case EVT_LE_CONN_UPDATE_COMPLETE: { hci_le_connection_update_complete_event_rp0* event = (hci_le_connection_update_complete_event_rp0*)meta_evt->data; gap->connection_params.conn_interval = event->Conn_Interval; @@ -149,7 +148,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { break; } - case HCI_LE_PHY_UPDATE_COMPLETE_SUBEVT_CODE: + case EVT_LE_PHY_UPDATE_COMPLETE: evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; if(evt_le_phy_update_complete->Status) { FURI_LOG_E( @@ -165,7 +164,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case HCI_LE_CONNECTION_COMPLETE_SUBEVT_CODE: { + case EVT_LE_CONN_COMPLETE: { hci_le_connection_complete_event_rp0* event = (hci_le_connection_complete_event_rp0*)meta_evt->data; gap->connection_params.conn_interval = event->Conn_Interval; @@ -192,16 +191,16 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE: - blue_evt = (evt_blecore_aci*)event_pckt->data; + case EVT_VENDOR: + blue_evt = (evt_blue_aci*)event_pckt->data; switch(blue_evt->ecode) { aci_gap_pairing_complete_event_rp0* pairing_complete; - case ACI_GAP_LIMITED_DISCOVERABLE_VSEVT_CODE: + case EVT_BLUE_GAP_LIMITED_DISCOVERABLE: FURI_LOG_I(TAG, "Limited discoverable event"); break; - case ACI_GAP_PASS_KEY_REQ_VSEVT_CODE: { + case EVT_BLUE_GAP_PASS_KEY_REQUEST: { // Generate random PIN code uint32_t pin = rand() % 999999; //-V1064 aci_gap_pass_key_resp(gap->service.connection_handle, pin); @@ -214,7 +213,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case ACI_ATT_EXCHANGE_MTU_RESP_VSEVT_CODE: { + case EVT_BLUE_ATT_EXCHANGE_MTU_RESP: { aci_att_exchange_mtu_resp_event_rp0* pr = (void*)blue_evt->data; FURI_LOG_I(TAG, "Rx MTU size: %d", pr->Server_RX_MTU); // Set maximum packet size given header size is 3 bytes @@ -223,28 +222,32 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case ACI_GAP_AUTHORIZATION_REQ_VSEVT_CODE: + case EVT_BLUE_GAP_AUTHORIZATION_REQUEST: FURI_LOG_D(TAG, "Authorization request event"); break; - case ACI_GAP_SLAVE_SECURITY_INITIATED_VSEVT_CODE: + case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED: FURI_LOG_D(TAG, "Slave security initiated"); break; - case ACI_GAP_BOND_LOST_VSEVT_CODE: + case EVT_BLUE_GAP_BOND_LOST: FURI_LOG_D(TAG, "Bond lost event. Start rebonding"); aci_gap_allow_rebond(gap->service.connection_handle); break; - case ACI_GAP_ADDR_NOT_RESOLVED_VSEVT_CODE: + case EVT_BLUE_GAP_DEVICE_FOUND: + FURI_LOG_D(TAG, "Device found event"); + break; + + case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: FURI_LOG_D(TAG, "Address not resolved event"); break; - case ACI_GAP_KEYPRESS_NOTIFICATION_VSEVT_CODE: + case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION: FURI_LOG_D(TAG, "Key press notification event"); break; - case ACI_GAP_NUMERIC_COMPARISON_VALUE_VSEVT_CODE: { + case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: { uint32_t pin = ((aci_gap_numeric_comparison_value_event_rp0*)(blue_evt->data))->Numeric_Value; FURI_LOG_I(TAG, "Verify numeric comparison: %06lu", pin); @@ -254,7 +257,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { break; } - case ACI_GAP_PAIRING_COMPLETE_VSEVT_CODE: + case EVT_BLUE_GAP_PAIRING_CMPLT: pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; if(pairing_complete->Status) { FURI_LOG_E( @@ -271,11 +274,11 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case ACI_L2CAP_CONNECTION_UPDATE_RESP_VSEVT_CODE: + case EVT_BLUE_GAP_PROCEDURE_COMPLETE: FURI_LOG_D(TAG, "Procedure complete event"); break; - case ACI_L2CAP_CONNECTION_UPDATE_REQ_VSEVT_CODE: { + case EVT_BLUE_L2CAP_CONNECTION_UPDATE_RESP: { uint16_t result = ((aci_l2cap_connection_update_resp_event_rp0*)(blue_evt->data))->Result; if(result == 0) { @@ -396,7 +399,7 @@ static void gap_init_svc(Gap* gap) { CFG_ENCRYPTION_KEY_SIZE_MAX, conf_used_fixed_pin, // 0x0 for no pin 0, - CFG_IDENTITY_ADDRESS); + PUBLIC_ADDR); // Configure whitelist aci_gap_configure_whitelist(); } @@ -431,7 +434,7 @@ static void gap_advertise_start(GapState new_state) { ADV_IND, min_interval, max_interval, - CFG_IDENTITY_ADDRESS, + PUBLIC_ADDR, 0, strlen(gap->service.adv_name), (uint8_t*)gap->service.adv_name, diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB index c4cec8ae5..a9e29b431 160000 --- a/lib/STM32CubeWB +++ b/lib/STM32CubeWB @@ -1 +1 @@ -Subproject commit c4cec8ae57a79e949a184cd0b4117a008a0a25a7 +Subproject commit a9e29b431f6dac95b6fc860a717834f35b7f78e5 diff --git a/scripts/ob.data b/scripts/ob.data index 605faccbf..5276a5103 100644 --- a/scripts/ob.data +++ b/scripts/ob.data @@ -14,7 +14,7 @@ IWDGSTOP:0x1:rw IWDGSW:0x1:rw IPCCDBA:0x0:rw ESE:0x1:r -SFSA:0xD5:r +SFSA:0xD7:r FSD:0x0:r DDS:0x1:r C2OPT:0x1:r @@ -22,7 +22,7 @@ NBRSD:0x0:r SNBRSA:0xD:r BRSD:0x0:r SBRSA:0x12:r -SBRV:0x35400:r +SBRV:0x35C00:r PCROP1A_STRT:0x1FF:r PCROP1A_END:0x0:r PCROP_RDP:0x1:rw From e2c3e8e53d80fffd407b0caac21459577cf20040 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 25 Apr 2023 16:22:37 +0100 Subject: [PATCH 121/282] Revert cubewb symbols update --- firmware/targets/f7/api_symbols.csv | 34 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 80e81923f..ddc753aa7 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -219,17 +219,17 @@ Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, Header,+,lib/u8g2/u8g2.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* -Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, const LL_ADC_CommonInitTypeDef*" +Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* Function,-,LL_ADC_DeInit,ErrorStatus,ADC_TypeDef* -Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_INJ_InitTypeDef*" +Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_INJ_InitTypeDef*" Function,-,LL_ADC_INJ_StructInit,void,LL_ADC_INJ_InitTypeDef* -Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_InitTypeDef*" -Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_REG_InitTypeDef*" +Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_InitTypeDef*" +Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_REG_InitTypeDef*" Function,-,LL_ADC_REG_StructInit,void,LL_ADC_REG_InitTypeDef* Function,-,LL_ADC_StructInit,void,LL_ADC_InitTypeDef* Function,-,LL_COMP_DeInit,ErrorStatus,COMP_TypeDef* -Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, const LL_COMP_InitTypeDef*" +Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, LL_COMP_InitTypeDef*" Function,-,LL_COMP_StructInit,void,LL_COMP_InitTypeDef* Function,-,LL_CRC_DeInit,ErrorStatus,CRC_TypeDef* Function,-,LL_CRS_DeInit,ErrorStatus, @@ -248,10 +248,10 @@ Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* Function,-,LL_Init1msTick,void,uint32_t Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* Function,-,LL_LPTIM_Disable,void,LPTIM_TypeDef* -Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, const LL_LPTIM_InitTypeDef*" +Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, LL_LPTIM_InitTypeDef*" Function,-,LL_LPTIM_StructInit,void,LL_LPTIM_InitTypeDef* -Function,-,LL_LPUART_DeInit,ErrorStatus,const USART_TypeDef* -Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, const LL_LPUART_InitTypeDef*" +Function,-,LL_LPUART_DeInit,ErrorStatus,USART_TypeDef* +Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, LL_LPUART_InitTypeDef*" Function,-,LL_LPUART_StructInit,void,LL_LPUART_InitTypeDef* Function,-,LL_PKA_DeInit,ErrorStatus,PKA_TypeDef* Function,-,LL_PKA_Init,ErrorStatus,"PKA_TypeDef*, LL_PKA_InitTypeDef*" @@ -296,23 +296,23 @@ Function,+,LL_SPI_Init,ErrorStatus,"SPI_TypeDef*, LL_SPI_InitTypeDef*" Function,-,LL_SPI_StructInit,void,LL_SPI_InitTypeDef* Function,-,LL_SetFlashLatency,ErrorStatus,uint32_t Function,+,LL_SetSystemCoreClock,void,uint32_t -Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_BDTR_InitTypeDef*" +Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_BDTR_InitTypeDef*" Function,-,LL_TIM_BDTR_StructInit,void,LL_TIM_BDTR_InitTypeDef* Function,+,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef* -Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_ENCODER_InitTypeDef*" +Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_ENCODER_InitTypeDef*" Function,-,LL_TIM_ENCODER_StructInit,void,LL_TIM_ENCODER_InitTypeDef* -Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_HALLSENSOR_InitTypeDef*" +Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_HALLSENSOR_InitTypeDef*" Function,-,LL_TIM_HALLSENSOR_StructInit,void,LL_TIM_HALLSENSOR_InitTypeDef* -Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_IC_InitTypeDef*" +Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_IC_InitTypeDef*" Function,-,LL_TIM_IC_StructInit,void,LL_TIM_IC_InitTypeDef* -Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_InitTypeDef*" -Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_OC_InitTypeDef*" +Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_InitTypeDef*" +Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_OC_InitTypeDef*" Function,-,LL_TIM_OC_StructInit,void,LL_TIM_OC_InitTypeDef* Function,-,LL_TIM_StructInit,void,LL_TIM_InitTypeDef* -Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, const LL_USART_ClockInitTypeDef*" +Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, LL_USART_ClockInitTypeDef*" Function,-,LL_USART_ClockStructInit,void,LL_USART_ClockInitTypeDef* -Function,-,LL_USART_DeInit,ErrorStatus,const USART_TypeDef* -Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, const LL_USART_InitTypeDef*" +Function,-,LL_USART_DeInit,ErrorStatus,USART_TypeDef* +Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, LL_USART_InitTypeDef*" Function,-,LL_USART_StructInit,void,LL_USART_InitTypeDef* Function,-,LL_mDelay,void,uint32_t Function,-,SK6805_get_led_count,uint8_t, From 10a7c8f84aae6255d79980aee8aa88b17b3fbf86 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 25 Apr 2023 17:18:14 +0100 Subject: [PATCH 122/282] Backup+restore internal U2F keys on factory reset --- .../services/storage/storages/storage_int.c | 55 +++++++++++++++---- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/applications/services/storage/storages/storage_int.c b/applications/services/storage/storages/storage_int.c index 2534d47a1..adf016f4e 100644 --- a/applications/services/storage/storages/storage_int.c +++ b/applications/services/storage/storages/storage_int.c @@ -193,22 +193,57 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) { was_fingerprint_outdated; if(need_format) { - // Format storage - err = lfs_format(lfs, &lfs_data->config); + err = lfs_mount(lfs, &lfs_data->config); if(err == 0) { - FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount"); - furi_hal_rtc_reset_flag(FuriHalRtcFlagFactoryReset); - err = lfs_mount(lfs, &lfs_data->config); + FURI_LOG_I(TAG, "Factory reset: Mounted for backup"); + + // Backup U2F keys + lfs_file_t file; + lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_RDWR | LFS_O_CREAT); + uint8_t cnt[file.ctz.size]; + lfs_file_read(lfs, &file, cnt, sizeof(cnt)); + lfs_file_close(lfs, &file); + lfs_file_open(lfs, &file, ".key.u2f", LFS_O_RDWR | LFS_O_CREAT); + uint8_t key[file.ctz.size]; + lfs_file_read(lfs, &file, key, sizeof(key)); + lfs_file_close(lfs, &file); + + err = lfs_unmount(lfs); if(err == 0) { - FURI_LOG_I(TAG, "Factory reset: Mounted"); - storage->status = StorageStatusOK; + FURI_LOG_E(TAG, "Factory reset: Unmounted after backup"); + // Format storage + err = lfs_format(lfs, &lfs_data->config); + if(err == 0) { + FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount"); + furi_hal_rtc_reset_flag(FuriHalRtcFlagFactoryReset); + err = lfs_mount(lfs, &lfs_data->config); + if(err == 0) { + FURI_LOG_I(TAG, "Factory reset: Mounted"); + + // Restore U2F keys + lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_RDWR | LFS_O_CREAT); + lfs_file_write(lfs, &file, cnt, sizeof(cnt)); + lfs_file_close(lfs, &file); + lfs_file_open(lfs, &file, ".key.u2f", LFS_O_RDWR | LFS_O_CREAT); + lfs_file_write(lfs, &file, key, sizeof(key)); + lfs_file_close(lfs, &file); + + storage->status = StorageStatusOK; + } else { + FURI_LOG_E(TAG, "Factory reset: Mount after format failed"); + storage->status = StorageStatusNotMounted; + } + } else { + FURI_LOG_E(TAG, "Factory reset: Format failed"); + storage->status = StorageStatusNoFS; + } } else { - FURI_LOG_E(TAG, "Factory reset: Mount after format failed"); + FURI_LOG_E(TAG, "Factory reset: Unmount after backup failed"); storage->status = StorageStatusNotMounted; } } else { - FURI_LOG_E(TAG, "Factory reset: Format failed"); - storage->status = StorageStatusNoFS; + FURI_LOG_E(TAG, "Factory reset: Mount for backup failed"); + storage->status = StorageStatusNotMounted; } } else { // Normal From d70ba2b74064e24586adea56231b569ff66d71df Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 25 Apr 2023 20:33:13 +0400 Subject: [PATCH 123/282] [FL-3286] Don't reboot on crash in debug builds (#2613) * furi: never reboot on furi_crash in debug builds * furi: crash info: added registers * furi: check and assert optimization, split registers and stack info dump * furi: macro uppercase Co-authored-by: SG --- furi/core/check.c | 31 ++++++++++++++++++++++++++++++- furi/core/check.h | 24 ++++++++++++++---------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/furi/core/check.c b/furi/core/check.c index 64f9f72f1..f5390639d 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -14,7 +14,7 @@ #include PLACE_IN_SECTION("MB_MEM2") const char* __furi_check_message = NULL; -PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; +PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[13] = {0}; /** Load r12 value to __furi_check_message and store registers to __furi_check_registers */ #define GET_MESSAGE_AND_STORE_REGISTERS() \ @@ -22,6 +22,7 @@ PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[12] = {0}; "str r12, [r11] \n" \ "ldr r12, =__furi_check_registers \n" \ "stm r12, {r0-r11} \n" \ + "str lr, [r12, #48] \n" \ : \ : \ : "memory"); @@ -62,6 +63,25 @@ static void __furi_put_uint32_as_text(uint32_t data) { furi_hal_console_puts(tmp_str); } +static void __furi_put_uint32_as_hex(uint32_t data) { + char tmp_str[] = "0xFFFFFFFF"; + itoa(data, tmp_str, 16); + furi_hal_console_puts(tmp_str); +} + +static void __furi_print_register_info() { + // Print registers + for(uint8_t i = 0; i < 12; i++) { + furi_hal_console_puts("\r\n\tr"); + __furi_put_uint32_as_text(i); + furi_hal_console_puts(" : "); + __furi_put_uint32_as_hex(__furi_check_registers[i]); + } + + furi_hal_console_puts("\r\n\tlr : "); + __furi_put_uint32_as_hex(__furi_check_registers[12]); +} + static void __furi_print_stack_info() { furi_hal_console_puts("\r\n\tstack watermark: "); __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4); @@ -101,32 +121,41 @@ FURI_NORETURN void __furi_crash() { if(__furi_check_message == NULL) { __furi_check_message = "Fatal Error"; + } else if(__furi_check_message == (void*)__FURI_ASSERT_MESSAGE_FLAG) { + __furi_check_message = "furi_assert failed"; + } else if(__furi_check_message == (void*)__FURI_CHECK_MESSAGE_FLAG) { + __furi_check_message = "furi_check failed"; } furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); __furi_print_name(isr); furi_hal_console_puts(__furi_check_message); + __furi_print_register_info(); if(!isr) { __furi_print_stack_info(); } __furi_print_heap_info(); +#ifndef FURI_DEBUG // Check if debug enabled by DAP // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; if(debug) { +#endif furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); furi_hal_console_puts("\033[0m\r\n"); furi_hal_debug_enable(); RESTORE_REGISTERS_AND_HALT_MCU(true); +#ifndef FURI_DEBUG } else { furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); furi_hal_console_puts("\r\nRebooting system.\r\n"); furi_hal_console_puts("\033[0m\r\n"); furi_hal_power_reset(); } +#endif __builtin_unreachable(); } diff --git a/furi/core/check.h b/furi/core/check.h index 192c5260e..a507fc1ea 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -21,6 +21,10 @@ extern "C" { #define FURI_NORETURN noreturn #endif +// Flags instead of pointers will save ~4 bytes on furi_assert and furi_check calls. +#define __FURI_ASSERT_MESSAGE_FLAG (0x01) +#define __FURI_CHECK_MESSAGE_FLAG (0x02) + /** Crash system */ FURI_NORETURN void __furi_crash(); @@ -44,20 +48,20 @@ FURI_NORETURN void __furi_halt(); } while(0) /** Check condition and crash if check failed */ -#define furi_check(__e) \ - do { \ - if(!(__e)) { \ - furi_crash("furi_check failed\r\n"); \ - } \ +#define furi_check(__e) \ + do { \ + if(!(__e)) { \ + furi_crash(__FURI_ASSERT_MESSAGE_FLAG); \ + } \ } while(0) /** Only in debug build: Assert condition and crash if assert failed */ #ifdef FURI_DEBUG -#define furi_assert(__e) \ - do { \ - if(!(__e)) { \ - furi_crash("furi_assert failed\r\n"); \ - } \ +#define furi_assert(__e) \ + do { \ + if(!(__e)) { \ + furi_crash(__FURI_CHECK_MESSAGE_FLAG); \ + } \ } while(0) #else #define furi_assert(__e) \ From 4229aab7e9da94a68c4662dbdbe37468f47df218 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 25 Apr 2023 17:50:41 +0100 Subject: [PATCH 124/282] Update subbrute --- .../external/subghz_bruteforcer/subbrute.c | 5 ++++- .../subghz_bruteforcer/subbrute_device.c | 4 +--- .../subghz_bruteforcer/subbrute_protocols.c | 21 +++++++------------ .../subghz_bruteforcer/subbrute_protocols.h | 4 +--- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/applications/external/subghz_bruteforcer/subbrute.c b/applications/external/subghz_bruteforcer/subbrute.c index 59d2c1ccb..99334c05e 100644 --- a/applications/external/subghz_bruteforcer/subbrute.c +++ b/applications/external/subghz_bruteforcer/subbrute.c @@ -185,7 +185,8 @@ int32_t subbrute_app(void* p) { // Auto switch to internal radio if external radio is not available furi_delay_ms(15); if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } furi_hal_power_suppress_charge_enter(); @@ -195,6 +196,8 @@ int32_t subbrute_app(void* p) { furi_hal_power_suppress_charge_exit(); // Disable power for External CC1101 if it was enabled and module is connected furi_hal_subghz_disable_ext_power(); + // Reinit SPI handles for internal radio / nfc + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); subbrute_free(instance); diff --git a/applications/external/subghz_bruteforcer/subbrute_device.c b/applications/external/subghz_bruteforcer/subbrute_device.c index 0971c380e..192b8bfa0 100644 --- a/applications/external/subghz_bruteforcer/subbrute_device.c +++ b/applications/external/subghz_bruteforcer/subbrute_device.c @@ -104,7 +104,6 @@ bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_na instance->current_step, instance->file_protocol_info->bits, instance->file_protocol_info->te, - instance->file_protocol_info->repeat, instance->bit_index, instance->key_from_file, instance->two_bytes); @@ -116,8 +115,7 @@ bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_na instance->protocol_info->file, instance->current_step, instance->protocol_info->bits, - instance->protocol_info->te, - instance->protocol_info->repeat); + instance->protocol_info->te); } result = true; diff --git a/applications/external/subghz_bruteforcer/subbrute_protocols.c b/applications/external/subghz_bruteforcer/subbrute_protocols.c index 6c6781b78..1f3e45129 100644 --- a/applications/external/subghz_bruteforcer/subbrute_protocols.c +++ b/applications/external/subghz_bruteforcer/subbrute_protocols.c @@ -522,9 +522,9 @@ static const char* subbrute_protocol_file_types[] = { * Values to not use less memory for packet parse operations */ static const char* subbrute_key_file_start_no_tail = - "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\nRepeat: %d\n"; + "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\n"; static const char* subbrute_key_file_start_with_tail = - "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\nTE: %d\nRepeat: %d\n"; + "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\nKey: %s\nTE: %d\n"; static const char* subbrute_key_small_no_tail = "Bit: %d\nKey: %s\nRepeat: %d\n"; //static const char* subbrute_key_small_raw = // "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d\n"; @@ -771,8 +771,7 @@ void subbrute_protocol_default_generate_file( SubBruteFileProtocol file, uint64_t step, uint8_t bits, - uint32_t te, - uint8_t repeat) { + uint32_t te) { FuriString* candidate = furi_string_alloc(); subbrute_protocol_create_candidate_for_default(candidate, file, step); @@ -790,8 +789,7 @@ void subbrute_protocol_default_generate_file( subbrute_protocol_file(file), bits, furi_string_get_cstr(candidate), - te, - repeat); + te); } else { stream_write_format( stream, @@ -800,8 +798,7 @@ void subbrute_protocol_default_generate_file( subbrute_protocol_preset(preset), subbrute_protocol_file(file), bits, - furi_string_get_cstr(candidate), - repeat); + furi_string_get_cstr(candidate)); } furi_string_free(candidate); @@ -815,7 +812,6 @@ void subbrute_protocol_file_generate_file( uint64_t step, uint8_t bits, uint32_t te, - uint8_t repeat, uint8_t bit_index, uint64_t file_key, bool two_bytes) { @@ -826,6 +822,7 @@ void subbrute_protocol_file_generate_file( candidate, step, bit_index, file_key, two_bytes); stream_clean(stream); + if(te) { stream_write_format( stream, @@ -835,8 +832,7 @@ void subbrute_protocol_file_generate_file( subbrute_protocol_file(file), bits, furi_string_get_cstr(candidate), - te, - repeat); + te); } else { stream_write_format( stream, @@ -845,8 +841,7 @@ void subbrute_protocol_file_generate_file( subbrute_protocol_preset(preset), subbrute_protocol_file(file), bits, - furi_string_get_cstr(candidate), - repeat); + furi_string_get_cstr(candidate)); } furi_string_free(candidate); diff --git a/applications/external/subghz_bruteforcer/subbrute_protocols.h b/applications/external/subghz_bruteforcer/subbrute_protocols.h index 2f41b185b..066040b10 100644 --- a/applications/external/subghz_bruteforcer/subbrute_protocols.h +++ b/applications/external/subghz_bruteforcer/subbrute_protocols.h @@ -110,8 +110,7 @@ void subbrute_protocol_default_generate_file( SubBruteFileProtocol file, uint64_t step, uint8_t bits, - uint32_t te, - uint8_t repeat); + uint32_t te); void subbrute_protocol_file_generate_file( Stream* stream, uint32_t frequency, @@ -120,7 +119,6 @@ void subbrute_protocol_file_generate_file( uint64_t step, uint8_t bits, uint32_t te, - uint8_t repeat, uint8_t bit_index, uint64_t file_key, bool two_bytes); From 8ebc3888a2ecf45b29a097e307a6eb60ea063d5b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:40:10 +0100 Subject: [PATCH 125/282] Fix PR webhook --- .github/workflow_data/webhook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflow_data/webhook.py b/.github/workflow_data/webhook.py index ee97e254b..08eab46fb 100644 --- a/.github/workflow_data/webhook.py +++ b/.github/workflow_data/webhook.py @@ -39,7 +39,7 @@ if __name__ == "__main__": url = pr["html_url"] branch = pr["base"]["ref"] + ( "" - if pr["base"]["full_name"] != pr["head"]["full_name"] + if pr["base"]["repo"]["full_name"] != pr["head"]["repo"]["full_name"] else f" <- {pr['head']['ref']}" ) name = pr["title"][:50] + ("..." if len(pr["title"]) > 50 else "") From 35d876b816a5359e140f91409f3c25cccbda8717 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:56:09 +0100 Subject: [PATCH 126/282] Updated ac.ir Updated "Last Checked", No new additions --- assets/resources/infrared/assets/ac.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index bf047c5e7..49a4acd20 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 14th Apr, 2023 -# Last Checked 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # name: POWER type: raw From 3bb5763b1b7315e3b64aa967eee2096a817cb151 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:56:26 +0100 Subject: [PATCH 127/282] Updated audio.ir Updated "Last Checked", No new additions --- assets/resources/infrared/assets/audio.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 7e93cb3b8..f6183ed86 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 14th Apr, 2023 -# Last Checked 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # name: POWER type: parsed From b49314deb01e044cc02412e34d8565cdd80adb81 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:56:44 +0100 Subject: [PATCH 128/282] Updated audio.ir Updated "Last Checked", No new additions --- assets/resources/infrared/assets/fans.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index 134133e17..6bef11b34 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 14th Apr, 2023 -# Last Checked 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # name: POWER type: raw From f6820531748646dce19e7cc628f2f1a5bbbc185b Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:57:02 +0100 Subject: [PATCH 129/282] Updated projectors.ir Updated "Last Checked", No new additions --- assets/resources/infrared/assets/projectors.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index c039f2389..00e331bd4 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 14th Apr, 2023 -# Last Checked 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # # ON name: POWER From a5f86b3025591d6db32c908325aa2fd846049bd1 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:57:18 +0100 Subject: [PATCH 130/282] Updated tv.ir Updated "Last Checked", No new additions --- assets/resources/infrared/assets/tv.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 4d9c38f98..c55ad267d 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 07th Mar, 2023 -# Last Checked 14th Apr, 2023 +# Last Checked 25th Apr, 2023 # name: POWER type: parsed From 0ec8fc4c55178e9ad7daa014a9268dcfbb8da573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 26 Apr 2023 05:11:42 +0900 Subject: [PATCH 131/282] FuriHal: use proper divider for core2 when transition to sleep, remove extra stop mode transition checks, cleanup code. Furi: proper assert and check messages. (#2615) --- firmware/targets/f7/ble_glue/ble_app.c | 2 +- firmware/targets/f7/furi_hal/furi_hal_bt.c | 8 ++----- firmware/targets/f7/furi_hal/furi_hal_clock.c | 10 ++++++++- firmware/targets/f7/furi_hal/furi_hal_power.c | 21 ++----------------- furi/core/check.h | 20 +++++++++--------- 5 files changed, 24 insertions(+), 37 deletions(-) diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index a325830cf..37d8f7cd0 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -88,7 +88,7 @@ bool ble_app_init() { .min_tx_power = 0, .max_tx_power = 0, .rx_model_config = 1, - /* New stack (13.3->16.0)*/ + /* New stack (13.3->15.0) */ .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 0857fe4ee..b08c9ea27 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -84,9 +84,7 @@ void furi_hal_bt_init() { } // Explicitly tell that we are in charge of CLK48 domain - if(!LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_CLK48_CONFIG_SEMID)) { - furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); - } + furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); // Start Core2 ble_glue_init(); @@ -129,9 +127,7 @@ bool furi_hal_bt_start_radio_stack() { furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever); // Explicitly tell that we are in charge of CLK48 domain - if(!LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_CLK48_CONFIG_SEMID)) { - furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); - } + furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); do { // Wait until C2 is started or timeout diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index d85524ce4..a4df4877e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -213,7 +213,11 @@ void furi_hal_clock_switch_to_hsi() { while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) ; - LL_FLASH_SetLatency(LL_FLASH_LATENCY_1); + LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + + LL_FLASH_SetLatency(LL_FLASH_LATENCY_0); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0) + ; } void furi_hal_clock_switch_to_pll() { @@ -228,7 +232,11 @@ void furi_hal_clock_switch_to_pll() { while(!LL_RCC_PLLSAI1_IsReady()) ; + LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); + LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) + ; LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 3e4e3f48b..7d9334c2c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -29,10 +29,6 @@ #define FURI_HAL_POWER_DEBUG_STOP_GPIO (&gpio_ext_pc3) #endif -#ifndef FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO -#define FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO (&gpio_ext_pb3) -#endif - #ifndef FURI_HAL_POWER_STOP_MODE #define FURI_HAL_POWER_STOP_MODE (LL_PWR_MODE_STOP2) #endif @@ -92,10 +88,8 @@ void furi_hal_power_init() { #ifdef FURI_HAL_POWER_DEBUG furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_WFI_GPIO, GpioModeOutputPushPull); furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_STOP_GPIO, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, GpioModeOutputPushPull); furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, 0); #endif LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); @@ -158,9 +152,7 @@ bool furi_hal_power_sleep_available() { static inline bool furi_hal_power_deep_sleep_available() { return furi_hal_bt_is_alive() && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) && - !furi_hal_debug_is_gdb_session_active() && !LL_PWR_IsActiveFlag_CRPE() && - !LL_PWR_IsActiveFlag_CRP() && !LL_PWR_IsActiveFlag_BLEA() && - !LL_PWR_IsActiveFlag_BLEWU(); + !furi_hal_debug_is_gdb_session_active(); } static inline void furi_hal_power_light_sleep() { @@ -211,16 +203,7 @@ static inline void furi_hal_power_deep_sleep() { __force_stores(); #endif - bool should_abort_sleep = LL_PWR_IsActiveFlag_CRPE() || LL_PWR_IsActiveFlag_CRP() || - LL_PWR_IsActiveFlag_BLEA() || LL_PWR_IsActiveFlag_BLEWU(); - - if(should_abort_sleep) { -#ifdef FURI_HAL_POWER_DEBUG - furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_ABNORMAL_GPIO, 1); -#endif - } else { - __WFI(); - } + __WFI(); LL_LPM_EnableSleep(); diff --git a/furi/core/check.h b/furi/core/check.h index a507fc1ea..ea83f2219 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -48,21 +48,21 @@ FURI_NORETURN void __furi_halt(); } while(0) /** Check condition and crash if check failed */ -#define furi_check(__e) \ - do { \ - if(!(__e)) { \ - furi_crash(__FURI_ASSERT_MESSAGE_FLAG); \ - } \ - } while(0) - -/** Only in debug build: Assert condition and crash if assert failed */ -#ifdef FURI_DEBUG -#define furi_assert(__e) \ +#define furi_check(__e) \ do { \ if(!(__e)) { \ furi_crash(__FURI_CHECK_MESSAGE_FLAG); \ } \ } while(0) + +/** Only in debug build: Assert condition and crash if assert failed */ +#ifdef FURI_DEBUG +#define furi_assert(__e) \ + do { \ + if(!(__e)) { \ + furi_crash(__FURI_ASSERT_MESSAGE_FLAG); \ + } \ + } while(0) #else #define furi_assert(__e) \ do { \ From 451ec9cba00ba8d43a7810092c8fe539522e8f06 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:50:37 +0300 Subject: [PATCH 132/282] Update TOTP https://github.com/akopachov/flipper-zero_authenticator --- applications/external/totp/application.fam | 3 - applications/external/totp/cli/cli.c | 2 +- applications/external/totp/cli/cli_helpers.c | 42 +- applications/external/totp/cli/cli_helpers.h | 67 +- .../external/totp/cli/commands/add/add.c | 167 ++-- .../totp/cli/commands/automation/automation.c | 20 +- .../totp/cli/commands/delete/delete.c | 53 +- .../totp/cli/commands/details/details.c | 56 +- .../external/totp/cli/commands/list/list.c | 30 +- .../external/totp/cli/commands/move/move.c | 59 +- .../cli/commands/notification/notification.c | 21 +- .../external/totp/cli/commands/pin/pin.c | 66 +- .../external/totp/cli/commands/reset/reset.c | 10 +- .../external/totp/cli/commands/reset/reset.h | 5 +- .../totp/cli/commands/timezone/timezone.c | 13 +- .../totp/cli/commands/update/update.c | 244 +++--- .../totp/cli/common_command_arguments.c | 8 +- .../totp/cli/common_command_arguments.h | 6 + .../external/totp/lib/base64/base64.c | 1 + .../totp/lib/linked_list/linked_list.c | 136 --- .../totp/lib/linked_list/linked_list.h | 98 --- .../external/totp/lib/roll_value/roll_value.c | 2 +- .../external/totp/lib/roll_value/roll_value.h | 5 +- .../external/totp/services/config/config.c | 805 ++++++------------ .../external/totp/services/config/config.h | 127 +-- .../services/config/config_file_context.h | 3 + .../external/totp/services/config/constants.h | 5 +- .../config/migrations/common_migration.c | 43 +- .../config/migrations/common_migration.h | 8 +- .../services/config/token_info_iterator.c | 551 ++++++++++++ .../services/config/token_info_iterator.h | 121 +++ .../external/totp/services/crypto/crypto.c | 14 +- .../external/totp/services/crypto/crypto.h | 25 +- applications/external/totp/totp_app.c | 43 +- applications/external/totp/types/common.c | 3 + applications/external/totp/types/common.h | 2 +- applications/external/totp/types/nullable.h | 17 - .../external/totp/types/plugin_state.h | 17 +- applications/external/totp/types/token_info.c | 45 +- applications/external/totp/types/token_info.h | 42 +- .../external/totp/ui/scene_director.c | 20 +- .../external/totp/ui/scene_director.h | 5 +- .../add_new_token/totp_scene_add_new_token.c | 99 +-- .../add_new_token/totp_scene_add_new_token.h | 8 +- .../scenes/app_settings/totp_app_settings.c | 32 +- .../scenes/app_settings/totp_app_settings.h | 8 +- .../authenticate/totp_scene_authenticate.c | 13 +- .../totp_scene_generate_token.c | 146 ++-- .../totp_scene_generate_token.h | 8 +- .../external/totp/ui/scenes/standby/standby.c | 12 + .../external/totp/ui/scenes/standby/standby.h | 5 + .../scenes/token_menu/totp_scene_token_menu.c | 75 +- .../scenes/token_menu/totp_scene_token_menu.h | 8 +- .../external/totp/ui/totp_scenes_enum.h | 7 +- .../totp/workers/bt_type_code/bt_type_code.c | 30 + .../totp/workers/bt_type_code/bt_type_code.h | 79 +- .../generate_totp_code/generate_totp_code.c | 18 +- .../generate_totp_code/generate_totp_code.h | 65 +- .../external/totp/workers/type_code_common.c | 8 +- .../external/totp/workers/type_code_common.h | 8 + .../workers/usb_type_code/usb_type_code.c | 13 + .../workers/usb_type_code/usb_type_code.h | 52 +- 62 files changed, 2025 insertions(+), 1679 deletions(-) delete mode 100644 applications/external/totp/lib/linked_list/linked_list.c delete mode 100644 applications/external/totp/lib/linked_list/linked_list.h create mode 100644 applications/external/totp/services/config/config_file_context.h create mode 100644 applications/external/totp/services/config/token_info_iterator.c create mode 100644 applications/external/totp/services/config/token_info_iterator.h create mode 100644 applications/external/totp/types/common.c delete mode 100644 applications/external/totp/types/nullable.h create mode 100644 applications/external/totp/ui/scenes/standby/standby.c create mode 100644 applications/external/totp/ui/scenes/standby/standby.h diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index 2c7445734..4f71c2ce8 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -27,9 +27,6 @@ App( Lib( name="base64", ), - Lib( - name="linked_list" - ), Lib( name="timezone_utils", ), diff --git a/applications/external/totp/cli/cli.c b/applications/external/totp/cli/cli.c index 30876c7e4..c860b5a36 100644 --- a/applications/external/totp/cli/cli.c +++ b/applications/external/totp/cli/cli.c @@ -63,7 +63,7 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_AUTOMATION) == 0) { totp_cli_command_automation_handle(plugin_state, args, cli); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) { - totp_cli_command_reset_handle(cli, cli_context->event_queue); + totp_cli_command_reset_handle(plugin_state, cli, cli_context->event_queue); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_UPDATE) == 0) { totp_cli_command_update_handle(plugin_state, args, cli); } else if( diff --git a/applications/external/totp/cli/cli_helpers.c b/applications/external/totp/cli/cli_helpers.c index 36b98cf65..226917237 100644 --- a/applications/external/totp/cli/cli_helpers.c +++ b/applications/external/totp/cli/cli_helpers.c @@ -3,6 +3,11 @@ #include #include "../types/plugin_event.h" +const char* TOTP_CLI_COLOR_ERROR = "91m"; +const char* TOTP_CLI_COLOR_WARNING = "93m"; +const char* TOTP_CLI_COLOR_SUCCESS = "92m"; +const char* TOTP_CLI_COLOR_INFO = "96m"; + bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) { if(plugin_state->current_scene == TotpSceneAuthentication) { TOTP_CLI_PRINTF("Pleases enter PIN on your flipper device\r\n"); @@ -13,10 +18,11 @@ bool totp_cli_ensure_authenticated(const PluginState* plugin_state, Cli* cli) { furi_delay_ms(100); } - TOTP_CLI_DELETE_LAST_LINE(); + totp_cli_delete_last_line(); if(plugin_state->current_scene == TotpSceneAuthentication || //-V560 plugin_state->current_scene == TotpSceneNone) { //-V560 + TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); return false; } } @@ -54,7 +60,7 @@ bool totp_cli_read_line(Cli* cli, FuriString* out_str, bool mask_user_input) { } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { size_t out_str_size = furi_string_size(out_str); if(out_str_size > 0) { - TOTP_CLI_DELETE_LAST_CHAR(); + totp_cli_delete_last_char(); furi_string_left(out_str, out_str_size - 1); } } else if(c == CliSymbolAsciiCR) { @@ -83,3 +89,35 @@ void furi_string_secure_free(FuriString* str) { furi_string_free(str); } + +void totp_cli_print_invalid_arguments() { + TOTP_CLI_PRINTF_ERROR( + "Invalid command arguments. use \"help\" command to get list of available commands"); +} + +void totp_cli_print_error_updating_config_file() { + TOTP_CLI_PRINTF_ERROR("An error has occurred during updating config file\r\n"); +} + +void totp_cli_print_error_loading_token_info() { + TOTP_CLI_PRINTF_ERROR("An error has occurred during loading token information\r\n"); +} + +void totp_cli_print_processing() { + TOTP_CLI_PRINTF("Processing, please wait...\r\n"); +} + +void totp_cli_delete_last_char() { + TOTP_CLI_PRINTF("\b \b"); + fflush(stdout); +} + +void totp_cli_delete_current_line() { + TOTP_CLI_PRINTF("\33[2K\r"); + fflush(stdout); +} + +void totp_cli_delete_last_line() { + TOTP_CLI_PRINTF("\033[A\33[2K\r"); + fflush(stdout); +} diff --git a/applications/external/totp/cli/cli_helpers.h b/applications/external/totp/cli/cli_helpers.h index dd5a282d4..0f8b24364 100644 --- a/applications/external/totp/cli/cli_helpers.h +++ b/applications/external/totp/cli/cli_helpers.h @@ -14,6 +14,11 @@ #define DOCOPT_OPTIONS "[options]" #define DOCOPT_DEFAULT(val) "[default: " val "]" +extern const char* TOTP_CLI_COLOR_ERROR; +extern const char* TOTP_CLI_COLOR_WARNING; +extern const char* TOTP_CLI_COLOR_SUCCESS; +extern const char* TOTP_CLI_COLOR_INFO; + #define TOTP_CLI_PRINTF(format, ...) printf(format, ##__VA_ARGS__) #define TOTP_CLI_PRINTF_COLORFUL(color, format, ...) \ @@ -22,11 +27,6 @@ printf("\e[0m"); \ fflush(stdout) -#define TOTP_CLI_COLOR_ERROR "91m" -#define TOTP_CLI_COLOR_WARNING "93m" -#define TOTP_CLI_COLOR_SUCCESS "92m" -#define TOTP_CLI_COLOR_INFO "96m" - #define TOTP_CLI_PRINTF_ERROR(format, ...) \ TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_ERROR, format, ##__VA_ARGS__) #define TOTP_CLI_PRINTF_WARNING(format, ...) \ @@ -36,24 +36,12 @@ #define TOTP_CLI_PRINTF_INFO(format, ...) \ TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_INFO, format, ##__VA_ARGS__) -#define TOTP_CLI_DELETE_LAST_LINE() \ - TOTP_CLI_PRINTF("\033[A\33[2K\r"); \ - fflush(stdout) +#define TOTP_CLI_LOCK_UI(plugin_state) \ + Scene __previous_scene = plugin_state->current_scene; \ + totp_scene_director_activate_scene(plugin_state, TotpSceneStandby) -#define TOTP_CLI_DELETE_CURRENT_LINE() \ - TOTP_CLI_PRINTF("\33[2K\r"); \ - fflush(stdout) - -#define TOTP_CLI_DELETE_LAST_CHAR() \ - TOTP_CLI_PRINTF("\b \b"); \ - fflush(stdout) - -#define TOTP_CLI_PRINT_INVALID_ARGUMENTS() \ - TOTP_CLI_PRINTF_ERROR( \ - "Invalid command arguments. use \"help\" command to get list of available commands") - -#define TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE() \ - TOTP_CLI_PRINTF_ERROR("An error has occurred during updating config file\r\n") +#define TOTP_CLI_UNLOCK_UI(plugin_state) \ + totp_scene_director_activate_scene(plugin_state, __previous_scene) /** * @brief Checks whether user is authenticated and entered correct PIN. @@ -92,3 +80,38 @@ bool args_read_uint8_and_trim(FuriString* args, uint8_t* value); * @param str instance to free */ void furi_string_secure_free(FuriString* str); + +/** + * @brief Deletes last printed line in console + */ +void totp_cli_delete_last_line(); + +/** + * @brief Deletes current printed line in console + */ +void totp_cli_delete_current_line(); + +/** + * @brief Deletes last printed char in console + */ +void totp_cli_delete_last_char(); + +/** + * @brief Prints error message about invalid command arguments + */ +void totp_cli_print_invalid_arguments(); + +/** + * @brief Prints error message about config file update error + */ +void totp_cli_print_error_updating_config_file(); + +/** + * @brief Prints error message about config file loading error + */ +void totp_cli_print_error_loading_token_info(); + +/** + * @brief Prints message to let user knwo that command is processing now + */ +void totp_cli_print_processing(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/add/add.c b/applications/external/totp/cli/commands/add/add.c index 3549e785b..b2cd01352 100644 --- a/applications/external/totp/cli/commands/add/add.c +++ b/applications/external/totp/cli/commands/add/add.c @@ -1,7 +1,6 @@ #include "add.h" #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" @@ -9,6 +8,77 @@ #include "../../../ui/scene_director.h" #include "../../common_command_arguments.h" +struct TotpAddContext { + FuriString* args; + Cli* cli; + uint8_t* iv; +}; + +enum TotpIteratorUpdateTokenResultsEx { + TotpIteratorUpdateTokenResultInvalidSecret = 1, + TotpIteratorUpdateTokenResultCancelled = 2, + TotpIteratorUpdateTokenResultInvalidArguments = 3 +}; + +static TotpIteratorUpdateTokenResult + add_token_handler(TokenInfo* token_info, const void* context) { + const struct TotpAddContext* context_t = context; + + // Reading token name + if(!args_read_probably_quoted_string_and_trim(context_t->args, token_info->name)) { + return TotpIteratorUpdateTokenResultInvalidArguments; + } + + FuriString* temp_str = furi_string_alloc(); + + // Read optional arguments + bool mask_user_input = true; + PlainTokenSecretEncoding token_secret_encoding = PlainTokenSecretEncodingBase32; + while(args_read_string_and_trim(context_t->args, temp_str)) { + bool parsed = false; + if(!totp_cli_try_read_algo(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_digits(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_duration(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && + !totp_cli_try_read_automation_features(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_plain_token_secret_encoding( + temp_str, context_t->args, &parsed, &token_secret_encoding)) { + totp_cli_printf_unknown_argument(temp_str); + } + + if(!parsed) { + furi_string_free(temp_str); + return TotpIteratorUpdateTokenResultInvalidArguments; + } + } + + // Reading token secret + furi_string_reset(temp_str); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + if(!totp_cli_read_line(context_t->cli, temp_str, mask_user_input)) { + totp_cli_delete_last_line(); + furi_string_secure_free(temp_str); + return TotpIteratorUpdateTokenResultCancelled; + } + + totp_cli_delete_last_line(); + + bool secret_set = token_info_set_secret( + token_info, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + token_secret_encoding, + context_t->iv); + + furi_string_secure_free(temp_str); + + if(!secret_set) { + return TotpIteratorUpdateTokenResultInvalidSecret; + } + + return TotpIteratorUpdateTokenResultSuccess; +} + void totp_cli_command_add_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD ", " TOTP_CLI_COMMAND_ADD_ALT ", " TOTP_CLI_COMMAND_ADD_ALT2 " Add new token\r\n"); @@ -75,90 +145,33 @@ void totp_cli_command_add_docopt_options() { } void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { - FuriString* temp_str = furi_string_alloc(); - TokenInfo* token_info = token_info_alloc(); - - // Reading token name - if(!args_read_probably_quoted_string_and_trim(args, temp_str)) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); - furi_string_free(temp_str); - token_info_free(token_info); + if(!totp_cli_ensure_authenticated(plugin_state, cli)) { return; } - size_t temp_cstr_len = furi_string_size(temp_str); - token_info->name = malloc(temp_cstr_len + 1); - furi_check(token_info->name != NULL); - strlcpy(token_info->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1); + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); - // Read optional arguments - bool mask_user_input = true; - PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; - while(args_read_string_and_trim(args, temp_str)) { - bool parsed = false; - if(!totp_cli_try_read_algo(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_digits(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_duration(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && - !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_plain_token_secret_encoding( - temp_str, args, &parsed, &token_secret_encoding)) { - totp_cli_printf_unknown_argument(temp_str); - } + TOTP_CLI_LOCK_UI(plugin_state); - if(!parsed) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); - furi_string_free(temp_str); - token_info_free(token_info); - return; - } - } + struct TotpAddContext add_context = {.args = args, .cli = cli, .iv = &plugin_state->iv[0]}; + TotpIteratorUpdateTokenResult add_result = + totp_token_info_iterator_add_new_token(iterator_context, &add_token_handler, &add_context); - // Reading token secret - furi_string_reset(temp_str); - TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); - if(!totp_cli_read_line(cli, temp_str, mask_user_input) || - !totp_cli_ensure_authenticated(plugin_state, cli)) { - TOTP_CLI_DELETE_LAST_LINE(); + if(add_result == TotpIteratorUpdateTokenResultSuccess) { + TOTP_CLI_PRINTF_SUCCESS( + "Token \"%s\" has been successfully added\r\n", + furi_string_get_cstr( + totp_token_info_iterator_get_current_token(iterator_context)->name)); + } else if(add_result == TotpIteratorUpdateTokenResultCancelled) { TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); - furi_string_secure_free(temp_str); - token_info_free(token_info); - return; - } - - TOTP_CLI_DELETE_LAST_LINE(); - - bool load_generate_token_scene = false; - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - load_generate_token_scene = true; - } - - bool secret_set = token_info_set_secret( - token_info, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str), - token_secret_encoding, - plugin_state->iv); - - furi_string_secure_free(temp_str); - - if(secret_set) { - TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, token_info, furi_check); - plugin_state->tokens_count++; - - if(totp_config_file_save_new_token(token_info) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully added\r\n", token_info->name); - } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - } - } else { - token_info_free(token_info); + } else if(add_result == TotpIteratorUpdateTokenResultInvalidArguments) { + totp_cli_print_invalid_arguments(); + } else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) { TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); + } else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) { + totp_cli_print_error_updating_config_file(); } - if(load_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/automation/automation.c b/applications/external/totp/cli/commands/automation/automation.c index 1af2e5dd7..c9f6ac34b 100644 --- a/applications/external/totp/cli/commands/automation/automation.c +++ b/applications/external/totp/cli/commands/automation/automation.c @@ -31,7 +31,7 @@ void totp_cli_command_automation_docopt_arguments() { "\r\n"); } -static void totp_cli_command_automation_print_method(AutomationMethod method, char* color) { +static void totp_cli_command_automation_print_method(AutomationMethod method, const char* color) { #ifdef TOTP_BADBT_TYPE_ENABLED bool has_previous_method = false; #endif @@ -88,26 +88,20 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a do { if(!args_valid) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); break; } if(new_method_provided) { - Scene previous_scene = TotpSceneNone; - if(plugin_state->current_scene == TotpSceneGenerateToken || - plugin_state->current_scene == TotpSceneAppSettings) { - previous_scene = plugin_state->current_scene; - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - } + TOTP_CLI_LOCK_UI(plugin_state); plugin_state->automation_method = new_method; - if(totp_config_file_update_automation_method(new_method) == - TotpConfigFileUpdateSuccess) { + if(totp_config_file_update_automation_method(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Automation method is set to "); totp_cli_command_automation_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); cli_nl(); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_print_error_updating_config_file(); } #ifdef TOTP_BADBT_TYPE_ENABLED @@ -118,9 +112,7 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a } #endif - if(previous_scene != TotpSceneNone) { - totp_scene_director_activate_scene(plugin_state, previous_scene, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_INFO("Current automation method is "); totp_cli_command_automation_print_method( diff --git a/applications/external/totp/cli/commands/delete/delete.c b/applications/external/totp/cli/commands/delete/delete.c index a45525e4b..9a084b23f 100644 --- a/applications/external/totp/cli/commands/delete/delete.c +++ b/applications/external/totp/cli/commands/delete/delete.c @@ -3,7 +3,6 @@ #include #include #include -#include #include "../../../services/config/config.h" #include "../../cli_helpers.h" #include "../../../ui/scene_director.h" @@ -37,10 +36,13 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, return; } + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + int token_number; if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || - token_number > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) { + totp_cli_print_invalid_arguments(); return; } @@ -51,23 +53,27 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, confirm_needed = false; } else { totp_cli_printf_unknown_argument(temp_str); - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); furi_string_free(temp_str); return; } } furi_string_free(temp_str); - ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1); + TOTP_CLI_LOCK_UI(plugin_state); - TokenInfo* token_info = list_node->data; + size_t original_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + totp_token_info_iterator_go_to(iterator_context, token_number - 1); + const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); + const char* token_info_name = furi_string_get_cstr(token_info->name); bool confirmed = !confirm_needed; if(confirm_needed) { TOTP_CLI_PRINTF_WARNING("WARNING!\r\n"); TOTP_CLI_PRINTF_WARNING( "TOKEN \"%s\" WILL BE PERMANENTLY DELETED WITHOUT ABILITY TO RECOVER IT.\r\n", - token_info->name); + token_info_name); TOTP_CLI_PRINTF_WARNING("Confirm? [y/n]\r\n"); fflush(stdout); char user_pick; @@ -80,32 +86,21 @@ void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, } if(confirmed) { - if(!totp_cli_ensure_authenticated(plugin_state, cli)) { - return; - } - - bool activate_generate_token_scene = false; - if(plugin_state->current_scene != TotpSceneAuthentication) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - activate_generate_token_scene = true; - } - - plugin_state->tokens_list = list_remove(plugin_state->tokens_list, list_node); - plugin_state->tokens_count--; - - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { + totp_cli_print_processing(); + if(totp_token_info_iterator_remove_current_token_info(iterator_context)) { + totp_cli_delete_last_line(); TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully deleted\r\n", token_info->name); + "Token \"%s\" has been successfully deleted\r\n", token_info_name); + totp_token_info_iterator_go_to(iterator_context, 0); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - } - - token_info_free(token_info); - - if(activate_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + totp_cli_delete_last_line(); + totp_cli_print_error_updating_config_file(); + totp_token_info_iterator_go_to(iterator_context, original_token_index); } } else { TOTP_CLI_PRINTF_INFO("User has not confirmed\r\n"); + totp_token_info_iterator_go_to(iterator_context, original_token_index); } + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/details/details.c b/applications/external/totp/cli/commands/details/details.c index 1b9289454..1677121b1 100644 --- a/applications/external/totp/cli/commands/details/details.c +++ b/applications/external/totp/cli/commands/details/details.c @@ -1,9 +1,10 @@ #include "details.h" #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/constants.h" +#include "../../../services/config/config.h" +#include "../../../ui/scene_director.h" #include "../../cli_helpers.h" #include "../../common_command_arguments.h" @@ -17,21 +18,21 @@ } while(false) static void print_automation_features(const TokenInfo* token_info) { - if(token_info->automation_features == TOKEN_AUTOMATION_FEATURE_NONE) { + if(token_info->automation_features == TokenAutomationFeatureNone) { TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Automation features", "None"); return; } bool header_printed = false; - if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) { + if(token_info->automation_features & TokenAutomationFeatureEnterAtTheEnd) { TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type key at the end", header_printed); } - if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END) { + if(token_info->automation_features & TokenAutomationFeatureTabAtTheEnd) { TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type key at the end", header_printed); } - if(token_info->automation_features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { + if(token_info->automation_features & TokenAutomationFeatureTypeSlower) { TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type slower", header_printed); } } @@ -53,26 +54,39 @@ void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args } int token_number; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || - token_number > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) { + totp_cli_print_invalid_arguments(); return; } - ListNode* list_node = list_element_at(plugin_state->tokens_list, token_number - 1); + TOTP_CLI_LOCK_UI(plugin_state); - TokenInfo* token_info = list_node->data; + size_t original_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + if(totp_token_info_iterator_go_to(iterator_context, token_number - 1)) { + const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); - TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value"); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); - TOTP_CLI_PRINTF("| %-20s | %-28d |\r\n", "Index", token_number); - TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Name", token_info->name); - TOTP_CLI_PRINTF( - "| %-20s | %-28s |\r\n", "Hashing algorithm", token_info_get_algo_as_cstr(token_info)); - TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", "Number of digits", token_info->digits); - TOTP_CLI_PRINTF( - "| %-20s | %" PRIu8 " sec.%-21s |\r\n", "Token lifetime", token_info->duration, " "); - print_automation_features(token_info); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value"); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("| %-20s | %-28d |\r\n", "Index", token_number); + TOTP_CLI_PRINTF( + "| %-20s | %-28.28s |\r\n", "Name", furi_string_get_cstr(token_info->name)); + TOTP_CLI_PRINTF( + "| %-20s | %-28s |\r\n", "Hashing algorithm", token_info_get_algo_as_cstr(token_info)); + TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", "Number of digits", token_info->digits); + TOTP_CLI_PRINTF( + "| %-20s | %" PRIu8 " sec.%-21s |\r\n", "Token lifetime", token_info->duration, " "); + print_automation_features(token_info); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + } else { + totp_cli_print_error_loading_token_info(); + } + + totp_token_info_iterator_go_to(iterator_context, original_token_index); + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/list/list.c b/applications/external/totp/cli/commands/list/list.c index 951c102a0..bf5a8da52 100644 --- a/applications/external/totp/cli/commands/list/list.c +++ b/applications/external/totp/cli/commands/list/list.c @@ -1,8 +1,9 @@ #include "list.h" #include -#include #include "../../../types/token_info.h" #include "../../../services/config/constants.h" +#include "../../../services/config/config.h" +#include "../../../ui/scene_director.h" #include "../../cli_helpers.h" void totp_cli_command_list_docopt_commands() { @@ -20,25 +21,36 @@ void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) { return; } - if(plugin_state->tokens_list == NULL) { + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t total_count = totp_token_info_iterator_get_total_count(iterator_context); + if(total_count <= 0) { TOTP_CLI_PRINTF("There are no tokens"); return; } + TOTP_CLI_LOCK_UI(plugin_state); + + size_t original_index = totp_token_info_iterator_get_current_token_index(iterator_context); + TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); TOTP_CLI_PRINTF("| %-3s | %-25s | %-6s | %-s | %-s |\r\n", "#", "Name", "Algo", "Ln", "Dur"); TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); - uint16_t index = 1; - TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { - TokenInfo* token_info = (TokenInfo*)node->data; + for(size_t i = 0; i < total_count; i++) { + totp_token_info_iterator_go_to(iterator_context, i); + const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); TOTP_CLI_PRINTF( "| %-3" PRIu16 " | %-25.25s | %-6s | %-2" PRIu8 " | %-3" PRIu8 " |\r\n", - index, - token_info->name, + i + 1, + furi_string_get_cstr(token_info->name), token_info_get_algo_as_cstr(token_info), token_info->digits, token_info->duration); - index++; - }); + } + TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); + + totp_token_info_iterator_go_to(iterator_context, original_index); + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/move/move.c b/applications/external/totp/cli/commands/move/move.c index 5c4bdfcd6..f26cda46e 100644 --- a/applications/external/totp/cli/commands/move/move.c +++ b/applications/external/totp/cli/commands/move/move.c @@ -2,7 +2,6 @@ #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../cli_helpers.h" @@ -33,42 +32,52 @@ void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, C return; } - int token_index; - if(!args_read_int_and_trim(args, &token_index) || token_index < 1 || - token_index > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + int token_number; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t total_count = totp_token_info_iterator_get_total_count(iterator_context); + if(!args_read_int_and_trim(args, &token_number) || token_number < 1 || + (size_t)token_number > total_count) { + totp_cli_print_invalid_arguments(); return; } - int new_token_index = 0; + int new_token_number = 0; - if(!args_read_int_and_trim(args, &new_token_index) || new_token_index < 1 || - new_token_index > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + if(!args_read_int_and_trim(args, &new_token_number) || new_token_number < 1 || + (size_t)new_token_number > total_count) { + totp_cli_print_invalid_arguments(); return; } - bool activate_generate_token_scene = false; - if(plugin_state->current_scene != TotpSceneAuthentication) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - activate_generate_token_scene = true; + if(token_number == new_token_number) { + TOTP_CLI_PRINTF_ERROR("New token number matches current token number\r\n"); + return; } - TokenInfo* token_info = NULL; - plugin_state->tokens_list = - list_remove_at(plugin_state->tokens_list, token_index - 1, (void**)&token_info); - furi_check(token_info != NULL); - plugin_state->tokens_list = - list_insert_at(plugin_state->tokens_list, new_token_index - 1, token_info); + TOTP_CLI_LOCK_UI(plugin_state); - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { + size_t token_index = token_number - 1; + size_t new_token_index = new_token_number - 1; + + size_t original_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + + totp_cli_print_processing(); + + if(totp_token_info_iterator_go_to(iterator_context, token_index) && + totp_token_info_iterator_move_current_token_info(iterator_context, new_token_index)) { + totp_cli_delete_last_line(); TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully updated\r\n", token_info->name); + "Token \"%s\" has been successfully updated\r\n", + furi_string_get_cstr( + totp_token_info_iterator_get_current_token(iterator_context)->name)); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_delete_last_line(); + totp_cli_print_error_updating_config_file(); } - if(activate_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_token_info_iterator_go_to(iterator_context, original_token_index); + + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/notification/notification.c b/applications/external/totp/cli/commands/notification/notification.c index b81b7371a..f91b1b982 100644 --- a/applications/external/totp/cli/commands/notification/notification.c +++ b/applications/external/totp/cli/commands/notification/notification.c @@ -28,7 +28,8 @@ void totp_cli_command_notification_docopt_arguments() { ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "\r\n"); } -static void totp_cli_command_notification_print_method(NotificationMethod method, char* color) { +static void + totp_cli_command_notification_print_method(NotificationMethod method, const char* color) { bool has_previous_method = false; if(method & NotificationMethodSound) { TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "\""); @@ -73,31 +74,23 @@ void totp_cli_command_notification_handle(PluginState* plugin_state, FuriString* do { if(!args_valid) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); break; } if(new_method_provided) { - Scene previous_scene = TotpSceneNone; - if(plugin_state->current_scene == TotpSceneGenerateToken || - plugin_state->current_scene == TotpSceneAppSettings) { - previous_scene = plugin_state->current_scene; - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - } + TOTP_CLI_LOCK_UI(plugin_state); plugin_state->notification_method = new_method; - if(totp_config_file_update_notification_method(new_method) == - TotpConfigFileUpdateSuccess) { + if(totp_config_file_update_notification_method(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Notification method is set to "); totp_cli_command_notification_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); cli_nl(); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_print_error_updating_config_file(); } - if(previous_scene != TotpSceneNone) { - totp_scene_director_activate_scene(plugin_state, previous_scene, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_INFO("Current notification method is "); totp_cli_command_notification_print_method( diff --git a/applications/external/totp/cli/commands/pin/pin.c b/applications/external/totp/cli/commands/pin/pin.c index 9b9038ae7..62531b96a 100644 --- a/applications/external/totp/cli/commands/pin/pin.c +++ b/applications/external/totp/cli/commands/pin/pin.c @@ -2,7 +2,6 @@ #include #include -#include #include "../../../types/token_info.h" #include "../../../types/user_pin_codes.h" #include "../../../services/config/config.h" @@ -65,14 +64,14 @@ static bool totp_cli_read_pin(Cli* cli, uint8_t* pin, uint8_t* pin_length) { } } } else if(c == CliSymbolAsciiETX) { - TOTP_CLI_DELETE_CURRENT_LINE(); + totp_cli_delete_current_line(); TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); return false; } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { if(*pin_length > 0) { *pin_length = *pin_length - 1; pin[*pin_length] = 0; - TOTP_CLI_DELETE_LAST_CHAR(); + totp_cli_delete_last_char(); } } else if(c == CliSymbolAsciiCR) { cli_nl(); @@ -80,7 +79,7 @@ static bool totp_cli_read_pin(Cli* cli, uint8_t* pin, uint8_t* pin_length) { } } - TOTP_CLI_DELETE_LAST_LINE(); + totp_cli_delete_last_line(); return true; } @@ -97,22 +96,22 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) == 0) { do_remove = true; } else { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); } } else { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + totp_cli_print_invalid_arguments(); } if((do_change || do_remove) && totp_cli_ensure_authenticated(plugin_state, cli)) { - bool load_generate_token_scene = false; + TOTP_CLI_LOCK_UI(plugin_state); do { uint8_t old_iv[TOTP_IV_SIZE]; memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); uint8_t new_pin[TOTP_IV_SIZE]; + memset(&new_pin[0], 0, TOTP_IV_SIZE); uint8_t new_pin_length = 0; if(do_change) { - if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length) || - !totp_cli_ensure_authenticated(plugin_state, cli)) { + if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length)) { memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); break; } @@ -121,7 +120,7 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl memset(&new_pin[0], 0, TOTP_IV_SIZE); } - char* backup_path = totp_config_file_backup(); + char* backup_path = totp_config_file_backup(plugin_state); if(backup_path != NULL) { TOTP_CLI_PRINTF_WARNING("Backup conf file %s has been created\r\n", backup_path); TOTP_CLI_PRINTF_WARNING( @@ -134,61 +133,28 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl break; } - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - load_generate_token_scene = true; - } + TOTP_CLI_PRINTF("Encrypting...\r\n"); - TOTP_CLI_PRINTF("Encrypting, please wait...\r\n"); - - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); - memset(&plugin_state->base_iv[0], 0, TOTP_IV_SIZE); - if(plugin_state->crypto_verify_data != NULL) { - free(plugin_state->crypto_verify_data); - plugin_state->crypto_verify_data = NULL; - } - - if(!totp_crypto_seed_iv( - plugin_state, new_pin_length > 0 ? &new_pin[0] : NULL, new_pin_length)) { - memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - break; - } + bool update_result = + totp_config_file_update_encryption(plugin_state, new_pin, new_pin_length); memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); - TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { - TokenInfo* token_info = node->data; - size_t plain_token_length; - uint8_t* plain_token = totp_crypto_decrypt( - token_info->token, token_info->token_length, &old_iv[0], &plain_token_length); - free(token_info->token); - token_info->token = totp_crypto_encrypt( - plain_token, - plain_token_length, - &plugin_state->iv[0], - &token_info->token_length); - memset_s(plain_token, plain_token_length, 0, plain_token_length); - free(plain_token); - }); + totp_cli_delete_last_line(); - TOTP_CLI_DELETE_LAST_LINE(); - - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { + if(update_result) { if(do_change) { TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully changed\r\n"); } else if(do_remove) { TOTP_CLI_PRINTF_SUCCESS("PIN has been successfully removed\r\n"); } } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); + totp_cli_print_error_updating_config_file(); } } while(false); - if(load_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + TOTP_CLI_UNLOCK_UI(plugin_state); } furi_string_free(temp_str); diff --git a/applications/external/totp/cli/commands/reset/reset.c b/applications/external/totp/cli/commands/reset/reset.c index cd2d1bf46..96b2cc56a 100644 --- a/applications/external/totp/cli/commands/reset/reset.c +++ b/applications/external/totp/cli/commands/reset/reset.c @@ -3,6 +3,7 @@ #include #include #include "../../cli_helpers.h" +#include "../../../ui/scene_director.h" #include "../../../services/config/config.h" #define TOTP_CLI_RESET_CONFIRMATION_KEYWORD "YES" @@ -16,7 +17,11 @@ void totp_cli_command_reset_docopt_usage() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_RESET "\r\n"); } -void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue) { +void totp_cli_command_reset_handle( + PluginState* plugin_state, + Cli* cli, + FuriMessageQueue* event_queue) { + TOTP_CLI_LOCK_UI(plugin_state); TOTP_CLI_PRINTF_WARNING( "As a result of reset all the settings and tokens will be permanently lost.\r\n"); TOTP_CLI_PRINTF_WARNING("Do you really want to reset application?\r\n"); @@ -27,11 +32,12 @@ void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue) { furi_string_cmpi_str(temp_str, TOTP_CLI_RESET_CONFIRMATION_KEYWORD) == 0; furi_string_free(temp_str); if(is_confirmed) { - totp_config_file_reset(); + totp_config_file_reset(plugin_state); TOTP_CLI_PRINTF_SUCCESS("Application has been successfully reset to default.\r\n"); TOTP_CLI_PRINTF_SUCCESS("Now application will be closed to apply all the changes.\r\n"); totp_cli_force_close_app(event_queue); } else { TOTP_CLI_PRINTF_INFO("Action was not confirmed by user\r\n"); + TOTP_CLI_UNLOCK_UI(plugin_state); } } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/reset/reset.h b/applications/external/totp/cli/commands/reset/reset.h index 7c879e13e..3a4675587 100644 --- a/applications/external/totp/cli/commands/reset/reset.h +++ b/applications/external/totp/cli/commands/reset/reset.h @@ -5,6 +5,9 @@ #define TOTP_CLI_COMMAND_RESET "reset" -void totp_cli_command_reset_handle(Cli* cli, FuriMessageQueue* event_queue); +void totp_cli_command_reset_handle( + PluginState* plugin_state, + Cli* cli, + FuriMessageQueue* event_queue); void totp_cli_command_reset_docopt_commands(); void totp_cli_command_reset_docopt_usage(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/timezone/timezone.c b/applications/external/totp/cli/commands/timezone/timezone.c index 61e4fa065..0f6bc5a76 100644 --- a/applications/external/totp/cli/commands/timezone/timezone.c +++ b/applications/external/totp/cli/commands/timezone/timezone.c @@ -33,19 +33,14 @@ void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* arg char* strtof_endptr; float tz = strtof(furi_string_get_cstr(temp_str), &strtof_endptr); if(*strtof_endptr == 0 && tz >= -12.75f && tz <= 12.75f) { + TOTP_CLI_LOCK_UI(plugin_state); plugin_state->timezone_offset = tz; - if(totp_config_file_update_timezone_offset(tz) == TotpConfigFileUpdateSuccess) { + if(totp_config_file_update_timezone_offset(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Timezone is set to %f\r\n", (double)tz); } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - } - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } else if(plugin_state->current_scene == TotpSceneAppSettings) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL); + totp_cli_print_error_updating_config_file(); } + TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_ERROR("Invalid timezone offset\r\n"); } diff --git a/applications/external/totp/cli/commands/update/update.c b/applications/external/totp/cli/commands/update/update.c index bba7cad35..49beb7b6d 100644 --- a/applications/external/totp/cli/commands/update/update.c +++ b/applications/external/totp/cli/commands/update/update.c @@ -1,7 +1,6 @@ #include "update.h" #include #include -#include #include "../../../types/token_info.h" #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" @@ -11,6 +10,103 @@ #define TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX "-s" +struct TotpUpdateContext { + FuriString* args; + Cli* cli; + uint8_t* iv; +}; + +enum TotpIteratorUpdateTokenResultsEx { + TotpIteratorUpdateTokenResultInvalidSecret = 1, + TotpIteratorUpdateTokenResultCancelled = 2, + TotpIteratorUpdateTokenResultInvalidArguments = 3 +}; + +static bool totp_cli_try_read_name( + TokenInfo* token_info, + const FuriString* arg, + FuriString* args, + bool* parsed) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_NAME_PREFIX) == 0) { + if(!args_read_probably_quoted_string_and_trim(args, token_info->name) || + furi_string_empty(token_info->name)) { + totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_NAME_PREFIX); + } else { + *parsed = true; + } + + return true; + } + + return false; +} + +static bool totp_cli_try_read_change_secret_flag(const FuriString* arg, bool* parsed, bool* flag) { + if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) == 0) { + *flag = true; + *parsed = true; + return true; + } + + return false; +} + +static TotpIteratorUpdateTokenResult + update_token_handler(TokenInfo* token_info, const void* context) { + const struct TotpUpdateContext* context_t = context; + + // Read optional arguments + FuriString* temp_str = furi_string_alloc(); + bool mask_user_input = true; + bool update_token_secret = false; + PlainTokenSecretEncoding token_secret_encoding = PlainTokenSecretEncodingBase32; + while(args_read_string_and_trim(context_t->args, temp_str)) { + bool parsed = false; + if(!totp_cli_try_read_name(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_algo(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_digits(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_duration(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && + !totp_cli_try_read_change_secret_flag(temp_str, &parsed, &update_token_secret) && + !totp_cli_try_read_automation_features(token_info, temp_str, context_t->args, &parsed) && + !totp_cli_try_read_plain_token_secret_encoding( + temp_str, context_t->args, &parsed, &token_secret_encoding)) { + totp_cli_printf_unknown_argument(temp_str); + } + + if(!parsed) { + furi_string_free(temp_str); + return TotpIteratorUpdateTokenResultInvalidArguments; + } + } + + if(update_token_secret) { + // Reading token secret + furi_string_reset(temp_str); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + bool token_secret_read = totp_cli_read_line(context_t->cli, temp_str, mask_user_input); + totp_cli_delete_last_line(); + if(!token_secret_read) { + furi_string_secure_free(temp_str); + return TotpIteratorUpdateTokenResultCancelled; + } + + if(!token_info_set_secret( + token_info, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + token_secret_encoding, + context_t->iv)) { + furi_string_secure_free(temp_str); + return TotpIteratorUpdateTokenResultInvalidSecret; + } + } + + furi_string_secure_free(temp_str); + + return TotpIteratorUpdateTokenResultSuccess; +} + void totp_cli_command_update_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_UPDATE " Update existing token\r\n"); } @@ -34,136 +130,46 @@ void totp_cli_command_update_docopt_options() { TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) " Update token secret\r\n"); } -static bool - totp_cli_try_read_name(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed) { - if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_ARG_NAME_PREFIX) == 0) { - if(!args_read_probably_quoted_string_and_trim(args, arg) || furi_string_empty(arg)) { - totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_NAME_PREFIX); - } else { - if(token_info->name != NULL) { - free(token_info->name); - } - - size_t temp_cstr_len = furi_string_size(arg); - token_info->name = malloc(temp_cstr_len + 1); - furi_check(token_info->name != NULL); - strlcpy(token_info->name, furi_string_get_cstr(arg), temp_cstr_len + 1); - *parsed = true; - } - - return true; - } - - return false; -} - -static bool totp_cli_try_read_change_secret_flag(const FuriString* arg, bool* parsed, bool* flag) { - if(furi_string_cmpi_str(arg, TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) == 0) { - *flag = true; - *parsed = true; - return true; - } - - return false; -} - void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { - FuriString* temp_str = furi_string_alloc(); - if(!totp_cli_ensure_authenticated(plugin_state, cli)) { return; } + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + int token_number; if(!args_read_int_and_trim(args, &token_number) || token_number <= 0 || - token_number > plugin_state->tokens_count) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); + (size_t)token_number > totp_token_info_iterator_get_total_count(iterator_context)) { + totp_cli_print_invalid_arguments(); return; } - ListNode* list_item = list_element_at(plugin_state->tokens_list, token_number - 1); - TokenInfo* existing_token_info = list_item->data; - TokenInfo* token_info = token_info_clone(existing_token_info); + TOTP_CLI_LOCK_UI(plugin_state); - // Read optional arguments - bool mask_user_input = true; - bool update_token_secret = false; - PlainTokenSecretEncoding token_secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; - while(args_read_string_and_trim(args, temp_str)) { - bool parsed = false; - if(!totp_cli_try_read_name(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_algo(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_digits(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_duration(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_unsecure_flag(temp_str, &parsed, &mask_user_input) && - !totp_cli_try_read_change_secret_flag(temp_str, &parsed, &update_token_secret) && - !totp_cli_try_read_automation_features(token_info, temp_str, args, &parsed) && - !totp_cli_try_read_plain_token_secret_encoding( - temp_str, args, &parsed, &token_secret_encoding)) { - totp_cli_printf_unknown_argument(temp_str); - } + size_t previous_index = totp_token_info_iterator_get_current_token_index(iterator_context); + totp_token_info_iterator_go_to(iterator_context, token_number - 1); - if(!parsed) { - TOTP_CLI_PRINT_INVALID_ARGUMENTS(); - furi_string_free(temp_str); - token_info_free(token_info); - return; - } + struct TotpUpdateContext update_context = { + .args = args, .cli = cli, .iv = &plugin_state->iv[0]}; + TotpIteratorUpdateTokenResult update_result = totp_token_info_iterator_update_current_token( + iterator_context, &update_token_handler, &update_context); + + if(update_result == TotpIteratorUpdateTokenResultSuccess) { + TOTP_CLI_PRINTF_SUCCESS( + "Token \"%s\" has been successfully updated\r\n", + furi_string_get_cstr( + totp_token_info_iterator_get_current_token(iterator_context)->name)); + } else if(update_result == TotpIteratorUpdateTokenResultInvalidArguments) { + totp_cli_print_invalid_arguments(); + } else if(update_result == TotpIteratorUpdateTokenResultCancelled) { + TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); + } else if(update_result == TotpIteratorUpdateTokenResultInvalidSecret) { + TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); + } else if(update_result == TotpIteratorUpdateTokenResultFileUpdateFailed) { + totp_cli_print_error_updating_config_file(); } - bool token_secret_read = false; - if(update_token_secret) { - // Reading token secret - furi_string_reset(temp_str); - TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); - token_secret_read = totp_cli_read_line(cli, temp_str, mask_user_input); - TOTP_CLI_DELETE_LAST_LINE(); - if(!token_secret_read) { - TOTP_CLI_PRINTF_INFO("Cancelled by user\r\n"); - } - } - - bool load_generate_token_scene = false; - if(plugin_state->current_scene == TotpSceneGenerateToken) { - totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL); - load_generate_token_scene = true; - } - - bool token_secret_set = false; - if(update_token_secret && token_secret_read) { - if(token_info->token != NULL) { - free(token_info->token); - } - token_secret_set = token_info_set_secret( - token_info, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str), - token_secret_encoding, - plugin_state->iv); - if(!token_secret_set) { - TOTP_CLI_PRINTF_ERROR("Token secret seems to be invalid and can not be parsed\r\n"); - } - } - - furi_string_secure_free(temp_str); - - if(!update_token_secret || (token_secret_read && token_secret_set)) { - list_item->data = token_info; - - if(totp_full_save_config_file(plugin_state) == TotpConfigFileUpdateSuccess) { - TOTP_CLI_PRINTF_SUCCESS( - "Token \"%s\" has been successfully updated\r\n", token_info->name); - token_info_free(existing_token_info); - } else { - TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE(); - list_item->data = existing_token_info; - token_info_free(token_info); - } - } else { - token_info_free(token_info); - } - - if(load_generate_token_scene) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_token_info_iterator_go_to(iterator_context, previous_index); + TOTP_CLI_UNLOCK_UI(plugin_state); } \ No newline at end of file diff --git a/applications/external/totp/cli/common_command_arguments.c b/applications/external/totp/cli/common_command_arguments.c index 9ed9f0126..c3129a157 100644 --- a/applications/external/totp/cli/common_command_arguments.c +++ b/applications/external/totp/cli/common_command_arguments.c @@ -1,11 +1,11 @@ #include "common_command_arguments.h" #include -inline void totp_cli_printf_missed_argument_value(char* arg) { +void totp_cli_printf_missed_argument_value(char* arg) { TOTP_CLI_PRINTF_ERROR("Missed or incorrect value for argument \"%s\"\r\n", arg); } -inline void totp_cli_printf_unknown_argument(const FuriString* arg) { +void totp_cli_printf_unknown_argument(const FuriString* arg) { TOTP_CLI_PRINTF("Unknown argument \"%s\"\r\n", furi_string_get_cstr(arg)); } @@ -121,10 +121,10 @@ bool totp_cli_try_read_plain_token_secret_encoding( totp_cli_printf_missed_argument_value(TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX); } else { if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE32_NAME) == 0) { - *secret_encoding = PLAIN_TOKEN_ENCODING_BASE32; + *secret_encoding = PlainTokenSecretEncodingBase32; *parsed = true; } else if(furi_string_cmpi_str(arg, PLAIN_TOKEN_ENCODING_BASE64_NAME) == 0) { - *secret_encoding = PLAIN_TOKEN_ENCODING_BASE64; + *secret_encoding = PlainTokenSecretEncodingBase64; *parsed = true; } else { TOTP_CLI_PRINTF_ERROR( diff --git a/applications/external/totp/cli/common_command_arguments.h b/applications/external/totp/cli/common_command_arguments.h index be01c216d..be1c0bdfe 100644 --- a/applications/external/totp/cli/common_command_arguments.h +++ b/applications/external/totp/cli/common_command_arguments.h @@ -19,23 +19,29 @@ #define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING "encoding" void totp_cli_printf_unknown_argument(const FuriString* arg); + void totp_cli_printf_missed_argument_value(char* arg); + bool totp_cli_try_read_algo(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_digits( TokenInfo* token_info, const FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_duration( TokenInfo* token_info, const FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_automation_features( TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed); + bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag); bool totp_cli_try_read_plain_token_secret_encoding( diff --git a/applications/external/totp/lib/base64/base64.c b/applications/external/totp/lib/base64/base64.c index dd0a12b5e..2cb0d2ffc 100644 --- a/applications/external/totp/lib/base64/base64.c +++ b/applications/external/totp/lib/base64/base64.c @@ -1,6 +1,7 @@ /* * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005, Jouni Malinen + * Modified and optimized for Flipepr Zero device purposes by Alex Kopachov (@akopachov) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/applications/external/totp/lib/linked_list/linked_list.c b/applications/external/totp/lib/linked_list/linked_list.c deleted file mode 100644 index 23d10c548..000000000 --- a/applications/external/totp/lib/linked_list/linked_list.c +++ /dev/null @@ -1,136 +0,0 @@ -#include "linked_list.h" - -ListNode* list_init_head(void* data) { - ListNode* new = malloc(sizeof(ListNode)); - if(new == NULL) return NULL; - new->data = data; - new->next = NULL; - return new; -} - -ListNode* list_add(ListNode* head, void* data) { - ListNode* new = malloc(sizeof(ListNode)); - if(new == NULL) return NULL; - new->data = data; - new->next = NULL; - - if(head == NULL) - head = new; - else { - ListNode* it; - - for(it = head; it->next != NULL; it = it->next) - ; - - it->next = new; - } - - return head; -} - -ListNode* list_find(ListNode* head, const void* data) { - ListNode* it = NULL; - - for(it = head; it != NULL; it = it->next) - if(it->data == data) break; - - return it; -} - -ListNode* list_element_at(ListNode* head, uint16_t index) { - ListNode* it; - uint16_t i; - for(it = head, i = 0; it != NULL && i < index; it = it->next, i++) - ; - return it; -} - -ListNode* list_remove(ListNode* head, ListNode* ep) { - if(head == NULL) { - return NULL; - } - - if(head == ep) { - ListNode* new_head = head->next; - free(head); - return new_head; - } - - ListNode* it; - - for(it = head; it->next != ep; it = it->next) - ; - - it->next = ep->next; - free(ep); - - return head; -} - -ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data) { - if(head == NULL) { - return NULL; - } - - ListNode* it; - ListNode* prev = NULL; - - uint16_t i; - - for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++) - ; - - if(it == NULL) return head; - - ListNode* new_head = head; - if(prev == NULL) { - new_head = it->next; - } else { - prev->next = it->next; - } - - if(removed_node_data != NULL) { - *removed_node_data = it->data; - } - - free(it); - - return new_head; -} - -ListNode* list_insert_at(ListNode* head, uint16_t index, void* data) { - if(index == 0 || head == NULL) { - ListNode* new_head = list_init_head(data); - if(new_head != NULL) { - new_head->next = head; - } - return new_head; - } - - ListNode* it; - ListNode* prev = NULL; - - uint16_t i; - - for(it = head, i = 0; it != NULL && i < index; prev = it, it = it->next, i++) - ; - - ListNode* new = malloc(sizeof(ListNode)); - if(new == NULL) return NULL; - new->data = data; - new->next = it; - prev->next = new; - - return head; -} - -void list_free(ListNode* head) { - ListNode* it = head; - ListNode* tmp; - - while(it != NULL) { - tmp = it; - it = it->next; - free(tmp); - } -} diff --git a/applications/external/totp/lib/linked_list/linked_list.h b/applications/external/totp/lib/linked_list/linked_list.h deleted file mode 100644 index 3c938e59a..000000000 --- a/applications/external/totp/lib/linked_list/linked_list.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include -#include - -/** - * @brief Single linked list node - */ -typedef struct ListNode { - /** - * @brief Pointer to the data assigned to the current list node - */ - void* data; - - /** - * @brief Pointer to the next list node - */ - struct ListNode* next; -} ListNode; - -/** - * @brief Initializes a new list node head - * @param data data to be assigned to the head list node - * @return Head list node - */ -ListNode* list_init_head(void* data); - -/** - * @brief Adds new list node to the end of the list - * @param head head list node - * @param data data to be assigned to the newly added list node - * @return Head list node - */ -ListNode* list_add( - ListNode* head, - void* data); /* adds element with specified data to the end of the list and returns new head node. */ - -/** - * @brief Searches list node with the given assigned \p data in the list - * @param head head list node - * @param data data to be searched - * @return List node containing \p data if there is such a node in the list; \c NULL otherwise - */ -ListNode* list_find(ListNode* head, const void* data); - -/** - * @brief Searches list node with the given \p index in the list - * @param head head list node - * @param index desired list node index - * @return List node with the given \p index in the list if there is such a list node; \c NULL otherwise - */ -ListNode* list_element_at(ListNode* head, uint16_t index); - -/** - * @brief Removes list node from the list - * @param head head list node - * @param ep list node to be removed - * @return Head list node - */ -ListNode* list_remove(ListNode* head, ListNode* ep); - -/** - * @brief Removes list node with the given \p index in the list from the list - * @param head head list node - * @param index index of the node to be removed - * @param[out] removed_node_data data which was assigned to the removed list node - * @return Head list node - */ -ListNode* list_remove_at(ListNode* head, uint16_t index, void** removed_node_data); - -/** - * @brief Inserts new list node at the given index - * @param head head list node - * @param index index in the list where the new list node should be inserted - * @param data data to be assgned to the new list node - * @return Head list node - */ -ListNode* list_insert_at(ListNode* head, uint16_t index, void* data); - -/** - * @brief Disposes all the list nodes in the list - * @param head head list node - */ -void list_free(ListNode* head); - -#define TOTP_LIST_INIT_OR_ADD(head, item, assert) \ - if(head == NULL) { \ - head = list_init_head(item); \ - assert(head != NULL); \ - } else { \ - assert(list_add(head, item) != NULL); \ - } - -#define TOTP_LIST_FOREACH(head, node, action) \ - ListNode* node = head; \ - while(node != NULL) { \ - action node = node->next; \ - } diff --git a/applications/external/totp/lib/roll_value/roll_value.c b/applications/external/totp/lib/roll_value/roll_value.c index b8f30e078..326c7846a 100644 --- a/applications/external/totp/lib/roll_value/roll_value.c +++ b/applications/external/totp/lib/roll_value/roll_value.c @@ -25,4 +25,4 @@ TOTP_ROLL_VALUE_FN(int8_t, int8_t) TOTP_ROLL_VALUE_FN(uint8_t, int8_t) -TOTP_ROLL_VALUE_FN(uint16_t, int16_t); \ No newline at end of file +TOTP_ROLL_VALUE_FN(size_t, int16_t); \ No newline at end of file diff --git a/applications/external/totp/lib/roll_value/roll_value.h b/applications/external/totp/lib/roll_value/roll_value.h index 3c270be9a..0c1894fd6 100644 --- a/applications/external/totp/lib/roll_value/roll_value.h +++ b/applications/external/totp/lib/roll_value/roll_value.h @@ -1,6 +1,7 @@ #pragma once #include +#include typedef uint8_t TotpRollValueOverflowBehavior; @@ -47,7 +48,7 @@ TOTP_ROLL_VALUE_FN_HEADER(int8_t, int8_t); TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t); /** - * @brief Rolls \c uint16_t \p value using \p min and \p max as an value constraints with \p step step. + * @brief Rolls \c size_t \p value using \p min and \p max as an value constraints with \p step step. * When value reaches constraint value \p overflow_behavior defines what to do next. * @param[in,out] value value to roll * @param step step to be used to change value @@ -55,4 +56,4 @@ TOTP_ROLL_VALUE_FN_HEADER(uint8_t, int8_t); * @param max maximum possible value * @param overflow_behavior defines what to do when value reaches constraint value */ -TOTP_ROLL_VALUE_FN_HEADER(uint16_t, int16_t); \ No newline at end of file +TOTP_ROLL_VALUE_FN_HEADER(size_t, int16_t); \ No newline at end of file diff --git a/applications/external/totp/services/config/config.c b/applications/external/totp/services/config/config.c index 0453338d3..f97bc6b26 100644 --- a/applications/external/totp/services/config/config.c +++ b/applications/external/totp/services/config/config.c @@ -1,19 +1,41 @@ #include "config.h" #include #include -#include +#include +#include +#include +#include +#include #include "../../types/common.h" #include "../../types/token_info.h" #include "../../features_config.h" +#include "../crypto/crypto.h" #include "migrations/common_migration.h" -#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator") #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf" -#define CONFIG_FILE_BACKUP_BASE_PATH CONFIG_FILE_PATH ".backup" +#define CONFIG_FILE_BACKUP_DIR CONFIG_FILE_DIRECTORY_PATH "/backups" +#define CONFIG_FILE_BACKUP_BASE_PATH CONFIG_FILE_BACKUP_DIR "/totp.conf" #define CONFIG_FILE_TEMP_PATH CONFIG_FILE_PATH ".tmp" #define CONFIG_FILE_ORIG_PATH CONFIG_FILE_PATH ".orig" #define CONFIG_FILE_PATH_PREVIOUS EXT_PATH("apps/Misc") "/totp.conf" +struct ConfigFileContext { + /** + * @brief Config file reference + */ + FlipperFormat* config_file; + + /** + * @brief Storage reference + */ + Storage* storage; + + /** + * @brief Token list iterator context + */ + TokenInfoIteratorContext* token_info_iterator_context; +}; + /** * @brief Opens storage record * @return Storage record @@ -45,16 +67,31 @@ static void totp_close_config_file(FlipperFormat* file) { * @return backup path if backup successfully taken; \c NULL otherwise */ static char* totp_config_file_backup_i(Storage* storage) { - uint8_t backup_path_size = sizeof(CONFIG_FILE_BACKUP_BASE_PATH) + 5; + if(!storage_dir_exists(storage, CONFIG_FILE_BACKUP_DIR) && + !storage_simply_mkdir(storage, CONFIG_FILE_BACKUP_DIR)) { + return NULL; + } + + FuriHalRtcDateTime current_datetime; + furi_hal_rtc_get_datetime(¤t_datetime); + + uint8_t backup_path_size = sizeof(CONFIG_FILE_BACKUP_BASE_PATH) + 14; char* backup_path = malloc(backup_path_size); furi_check(backup_path != NULL); memcpy(backup_path, CONFIG_FILE_BACKUP_BASE_PATH, sizeof(CONFIG_FILE_BACKUP_BASE_PATH)); uint16_t i = 1; bool backup_file_exists; - while((backup_file_exists = storage_common_exists(storage, backup_path)) && i <= 9999) { - snprintf(backup_path, backup_path_size, CONFIG_FILE_BACKUP_BASE_PATH ".%" PRIu16, i); + do { + snprintf( + backup_path, + backup_path_size, + CONFIG_FILE_BACKUP_BASE_PATH ".%4" PRIu16 "%02" PRIu8 "%02" PRIu8 "-%" PRIu16, + current_datetime.year, + current_datetime.month, + current_datetime.day, + i); i++; - } + } while((backup_file_exists = storage_common_exists(storage, backup_path)) && i <= 9999); if(backup_file_exists || storage_common_copy(storage, CONFIG_FILE_PATH, backup_path) != FSE_OK) { @@ -73,7 +110,7 @@ static char* totp_config_file_backup_i(Storage* storage) { * @param[out] file opened config file * @return Config file open result */ -static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperFormat** file) { +static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK) { @@ -81,7 +118,7 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF if(!flipper_format_file_open_existing(fff_data_file, CONFIG_FILE_PATH)) { FURI_LOG_E(LOGGING_TAG, "Error opening existing file %s", CONFIG_FILE_PATH); totp_close_config_file(fff_data_file); - return TotpConfigFileOpenError; + return false; } } else if(storage_common_stat(storage, CONFIG_FILE_PATH_PREVIOUS, NULL) == FSE_OK) { FURI_LOG_D(LOGGING_TAG, "Old config file %s found", CONFIG_FILE_PATH_PREVIOUS); @@ -93,13 +130,13 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); totp_close_config_file(fff_data_file); - return TotpConfigFileOpenError; + return false; } } if(storage_common_rename(storage, CONFIG_FILE_PATH_PREVIOUS, CONFIG_FILE_PATH) != FSE_OK) { FURI_LOG_E(LOGGING_TAG, "Error moving config to %s", CONFIG_FILE_PATH); totp_close_config_file(fff_data_file); - return TotpConfigFileOpenError; + return false; } FURI_LOG_I(LOGGING_TAG, "Applied config file path migration"); return totp_open_config_file(storage, file); @@ -112,421 +149,155 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF CONFIG_FILE_DIRECTORY_PATH); if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); - return TotpConfigFileOpenError; + return false; } } if(!flipper_format_file_open_new(fff_data_file, CONFIG_FILE_PATH)) { totp_close_config_file(fff_data_file); FURI_LOG_E(LOGGING_TAG, "Error creating new file %s", CONFIG_FILE_PATH); - return TotpConfigFileOpenError; + return false; } flipper_format_write_header_cstr( fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION); - float tmp_tz = 0; - flipper_format_write_comment_cstr(fff_data_file, " "); + flipper_format_write_comment_cstr( fff_data_file, - "Timezone offset in hours. Important note: do not put '+' sign for positive values"); + "Config file format specification can be found here: https://github.com/akopachov/flipper-zero_authenticator/blob/master/docs/conf-file_description.md"); + + float tmp_tz = 0; flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &tmp_tz, 1); uint32_t tmp_uint32 = NotificationMethodSound | NotificationMethodVibro; - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr( - fff_data_file, - "How to notify user when new token is generated or badusb mode is activated (possible values: 0 - do not notify, 1 - sound, 2 - vibro, 3 sound and vibro)"); flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); tmp_uint32 = AutomationMethodBadUsb; - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr( - fff_data_file, - "Automation method (0 - None, 1 - BadUSB, 2 - BadBT, 3 - BadUSB and BadBT)"); flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1); - FuriString* temp_str = furi_string_alloc(); - - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE BEGIN ==="); - flipper_format_write_comment_cstr(fff_data_file, " "); - flipper_format_write_comment_cstr( - fff_data_file, "# Token name which will be visible in the UI."); - furi_string_printf(temp_str, "%s: Sample token name", TOTP_CONFIG_KEY_TOKEN_NAME); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# Plain token secret without spaces, dashes and etc, just pure alpha-numeric characters. Important note: plain token will be encrypted and replaced by TOTP app"); - furi_string_printf(temp_str, "%s: plaintokensecret", TOTP_CONFIG_KEY_TOKEN_SECRET); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - furi_string_printf( - temp_str, - " # Token hashing algorithm to use during code generation. Supported options are %s, %s, %s, and %s. If you are not use which one to use - use %s", - TOTP_TOKEN_ALGO_SHA1_NAME, - TOTP_TOKEN_ALGO_SHA256_NAME, - TOTP_TOKEN_ALGO_SHA512_NAME, - TOTP_TOKEN_ALGO_STEAM_NAME, - TOTP_TOKEN_ALGO_SHA1_NAME); - flipper_format_write_comment(fff_data_file, temp_str); - furi_string_printf( - temp_str, "%s: %s", TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# How many digits there should be in generated code. Available options are 5, 6 and 8. Majority websites requires 6 digits code, however some rare websites wants to get 8 digits code. If you are not sure which one to use - use 6"); - furi_string_printf(temp_str, "%s: 6", TOTP_CONFIG_KEY_TOKEN_DIGITS); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# Token lifetime duration in seconds. Should be between 15 and 255. Majority websites requires 30, however some rare websites may require custom lifetime. If you are not sure which one to use - use 30"); - furi_string_printf(temp_str, "%s: 30", TOTP_CONFIG_KEY_TOKEN_DURATION); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr( - fff_data_file, - "# Token input automation features (0 - None, 1 - press \"Enter\" key at the end of automation)"); - furi_string_printf(temp_str, "%s: 0", TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES); - flipper_format_write_comment(fff_data_file, temp_str); - flipper_format_write_comment_cstr(fff_data_file, " "); - - flipper_format_write_comment_cstr(fff_data_file, "=== TOKEN SAMPLE END ==="); - flipper_format_write_comment_cstr(fff_data_file, " "); - - furi_string_free(temp_str); if(!flipper_format_rewind(fff_data_file)) { totp_close_config_file(fff_data_file); FURI_LOG_E(LOGGING_TAG, "Rewind error"); - return TotpConfigFileOpenError; + return false; } } *file = fff_data_file; - return TotpConfigFileOpenSuccess; + return true; } -static TotpConfigFileUpdateResult - totp_config_file_save_new_token_i(FlipperFormat* file, const TokenInfo* token_info) { - TotpConfigFileUpdateResult update_result; +char* totp_config_file_backup(const PluginState* plugin_state) { + if(plugin_state->config_file_context == NULL) return NULL; + + totp_close_config_file(plugin_state->config_file_context->config_file); + + char* result = totp_config_file_backup_i(plugin_state->config_file_context->storage); + + totp_open_config_file( + plugin_state->config_file_context->storage, + &plugin_state->config_file_context->config_file); + + totp_token_info_iterator_attach_to_config_file( + plugin_state->config_file_context->token_info_iterator_context, + plugin_state->config_file_context->config_file); + + return result; +} + +bool totp_config_file_update_timezone_offset(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; + do { - if(!flipper_format_seek_to_end(file)) { - update_result = TotpConfigFileUpdateError; + if(!flipper_format_insert_or_update_float( + file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { break; } - if(!flipper_format_write_string_cstr(file, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name)) { - update_result = TotpConfigFileUpdateError; - break; - } - - bool token_is_valid = token_info->token != NULL && token_info->token_length > 0; - if(!token_is_valid && - !flipper_format_write_comment_cstr(file, "!!! WARNING BEGIN: INVALID TOKEN !!!")) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_write_hex( - file, TOTP_CONFIG_KEY_TOKEN_SECRET, token_info->token, token_info->token_length)) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!token_is_valid && !flipper_format_write_comment_cstr(file, "!!! WARNING END !!!")) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_write_string_cstr( - file, TOTP_CONFIG_KEY_TOKEN_ALGO, token_info_get_algo_as_cstr(token_info))) { - update_result = TotpConfigFileUpdateError; - break; - } - - uint32_t tmp_uint32 = token_info->digits; - if(!flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - tmp_uint32 = token_info->duration; - if(!flipper_format_write_uint32(file, TOTP_CONFIG_KEY_TOKEN_DURATION, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - tmp_uint32 = token_info->automation_features; - if(!flipper_format_write_uint32( - file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; + update_result = true; } while(false); return update_result; } -char* totp_config_file_backup() { - Storage* storage = totp_open_storage(); - char* result = totp_config_file_backup_i(storage); - totp_close_storage(); - return result; -} - -TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - if(totp_config_file_save_new_token_i(file, token_info) != - TotpConfigFileUpdateSuccess) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - if(!flipper_format_insert_or_update_float( - file, TOTP_CONFIG_KEY_TIMEZONE, &new_timezone_offset, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult - totp_config_file_update_notification_method(NotificationMethod new_notification_method) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - uint32_t tmp_uint32 = new_notification_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult - totp_config_file_update_automation_method(AutomationMethod new_automation_method) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - uint32_t tmp_uint32 = new_automation_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state) { - Storage* cfg_storage = totp_open_storage(); - FlipperFormat* file; - TotpConfigFileUpdateResult update_result; - if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) { - do { - if(!flipper_format_insert_or_update_float( - file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - uint32_t tmp_uint32 = plugin_state->notification_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - tmp_uint32 = plugin_state->automation_method; - if(!flipper_format_insert_or_update_uint32( - file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state) { - Storage* storage = totp_open_storage(); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - TotpConfigFileUpdateResult result = TotpConfigFileUpdateSuccess; +bool totp_config_file_update_notification_method(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; do { - if(!flipper_format_file_open_always(fff_data_file, CONFIG_FILE_TEMP_PATH)) { - result = TotpConfigFileUpdateError; + uint32_t tmp_uint32 = plugin_state->notification_method; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { break; } - if(!flipper_format_write_header_cstr( - fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION)) { - result = TotpConfigFileUpdateError; + update_result = true; + } while(false); + + return update_result; +} + +bool totp_config_file_update_automation_method(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; + + do { + uint32_t tmp_uint32 = plugin_state->automation_method; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { break; } - if(!flipper_format_write_hex( - fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) { - result = TotpConfigFileUpdateError; - break; - } + update_result = true; + } while(false); - if(!flipper_format_write_hex( - fff_data_file, - TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, - plugin_state->crypto_verify_data_length)) { - result = TotpConfigFileUpdateError; - break; - } + return update_result; +} - if(!flipper_format_write_float( - fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { - result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_write_bool( - fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { - result = TotpConfigFileUpdateError; +bool totp_config_file_update_user_settings(const PluginState* plugin_state) { + FlipperFormat* file = plugin_state->config_file_context->config_file; + flipper_format_rewind(file); + bool update_result = false; + do { + if(!flipper_format_insert_or_update_float( + file, TOTP_CONFIG_KEY_TIMEZONE, &plugin_state->timezone_offset, 1)) { break; } uint32_t tmp_uint32 = plugin_state->notification_method; - if(!flipper_format_write_uint32( - fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { - result = TotpConfigFileUpdateError; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { break; } tmp_uint32 = plugin_state->automation_method; - if(!flipper_format_write_uint32( - fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { - result = TotpConfigFileUpdateError; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { break; } - bool tokens_written = true; - TOTP_LIST_FOREACH(plugin_state->tokens_list, node, { - const TokenInfo* token_info = node->data; - tokens_written = tokens_written && - totp_config_file_save_new_token_i(fff_data_file, token_info) == - TotpConfigFileUpdateSuccess; - }); - - if(!tokens_written) { - result = TotpConfigFileUpdateError; - break; - } + update_result = true; } while(false); - totp_close_config_file(fff_data_file); - - if(result == TotpConfigFileUpdateSuccess) { - if(storage_file_exists(storage, CONFIG_FILE_ORIG_PATH)) { - storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH); - } - - if(storage_common_rename(storage, CONFIG_FILE_PATH, CONFIG_FILE_ORIG_PATH) != FSE_OK) { - result = TotpConfigFileUpdateError; - } else if(storage_common_rename(storage, CONFIG_FILE_TEMP_PATH, CONFIG_FILE_PATH) != FSE_OK) { - result = TotpConfigFileUpdateError; - } else if(!storage_simply_remove(storage, CONFIG_FILE_ORIG_PATH)) { - result = TotpConfigFileUpdateError; - } - } - - totp_close_storage(); - return result; + return update_result; } -TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state) { +bool totp_config_file_load(PluginState* const plugin_state) { Storage* storage = totp_open_storage(); FlipperFormat* fff_data_file; - - TotpConfigFileOpenResult result; - if((result = totp_open_config_file(storage, &fff_data_file)) != TotpConfigFileOpenSuccess) { + if(!totp_open_config_file(storage, &fff_data_file)) { totp_close_storage(); - return result; + return false; } + flipper_format_rewind(fff_data_file); + + bool result = false; + plugin_state->timezone_offset = 0; FuriString* temp_str = furi_string_alloc(); @@ -535,7 +306,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st uint32_t file_version; if(!flipper_format_read_header(fff_data_file, temp_str, &file_version)) { FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header"); - result = TotpConfigFileOpenError; break; } @@ -551,8 +321,7 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st char* backup_path = totp_config_file_backup_i(storage); if(backup_path != NULL) { - if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) { - result = TotpConfigFileOpenError; + if(totp_open_config_file(storage, &fff_data_file) != true) { break; } @@ -560,7 +329,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st if(!flipper_format_file_open_existing(fff_backup_data_file, backup_path)) { flipper_format_file_close(fff_backup_data_file); flipper_format_free(fff_backup_data_file); - result = TotpConfigFileOpenError; break; } @@ -575,7 +343,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st LOGGING_TAG, "An error occurred during migration to version %" PRId16, CONFIG_FILE_ACTUAL_VERSION); - result = TotpConfigFileOpenError; break; } @@ -588,7 +355,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st LOGGING_TAG, "An error occurred during taking backup of %s before migration", CONFIG_FILE_PATH); - result = TotpConfigFileOpenError; break; } } @@ -599,7 +365,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } if(!flipper_format_rewind(fff_data_file)) { - result = TotpConfigFileOpenError; break; } @@ -626,7 +391,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } if(!flipper_format_rewind(fff_data_file)) { - result = TotpConfigFileOpenError; break; } @@ -637,7 +401,6 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } if(!flipper_format_rewind(fff_data_file)) { - result = TotpConfigFileOpenError; break; } @@ -664,186 +427,176 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st } plugin_state->automation_method = tmp_uint32; + + plugin_state->config_file_context = malloc(sizeof(ConfigFileContext)); + furi_check(plugin_state->config_file_context != NULL); + plugin_state->config_file_context->storage = storage; + plugin_state->config_file_context->config_file = fff_data_file; + plugin_state->config_file_context->token_info_iterator_context = + totp_token_info_iterator_alloc( + storage, plugin_state->config_file_context->config_file, plugin_state->iv); + result = true; } while(false); furi_string_free(temp_str); - totp_close_config_file(fff_data_file); - totp_close_storage(); return result; } -TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state) { - Storage* storage = totp_open_storage(); - FlipperFormat* fff_data_file; - if(totp_open_config_file(storage, &fff_data_file) != TotpConfigFileOpenSuccess) { - totp_close_storage(); - return TokenLoadingResultError; - } - - FuriString* temp_str = furi_string_alloc(); - uint32_t temp_data32; - - if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { - FURI_LOG_E(LOGGING_TAG, "Missing or incorrect header"); - totp_close_storage(); - furi_string_free(temp_str); - return TokenLoadingResultError; - } - - TokenLoadingResult result = TokenLoadingResultSuccess; - uint16_t index = 0; - bool has_any_plain_secret = false; - - while(true) { - if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { +bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state) { + FlipperFormat* config_file = plugin_state->config_file_context->config_file; + flipper_format_rewind(config_file); + bool update_result = false; + do { + if(!flipper_format_insert_or_update_hex( + config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE)) { break; } - TokenInfo* tokenInfo = token_info_alloc(); - - size_t temp_cstr_len = furi_string_size(temp_str); - tokenInfo->name = malloc(temp_cstr_len + 1); - furi_check(tokenInfo->name != NULL); - strlcpy(tokenInfo->name, furi_string_get_cstr(temp_str), temp_cstr_len + 1); - - uint32_t secret_bytes_count; - if(!flipper_format_get_value_count( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) { - secret_bytes_count = 0; + if(!flipper_format_insert_or_update_hex( + config_file, + TOTP_CONFIG_KEY_CRYPTO_VERIFY, + plugin_state->crypto_verify_data, + plugin_state->crypto_verify_data_length)) { + break; } - if(secret_bytes_count == 1) { // Plain secret key - if(flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) { - if(token_info_set_secret( - tokenInfo, - furi_string_get_cstr(temp_str), - furi_string_size(temp_str), - PLAIN_TOKEN_ENCODING_BASE32, - &plugin_state->iv[0])) { - FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has plain secret", tokenInfo->name); - } else { - tokenInfo->token = NULL; - tokenInfo->token_length = 0; - FURI_LOG_W(LOGGING_TAG, "Token \"%s\" has invalid secret", tokenInfo->name); - result = TokenLoadingResultWarning; - } - } else { - tokenInfo->token = NULL; - tokenInfo->token_length = 0; - result = TokenLoadingResultWarning; - } - - has_any_plain_secret = true; - } else { // encrypted - tokenInfo->token_length = secret_bytes_count; - if(secret_bytes_count > 0) { - tokenInfo->token = malloc(tokenInfo->token_length); - furi_check(tokenInfo->token != NULL); - if(!flipper_format_read_hex( - fff_data_file, - TOTP_CONFIG_KEY_TOKEN_SECRET, - tokenInfo->token, - tokenInfo->token_length)) { - free(tokenInfo->token); - tokenInfo->token = NULL; - tokenInfo->token_length = 0; - result = TokenLoadingResultWarning; - } - } else { - tokenInfo->token = NULL; - result = TokenLoadingResultWarning; - } + if(!flipper_format_insert_or_update_bool( + config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { + break; } - if(!flipper_format_read_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str) || - !token_info_set_algo_from_str(tokenInfo, temp_str)) { - tokenInfo->algo = SHA1; - } + update_result = true; + } while(false); - if(!flipper_format_read_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1) || - !token_info_set_digits_from_int(tokenInfo, temp_data32)) { - tokenInfo->digits = TOTP_6_DIGITS; - } + return update_result; +} - if(!flipper_format_read_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_DURATION, &temp_data32, 1) || - !token_info_set_duration_from_int(tokenInfo, temp_data32)) { - tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; - } - - if(flipper_format_read_uint32( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &temp_data32, 1)) { - tokenInfo->automation_features = temp_data32; - } else { - tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; - } - - FURI_LOG_D(LOGGING_TAG, "Found token \"%s\"", tokenInfo->name); - - TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check); - - index++; - } - - plugin_state->tokens_count = index; - plugin_state->token_list_loaded = true; - - FURI_LOG_D(LOGGING_TAG, "Found %" PRIu16 " tokens", index); - - furi_string_free(temp_str); - totp_close_config_file(fff_data_file); +void totp_config_file_close(PluginState* const plugin_state) { + if(plugin_state->config_file_context == NULL) return; + totp_token_info_iterator_free(plugin_state->config_file_context->token_info_iterator_context); + totp_close_config_file(plugin_state->config_file_context->config_file); + free(plugin_state->config_file_context); + plugin_state->config_file_context = NULL; totp_close_storage(); +} - if(has_any_plain_secret) { - totp_full_save_config_file(plugin_state); +void totp_config_file_reset(PluginState* const plugin_state) { + totp_config_file_close(plugin_state); + Storage* storage = totp_open_storage(); + storage_simply_remove(storage, CONFIG_FILE_PATH); + totp_close_storage(); +} + +bool totp_config_file_update_encryption( + PluginState* plugin_state, + const uint8_t* new_pin, + uint8_t new_pin_length) { + FlipperFormat* config_file = plugin_state->config_file_context->config_file; + Stream* stream = flipper_format_get_raw_stream(config_file); + size_t original_offset = stream_tell(stream); + if(!stream_rewind(stream)) { + return false; } + uint8_t old_iv[TOTP_IV_SIZE]; + memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); + + memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + memset(&plugin_state->base_iv[0], 0, TOTP_IV_SIZE); + if(plugin_state->crypto_verify_data != NULL) { + free(plugin_state->crypto_verify_data); + plugin_state->crypto_verify_data = NULL; + } + + CryptoSeedIVResult seed_result = + totp_crypto_seed_iv(plugin_state, new_pin_length > 0 ? new_pin : NULL, new_pin_length); + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + if(!totp_config_file_update_crypto_signatures(plugin_state)) { + return false; + } + } else if(seed_result == CryptoSeedIVResultFailed) { + return false; + } + + char buffer[sizeof(TOTP_CONFIG_KEY_TOKEN_SECRET) + 1]; + bool result = true; + + while(true) { + if(!stream_seek_to_char(stream, '\n', StreamDirectionForward)) { + break; + } + + size_t buffer_read_size; + if((buffer_read_size = stream_read(stream, (uint8_t*)&buffer[0], sizeof(buffer))) == 0) { + break; + } + + if(!stream_seek(stream, -(int32_t)buffer_read_size, StreamOffsetFromCurrent)) { + result = false; + break; + } + + if(strncmp(buffer, "\n" TOTP_CONFIG_KEY_TOKEN_SECRET ":", sizeof(buffer)) == 0) { + uint32_t secret_bytes_count; + if(!flipper_format_get_value_count( + config_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) { + secret_bytes_count = 0; + } + + if(secret_bytes_count > 1) { + size_t secret_token_start = stream_tell(stream) + 1; + uint8_t* encrypted_token = malloc(secret_bytes_count); + furi_check(encrypted_token != NULL); + + if(!flipper_format_read_hex( + config_file, + TOTP_CONFIG_KEY_TOKEN_SECRET, + encrypted_token, + secret_bytes_count)) { + result = false; + free(encrypted_token); + break; + } + + size_t plain_token_length; + uint8_t* plain_token = totp_crypto_decrypt( + encrypted_token, secret_bytes_count, &old_iv[0], &plain_token_length); + + free(encrypted_token); + size_t encrypted_token_length; + encrypted_token = totp_crypto_encrypt( + plain_token, plain_token_length, &plugin_state->iv[0], &encrypted_token_length); + + memset_s(plain_token, plain_token_length, 0, plain_token_length); + free(plain_token); + + if(!stream_seek(stream, secret_token_start, StreamOffsetFromStart)) { + result = false; + free(encrypted_token); + break; + } + + if(!flipper_format_write_hex( + config_file, + TOTP_CONFIG_KEY_TOKEN_SECRET, + encrypted_token, + encrypted_token_length)) { + free(encrypted_token); + result = false; + break; + } + + free(encrypted_token); + } + } + } + + stream_seek(stream, original_offset, StreamOffsetFromStart); + return result; } -TotpConfigFileUpdateResult - totp_config_file_update_crypto_signatures(const PluginState* plugin_state) { - Storage* storage = totp_open_storage(); - FlipperFormat* config_file; - TotpConfigFileUpdateResult update_result; - if(totp_open_config_file(storage, &config_file) == TotpConfigFileOpenSuccess) { - do { - if(!flipper_format_insert_or_update_hex( - config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE)) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_insert_or_update_hex( - config_file, - TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, - plugin_state->crypto_verify_data_length)) { - update_result = TotpConfigFileUpdateError; - break; - } - - if(!flipper_format_insert_or_update_bool( - config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { - update_result = TotpConfigFileUpdateError; - break; - } - - update_result = TotpConfigFileUpdateSuccess; - } while(false); - - totp_close_config_file(config_file); - } else { - update_result = TotpConfigFileUpdateError; - } - - totp_close_storage(); - return update_result; -} - -void totp_config_file_reset() { - Storage* storage = totp_open_storage(); - storage_simply_remove(storage, CONFIG_FILE_PATH); - totp_close_storage(); +TokenInfoIteratorContext* totp_config_get_token_iterator_context(const PluginState* plugin_state) { + return plugin_state->config_file_context->token_info_iterator_context; } \ No newline at end of file diff --git a/applications/external/totp/services/config/config.h b/applications/external/totp/services/config/config.h index 5bd169525..d2fe957c6 100644 --- a/applications/external/totp/services/config/config.h +++ b/applications/external/totp/services/config/config.h @@ -1,137 +1,90 @@ #pragma once -#include #include "../../types/plugin_state.h" #include "../../types/token_info.h" +#include "config_file_context.h" #include "constants.h" +#include "token_info_iterator.h" -typedef uint8_t TokenLoadingResult; typedef uint8_t TotpConfigFileOpenResult; typedef uint8_t TotpConfigFileUpdateResult; -/** - * @brief Token loading results - */ -enum TokenLoadingResults { - /** - * @brief All the tokens loaded successfully - */ - TokenLoadingResultSuccess, - - /** - * @brief All the tokens loaded, but there are some warnings - */ - TokenLoadingResultWarning, - - /** - * @brief Tokens not loaded because of error(s) - */ - TokenLoadingResultError -}; - -/** - * @brief Config file opening result - */ -enum TotpConfigFileOpenResults { - /** - * @brief Config file opened successfully - */ - TotpConfigFileOpenSuccess = 0, - - /** - * @brief An error has occurred during opening config file - */ - TotpConfigFileOpenError = 1 -}; - -/** - * @brief Config file updating result - */ -enum TotpConfigFileUpdateResults { - /** - * @brief Config file updated successfully - */ - TotpConfigFileUpdateSuccess, - - /** - * @brief An error has occurred during updating config file - */ - TotpConfigFileUpdateError -}; - /** * @brief Tries to take a config file backup + * @param plugin_state application state * @return backup path if backup successfully taken; \c NULL otherwise */ -char* totp_config_file_backup(); - -/** - * @brief Saves all the settings and tokens to an application config file - * @param plugin_state application state - * @return Config file update result - */ -TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const plugin_state); +char* totp_config_file_backup(const PluginState* plugin_state); /** * @brief Loads basic information from an application config file into application state without loading all the tokens * @param plugin_state application state * @return Config file open result */ -TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_state); - -/** - * @brief Loads tokens from an application config file into application state - * @param plugin_state application state - * @return Results of the loading - */ -TokenLoadingResult totp_config_file_load_tokens(PluginState* const plugin_state); - -/** - * @brief Add new token to the end of the application config file - * @param token_info token information to be saved - * @return Config file update result - */ -TotpConfigFileUpdateResult totp_config_file_save_new_token(const TokenInfo* token_info); +bool totp_config_file_load(PluginState* const plugin_state); /** * @brief Updates timezone offset in an application config file - * @param new_timezone_offset new timezone offset to be set + * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_timezone_offset); +bool totp_config_file_update_timezone_offset(const PluginState* plugin_state); /** * @brief Updates notification method in an application config file - * @param new_notification_method new notification method to be set + * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult - totp_config_file_update_notification_method(NotificationMethod new_notification_method); +bool totp_config_file_update_notification_method(const PluginState* plugin_state); /** * @brief Updates automation method in an application config file - * @param new_automation_method new automation method to be set + * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult - totp_config_file_update_automation_method(AutomationMethod new_automation_method); +bool totp_config_file_update_automation_method(const PluginState* plugin_state); /** * @brief Updates application user settings * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state); +bool totp_config_file_update_user_settings(const PluginState* plugin_state); /** * @brief Updates crypto signatures information * @param plugin_state application state * @return Config file update result */ -TotpConfigFileUpdateResult - totp_config_file_update_crypto_signatures(const PluginState* plugin_state); +bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state); /** * @brief Reset all the settings to default + * @param plugin_state application state */ -void totp_config_file_reset(); \ No newline at end of file +void totp_config_file_reset(PluginState* const plugin_state); + +/** + * @brief Closes config file and releases all the resources + * @param plugin_state application state + */ +void totp_config_file_close(PluginState* const plugin_state); + +/** + * @brief Updates config file encryption by re-encrypting it using new user's PIN and new randomly generated IV + * @param plugin_state application state + * @param new_pin new user's PIN + * @param new_pin_length new user's PIN length + * @return \c true if config file encryption successfully updated; \c false otherwise + */ +bool totp_config_file_update_encryption( + PluginState* plugin_state, + const uint8_t* new_pin, + uint8_t new_pin_length); + +/** + * @brief Gets token info iterator context + * @param plugin_state application state + * @return token info iterator context + */ +TokenInfoIteratorContext* totp_config_get_token_iterator_context(const PluginState* plugin_state); \ No newline at end of file diff --git a/applications/external/totp/services/config/config_file_context.h b/applications/external/totp/services/config/config_file_context.h new file mode 100644 index 000000000..98badbcbb --- /dev/null +++ b/applications/external/totp/services/config/config_file_context.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct ConfigFileContext ConfigFileContext; \ No newline at end of file diff --git a/applications/external/totp/services/config/constants.h b/applications/external/totp/services/config/constants.h index 7137e2374..3a33c80b3 100644 --- a/applications/external/totp/services/config/constants.h +++ b/applications/external/totp/services/config/constants.h @@ -1,7 +1,10 @@ #pragma once +#include + +#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator") #define CONFIG_FILE_HEADER "Flipper TOTP plugin config file" -#define CONFIG_FILE_ACTUAL_VERSION (4) +#define CONFIG_FILE_ACTUAL_VERSION (5) #define TOTP_CONFIG_KEY_TIMEZONE "Timezone" #define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName" diff --git a/applications/external/totp/services/config/migrations/common_migration.c b/applications/external/totp/services/config/migrations/common_migration.c index 073eaab12..07026fb1f 100644 --- a/applications/external/totp/services/config/migrations/common_migration.c +++ b/applications/external/totp/services/config/migrations/common_migration.c @@ -1,6 +1,7 @@ #include "common_migration.h" #include "../constants.h" #include "../../../types/token_info.h" +#include bool totp_config_migrate_to_latest( FlipperFormat* fff_data_file, @@ -57,18 +58,12 @@ bool totp_config_migrate_to_latest( flipper_format_rewind(fff_backup_data_file); - FuriString* comment_str = furi_string_alloc(); - while(true) { if(!flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { break; } - furi_string_printf( - comment_str, "=== BEGIN \"%s\" ===", furi_string_get_cstr(temp_str)); - flipper_format_write_comment(fff_data_file, comment_str); - furi_string_printf(comment_str, "=== END \"%s\" ===", furi_string_get_cstr(temp_str)); flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str); flipper_format_read_string( @@ -78,15 +73,32 @@ bool totp_config_migrate_to_latest( if(current_version > 1) { flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); + + if(current_version < 5) { + uint32_t algo_as_uint32t = SHA1; + if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_SHA256_NAME) == 0) { + algo_as_uint32t = SHA256; + } else if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_SHA512_NAME) == 0) { + algo_as_uint32t = SHA512; + } else if(furi_string_cmpi_str(temp_str, TOTP_TOKEN_ALGO_STEAM_NAME) == 0) { + algo_as_uint32t = STEAM; + } + + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &algo_as_uint32t, 1); + } else { + flipper_format_write_string( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, temp_str); + } flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str); flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, temp_str); } else { - flipper_format_write_string_cstr( - fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, TOTP_TOKEN_ALGO_SHA1_NAME); - const uint32_t default_digits = TOTP_6_DIGITS; + const uint32_t default_algo = SHA1; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &default_algo, 1); + const uint32_t default_digits = TotpSixDigitsCount; flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &default_digits, 1); } @@ -108,18 +120,21 @@ bool totp_config_migrate_to_latest( flipper_format_write_string( fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, temp_str); } else { - const uint32_t default_automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + const uint32_t default_automation_features = TokenAutomationFeatureNone; flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &default_automation_features, 1); } - - flipper_format_write_comment(fff_data_file, comment_str); } - furi_string_free(comment_str); + Stream* stream = flipper_format_get_raw_stream(fff_data_file); + size_t current_pos = stream_tell(stream); + size_t total_size = stream_size(stream); + if(current_pos < total_size) { + stream_delete(stream, total_size - current_pos); + } result = true; } while(false); diff --git a/applications/external/totp/services/config/migrations/common_migration.h b/applications/external/totp/services/config/migrations/common_migration.h index 71defc384..326277f14 100644 --- a/applications/external/totp/services/config/migrations/common_migration.h +++ b/applications/external/totp/services/config/migrations/common_migration.h @@ -2,6 +2,12 @@ #include +/** + * @brief Migrates config file to the latest version + * @param fff_data_file original config file to be migrated + * @param fff_backup_data_file backup copy of original config file + * @return \c true if operation succeeded; \c false otherwise + */ bool totp_config_migrate_to_latest( FlipperFormat* fff_data_file, - FlipperFormat* fff_backup_data_file); \ No newline at end of file + FlipperFormat* fff_backup_data_file); diff --git a/applications/external/totp/services/config/token_info_iterator.c b/applications/external/totp/services/config/token_info_iterator.c new file mode 100644 index 000000000..9b7dd5550 --- /dev/null +++ b/applications/external/totp/services/config/token_info_iterator.c @@ -0,0 +1,551 @@ +#include "token_info_iterator.h" + +#include +#include +#include +#include "../../types/common.h" + +#define CONFIG_FILE_PART_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf.part" +#define STREAM_COPY_BUFFER_SIZE 128 + +struct TokenInfoIteratorContext { + size_t total_count; + size_t current_index; + size_t last_seek_offset; + size_t last_seek_index; + TokenInfo* current_token; + FlipperFormat* config_file; + uint8_t* iv; + Storage* storage; +}; + +static bool + flipper_format_seek_to_siblinig_token_start(Stream* stream, StreamDirection direction) { + char buffer[sizeof(TOTP_CONFIG_KEY_TOKEN_NAME) + 1]; + bool found = false; + while(!found) { + if(!stream_seek_to_char(stream, '\n', direction)) { + break; + } + + size_t buffer_read_size; + if((buffer_read_size = stream_read(stream, (uint8_t*)&buffer[0], sizeof(buffer))) == 0) { + break; + } + + if(!stream_seek(stream, -(int32_t)buffer_read_size, StreamOffsetFromCurrent)) { + break; + } + + if(strncmp(buffer, "\n" TOTP_CONFIG_KEY_TOKEN_NAME ":", sizeof(buffer)) == 0) { + found = true; + } + } + + return found; +} + +static bool seek_to_token(size_t token_index, TokenInfoIteratorContext* context) { + furi_check(context != NULL && context->config_file != NULL); + if(token_index >= context->total_count) { + return false; + } + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + long token_index_diff = (long)token_index - (long)context->last_seek_index; + size_t token_index_diff_weight = (size_t)labs(token_index_diff); + StreamDirection direction = token_index_diff >= 0 ? StreamDirectionForward : + StreamDirectionBackward; + if(token_index_diff_weight > token_index || context->last_seek_offset == 0) { + context->last_seek_offset = 0; + context->last_seek_index = 0; + token_index_diff = token_index + 1; + direction = StreamDirectionForward; + } else if(token_index_diff_weight > (context->total_count - token_index - 1)) { + context->last_seek_offset = stream_size(stream); + context->last_seek_index = context->total_count - 1; + token_index_diff = -(long)(context->total_count - token_index); + direction = StreamDirectionBackward; + } + + stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart); + + if(token_index_diff != 0) { + long i = 0; + long i_inc = token_index_diff >= 0 ? 1 : -1; + do { + if(!flipper_format_seek_to_siblinig_token_start(stream, direction)) { + break; + } + + i += i_inc; + } while((i_inc > 0 && i < token_index_diff) || (i_inc < 0 && i > token_index_diff)); + + if((i_inc > 0 && i < token_index_diff) || (i_inc < 0 && i > token_index_diff)) { + context->last_seek_offset = 0; + FURI_LOG_D(LOGGING_TAG, "Was not able to move"); + return false; + } + + context->last_seek_offset = stream_tell(stream); + context->last_seek_index = token_index; + } else { + if(!stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart)) { + return false; + } + } + + return true; +} + +static bool stream_insert_stream(Stream* dst, Stream* src) { + uint8_t buffer[STREAM_COPY_BUFFER_SIZE]; + size_t buffer_read_size; + while((buffer_read_size = stream_read(src, buffer, sizeof(buffer))) != 0) { + if(!stream_insert(dst, buffer, buffer_read_size)) { + return false; + } + } + + return true; +} + +static bool ensure_stream_ends_with_lf(Stream* stream) { + uint8_t last_char; + size_t original_pos = stream_tell(stream); + if(!stream_seek(stream, -1, StreamOffsetFromEnd) || stream_read(stream, &last_char, 1) < 1) { + return false; + } + + const uint8_t lf = '\n'; + if(last_char != lf && !stream_write(stream, &lf, 1)) { + return false; + } + + if(!stream_seek(stream, original_pos, StreamOffsetFromStart)) { + return false; + } + + return true; +} + +static bool + totp_token_info_iterator_save_current_token_info_changes(TokenInfoIteratorContext* context) { + bool is_new_token = context->current_index >= context->total_count; + Stream* stream = flipper_format_get_raw_stream(context->config_file); + if(is_new_token) { + if(!ensure_stream_ends_with_lf(stream) || + !flipper_format_seek_to_end(context->config_file)) { + return false; + } + } else { + if(!seek_to_token(context->current_index, context)) { + return false; + } + } + + size_t offset_start = stream_tell(stream); + + size_t offset_end; + if(is_new_token) { + offset_end = offset_start; + } else if(context->current_index + 1 >= context->total_count) { + offset_end = stream_size(stream); + } else if(seek_to_token(context->current_index + 1, context)) { + offset_end = stream_tell(stream); + } else { + return false; + } + + FlipperFormat* temp_ff = flipper_format_file_alloc(context->storage); + if(!flipper_format_file_open_always(temp_ff, CONFIG_FILE_PART_FILE_PATH)) { + flipper_format_free(temp_ff); + return false; + } + + TokenInfo* token_info = context->current_token; + bool result = false; + + do { + if(!flipper_format_write_string(temp_ff, TOTP_CONFIG_KEY_TOKEN_NAME, token_info->name)) { + break; + } + + if(!flipper_format_write_hex( + temp_ff, + TOTP_CONFIG_KEY_TOKEN_SECRET, + token_info->token, + token_info->token_length)) { + break; + } + + uint32_t tmp_uint32 = token_info->algo; + if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_ALGO, &tmp_uint32, 1)) { + break; + } + + tmp_uint32 = token_info->digits; + if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_DIGITS, &tmp_uint32, 1)) { + break; + } + + tmp_uint32 = token_info->duration; + if(!flipper_format_write_uint32(temp_ff, TOTP_CONFIG_KEY_TOKEN_DURATION, &tmp_uint32, 1)) { + break; + } + + tmp_uint32 = token_info->automation_features; + if(!flipper_format_write_uint32( + temp_ff, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &tmp_uint32, 1)) { + break; + } + + Stream* temp_stream = flipper_format_get_raw_stream(temp_ff); + + if(!stream_rewind(temp_stream)) { + break; + } + + if(!stream_seek(stream, offset_start, StreamOffsetFromStart)) { + break; + } + + if(offset_end != offset_start && !stream_delete(stream, offset_end - offset_start)) { + break; + } + + if(!is_new_token && !stream_write_char(stream, '\n')) { + break; + } + + if(!stream_insert_stream(stream, temp_stream)) { + break; + } + + if(is_new_token) { + context->total_count++; + } + + result = true; + } while(false); + + flipper_format_free(temp_ff); + storage_common_remove(context->storage, CONFIG_FILE_PART_FILE_PATH); + + stream_seek(stream, offset_start, StreamOffsetFromStart); + context->last_seek_offset = offset_start; + context->last_seek_index = context->current_index; + + return result; +} + +TokenInfoIteratorContext* + totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv) { + Stream* stream = flipper_format_get_raw_stream(config_file); + stream_rewind(stream); + size_t tokens_count = 0; + while(true) { + if(!flipper_format_seek_to_siblinig_token_start(stream, StreamDirectionForward)) { + break; + } + + tokens_count++; + } + + TokenInfoIteratorContext* context = malloc(sizeof(TokenInfoIteratorContext)); + furi_check(context != NULL); + + context->total_count = tokens_count; + context->current_token = token_info_alloc(); + context->config_file = config_file; + context->iv = iv; + context->storage = storage; + return context; +} + +void totp_token_info_iterator_free(TokenInfoIteratorContext* context) { + if(context == NULL) return; + token_info_free(context->current_token); + free(context); +} + +bool totp_token_info_iterator_remove_current_token_info(TokenInfoIteratorContext* context) { + if(!seek_to_token(context->current_index, context)) { + return false; + } + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + size_t begin_offset = stream_tell(stream); + size_t end_offset; + if(!ensure_stream_ends_with_lf(stream)) { + return false; + } + + if(context->current_index >= context->total_count - 1) { + end_offset = stream_size(stream) - 1; + } else if(seek_to_token(context->current_index + 1, context)) { + end_offset = stream_tell(stream); + } else { + return false; + } + + if(!stream_seek(stream, begin_offset, StreamOffsetFromStart) || + !stream_delete(stream, end_offset - begin_offset)) { + return false; + } + + context->total_count--; + if(context->current_index >= context->total_count) { + context->current_index = context->total_count - 1; + } + + return true; +} + +bool totp_token_info_iterator_move_current_token_info( + TokenInfoIteratorContext* context, + size_t new_index) { + if(context->current_index == new_index) return true; + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + + if(!ensure_stream_ends_with_lf(stream)) { + return false; + } + + if(!seek_to_token(context->current_index, context)) { + return false; + } + + size_t begin_offset = stream_tell(stream); + size_t end_offset; + if(context->current_index >= context->total_count - 1) { + end_offset = stream_size(stream) - 1; + } else if(seek_to_token(context->current_index + 1, context)) { + end_offset = stream_tell(stream); + } else { + return false; + } + + Stream* temp_stream = file_stream_alloc(context->storage); + if(!file_stream_open( + temp_stream, CONFIG_FILE_PART_FILE_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) { + stream_free(temp_stream); + return false; + } + + size_t moving_size = end_offset - begin_offset; + + bool result = false; + do { + if(!stream_seek(stream, begin_offset, StreamOffsetFromStart)) { + break; + } + + if(stream_copy(stream, temp_stream, moving_size) < moving_size) { + break; + } + + if(!stream_rewind(temp_stream)) { + break; + } + + if(!stream_seek(stream, begin_offset, StreamOffsetFromStart)) { + break; + } + + if(!stream_delete(stream, moving_size)) { + break; + } + + context->last_seek_offset = 0; + context->last_seek_index = 0; + if(new_index >= context->total_count - 1) { + if(!stream_seek(stream, stream_size(stream) - 1, StreamOffsetFromStart)) { + break; + } + } else if(!seek_to_token(new_index, context)) { + break; + } + + result = stream_insert_stream(stream, temp_stream); + } while(false); + + stream_free(temp_stream); + storage_common_remove(context->storage, CONFIG_FILE_PART_FILE_PATH); + + context->last_seek_offset = 0; + context->last_seek_index = 0; + + return result; +} + +TotpIteratorUpdateTokenResult totp_token_info_iterator_update_current_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context) { + TotpIteratorUpdateTokenResult result = update(context->current_token, update_context); + if(result == TotpIteratorUpdateTokenResultSuccess) { + if(!totp_token_info_iterator_save_current_token_info_changes(context)) { + result = TotpIteratorUpdateTokenResultFileUpdateFailed; + } + + return result; + } + + totp_token_info_iterator_go_to(context, context->current_index); + return result; +} + +TotpIteratorUpdateTokenResult totp_token_info_iterator_add_new_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context) { + size_t previous_index = context->current_index; + context->current_index = context->total_count; + token_info_set_defaults(context->current_token); + TotpIteratorUpdateTokenResult result = update(context->current_token, update_context); + if(result == TotpIteratorUpdateTokenResultSuccess && + !totp_token_info_iterator_save_current_token_info_changes(context)) { + result = TotpIteratorUpdateTokenResultFileUpdateFailed; + } + + if(result != TotpIteratorUpdateTokenResultSuccess) { + totp_token_info_iterator_go_to(context, previous_index); + } + + return result; +} + +bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t token_index) { + furi_check(context != NULL); + context->current_index = token_index; + if(!seek_to_token(context->current_index, context)) { + return false; + } + + Stream* stream = flipper_format_get_raw_stream(context->config_file); + size_t original_offset = stream_tell(stream); + + if(!flipper_format_read_string( + context->config_file, TOTP_CONFIG_KEY_TOKEN_NAME, context->current_token->name)) { + stream_seek(stream, original_offset, StreamOffsetFromStart); + return false; + } + + uint32_t secret_bytes_count; + if(!flipper_format_get_value_count( + context->config_file, TOTP_CONFIG_KEY_TOKEN_SECRET, &secret_bytes_count)) { + secret_bytes_count = 0; + } + TokenInfo* tokenInfo = context->current_token; + bool token_update_needed = false; + if(tokenInfo->token != NULL) { + free(tokenInfo->token); + tokenInfo->token_length = 0; + } + + if(secret_bytes_count == 1) { // Plain secret key + FuriString* temp_str = furi_string_alloc(); + + if(flipper_format_read_string( + context->config_file, TOTP_CONFIG_KEY_TOKEN_SECRET, temp_str)) { + if(token_info_set_secret( + tokenInfo, + furi_string_get_cstr(temp_str), + furi_string_size(temp_str), + PlainTokenSecretEncodingBase32, + context->iv)) { + FURI_LOG_W( + LOGGING_TAG, + "Token \"%s\" has plain secret", + furi_string_get_cstr(tokenInfo->name)); + token_update_needed = true; + } else { + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + FURI_LOG_W( + LOGGING_TAG, + "Token \"%s\" has invalid secret", + furi_string_get_cstr(tokenInfo->name)); + } + } else { + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + } + + furi_string_free(temp_str); + } else { // encrypted + tokenInfo->token_length = secret_bytes_count; + if(secret_bytes_count > 0) { + tokenInfo->token = malloc(tokenInfo->token_length); + furi_check(tokenInfo->token != NULL); + if(!flipper_format_read_hex( + context->config_file, + TOTP_CONFIG_KEY_TOKEN_SECRET, + tokenInfo->token, + tokenInfo->token_length)) { + free(tokenInfo->token); + tokenInfo->token = NULL; + tokenInfo->token_length = 0; + } + } else { + tokenInfo->token = NULL; + } + } + + uint32_t temp_data32; + if(flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &temp_data32, 1) && + temp_data32 <= STEAM) { + tokenInfo->algo = (TokenHashAlgo)temp_data32; + } else { + tokenInfo->algo = SHA1; + } + + if(!flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_DIGITS, &temp_data32, 1) || + !token_info_set_digits_from_int(tokenInfo, temp_data32)) { + tokenInfo->digits = TotpSixDigitsCount; + } + + if(!flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_DURATION, &temp_data32, 1) || + !token_info_set_duration_from_int(tokenInfo, temp_data32)) { + tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; + } + + if(flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES, &temp_data32, 1)) { + tokenInfo->automation_features = temp_data32; + } else { + tokenInfo->automation_features = TokenAutomationFeatureNone; + } + + stream_seek(stream, original_offset, StreamOffsetFromStart); + + if(token_update_needed && !totp_token_info_iterator_save_current_token_info_changes(context)) { + return false; + } + + return true; +} + +const TokenInfo* + totp_token_info_iterator_get_current_token(const TokenInfoIteratorContext* context) { + return context->current_token; +} + +size_t totp_token_info_iterator_get_current_token_index(const TokenInfoIteratorContext* context) { + return context->current_index; +} + +size_t totp_token_info_iterator_get_total_count(const TokenInfoIteratorContext* context) { + return context->total_count; +} + +void totp_token_info_iterator_attach_to_config_file( + TokenInfoIteratorContext* context, + FlipperFormat* config_file) { + context->config_file = config_file; +} \ No newline at end of file diff --git a/applications/external/totp/services/config/token_info_iterator.h b/applications/external/totp/services/config/token_info_iterator.h new file mode 100644 index 000000000..7e9a65853 --- /dev/null +++ b/applications/external/totp/services/config/token_info_iterator.h @@ -0,0 +1,121 @@ +#pragma once + +#include "../../types/token_info.h" +#include +#include "constants.h" + +typedef int TotpIteratorUpdateTokenResult; + +typedef TotpIteratorUpdateTokenResult ( + *TOTP_ITERATOR_UPDATE_TOKEN_ACTION)(TokenInfo* const token_info, const void* context); + +typedef struct TokenInfoIteratorContext TokenInfoIteratorContext; + +enum TotpIteratorUpdateTokenResults { + + /** + * @brief Token successfully updated + */ + TotpIteratorUpdateTokenResultSuccess = 0, + + /** + * @brief An error ocurred during updating config file + */ + TotpIteratorUpdateTokenResultFileUpdateFailed = -1 +}; + +/** + * @brief Initializes a new token info iterator + * @param storage storage reference + * @param config_file config file to use + * @param iv initialization vector (IV) to be used for encryption\decryption + * @return Token info iterator context + */ +TokenInfoIteratorContext* + totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv); + +/** + * @brief Navigates iterator to the token with given index + * @param context token info iterator context + * @param token_index token index to navigate to + * @return \c true if navigation succeeded; \c false otherwise + */ +bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t token_index); + +/** + * @brief Moves current token to a given new index + * @param context token info iterator context + * @param new_index new token index to move current token to + * @return \c true if operation succeeded; \c false otherwise + */ +bool totp_token_info_iterator_move_current_token_info( + TokenInfoIteratorContext* context, + size_t new_index); + +/** + * @brief Updates current token info using given update action + * @param context token info iterator context + * @param update action which is responsible to make all the necessary updates to token info + * @param update_context update action context + * @return \c true if operation succeeded; \c false otherwise + */ +TotpIteratorUpdateTokenResult totp_token_info_iterator_update_current_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context); + +/** + * @brief Adds new token info to the end of the list using given update action + * @param context token info iterator context + * @param update action which is responsible to make all the necessary updates to token info + * @param update_context update action context + * @return \c true if operation succeeded; \c false otherwise + */ +TotpIteratorUpdateTokenResult totp_token_info_iterator_add_new_token( + TokenInfoIteratorContext* context, + TOTP_ITERATOR_UPDATE_TOKEN_ACTION update, + const void* update_context); + +/** + * @brief Remvoves current token info + * @param context token info iterator context + * @return \c true if operation succeeded; \c false otherwise + */ +bool totp_token_info_iterator_remove_current_token_info(TokenInfoIteratorContext* context); + +/** + * @brief Disposes token info iterator and releases all the resources + * @param context token info iterator context + */ +void totp_token_info_iterator_free(TokenInfoIteratorContext* context); + +/** + * @brief Gets current token info + * @param context token info iterator context + * @return current token info + */ +const TokenInfo* + totp_token_info_iterator_get_current_token(const TokenInfoIteratorContext* context); + +/** + * @brief Gets current token info index + * @param context token info iterator context + * @return current token info index + */ +size_t totp_token_info_iterator_get_current_token_index(const TokenInfoIteratorContext* context); + +/** + * @brief Gets total amount of token infos found + * @param context token info iterator context + * @return amount of token infos found + */ +size_t totp_token_info_iterator_get_total_count(const TokenInfoIteratorContext* context); + +/** + * @brief Attaches token info iterator to another config file + * @param context token info iterator context + * @param config_file config file reference to attach token info iterator to + */ +void totp_token_info_iterator_attach_to_config_file( + TokenInfoIteratorContext* context, + FlipperFormat* config_file); diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index 00f5ab0a8..03d9c9d51 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -2,7 +2,6 @@ #include #include #include -#include "../config/config.h" #include "../../types/common.h" #include "memset_s.h" @@ -62,9 +61,11 @@ uint8_t* totp_crypto_decrypt( return decrypted_data; } -bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) { +CryptoSeedIVResult + totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) { + CryptoSeedIVResult result; if(plugin_state->crypto_verify_data == NULL) { - FURI_LOG_D(LOGGING_TAG, "Generating new IV"); + FURI_LOG_I(LOGGING_TAG, "Generating new IV"); furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE); } @@ -95,9 +96,9 @@ bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t } } - bool result = true; + result = CryptoSeedIVResultFlagSuccess; if(plugin_state->crypto_verify_data == NULL) { - FURI_LOG_D(LOGGING_TAG, "Generating crypto verify data"); + FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data"); plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH); furi_check(plugin_state->crypto_verify_data != NULL); plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; @@ -110,8 +111,7 @@ bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t plugin_state->pin_set = pin != NULL && pin_length > 0; - result = totp_config_file_update_crypto_signatures(plugin_state) == - TotpConfigFileUpdateSuccess; + result |= CryptoSeedIVResultFlagNewCryptoVerifyData; } return result; diff --git a/applications/external/totp/services/crypto/crypto.h b/applications/external/totp/services/crypto/crypto.h index 3442b9a6e..ab27191a8 100644 --- a/applications/external/totp/services/crypto/crypto.h +++ b/applications/external/totp/services/crypto/crypto.h @@ -2,6 +2,26 @@ #include "../../types/plugin_state.h" +typedef uint8_t CryptoSeedIVResult; + +enum CryptoSeedIVResults { + + /** + * @brief IV seeding operation failed + */ + CryptoSeedIVResultFailed = 0b00, + + /** + * @brief IV seeding operation succeeded + */ + CryptoSeedIVResultFlagSuccess = 0b01, + + /** + * @brief As a part of IV seeding operation new crypto verify data has been generated + */ + CryptoSeedIVResultFlagNewCryptoVerifyData = 0b10 +}; + /** * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) * @param plain_data plain data to be encrypted @@ -35,9 +55,10 @@ uint8_t* totp_crypto_decrypt( * @param plugin_state application state * @param pin user's PIN * @param pin_length user's PIN length - * @return \c true on success; \c false otherwise + * @return Results of seeding IV */ -bool totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length); +CryptoSeedIVResult + totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length); /** * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index 0b70167a2..f620a3b48 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -51,23 +51,39 @@ static bool totp_activate_initial_scene(PluginState* const plugin_state) { dialog_message_show(plugin_state->dialogs_app, message); dialog_message_free(message); if(dialog_result == DialogMessageButtonRight) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } else { - if(!totp_crypto_seed_iv(plugin_state, NULL, 0)) { + CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0); + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + if(!totp_config_file_update_crypto_signatures(plugin_state)) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } + } else if(seed_result == CryptoSeedIVResultFailed) { totp_dialogs_config_loading_error(plugin_state); return false; } - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } } else if(plugin_state->pin_set) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } else { - if(!totp_crypto_seed_iv(plugin_state, NULL, 0)) { + CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0); + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + if(!totp_config_file_update_crypto_signatures(plugin_state)) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } + } else if(seed_result == CryptoSeedIVResultFailed) { totp_dialogs_config_loading_error(plugin_state); return false; } + if(totp_crypto_verify_key(plugin_state)) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } else { FURI_LOG_E( LOGGING_TAG, @@ -96,7 +112,7 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS); memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); - if(totp_config_file_load_base(plugin_state) != TotpConfigFileOpenSuccess) { + if(!totp_config_file_load(plugin_state)) { totp_dialogs_config_loading_error(plugin_state); return false; } @@ -119,15 +135,7 @@ static void totp_plugin_state_free(PluginState* plugin_state) { furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); - ListNode* node = plugin_state->tokens_list; - ListNode* tmp; - while(node != NULL) { - tmp = node->next; - TokenInfo* tokenInfo = node->data; - token_info_free(tokenInfo); - free(node); - node = tmp; - } + totp_config_file_close(plugin_state); if(plugin_state->crypto_verify_data != NULL) { free(plugin_state->crypto_verify_data); @@ -193,8 +201,9 @@ int32_t totp_app() { } } else if( plugin_state->pin_set && plugin_state->current_scene != TotpSceneAuthentication && + plugin_state->current_scene != TotpSceneStandby && furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } view_port_update(view_port); diff --git a/applications/external/totp/types/common.c b/applications/external/totp/types/common.c new file mode 100644 index 000000000..ec5eb3ebd --- /dev/null +++ b/applications/external/totp/types/common.c @@ -0,0 +1,3 @@ +#include "common.h" + +const char* LOGGING_TAG = "TOTP APP"; \ No newline at end of file diff --git a/applications/external/totp/types/common.h b/applications/external/totp/types/common.h index 2c6d6b293..737adb82d 100644 --- a/applications/external/totp/types/common.h +++ b/applications/external/totp/types/common.h @@ -1,3 +1,3 @@ #pragma once -#define LOGGING_TAG "TOTP APP" +extern const char* LOGGING_TAG; diff --git a/applications/external/totp/types/nullable.h b/applications/external/totp/types/nullable.h deleted file mode 100644 index 4f9b7bc01..000000000 --- a/applications/external/totp/types/nullable.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -#define TOTP_NULLABLE_STRUCT(value_type) \ - typedef struct TotpNullable_##value_type { \ - bool is_null; \ - value_type value; \ - } TotpNullable_##value_type - -#define TOTP_NULLABLE_NULL(s) s.is_null = true -#define TOTP_NULLABLE_VALUE(s, v) \ - s.is_null = false; \ - s.value = v - -TOTP_NULLABLE_STRUCT(uint16_t); diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index cacf68426..c20594f37 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -4,8 +4,8 @@ #include #include #include "../features_config.h" -#include #include "../ui/totp_scenes_enum.h" +#include "../services/config/config_file_context.h" #include "notification_method.h" #include "automation_method.h" #ifdef TOTP_BADBT_TYPE_ENABLED @@ -48,20 +48,7 @@ typedef struct { */ float timezone_offset; - /** - * @brief Token list head node - */ - ListNode* tokens_list; - - /** - * @brief Whether token list is loaded or not - */ - bool token_list_loaded; - - /** - * @brief Tokens list length - */ - uint16_t tokens_count; + ConfigFileContext* config_file_context; /** * @brief Encrypted well-known string data diff --git a/applications/external/totp/types/token_info.c b/applications/external/totp/types/token_info.c index 5b85de719..2f108033b 100644 --- a/applications/external/totp/types/token_info.c +++ b/applications/external/totp/types/token_info.c @@ -8,17 +8,15 @@ TokenInfo* token_info_alloc() { TokenInfo* tokenInfo = malloc(sizeof(TokenInfo)); furi_check(tokenInfo != NULL); - tokenInfo->algo = SHA1; - tokenInfo->digits = TOTP_6_DIGITS; - tokenInfo->duration = TOTP_TOKEN_DURATION_DEFAULT; - tokenInfo->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + tokenInfo->name = furi_string_alloc(); + token_info_set_defaults(tokenInfo); return tokenInfo; } void token_info_free(TokenInfo* token_info) { if(token_info == NULL) return; - free(token_info->name); free(token_info->token); + furi_string_free(token_info->name); free(token_info); } @@ -32,13 +30,13 @@ bool token_info_set_secret( uint8_t* plain_secret; size_t plain_secret_length; size_t plain_secret_size; - if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE32) { + if(plain_token_secret_encoding == PlainTokenSecretEncodingBase32) { plain_secret_size = token_secret_length; plain_secret = malloc(plain_secret_size); furi_check(plain_secret != NULL); plain_secret_length = base32_decode((const uint8_t*)plain_token_secret, plain_secret, plain_secret_size); - } else if(plain_token_secret_encoding == PLAIN_TOKEN_ENCODING_BASE64) { + } else if(plain_token_secret_encoding == PlainTokenSecretEncodingBase64) { plain_secret_length = 0; plain_secret = base64_decode( (const uint8_t*)plain_token_secret, @@ -52,6 +50,10 @@ bool token_info_set_secret( bool result; if(plain_secret_length > 0) { + if(token_info->token != NULL) { + free(token_info->token); + } + token_info->token = totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length); result = true; @@ -67,13 +69,13 @@ bool token_info_set_secret( bool token_info_set_digits_from_int(TokenInfo* token_info, uint8_t digits) { switch(digits) { case 5: - token_info->digits = TOTP_5_DIGITS; + token_info->digits = TotpFiveDigitsCount; return true; case 6: - token_info->digits = TOTP_6_DIGITS; + token_info->digits = TotpSixDigitsCount; return true; case 8: - token_info->digits = TOTP_8_DIGITS; + token_info->digits = TotpEightDigitsCount; return true; default: break; @@ -134,22 +136,22 @@ char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { bool token_info_set_automation_feature_from_str(TokenInfo* token_info, const FuriString* str) { if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END_NAME) == 0) { - token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END; + token_info->automation_features |= TokenAutomationFeatureEnterAtTheEnd; return true; } if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME) == 0) { - token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END; + token_info->automation_features |= TokenAutomationFeatureTabAtTheEnd; return true; } if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME) == 0) { - token_info->automation_features |= TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER; + token_info->automation_features |= TokenAutomationFeatureTypeSlower; return true; } if(furi_string_cmpi_str(str, TOTP_TOKEN_AUTOMATION_FEATURE_NONE_NAME) == 0) { - token_info->automation_features = TOKEN_AUTOMATION_FEATURE_NONE; + token_info->automation_features = TokenAutomationFeatureNone; return true; } @@ -164,10 +166,17 @@ TokenInfo* token_info_clone(const TokenInfo* src) { furi_check(clone->token != NULL); memcpy(clone->token, src->token, src->token_length); - int name_length = strnlen(src->name, TOTP_TOKEN_MAX_LENGTH); - clone->name = malloc(name_length + 1); - furi_check(clone->name != NULL); - strlcpy(clone->name, src->name, name_length + 1); + clone->name = furi_string_alloc(); + furi_string_set(clone->name, src->name); return clone; +} + +void token_info_set_defaults(TokenInfo* token_info) { + furi_check(token_info != NULL); + token_info->algo = SHA1; + token_info->digits = TotpSixDigitsCount; + token_info->duration = TOTP_TOKEN_DURATION_DEFAULT; + token_info->automation_features = TokenAutomationFeatureNone; + furi_string_reset(token_info->name); } \ No newline at end of file diff --git a/applications/external/totp/types/token_info.h b/applications/external/totp/types/token_info.h index 21968553f..138ad32b1 100644 --- a/applications/external/totp/types/token_info.h +++ b/applications/external/totp/types/token_info.h @@ -20,6 +20,8 @@ #define TOTP_TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END_NAME "tab" #define TOTP_TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME "slower" +#define TOTP_TOKEN_DIGITS_MAX_COUNT (8) + typedef uint8_t TokenHashAlgo; typedef uint8_t TokenDigitsCount; typedef uint8_t TokenAutomationFeature; @@ -32,22 +34,22 @@ enum TokenHashAlgos { /** * @brief SHA1 hashing algorithm */ - SHA1, + SHA1 = 0, /** * @brief SHA256 hashing algorithm */ - SHA256, + SHA256 = 1, /** * @brief SHA512 hashing algorithm */ - SHA512, + SHA512 = 2, /** * @brief Algorithm used by Steam (Valve) */ - STEAM + STEAM = 3 }; /** @@ -55,19 +57,19 @@ enum TokenHashAlgos { */ enum TokenDigitsCounts { /** - * @brief 6 digits + * @brief 5 digits */ - TOTP_5_DIGITS = 5, + TotpFiveDigitsCount = 5, /** * @brief 6 digits */ - TOTP_6_DIGITS = 6, + TotpSixDigitsCount = 6, /** * @brief 8 digits */ - TOTP_8_DIGITS = 8 + TotpEightDigitsCount = 8 }; /** @@ -77,22 +79,22 @@ enum TokenAutomationFeatures { /** * @brief No features enabled */ - TOKEN_AUTOMATION_FEATURE_NONE = 0b000, + TokenAutomationFeatureNone = 0b000, /** * @brief Press "Enter" key at the end as a part of token input automation */ - TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END = 0b001, + TokenAutomationFeatureEnterAtTheEnd = 0b001, /** * @brief Press "Tab" key at the end as a part of token input automation */ - TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END = 0b010, + TokenAutomationFeatureTabAtTheEnd = 0b010, /** * @brief Press keys slower and wait longer between keystrokes */ - TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER = 0b100 + TokenAutomationFeatureTypeSlower = 0b100 }; /** @@ -103,16 +105,14 @@ enum PlainTokenSecretEncodings { /** * @brief Base32 encoding */ - PLAIN_TOKEN_ENCODING_BASE32 = 0, + PlainTokenSecretEncodingBase32 = 0, /** * @brief Base64 encoding */ - PLAIN_TOKEN_ENCODING_BASE64 = 1 + PlainTokenSecretEncodingBase64 = 1 }; -#define TOTP_TOKEN_DIGITS_MAX_COUNT (8) - /** * @brief TOTP token information */ @@ -130,7 +130,7 @@ typedef struct { /** * @brief User-friendly token name */ - char* name; + FuriString* name; /** * @brief Hashing algorithm @@ -225,4 +225,10 @@ bool token_info_set_automation_feature_from_str(TokenInfo* token_info, const Fur * @param src instance to clone * @return cloned instance */ -TokenInfo* token_info_clone(const TokenInfo* src); \ No newline at end of file +TokenInfo* token_info_clone(const TokenInfo* src); + +/** + * @brief Sets default values to all the properties of \c token_info + * @param token_info instance to set defaults to + */ +void token_info_set_defaults(TokenInfo* token_info); diff --git a/applications/external/totp/ui/scene_director.c b/applications/external/totp/ui/scene_director.c index c6f709006..657762a94 100644 --- a/applications/external/totp/ui/scene_director.c +++ b/applications/external/totp/ui/scene_director.c @@ -5,29 +5,28 @@ #include "scenes/add_new_token/totp_scene_add_new_token.h" #include "scenes/token_menu/totp_scene_token_menu.h" #include "scenes/app_settings/totp_app_settings.h" +#include "scenes/standby/standby.h" -void totp_scene_director_activate_scene( - PluginState* const plugin_state, - Scene scene, - const void* context) { +void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene) { totp_scene_director_deactivate_active_scene(plugin_state); switch(scene) { case TotpSceneGenerateToken: - totp_scene_generate_token_activate(plugin_state, context); + totp_scene_generate_token_activate(plugin_state); break; case TotpSceneAuthentication: totp_scene_authenticate_activate(plugin_state); break; case TotpSceneAddNewToken: - totp_scene_add_new_token_activate(plugin_state, context); + totp_scene_add_new_token_activate(plugin_state); break; case TotpSceneTokenMenu: - totp_scene_token_menu_activate(plugin_state, context); + totp_scene_token_menu_activate(plugin_state); break; case TotpSceneAppSettings: - totp_scene_app_settings_activate(plugin_state, context); + totp_scene_app_settings_activate(plugin_state); break; case TotpSceneNone: + case TotpSceneStandby: break; default: break; @@ -56,6 +55,7 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state totp_scene_app_settings_deactivate(plugin_state); break; case TotpSceneNone: + case TotpSceneStandby: break; default: break; @@ -81,6 +81,9 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_ break; case TotpSceneNone: break; + case TotpSceneStandby: + totp_scene_standby_render(canvas); + break; default: break; } @@ -105,6 +108,7 @@ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* con processing = totp_scene_app_settings_handle_event(event, plugin_state); break; case TotpSceneNone: + case TotpSceneStandby: break; default: break; diff --git a/applications/external/totp/ui/scene_director.h b/applications/external/totp/ui/scene_director.h index 71709978f..e45223997 100644 --- a/applications/external/totp/ui/scene_director.h +++ b/applications/external/totp/ui/scene_director.h @@ -11,10 +11,7 @@ * @param scene scene to be activated * @param context scene context to be passed to the scene activation method */ -void totp_scene_director_activate_scene( - PluginState* const plugin_state, - Scene scene, - const void* context); +void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene scene); /** * @brief Deactivate current scene diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c index 3f8e4fd93..d525e3399 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c @@ -4,17 +4,18 @@ #include "../../scene_director.h" #include "totp_input_text.h" #include "../../../types/token_info.h" -#include #include "../../../services/config/config.h" #include "../../ui_controls.h" #include "../../common_dialogs.h" #include -#include "../../../types/nullable.h" #include "../generate_token/totp_scene_generate_token.h" char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512", "Steam"}; char* TOKEN_DIGITS_TEXT_LIST[] = {"5 digits", "6 digits", "8 digits"}; -TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = {TOTP_5_DIGITS, TOTP_6_DIGITS, TOTP_8_DIGITS}; +TokenDigitsCount TOKEN_DIGITS_VALUE_LIST[] = { + TotpFiveDigitsCount, + TotpSixDigitsCount, + TotpEightDigitsCount}; typedef enum { TokenNameTextBox, @@ -36,7 +37,6 @@ typedef struct { InputTextSceneContext* token_secret_input_context; InputTextSceneState* input_state; uint32_t input_started_at; - TotpNullable_uint16_t current_token_index; int16_t screen_y_offset; TokenHashAlgo algo; uint8_t digits_count_index; @@ -44,6 +44,13 @@ typedef struct { FuriString* duration_text; } SceneState; +struct TotpAddContext { + SceneState* scene_state; + uint8_t* iv; +}; + +enum TotpIteratorUpdateTokenResultsEx { TotpIteratorUpdateTokenResultInvalidSecret = 1 }; + static void on_token_name_user_comitted(InputTextSceneCallbackResult* result) { SceneState* scene_state = result->callback_data; free(scene_state->token_name); @@ -66,9 +73,29 @@ static void update_duration_text(SceneState* scene_state) { furi_string_printf(scene_state->duration_text, "%d sec.", scene_state->duration); } -void totp_scene_add_new_token_activate( - PluginState* plugin_state, - const TokenAddEditSceneContext* context) { +static TotpIteratorUpdateTokenResult add_token_handler(TokenInfo* tokenInfo, const void* context) { + const struct TotpAddContext* context_t = context; + if(!token_info_set_secret( + tokenInfo, + context_t->scene_state->token_secret, + context_t->scene_state->token_secret_length, + PlainTokenSecretEncodingBase32, + context_t->iv)) { + return TotpIteratorUpdateTokenResultInvalidSecret; + } + + furi_string_set_strn( + tokenInfo->name, + context_t->scene_state->token_name, + context_t->scene_state->token_name_length + 1); + tokenInfo->algo = context_t->scene_state->algo; + tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[context_t->scene_state->digits_count_index]; + tokenInfo->duration = context_t->scene_state->duration; + + return TotpIteratorUpdateTokenResultSuccess; +} + +void totp_scene_add_new_token_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); plugin_state->current_scene_state = scene_state; @@ -97,12 +124,6 @@ void totp_scene_add_new_token_activate( scene_state->duration = TOTP_TOKEN_DURATION_DEFAULT; scene_state->duration_text = furi_string_alloc(); update_duration_text(scene_state); - - if(context == NULL) { - TOTP_NULLABLE_NULL(scene_state->current_token_index); - } else { - TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); - } } void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state) { @@ -260,38 +281,16 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState case TokenDurationSelect: break; case ConfirmButton: { - TokenInfo* tokenInfo = token_info_alloc(); - bool token_secret_set = token_info_set_secret( - tokenInfo, - scene_state->token_secret, - scene_state->token_secret_length, - PLAIN_TOKEN_ENCODING_BASE32, - &plugin_state->iv[0]); + struct TotpAddContext add_context = { + .iv = plugin_state->iv, .scene_state = scene_state}; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + TotpIteratorUpdateTokenResult add_result = totp_token_info_iterator_add_new_token( + iterator_context, &add_token_handler, &add_context); - if(token_secret_set) { - tokenInfo->name = malloc(scene_state->token_name_length + 1); - furi_check(tokenInfo->name != NULL); - strlcpy( - tokenInfo->name, scene_state->token_name, scene_state->token_name_length + 1); - tokenInfo->algo = scene_state->algo; - tokenInfo->digits = TOKEN_DIGITS_VALUE_LIST[scene_state->digits_count_index]; - tokenInfo->duration = scene_state->duration; - - TOTP_LIST_INIT_OR_ADD(plugin_state->tokens_list, tokenInfo, furi_check); - plugin_state->tokens_count++; - - if(totp_config_file_save_new_token(tokenInfo) != TotpConfigFileUpdateSuccess) { - token_info_free(tokenInfo); - totp_dialogs_config_updating_error(plugin_state); - return false; - } - - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = plugin_state->tokens_count - 1}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - token_info_free(tokenInfo); + if(add_result == TotpIteratorUpdateTokenResultSuccess) { + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); + } else if(add_result == TotpIteratorUpdateTokenResultInvalidSecret) { DialogMessage* message = dialog_message_alloc(); dialog_message_set_buttons(message, "Back", NULL, NULL); dialog_message_set_text( @@ -305,7 +304,10 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState dialog_message_free(message); scene_state->selected_control = TokenSecretTextBox; update_screen_y_offset(scene_state); + } else if(add_result == TotpIteratorUpdateTokenResultFileUpdateFailed) { + totp_dialogs_config_updating_error(plugin_state); } + break; } default: @@ -313,14 +315,7 @@ bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState } break; case InputKeyBack: - if(!scene_state->current_token_index.is_null) { - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); break; default: break; diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h index e05a95dbd..dd6b32994 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h @@ -4,13 +4,7 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} TokenAddEditSceneContext; - -void totp_scene_add_new_token_activate( - PluginState* plugin_state, - const TokenAddEditSceneContext* context); +void totp_scene_add_new_token_activate(PluginState* plugin_state); void totp_scene_add_new_token_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_add_new_token_handle_event(PluginEvent* const event, PluginState* plugin_state); void totp_scene_add_new_token_deactivate(PluginState* plugin_state); diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c index d2cf629d2..6dcf0dbc9 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c @@ -9,7 +9,6 @@ #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" #include -#include "../../../types/nullable.h" #include "../../../features_config.h" #ifdef TOTP_BADBT_TYPE_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" @@ -40,21 +39,13 @@ typedef struct { bool badbt_enabled; #endif uint8_t y_offset; - TotpNullable_uint16_t current_token_index; Control selected_control; } SceneState; -void totp_scene_app_settings_activate( - PluginState* plugin_state, - const AppSettingsSceneContext* context) { +void totp_scene_app_settings_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); plugin_state->current_scene_state = scene_state; - if(context != NULL) { - TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); - } else { - TOTP_NULLABLE_NULL(scene_state->current_token_index); - } float off_int; float off_dec = modff(plugin_state->timezone_offset, &off_int); @@ -281,8 +272,7 @@ bool totp_scene_app_settings_handle_event( AutomationMethodNone; #endif - if(totp_config_file_update_user_settings(plugin_state) != - TotpConfigFileUpdateSuccess) { + if(!totp_config_file_update_user_settings(plugin_state)) { totp_dialogs_config_updating_error(plugin_state); return false; } @@ -294,25 +284,11 @@ bool totp_scene_app_settings_handle_event( } #endif - if(!scene_state->current_token_index.is_null) { - TokenMenuSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneTokenMenu, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu); } break; case InputKeyBack: { - if(!scene_state->current_token_index.is_null) { - TokenMenuSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneTokenMenu, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu); break; } default: diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h index a0e408b00..e54aab87b 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h @@ -4,13 +4,7 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} AppSettingsSceneContext; - -void totp_scene_app_settings_activate( - PluginState* plugin_state, - const AppSettingsSceneContext* context); +void totp_scene_app_settings_activate(PluginState* plugin_state); void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state); bool totp_scene_app_settings_handle_event( const PluginEvent* const event, diff --git a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c index c0a0b5744..86e1e8e2b 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -114,12 +114,18 @@ bool totp_scene_authenticate_handle_event( scene_state->code_length++; } break; - case InputKeyOk: - totp_crypto_seed_iv(plugin_state, &scene_state->code_input[0], scene_state->code_length); + case InputKeyOk: { + CryptoSeedIVResult seed_result = totp_crypto_seed_iv( + plugin_state, &scene_state->code_input[0], scene_state->code_length); + + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + totp_config_file_update_crypto_signatures(plugin_state); + } if(totp_crypto_verify_key(plugin_state)) { FURI_LOG_D(LOGGING_TAG, "PIN is valid"); - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } else { FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid"); memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); @@ -140,6 +146,7 @@ bool totp_scene_authenticate_handle_event( dialog_message_free(message); } break; + } case InputKeyBack: if(scene_state->code_length > 0) { scene_state->code_input[scene_state->code_length - 1] = 0; diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index 92a45eb4a..b111242dc 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -31,9 +31,7 @@ typedef struct { } UiPrecalculatedDimensions; typedef struct { - uint16_t current_token_index; char last_code[TOTP_TOKEN_DIGITS_MAX_COUNT + 1]; - TokenInfo* current_token; TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context; NotificationMessage const** notification_sequence_new_token; NotificationMessage const** notification_sequence_automation; @@ -128,19 +126,21 @@ static const NotificationSequence* return (NotificationSequence*)scene_state->notification_sequence_automation; } -static void update_totp_params(PluginState* const plugin_state) { +static void update_totp_params(PluginState* const plugin_state, size_t token_index) { SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - - if(scene_state->current_token_index < plugin_state->tokens_count) { - scene_state->current_token = - list_element_at(plugin_state->tokens_list, scene_state->current_token_index)->data; + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_go_to(iterator_context, token_index)) { totp_generate_code_worker_notify( scene_state->generate_code_worker_context, TotpGenerateCodeWorkerEventForceUpdate); } } -static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_state) { - uint8_t code_length = scene_state->current_token->digits; +static void draw_totp_code(Canvas* const canvas, const PluginState* const plugin_state) { + const SceneState* scene_state = plugin_state->current_scene_state; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + uint8_t code_length = totp_token_info_iterator_get_current_token(iterator_context)->digits; uint8_t offset_x = scene_state->ui_precalculated_dimensions.code_offset_x; uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width; uint8_t offset_x_inc = scene_state->ui_precalculated_dimensions.code_offset_x_inc; @@ -163,10 +163,18 @@ static void draw_totp_code(Canvas* const canvas, const SceneState* const scene_s static void on_new_token_code_generated(bool time_left, void* context) { const PluginState* plugin_state = context; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_get_total_count(iterator_context) == 0) { + return; + } + SceneState* scene_state = plugin_state->current_scene_state; + const TokenInfo* current_token = totp_token_info_iterator_get_current_token(iterator_context); + uint8_t char_width = modeNine_15ptFontInfo.charInfo[0].width; scene_state->ui_precalculated_dimensions.code_total_length = - scene_state->current_token->digits * (char_width + modeNine_15ptFontInfo.spacePixels); + current_token->digits * (char_width + modeNine_15ptFontInfo.spacePixels); scene_state->ui_precalculated_dimensions.code_offset_x = (SCREEN_WIDTH - scene_state->ui_precalculated_dimensions.code_total_length) >> 1; scene_state->ui_precalculated_dimensions.code_offset_x_inc = @@ -192,43 +200,9 @@ static void on_code_lifetime_updated_generated(float code_lifetime_percent, void PROGRESS_BAR_MARGIN; } -void totp_scene_generate_token_activate( - PluginState* plugin_state, - const GenerateTokenSceneContext* context) { - if(!plugin_state->token_list_loaded) { - TokenLoadingResult token_load_result = totp_config_file_load_tokens(plugin_state); - if(token_load_result != TokenLoadingResultSuccess) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, NULL, "Okay", NULL); - if(token_load_result == TokenLoadingResultWarning) { - dialog_message_set_text( - message, - "Unable to load some tokens\nPlease review conf file", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - } else if(token_load_result == TokenLoadingResultError) { - dialog_message_set_text( - message, - "Unable to load tokens\nPlease review conf file", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - } - - dialog_message_show(plugin_state->dialogs_app, message); - dialog_message_free(message); - } - } +void totp_scene_generate_token_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); - if(context == NULL || context->current_token_index > plugin_state->tokens_count) { - scene_state->current_token_index = 0; - } else { - scene_state->current_token_index = context->current_token_index; - } plugin_state->current_scene_state = scene_state; FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset); @@ -254,10 +228,11 @@ void totp_scene_generate_token_activate( scene_state->last_code_update_sync); } #endif - + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); scene_state->generate_code_worker_context = totp_generate_code_worker_start( scene_state->last_code, - &scene_state->current_token, + totp_token_info_iterator_get_current_token(iterator_context), scene_state->last_code_update_sync, plugin_state->timezone_offset, plugin_state->iv); @@ -270,11 +245,14 @@ void totp_scene_generate_token_activate( &on_code_lifetime_updated_generated, scene_state); - update_totp_params(plugin_state); + update_totp_params( + plugin_state, totp_token_info_iterator_get_current_token_index(iterator_context)); } void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) { - if(plugin_state->tokens_count == 0) { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_get_total_count(iterator_context) == 0) { canvas_draw_str_aligned( canvas, SCREEN_WIDTH_CENTER, @@ -295,7 +273,9 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; canvas_set_font(canvas, FontPrimary); - uint16_t token_name_width = canvas_string_width(canvas, scene_state->current_token->name); + const char* token_name_cstr = + furi_string_get_cstr(totp_token_info_iterator_get_current_token(iterator_context)->name); + uint16_t token_name_width = canvas_string_width(canvas, token_name_cstr); if(SCREEN_WIDTH - token_name_width > 18) { canvas_draw_str_aligned( canvas, @@ -303,22 +283,17 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ SCREEN_HEIGHT_CENTER - 20, AlignCenter, AlignCenter, - scene_state->current_token->name); + token_name_cstr); } else { canvas_draw_str_aligned( - canvas, - 9, - SCREEN_HEIGHT_CENTER - 20, - AlignLeft, - AlignCenter, - scene_state->current_token->name); + canvas, 9, SCREEN_HEIGHT_CENTER - 20, AlignLeft, AlignCenter, token_name_cstr); canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 0, SCREEN_HEIGHT_CENTER - 24, 9, 9); canvas_draw_box(canvas, SCREEN_WIDTH - 10, SCREEN_HEIGHT_CENTER - 24, 9, 9); canvas_set_color(canvas, ColorBlack); } - draw_totp_code(canvas, scene_state); + draw_totp_code(canvas, plugin_state); canvas_draw_box( canvas, @@ -326,11 +301,10 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ SCREEN_HEIGHT - PROGRESS_BAR_MARGIN - PROGRESS_BAR_HEIGHT, scene_state->ui_precalculated_dimensions.progress_bar_width, PROGRESS_BAR_HEIGHT); - - if(plugin_state->tokens_count > 1) { + if(totp_token_info_iterator_get_total_count(iterator_context) > 1) { canvas_draw_icon(canvas, 0, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_left_8x9); canvas_draw_icon( - canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); + canvas, SCREEN_WIDTH - 8, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); } #ifdef TOTP_AUTOMATION_ICONS_ENABLED @@ -351,7 +325,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ #ifdef TOTP_BADBT_TYPE_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt && plugin_state->bt_type_code_worker_context != NULL && - plugin_state->bt_type_code_worker_context->is_advertising) { + totp_bt_type_code_worker_is_advertising(plugin_state->bt_type_code_worker_context)) { canvas_draw_icon( canvas, SCREEN_WIDTH_CENTER + @@ -379,10 +353,12 @@ bool totp_scene_generate_token_handle_event( if(event->input.key == InputKeyDown && plugin_state->automation_method & AutomationMethodBadUsb) { scene_state = (SceneState*)plugin_state->current_scene_state; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_usb_type_code_worker_notify( scene_state->usb_type_code_worker_context, TotpUsbTypeCodeWorkerEventType, - scene_state->current_token->automation_features); + totp_token_info_iterator_get_current_token(iterator_context)->automation_features); notification_message( plugin_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); @@ -393,10 +369,12 @@ bool totp_scene_generate_token_handle_event( event->input.key == InputKeyUp && plugin_state->automation_method & AutomationMethodBadBt) { scene_state = (SceneState*)plugin_state->current_scene_state; + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_bt_type_code_worker_notify( plugin_state->bt_type_code_worker_context, TotpBtTypeCodeWorkerEventType, - scene_state->current_token->automation_features); + totp_token_info_iterator_get_current_token(iterator_context)->automation_features); notification_message( plugin_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); @@ -409,37 +387,43 @@ bool totp_scene_generate_token_handle_event( return true; } - scene_state = (SceneState*)plugin_state->current_scene_state; switch(event->input.key) { case InputKeyUp: break; case InputKeyDown: break; - case InputKeyRight: - totp_roll_value_uint16_t( - &scene_state->current_token_index, + case InputKeyRight: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t current_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + totp_roll_value_size_t( + ¤t_token_index, 1, 0, - plugin_state->tokens_count - 1, + totp_token_info_iterator_get_total_count(iterator_context) - 1, RollOverflowBehaviorRoll); - update_totp_params(plugin_state); + + update_totp_params(plugin_state, current_token_index); break; - case InputKeyLeft: - totp_roll_value_uint16_t( - &scene_state->current_token_index, + } + case InputKeyLeft: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + size_t current_token_index = + totp_token_info_iterator_get_current_token_index(iterator_context); + totp_roll_value_size_t( + ¤t_token_index, -1, 0, - plugin_state->tokens_count - 1, + totp_token_info_iterator_get_total_count(iterator_context) - 1, RollOverflowBehaviorRoll); - update_totp_params(plugin_state); + + update_totp_params(plugin_state, current_token_index); break; + } case InputKeyOk: - if(plugin_state->tokens_count == 0) { - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, NULL); - } else { - TokenMenuSceneContext ctx = {.current_token_index = scene_state->current_token_index}; - totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu, &ctx); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu); break; case InputKeyBack: break; diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h index e183f53d2..3f7bc0408 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h @@ -4,13 +4,7 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} GenerateTokenSceneContext; - -void totp_scene_generate_token_activate( - PluginState* plugin_state, - const GenerateTokenSceneContext* context); +void totp_scene_generate_token_activate(PluginState* plugin_state); void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_generate_token_handle_event( const PluginEvent* const event, diff --git a/applications/external/totp/ui/scenes/standby/standby.c b/applications/external/totp/ui/scenes/standby/standby.c new file mode 100644 index 000000000..5cd6bae6a --- /dev/null +++ b/applications/external/totp/ui/scenes/standby/standby.c @@ -0,0 +1,12 @@ +#include "standby.h" +#include +#include "../../constants.h" + +void totp_scene_standby_render(Canvas* const canvas) { + canvas_draw_icon(canvas, SCREEN_WIDTH - 56, SCREEN_HEIGHT - 48, &I_DolphinCommon_56x48); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignTop, "CLI command"); + + canvas_draw_str_aligned(canvas, 5, 24, AlignLeft, AlignTop, "is running now"); +} \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/standby/standby.h b/applications/external/totp/ui/scenes/standby/standby.h new file mode 100644 index 000000000..78e2b0915 --- /dev/null +++ b/applications/external/totp/ui/scenes/standby/standby.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void totp_scene_standby_render(Canvas* const canvas); \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c index 7b00f0a1b..a8c8de28a 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c @@ -6,12 +6,10 @@ #include "../../constants.h" #include "../../scene_director.h" #include "../../../services/config/config.h" -#include #include "../../../types/token_info.h" #include "../generate_token/totp_scene_generate_token.h" #include "../add_new_token/totp_scene_add_new_token.h" #include "../app_settings/totp_app_settings.h" -#include "../../../types/nullable.h" #include #define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3) @@ -21,25 +19,19 @@ typedef enum { AddNewToken, DeleteToken, AppSettings } Control; typedef struct { Control selected_control; - TotpNullable_uint16_t current_token_index; } SceneState; -void totp_scene_token_menu_activate( - PluginState* plugin_state, - const TokenMenuSceneContext* context) { +void totp_scene_token_menu_activate(PluginState* plugin_state) { SceneState* scene_state = malloc(sizeof(SceneState)); furi_check(scene_state != NULL); plugin_state->current_scene_state = scene_state; - if(context != NULL) { - TOTP_NULLABLE_VALUE(scene_state->current_token_index, context->current_token_index); - } else { - TOTP_NULLABLE_NULL(scene_state->current_token_index); - } } void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state) { const SceneState* scene_state = (SceneState*)plugin_state->current_scene_state; - if(scene_state->current_token_index.is_null) { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); + if(totp_token_info_iterator_get_total_count(iterator_context) == 0) { ui_control_button_render( canvas, SCREEN_WIDTH_CENTER - 36, @@ -95,22 +87,28 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt } switch(event->input.key) { - case InputKeyUp: + case InputKeyUp: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_roll_value_uint8_t( &scene_state->selected_control, -1, AddNewToken, AppSettings, RollOverflowBehaviorRoll); if(scene_state->selected_control == DeleteToken && - scene_state->current_token_index.is_null) { + totp_token_info_iterator_get_total_count(iterator_context) == 0) { scene_state->selected_control--; } break; - case InputKeyDown: + } + case InputKeyDown: { + const TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); totp_roll_value_uint8_t( &scene_state->selected_control, 1, AddNewToken, AppSettings, RollOverflowBehaviorRoll); if(scene_state->selected_control == DeleteToken && - scene_state->current_token_index.is_null) { + totp_token_info_iterator_get_total_count(iterator_context) == 0) { scene_state->selected_control++; } break; + } case InputKeyRight: break; case InputKeyLeft: @@ -118,14 +116,7 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt case InputKeyOk: switch(scene_state->selected_control) { case AddNewToken: { - if(scene_state->current_token_index.is_null) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken, NULL); - } else { - TokenAddEditSceneContext add_new_token_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneAddNewToken, &add_new_token_scene_context); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken); break; } case DeleteToken: { @@ -142,34 +133,21 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt DialogMessageButton dialog_result = dialog_message_show(plugin_state->dialogs_app, message); dialog_message_free(message); + TokenInfoIteratorContext* iterator_context = + totp_config_get_token_iterator_context(plugin_state); if(dialog_result == DialogMessageButtonRight && - !scene_state->current_token_index.is_null) { - TokenInfo* tokenInfo = NULL; - plugin_state->tokens_list = list_remove_at( - plugin_state->tokens_list, - scene_state->current_token_index.value, - (void**)&tokenInfo); - plugin_state->tokens_count--; - furi_check(tokenInfo != NULL); - token_info_free(tokenInfo); - - if(totp_full_save_config_file(plugin_state) != TotpConfigFileUpdateSuccess) { + totp_token_info_iterator_get_total_count(iterator_context) > 0) { + if(!totp_token_info_iterator_remove_current_token_info(iterator_context)) { totp_dialogs_config_updating_error(plugin_state); return false; } - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); + + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } break; } case AppSettings: { - if(!scene_state->current_token_index.is_null) { - AppSettingsSceneContext app_settings_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneAppSettings, &app_settings_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneAppSettings); break; } default: @@ -177,14 +155,7 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt } break; case InputKeyBack: { - if(!scene_state->current_token_index.is_null) { - GenerateTokenSceneContext generate_scene_context = { - .current_token_index = scene_state->current_token_index.value}; - totp_scene_director_activate_scene( - plugin_state, TotpSceneGenerateToken, &generate_scene_context); - } else { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken, NULL); - } + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); break; } default: diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h index f9d4b4cbf..a715c9748 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h @@ -4,13 +4,7 @@ #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" -typedef struct { - uint16_t current_token_index; -} TokenMenuSceneContext; - -void totp_scene_token_menu_activate( - PluginState* plugin_state, - const TokenMenuSceneContext* context); +void totp_scene_token_menu_activate(PluginState* plugin_state); void totp_scene_token_menu_render(Canvas* const canvas, PluginState* plugin_state); bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginState* plugin_state); void totp_scene_token_menu_deactivate(PluginState* plugin_state); diff --git a/applications/external/totp/ui/totp_scenes_enum.h b/applications/external/totp/ui/totp_scenes_enum.h index 0c73af772..4624eddd9 100644 --- a/applications/external/totp/ui/totp_scenes_enum.h +++ b/applications/external/totp/ui/totp_scenes_enum.h @@ -34,5 +34,10 @@ enum Scenes { /** * @brief Scene where user can change application settings */ - TotpSceneAppSettings + TotpSceneAppSettings, + + /** + * @brief Scene which informs user that CLI command is running + */ + TotpSceneStandby }; diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.c b/applications/external/totp/workers/bt_type_code/bt_type_code.c index 5a1f56298..bf55dba6a 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.c +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.c @@ -2,13 +2,39 @@ #include #include #include +#include +#include +#include +#include +#include #include #include "../../types/common.h" #include "../../types/token_info.h" #include "../type_code_common.h" +#include "../../features_config.h" + +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME +#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH +#define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE +#endif #define HID_BT_KEYS_STORAGE_PATH EXT_PATH("authenticator/.bt_hid.keys") +struct TotpBtTypeCodeWorkerContext { + char* code_buffer; + uint8_t code_buffer_size; + uint8_t flags; + FuriThread* thread; + FuriMutex* code_buffer_sync; + Bt* bt; + bool is_advertising; + bool is_connected; +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME + char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; + uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; +#endif +}; + static inline bool totp_type_code_worker_stop_requested() { return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop; } @@ -197,4 +223,8 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) { context->bt = NULL; free(context); +} + +bool totp_bt_type_code_worker_is_advertising(const TotpBtTypeCodeWorkerContext* context) { + return context->is_advertising; } \ No newline at end of file diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.h b/applications/external/totp/workers/bt_type_code/bt_type_code.h index 6c7e502c6..85016592e 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.h +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.h @@ -1,50 +1,79 @@ #pragma once -#include -#include +#include +#include #include -#include -#include -#include -#include "../../features_config.h" - -#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME -#define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH -#define TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE -#endif typedef uint8_t TotpBtTypeCodeWorkerEvent; -typedef struct { - char* code_buffer; - uint8_t code_buffer_size; - uint8_t flags; - FuriThread* thread; - FuriMutex* code_buffer_sync; - Bt* bt; - bool is_advertising; - bool is_connected; -#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME - char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; - uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; -#endif -} TotpBtTypeCodeWorkerContext; +typedef struct TotpBtTypeCodeWorkerContext TotpBtTypeCodeWorkerContext; +/** + * @brief Bluetooth token input automation worker events + */ enum TotpBtTypeCodeWorkerEvents { + + /** + * @brief Reserved, should not be used anywhere + */ TotpBtTypeCodeWorkerEventReserved = 0b00, + + /** + * @brief Stop worker + */ TotpBtTypeCodeWorkerEventStop = 0b01, + + /** + * @brief Trigger token input automation + */ TotpBtTypeCodeWorkerEventType = 0b10 }; +/** + * @brief Initializes bluetooth token input automation worker + * @return worker context + */ TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init(); + +/** + * @brief Disposes bluetooth token input automation worker and releases all the allocated resources + * @param context worker context + */ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context); + +/** + * @brief Starts bluetooth token input automation worker + * @param context worker context + * @param code_buffer code buffer to be used to automate + * @param code_buffer_size code buffer size + * @param code_buffer_sync code buffer synchronization primitive + */ void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, char* code_buffer, uint8_t code_buffer_size, FuriMutex* code_buffer_sync); + +/** + * @brief Stops bluetooth token input automation worker + * @param context worker context + */ void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context); + +/** + * @brief Notifies bluetooth token input automation worker with a given event + * @param context worker context + * @param event event to notify worker with + * @param flags event flags + */ void totp_bt_type_code_worker_notify( TotpBtTypeCodeWorkerContext* context, TotpBtTypeCodeWorkerEvent event, uint8_t flags); + +/** + * @brief Gets information whether Bluetooth is advertising now or not + * @param context worker context + * @return \c true if Bluetooth is advertising now; \c false otherwise + */ +bool totp_bt_type_code_worker_is_advertising(const TotpBtTypeCodeWorkerContext* context); diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.c b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c index 4919cf942..7e9356c45 100644 --- a/applications/external/totp/workers/generate_totp_code/generate_totp_code.c +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c @@ -1,4 +1,5 @@ #include "generate_totp_code.h" +#include #include "../../services/crypto/crypto.h" #include "../../services/totp/totp.h" #include "../../services/convert/convert.h" @@ -7,6 +8,19 @@ #define ONE_SEC_MS (1000) +struct TotpGenerateCodeWorkerContext { + char* code_buffer; + FuriThread* thread; + FuriMutex* code_buffer_sync; + const TokenInfo* token_info; + float timezone_offset; + uint8_t* iv; + TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler; + void* on_new_code_generated_handler_context; + TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler; + void* on_code_lifetime_changed_handler_context; +}; + static const char* STEAM_ALGO_ALPHABET = "23456789BCDFGHJKMNPQRTVWXY"; static void @@ -93,7 +107,7 @@ static int32_t totp_generate_worker_callback(void* context) { if(flags & TotpGenerateCodeWorkerEventStop) break; - const TokenInfo* token_info = *(t_context->token_info); + const TokenInfo* token_info = t_context->token_info; if(token_info == NULL) { continue; } @@ -127,7 +141,7 @@ static int32_t totp_generate_worker_callback(void* context) { TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( char* code_buffer, - TokenInfo** token_info, + const TokenInfo* token_info, FuriMutex* code_buffer_sync, float timezone_offset, uint8_t* iv) { diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.h b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h index c7a93dc95..f351ffa68 100644 --- a/applications/external/totp/workers/generate_totp_code/generate_totp_code.h +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include "../../types/token_info.h" @@ -10,39 +9,77 @@ typedef uint8_t TotpGenerateCodeWorkerEvent; typedef void (*TOTP_NEW_CODE_GENERATED_HANDLER)(bool time_left, void* context); typedef void (*TOTP_CODE_LIFETIME_CHANGED_HANDLER)(float code_lifetime_percent, void* context); -typedef struct { - char* code_buffer; - FuriThread* thread; - FuriMutex* code_buffer_sync; - TokenInfo** token_info; - float timezone_offset; - uint8_t* iv; - TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler; - void* on_new_code_generated_handler_context; - TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler; - void* on_code_lifetime_changed_handler_context; -} TotpGenerateCodeWorkerContext; +typedef struct TotpGenerateCodeWorkerContext TotpGenerateCodeWorkerContext; +/** + * @brief Generate token worker events + */ enum TotGenerateCodeWorkerEvents { + + /** + * @brief Reserved, should not be used anywhere + */ TotpGenerateCodeWorkerEventReserved = 0b00, + + /** + * @brief Stop worker + */ TotpGenerateCodeWorkerEventStop = 0b01, + + /** + * @brief Trigger token input automation + */ TotpGenerateCodeWorkerEventForceUpdate = 0b10 }; +/** + * @brief Starts generate code worker + * @param code_buffer code buffer to generate code to + * @param token_info token info to be used to generate code + * @param code_buffer_sync code buffer synchronization primitive + * @param timezone_offset timezone offset to be used to generate code + * @param iv initialization vector (IV) to be used to decrypt token secret + * @return worker context + */ TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( char* code_buffer, - TokenInfo** token_info, + const TokenInfo* token_info, FuriMutex* code_buffer_sync, float timezone_offset, uint8_t* iv); + +/** + * @brief Stops generate code worker + * @param context worker context + */ void totp_generate_code_worker_stop(TotpGenerateCodeWorkerContext* context); + +/** + * @brief Notifies generate code worker with a given event + * @param context worker context + * @param event event to notify worker with + */ void totp_generate_code_worker_notify( TotpGenerateCodeWorkerContext* context, TotpGenerateCodeWorkerEvent event); + +/** + * @brief Sets new handler for "on new code generated" event + * @param context worker context + * @param on_new_code_generated_handler handler + * @param on_new_code_generated_handler_context handler context + */ void totp_generate_code_worker_set_code_generated_handler( TotpGenerateCodeWorkerContext* context, TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler, void* on_new_code_generated_handler_context); + +/** + * @brief Sets new handler for "on code lifetime changed" event + * @param context worker context + * @param on_code_lifetime_changed_handler handler + * @param on_code_lifetime_changed_handler_context handler context + */ void totp_generate_code_worker_set_lifetime_changed_handler( TotpGenerateCodeWorkerContext* context, TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler, diff --git a/applications/external/totp/workers/type_code_common.c b/applications/external/totp/workers/type_code_common.c index fa5e7290f..696df3b1f 100644 --- a/applications/external/totp/workers/type_code_common.c +++ b/applications/external/totp/workers/type_code_common.c @@ -14,7 +14,7 @@ static const uint8_t hid_number_keys[] = { HID_KEYBOARD_Z}; static uint32_t get_keystroke_delay(TokenAutomationFeature features) { - if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { + if(features & TokenAutomationFeatureTypeSlower) { return 100; } @@ -22,7 +22,7 @@ static uint32_t get_keystroke_delay(TokenAutomationFeature features) { } static uint32_t get_keypress_delay(TokenAutomationFeature features) { - if(features & TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER) { + if(features & TokenAutomationFeatureTypeSlower) { return 60; } @@ -64,13 +64,13 @@ void totp_type_code_worker_execute_automation( i++; } - if(features & TOKEN_AUTOMATION_FEATURE_ENTER_AT_THE_END) { + if(features & TokenAutomationFeatureEnterAtTheEnd) { furi_delay_ms(get_keystroke_delay(features)); totp_type_code_worker_press_key( HID_KEYBOARD_RETURN, key_press_fn, key_release_fn, features); } - if(features & TOKEN_AUTOMATION_FEATURE_TAB_AT_THE_END) { + if(features & TokenAutomationFeatureTabAtTheEnd) { furi_delay_ms(get_keystroke_delay(features)); totp_type_code_worker_press_key(HID_KEYBOARD_TAB, key_press_fn, key_release_fn, features); } diff --git a/applications/external/totp/workers/type_code_common.h b/applications/external/totp/workers/type_code_common.h index 1516928cf..db357329a 100644 --- a/applications/external/totp/workers/type_code_common.h +++ b/applications/external/totp/workers/type_code_common.h @@ -4,6 +4,14 @@ typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key); +/** + * @brief Executes token input automation using given key press\release handlers + * @param key_press_fn key press handler + * @param key_release_fn key release handler + * @param code_buffer code buffer to be typed + * @param code_buffer_size code buffer size + * @param features automation features + */ void totp_type_code_worker_execute_automation( TOTP_AUTOMATION_KEY_HANDLER key_press_fn, TOTP_AUTOMATION_KEY_HANDLER key_release_fn, diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.c b/applications/external/totp/workers/usb_type_code/usb_type_code.c index 10034907d..a391bdf82 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.c +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.c @@ -1,9 +1,22 @@ #include "usb_type_code.h" +#include #include +#include +#include +#include #include "../../services/convert/convert.h" #include "../../types/token_info.h" #include "../type_code_common.h" +struct TotpUsbTypeCodeWorkerContext { + char* code_buffer; + uint8_t code_buffer_size; + uint8_t flags; + FuriThread* thread; + FuriMutex* code_buffer_sync; + FuriHalUsbInterface* usb_mode_prev; +}; + static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) { if(context->usb_mode_prev != NULL) { furi_hal_usb_set_config(context->usb_mode_prev, NULL); diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.h b/applications/external/totp/workers/usb_type_code/usb_type_code.h index 21213f4d4..0a700e7fe 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.h +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.h @@ -1,35 +1,59 @@ #pragma once -#include -#include +#include +#include #include -#include -#include -#include typedef uint8_t TotpUsbTypeCodeWorkerEvent; -typedef struct { - char* code_buffer; - uint8_t code_buffer_size; - uint8_t flags; - FuriThread* thread; - FuriMutex* code_buffer_sync; - FuriHalUsbInterface* usb_mode_prev; -} TotpUsbTypeCodeWorkerContext; +typedef struct TotpUsbTypeCodeWorkerContext TotpUsbTypeCodeWorkerContext; +/** + * @brief USB token input automation worker events + */ enum TotpUsbTypeCodeWorkerEvents { + + /** + * @brief Reserved, should not be used anywhere + */ TotpUsbTypeCodeWorkerEventReserved = 0b00, + + /** + * @brief Stop worker + */ TotpUsbTypeCodeWorkerEventStop = 0b01, + + /** + * @brief Trigger token input automation + */ TotpUsbTypeCodeWorkerEventType = 0b10 }; +/** + * @brief Starts USB token input automation worker + * @param code_buffer code buffer to be used to automate + * @param code_buffer_size code buffer size + * @param code_buffer_sync code buffer synchronization primitive + * @return worker context + */ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( char* code_buffer, uint8_t code_buffer_size, FuriMutex* code_buffer_sync); + +/** + * @brief Stops USB token input automation worker + * @param context worker context + */ void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context); + +/** + * @brief Notifies USB token input automation worker with a given event + * @param context worker context + * @param event event to notify worker with + * @param flags event flags + */ void totp_usb_type_code_worker_notify( TotpUsbTypeCodeWorkerContext* context, TotpUsbTypeCodeWorkerEvent event, - uint8_t flags); \ No newline at end of file + uint8_t flags); From 10c4cd0f419993cd6d8551fb4c8c89d42448fbee Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 00:13:28 +0300 Subject: [PATCH 133/282] Update WifI marauder app https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/11 --- .../assets/DolphinCommon_56x48.png | Bin 0 -> 1416 bytes .../file/sequential_file.c | 46 + .../file/sequential_file.h | 15 + .../scenes/wifi_marauder_scene_config.h | 9 + .../wifi_marauder_scene_console_output.c | 106 +- ...ifi_marauder_scene_script_confirm_delete.c | 83 + .../scenes/wifi_marauder_scene_script_edit.c | 125 + .../wifi_marauder_scene_script_edit_list.c | 188 ++ .../wifi_marauder_scene_script_options.c | 111 + .../wifi_marauder_scene_script_select.c | 90 + .../wifi_marauder_scene_script_settings.c | 87 + .../wifi_marauder_scene_script_stage_add.c | 297 ++ .../wifi_marauder_scene_script_stage_edit.c | 203 ++ .../scenes/wifi_marauder_scene_start.c | 26 +- .../scenes/wifi_marauder_scene_user_input.c | 155 + .../wifi_marauder_companion/script/cJSON.c | 2743 +++++++++++++++++ .../wifi_marauder_companion/script/cJSON.h | 321 ++ .../menu/wifi_marauder_script_stage_menu.c | 32 + .../menu/wifi_marauder_script_stage_menu.h | 42 + ...wifi_marauder_script_stage_menu_beaconap.c | 27 + ...fi_marauder_script_stage_menu_beaconlist.c | 59 + .../wifi_marauder_script_stage_menu_config.h | 14 + .../wifi_marauder_script_stage_menu_deauth.c | 27 + .../wifi_marauder_script_stage_menu_delay.c | 27 + .../wifi_marauder_script_stage_menu_exec.c | 30 + .../wifi_marauder_script_stage_menu_probe.c | 27 + .../wifi_marauder_script_stage_menu_scan.c | 93 + .../wifi_marauder_script_stage_menu_select.c | 95 + ...i_marauder_script_stage_menu_sniffbeacon.c | 27 + ...i_marauder_script_stage_menu_sniffdeauth.c | 27 + ...wifi_marauder_script_stage_menu_sniffesp.c | 27 + ...fi_marauder_script_stage_menu_sniffpmkid.c | 91 + ...wifi_marauder_script_stage_menu_sniffpwn.c | 27 + ...wifi_marauder_script_stage_menu_sniffraw.c | 27 + .../script/wifi_marauder_script.c | 947 ++++++ .../script/wifi_marauder_script.h | 257 ++ .../script/wifi_marauder_script_executor.c | 307 ++ .../script/wifi_marauder_script_executor.h | 6 + .../script/wifi_marauder_script_worker.c | 74 + .../script/wifi_marauder_script_worker.h | 43 + .../wifi_marauder_app.c | 13 + .../wifi_marauder_app_i.h | 41 +- .../wifi_marauder_custom_event.h | 3 +- 43 files changed, 6966 insertions(+), 29 deletions(-) create mode 100644 applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png create mode 100644 applications/external/wifi_marauder_companion/file/sequential_file.c create mode 100644 applications/external/wifi_marauder_companion/file/sequential_file.h create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c create mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c create mode 100644 applications/external/wifi_marauder_companion/script/cJSON.c create mode 100644 applications/external/wifi_marauder_companion/script/cJSON.h create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c create mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script.c create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script.h create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c create mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h diff --git a/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png b/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png new file mode 100644 index 0000000000000000000000000000000000000000..089aaed83507431993a76ca25d32fdd9664c1c84 GIT binary patch literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj literal 0 HcmV?d00001 diff --git a/applications/external/wifi_marauder_companion/file/sequential_file.c b/applications/external/wifi_marauder_companion/file/sequential_file.c new file mode 100644 index 000000000..d780deb12 --- /dev/null +++ b/applications/external/wifi_marauder_companion/file/sequential_file.c @@ -0,0 +1,46 @@ +#include "sequential_file.h" + +char* sequential_file_resolve_path( + Storage* storage, + const char* dir, + const char* prefix, + const char* extension) { + if(storage == NULL || dir == NULL || prefix == NULL || extension == NULL) { + return NULL; + } + + char file_path[256]; + int file_index = 0; + + do { + if(snprintf( + file_path, sizeof(file_path), "%s/%s_%d.%s", dir, prefix, file_index, extension) < + 0) { + return NULL; + } + file_index++; + } while(storage_file_exists(storage, file_path)); + + return strdup(file_path); +} + +bool sequential_file_open( + Storage* storage, + File* file, + const char* dir, + const char* prefix, + const char* extension) { + if(storage == NULL || file == NULL || dir == NULL || prefix == NULL || extension == NULL) { + return false; + } + + char* file_path = sequential_file_resolve_path(storage, dir, prefix, extension); + if(file_path == NULL) { + return false; + } + + bool success = storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS); + free(file_path); + + return success; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/file/sequential_file.h b/applications/external/wifi_marauder_companion/file/sequential_file.h new file mode 100644 index 000000000..4fd4794c2 --- /dev/null +++ b/applications/external/wifi_marauder_companion/file/sequential_file.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +char* sequential_file_resolve_path( + Storage* storage, + const char* dir, + const char* prefix, + const char* extension); +bool sequential_file_open( + Storage* storage, + File* file, + const char* dir, + const char* prefix, + const char* extension); \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h index 715897d17..ae976c6bf 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h @@ -3,3 +3,12 @@ ADD_SCENE(wifi_marauder, console_output, ConsoleOutput) ADD_SCENE(wifi_marauder, text_input, TextInput) ADD_SCENE(wifi_marauder, settings_init, SettingsInit) ADD_SCENE(wifi_marauder, log_viewer, LogViewer) +ADD_SCENE(wifi_marauder, user_input, UserInput) +ADD_SCENE(wifi_marauder, script_select, ScriptSelect) +ADD_SCENE(wifi_marauder, script_options, ScriptOptions) +ADD_SCENE(wifi_marauder, script_edit, ScriptEdit) +ADD_SCENE(wifi_marauder, script_settings, ScriptSettings) +ADD_SCENE(wifi_marauder, script_confirm_delete, ScriptConfirmDelete) +ADD_SCENE(wifi_marauder, script_stage_edit, ScriptStageEdit) +ADD_SCENE(wifi_marauder, script_stage_add, ScriptStageAdd) +ADD_SCENE(wifi_marauder, script_stage_edit_list, ScriptStageEditList) diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c index 0729500eb..05d94fe80 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c @@ -1,5 +1,34 @@ #include "../wifi_marauder_app_i.h" +char* _wifi_marauder_get_prefix_from_cmd(const char* command) { + int end = strcspn(command, " "); + char* prefix = (char*)malloc(sizeof(char) * (end + 1)); + strncpy(prefix, command, end); + prefix[end] = '\0'; + return prefix; +} + +bool _wifi_marauder_is_save_pcaps_enabled(WifiMarauderApp* app) { + if(!app->ok_to_save_pcaps) { + return false; + } + // If it is a script that contains a sniff function + if(app->script != NULL) { + return wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffRaw) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffBeacon) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffDeauth) || + wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffEsp) || + wifi_marauder_script_has_stage( + app->script, WifiMarauderScriptStageTypeSniffPmkid) || + wifi_marauder_script_has_stage(app->script, WifiMarauderScriptStageTypeSniffPwn); + } + // If it is a sniff function + return app->is_command && app->selected_tx_string && + strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0; +} + void wifi_marauder_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) { furi_assert(context); WifiMarauderApp* app = context; @@ -34,23 +63,29 @@ void wifi_marauder_console_output_handle_rx_packets_cb(uint8_t* buf, size_t len, void wifi_marauder_scene_console_output_on_enter(void* context) { WifiMarauderApp* app = context; + // Reset text box and set font TextBox* text_box = app->text_box; - text_box_reset(app->text_box); + text_box_reset(text_box); text_box_set_font(text_box, TextBoxFontText); + + // Set focus on start or end if(app->focus_console_start) { text_box_set_focus(text_box, TextBoxFocusStart); } else { text_box_set_focus(text_box, TextBoxFocusEnd); } + + // Set command-related messages if(app->is_command) { furi_string_reset(app->text_box_store); app->text_box_store_strlen = 0; + // Help message if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) { const char* help_msg = "Marauder companion " WIFI_MARAUDER_APP_VERSION "\n"; furi_string_cat_str(app->text_box_store, help_msg); app->text_box_store_strlen += strlen(help_msg); } - + // Stopscan message if(app->show_stopscan_tip) { const char* help_msg = "Press BACK to send stopscan\n"; furi_string_cat_str(app->text_box_store, help_msg); @@ -58,13 +93,14 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { } } - // Set starting text - for "View Log from end", this will just be what was already in the text box store + // Set starting text text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); + // Set scene state and switch view scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneConsoleOutput, 0); view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput); - // Register callback to receive data + // Register callbacks to receive data wifi_marauder_uart_set_handle_rx_data_cb( app->uart, wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread @@ -73,25 +109,53 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread // Get ready to send command - if(app->is_command && app->selected_tx_string) { + if((app->is_command && app->selected_tx_string) || app->script) { + const char* prefix = + strlen(app->selected_tx_string) > 0 ? + _wifi_marauder_get_prefix_from_cmd(app->selected_tx_string) : // Function name + app->script->name; // Script name + // Create files *before* sending command // (it takes time to iterate through the directory) if(app->ok_to_save_logs) { - app->is_writing_log = true; - wifi_marauder_create_log_file(app); + strcpy( + app->log_file_path, + sequential_file_resolve_path( + app->storage, MARAUDER_APP_FOLDER_LOGS, prefix, "log")); + if(app->log_file_path != NULL) { + if(storage_file_open( + app->log_file, app->log_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + app->is_writing_log = true; + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot open log file"); + } + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot resolve log path"); + } } - // If it is a sniff function, open the pcap file for recording - if(app->ok_to_save_pcaps && - strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0) { - app->is_writing_pcap = true; - wifi_marauder_create_pcap_file(app); + // If it is a sniff function or script, open the pcap file for recording + if(_wifi_marauder_is_save_pcaps_enabled(app)) { + if(sequential_file_open( + app->storage, app->capture_file, MARAUDER_APP_FOLDER_PCAPS, prefix, "pcap")) { + app->is_writing_pcap = true; + } else { + dialog_message_show_storage_error(app->dialogs, "Cannot open pcap file"); + } } // Send command with newline '\n' - wifi_marauder_uart_tx( - (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); - wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + if(app->selected_tx_string) { + wifi_marauder_uart_tx( + (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); + wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + } + + // Run the script if the file with the script has been opened + if(app->script != NULL) { + app->script_worker = wifi_marauder_script_worker_alloc(); + wifi_marauder_script_worker_start(app->script_worker, app->script); + } } } @@ -113,14 +177,18 @@ bool wifi_marauder_scene_console_output_on_event(void* context, SceneManagerEven void wifi_marauder_scene_console_output_on_exit(void* context) { WifiMarauderApp* app = context; + // Automatically stop the scan when exiting view + if(app->is_command) { + wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); + furi_delay_ms(50); + } + // Unregister rx callback wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL); wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL); - // Automatically stop the scan when exiting view - if(app->is_command) { - wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); - } + wifi_marauder_script_worker_free(app->script_worker); + app->script_worker = NULL; app->is_writing_pcap = false; if(app->capture_file && storage_file_is_open(app->capture_file)) { diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c new file mode 100644 index 000000000..8e436fe2b --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c @@ -0,0 +1,83 @@ +#include "../wifi_marauder_app_i.h" + +void wifi_marauder_scene_script_confirm_delete_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + WifiMarauderApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void wifi_marauder_scene_script_confirm_delete_on_enter(void* context) { + WifiMarauderApp* app = context; + + widget_add_button_element( + app->widget, + GuiButtonTypeLeft, + "No", + wifi_marauder_scene_script_confirm_delete_widget_callback, + app); + widget_add_button_element( + app->widget, + GuiButtonTypeRight, + "Yes", + wifi_marauder_scene_script_confirm_delete_widget_callback, + app); + + widget_add_string_element( + app->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "Are you sure?"); + widget_add_text_box_element( + app->widget, + 0, + 12, + 128, + 38, + AlignCenter, + AlignCenter, + "The script will be\npermanently deleted", + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewWidget); +} + +bool wifi_marauder_scene_script_confirm_delete_on_event(void* context, SceneManagerEvent event) { + WifiMarauderApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + // get which button press: "Yes" or "No" + if(event.event == GuiButtonTypeRight) { + // Yes + if(app->script != NULL) { + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + app->script->name); + storage_simply_remove(app->storage, script_path); + wifi_marauder_script_free(app->script); + app->script = NULL; + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_text(message, "Deleted!", 88, 32, AlignCenter, AlignCenter); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 5, 6); + dialog_message_set_buttons(message, NULL, "Ok", NULL); + dialog_message_show(app->dialogs, message); + dialog_message_free(message); + } + } + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + + return consumed; +} + +void wifi_marauder_scene_script_confirm_delete_on_exit(void* context) { + WifiMarauderApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c new file mode 100644 index 000000000..697daba4f --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c @@ -0,0 +1,125 @@ +#include "../wifi_marauder_app_i.h" + +static void wifi_marauder_scene_script_edit_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + WifiMarauderScriptStage* current_stage = app->script->first_stage; + uint32_t stage_index = 0; + + while(current_stage != NULL && stage_index < index) { + current_stage = current_stage->next_stage; + stage_index++; + } + app->script_edit_selected_stage = current_stage; + + if(app->script_edit_selected_stage != NULL) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEdit); + } +} + +static void wifi_marauder_scene_script_edit_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageAdd); +} + +void wifi_marauder_scene_script_edit_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + WifiMarauderScript* script = app->script; + submenu_set_header(submenu, script->name); + + WifiMarauderScriptStage* current_stage = script->first_stage; + int stage_index = 0; + while(current_stage != NULL) { + switch(current_stage->type) { + case WifiMarauderScriptStageTypeScan: + submenu_add_item( + submenu, "Scan", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSelect: + submenu_add_item( + submenu, "Select", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeDeauth: + submenu_add_item( + submenu, "Deauth", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeProbe: + submenu_add_item( + submenu, "Probe", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffRaw: + submenu_add_item( + submenu, "Sniff raw", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + submenu_add_item( + submenu, + "Sniff beacon", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + submenu_add_item( + submenu, + "Sniff deauth", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeSniffEsp: + submenu_add_item( + submenu, "Sniff esp", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + submenu_add_item( + submenu, "Sniff PMKID", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeSniffPwn: + submenu_add_item( + submenu, "Sniff pwn", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeBeaconList: + submenu_add_item( + submenu, "Beacon list", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeBeaconAp: + submenu_add_item( + submenu, "Beacon AP", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + case WifiMarauderScriptStageTypeExec: + submenu_add_item( + submenu, + "Custom command", + stage_index, + wifi_marauder_scene_script_edit_callback, + app); + break; + case WifiMarauderScriptStageTypeDelay: + submenu_add_item( + submenu, "Delay", stage_index, wifi_marauder_scene_script_edit_callback, app); + break; + } + current_stage = current_stage->next_stage; + stage_index++; + } + + submenu_add_item( + submenu, "[+] ADD STAGE", stage_index++, wifi_marauder_scene_script_edit_add_callback, app); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_edit_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_edit_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c new file mode 100644 index 000000000..7a3284caf --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c @@ -0,0 +1,188 @@ +#include "../wifi_marauder_app_i.h" + +static void + wifi_marauder_scene_script_stage_edit_list_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + // Creates new item + WifiMarauderScriptStageListItem* new_item = + (WifiMarauderScriptStageListItem*)malloc(sizeof(WifiMarauderScriptStageListItem)); + new_item->value = malloc(64); + new_item->next_item = NULL; + + if(app->script_stage_edit_first_item == NULL) { + app->script_stage_edit_first_item = new_item; + } else { + WifiMarauderScriptStageListItem* last_item = app->script_stage_edit_first_item; + while(last_item->next_item != NULL) { + last_item = last_item->next_item; + } + last_item->next_item = new_item; + } + + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList, index); + app->user_input_type = WifiMarauderUserInputTypeString; + app->user_input_string_reference = &new_item->value; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); +} + +static void wifi_marauder_scene_script_stage_edit_list_deallocate_items(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + app->script_stage_edit_first_item = NULL; +} + +static void wifi_marauder_scene_script_stage_edit_list_save_strings(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + int array_size = 0; + + // Calculates the required array size + while(current_item != NULL) { + array_size++; + current_item = current_item->next_item; + } + + // Reallocate the array of strings if necessary + if(*app->script_stage_edit_string_count_reference < array_size) { + *app->script_stage_edit_strings_reference = + realloc(*app->script_stage_edit_strings_reference, array_size * sizeof(char*)); + } + + // Fills the array of strings + current_item = app->script_stage_edit_first_item; + int i = 0; + while(current_item != NULL) { + char* current_str = malloc(strlen(current_item->value) + 1); + strncpy(current_str, current_item->value, strlen(current_item->value) + 1); + (*app->script_stage_edit_strings_reference)[i] = current_str; + current_item = current_item->next_item; + i++; + } + + *app->script_stage_edit_string_count_reference = array_size; +} + +static void wifi_marauder_scene_script_stage_edit_list_save_numbers(WifiMarauderApp* app) { + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + int array_size = 0; + + // Calculates the required array size + while(current_item != NULL) { + array_size++; + current_item = current_item->next_item; + } + + // Reallocate the array of integers if necessary + if(*app->script_stage_edit_number_count_reference < array_size) { + *app->script_stage_edit_numbers_reference = + realloc(*app->script_stage_edit_numbers_reference, array_size * sizeof(int)); + } + + // Fills the array of integers + current_item = app->script_stage_edit_first_item; + int i = 0; + while(current_item != NULL) { + (*app->script_stage_edit_numbers_reference)[i] = atoi(current_item->value); + current_item = current_item->next_item; + i++; + } + + *app->script_stage_edit_number_count_reference = array_size; +} + +static void + wifi_marauder_scene_script_stage_edit_list_save_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + if(app->script_stage_edit_strings_reference != NULL && + app->script_stage_edit_string_count_reference != NULL) { + wifi_marauder_scene_script_stage_edit_list_save_strings(app); + } + + if(app->script_stage_edit_numbers_reference != NULL && + app->script_stage_edit_number_count_reference != NULL) { + wifi_marauder_scene_script_stage_edit_list_save_numbers(app); + } + + wifi_marauder_scene_script_stage_edit_list_deallocate_items(app); + scene_manager_previous_scene(app->scene_manager); +} + +static void + wifi_marauder_scene_script_stage_edit_list_clear_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + wifi_marauder_scene_script_stage_edit_list_deallocate_items(app); + + submenu_reset(app->submenu); + submenu_add_item( + app->submenu, + "[+] ADD ITEM", + 99, + wifi_marauder_scene_script_stage_edit_list_add_callback, + app); + submenu_add_item( + app->submenu, + "[*] SAVE ITEMS", + 99, + wifi_marauder_scene_script_stage_edit_list_save_callback, + app); + submenu_add_item( + app->submenu, + "[-] CLEAR LIST", + 99, + wifi_marauder_scene_script_stage_edit_list_clear_callback, + app); +} + +void wifi_marauder_scene_script_stage_edit_list_on_enter(void* context) { + WifiMarauderApp* app = context; + int item_index = 0; + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + + while(current_item != NULL) { + submenu_add_item(app->submenu, current_item->value, item_index++, NULL, app); + current_item = current_item->next_item; + } + submenu_add_item( + app->submenu, + "[+] ADD ITEM", + 99, + wifi_marauder_scene_script_stage_edit_list_add_callback, + app); + submenu_add_item( + app->submenu, + "[*] SAVE ITEMS", + 99, + wifi_marauder_scene_script_stage_edit_list_save_callback, + app); + submenu_add_item( + app->submenu, + "[-] CLEAR LIST", + 99, + wifi_marauder_scene_script_stage_edit_list_clear_callback, + app); + + submenu_set_selected_item( + app->submenu, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEditList)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_stage_edit_list_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_edit_list_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c new file mode 100644 index 000000000..35b374f61 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c @@ -0,0 +1,111 @@ +#include "../wifi_marauder_app_i.h" + +enum SubmenuIndex { + SubmenuIndexRun, + SubmenuIndexSettings, + SubmenuIndexEditStages, + SubmenuIndexSave, + SubmenuIndexDelete +}; + +void wifi_marauder_scene_script_options_save_script(WifiMarauderApp* app) { + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + app->script->name); + wifi_marauder_script_save_json(app->storage, script_path, app->script); + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_text(message, "Saved!", 88, 32, AlignCenter, AlignCenter); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 5, 6); + dialog_message_set_buttons(message, NULL, "Ok", NULL); + dialog_message_show(app->dialogs, message); + dialog_message_free(message); +} + +static void wifi_marauder_scene_script_options_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + switch(index) { + case SubmenuIndexRun: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); + break; + case SubmenuIndexSettings: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptSettings); + break; + case SubmenuIndexEditStages: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptEdit); + break; + case SubmenuIndexSave: + wifi_marauder_scene_script_options_save_script(app); + break; + case SubmenuIndexDelete: + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptConfirmDelete); + break; + } +} + +void wifi_marauder_scene_script_options_on_enter(void* context) { + WifiMarauderApp* app = context; + + // If returning after confirming script deletion + if(app->script == NULL) { + scene_manager_previous_scene(app->scene_manager); + return; + } + + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, app->script->name); + submenu_add_item( + submenu, "[>] RUN", SubmenuIndexRun, wifi_marauder_scene_script_options_callback, app); + submenu_add_item( + submenu, + "[S] SETTINGS", + SubmenuIndexSettings, + wifi_marauder_scene_script_options_callback, + app); + submenu_add_item( + submenu, + "[+] EDIT STAGES", + SubmenuIndexEditStages, + wifi_marauder_scene_script_options_callback, + app); + submenu_add_item( + submenu, "[*] SAVE", SubmenuIndexSave, wifi_marauder_scene_script_options_callback, app); + submenu_add_item( + submenu, + "[X] DELETE", + SubmenuIndexDelete, + wifi_marauder_scene_script_options_callback, + app); + + submenu_set_selected_item( + submenu, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptOptions)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_options_on_event(void* context, SceneManagerEvent event) { + WifiMarauderApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + wifi_marauder_script_free(app->script); + app->script = NULL; + } + + return consumed; +} + +void wifi_marauder_scene_script_options_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c new file mode 100644 index 000000000..bc0746858 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c @@ -0,0 +1,90 @@ +#include "../wifi_marauder_app_i.h" + +static void wifi_marauder_scene_script_select_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + + char script_path[256]; + snprintf( + script_path, + sizeof(script_path), + "%s/%s.json", + MARAUDER_APP_FOLDER_SCRIPTS, + furi_string_get_cstr(app->script_list[index])); + + app->script = wifi_marauder_script_parse_json(app->storage, script_path); + if(app->script) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptOptions); + } +} + +static void wifi_marauder_scene_script_select_add_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect, index); + + app->user_input_type = WifiMarauderUserInputTypeFileName; + app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER_SCRIPTS); + app->user_input_file_extension = strdup("json"); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); +} + +void wifi_marauder_scene_script_select_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + + File* dir_scripts = storage_file_alloc(app->storage); + if(storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS)) { + FileInfo file_info; + char file_path[255]; + app->script_list_count = 0; + // Goes through the files in the folder counting the ones that end with the json extension + while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) { + app->script_list_count++; + } + if(app->script_list_count > 0) { + submenu_set_header(submenu, "Select a script:"); + app->script_list = malloc(app->script_list_count * sizeof(FuriString*)); + storage_dir_close(dir_scripts); + storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS); + // Read the files again from the beginning, adding the scripts in the list + int script_index = 0; + while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) { + app->script_list[script_index] = furi_string_alloc(); + path_extract_filename_no_ext(file_path, app->script_list[script_index]); + submenu_add_item( + submenu, + furi_string_get_cstr(app->script_list[script_index]), + script_index, + wifi_marauder_scene_script_select_callback, + app); + script_index++; + } + } else { + submenu_set_header(submenu, "No script found"); + } + submenu_add_item( + submenu, "[+] ADD SCRIPT", 99, wifi_marauder_scene_script_select_add_callback, app); + storage_dir_close(dir_scripts); + } + storage_file_free(dir_scripts); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSelect)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_select_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); + + for(int i = 0; i < app->script_list_count; i++) { + furi_string_free(app->script_list[i]); + } + free(app->script_list); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c new file mode 100644 index 000000000..b4903af05 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c @@ -0,0 +1,87 @@ +#include "../wifi_marauder_app_i.h" + +enum ScriptSettingsOption { + ScriptSettingsOptionRepeat, + ScriptSettingsOptionSavePcap, + ScriptSettingsOptionEnableLed +}; + +const char* option_values[3] = {"No", "Yes", "Default"}; + +static void wifi_marauder_scene_script_settings_enter_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + // Accept script repeat value + if(index == ScriptSettingsOptionRepeat) { + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptSettings, index); + app->user_input_type = WifiMarauderUserInputTypeNumber; + app->user_input_number_reference = &app->script->repeat; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } +} + +static void wifi_marauder_scene_script_settings_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + uint8_t current_option = variable_item_list_get_selected_item_index(app->var_item_list); + uint8_t option_value_index = variable_item_get_current_value_index(item); + + switch(current_option) { + case ScriptSettingsOptionSavePcap: + variable_item_set_current_value_text(item, option_values[option_value_index]); + app->script->save_pcap = option_value_index; + break; + case ScriptSettingsOptionEnableLed: + variable_item_set_current_value_text(item, option_values[option_value_index]); + app->script->enable_led = option_value_index; + break; + } +} + +void wifi_marauder_scene_script_settings_on_enter(void* context) { + WifiMarauderApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + variable_item_list_set_enter_callback( + app->var_item_list, wifi_marauder_scene_script_settings_enter_callback, app); + + // Script repeat option + VariableItem* repeat_item = variable_item_list_add(app->var_item_list, "Repeat", 1, NULL, app); + char repeat_str[32]; + snprintf(repeat_str, sizeof(repeat_str), "%d", app->script->repeat); + variable_item_set_current_value_text(repeat_item, repeat_str); + + // Save PCAP option + VariableItem* save_pcap_item = variable_item_list_add( + app->var_item_list, + "Save PCAP", + 3, + wifi_marauder_scene_script_settings_change_callback, + app); + variable_item_set_current_value_index(save_pcap_item, app->script->save_pcap); + variable_item_set_current_value_text(save_pcap_item, option_values[app->script->save_pcap]); + + // Enable board LED option + VariableItem* enable_led_item = variable_item_list_add( + app->var_item_list, + "Enable LED", + 3, + wifi_marauder_scene_script_settings_change_callback, + app); + variable_item_set_current_value_index(enable_led_item, app->script->enable_led); + variable_item_set_current_value_text(enable_led_item, option_values[app->script->enable_led]); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptSettings)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList); +} + +bool wifi_marauder_scene_script_settings_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_settings_on_exit(void* context) { + WifiMarauderApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c new file mode 100644 index 000000000..33f1a2f03 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c @@ -0,0 +1,297 @@ +#include "../wifi_marauder_app_i.h" + +// Scan +static void wifi_marauder_scene_script_stage_add_scan_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageScan* stage = + (WifiMarauderScriptStageScan*)malloc(sizeof(WifiMarauderScriptStageScan)); + stage->type = WifiMarauderScriptScanTypeAp; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeScan, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Select +static void wifi_marauder_scene_script_stage_add_select_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSelect* stage = + (WifiMarauderScriptStageSelect*)malloc(sizeof(WifiMarauderScriptStageSelect)); + stage->type = WifiMarauderScriptSelectTypeAp; + stage->filter = strdup("all"); + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSelect, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Deauth +static void wifi_marauder_scene_script_stage_add_deauth_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageDeauth* stage = + (WifiMarauderScriptStageDeauth*)malloc(sizeof(WifiMarauderScriptStageDeauth)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeDeauth, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Probe +static void wifi_marauder_scene_script_stage_add_probe_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageProbe* stage = + (WifiMarauderScriptStageProbe*)malloc(sizeof(WifiMarauderScriptStageProbe)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeProbe, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff RAW +static void wifi_marauder_scene_script_stage_add_sniffraw_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffRaw* stage = + (WifiMarauderScriptStageSniffRaw*)malloc(sizeof(WifiMarauderScriptStageSniffRaw)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffRaw, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Beacon +static void + wifi_marauder_scene_script_stage_add_sniffbeacon_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffBeacon* stage = + (WifiMarauderScriptStageSniffBeacon*)malloc(sizeof(WifiMarauderScriptStageSniffBeacon)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffBeacon, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Deauth +static void + wifi_marauder_scene_script_stage_add_sniffdeauth_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffDeauth* stage = + (WifiMarauderScriptStageSniffDeauth*)malloc(sizeof(WifiMarauderScriptStageSniffDeauth)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffDeauth, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Esp +static void wifi_marauder_scene_script_stage_add_sniffesp_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffEsp* stage = + (WifiMarauderScriptStageSniffEsp*)malloc(sizeof(WifiMarauderScriptStageSniffEsp)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffEsp, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff PMKID +static void + wifi_marauder_scene_script_stage_add_sniffpmkid_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffPmkid* stage = + (WifiMarauderScriptStageSniffPmkid*)malloc(sizeof(WifiMarauderScriptStageSniffPmkid)); + stage->channel = 0; + stage->force_deauth = WifiMarauderScriptBooleanTrue; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPmkid, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Sniff Pwn +static void wifi_marauder_scene_script_stage_add_sniffpwn_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageSniffPwn* stage = + (WifiMarauderScriptStageSniffPwn*)malloc(sizeof(WifiMarauderScriptStageSniffPwn)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeSniffPwn, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Beacon list +static void + wifi_marauder_scene_script_stage_add_beaconlist_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageBeaconList* stage = + (WifiMarauderScriptStageBeaconList*)malloc(sizeof(WifiMarauderScriptStageBeaconList)); + stage->ssids = NULL; + stage->ssid_count = 0; + stage->random_ssids = 0; + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconList, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Beacon AP +static void wifi_marauder_scene_script_stage_add_beaconap_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageBeaconAp* stage = + (WifiMarauderScriptStageBeaconAp*)malloc(sizeof(WifiMarauderScriptStageBeaconAp)); + stage->timeout = WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeBeaconAp, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Exec +static void wifi_marauder_scene_script_stage_add_exec_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageExec* stage = + (WifiMarauderScriptStageExec*)malloc(sizeof(WifiMarauderScriptStageExec)); + stage->command = NULL; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeExec, stage); + scene_manager_previous_scene(app->scene_manager); +} + +// Delay +static void wifi_marauder_scene_script_stage_add_delay_callback(void* context, uint32_t index) { + UNUSED(index); + WifiMarauderApp* app = context; + + WifiMarauderScriptStageDelay* stage = + (WifiMarauderScriptStageDelay*)malloc(sizeof(WifiMarauderScriptStageDelay)); + stage->timeout = 0; + + wifi_marauder_script_add_stage(app->script, WifiMarauderScriptStageTypeDelay, stage); + scene_manager_previous_scene(app->scene_manager); +} + +void wifi_marauder_scene_script_stage_add_on_enter(void* context) { + WifiMarauderApp* app = context; + Submenu* submenu = app->submenu; + submenu_set_header(submenu, "Add stage"); + + int menu_index = 0; + submenu_add_item( + submenu, "[+] Scan", menu_index++, wifi_marauder_scene_script_stage_add_scan_callback, app); + submenu_add_item( + submenu, + "[+] Select", + menu_index++, + wifi_marauder_scene_script_stage_add_select_callback, + app); + submenu_add_item( + submenu, + "[+] Deauth", + menu_index++, + wifi_marauder_scene_script_stage_add_deauth_callback, + app); + submenu_add_item( + submenu, + "[+] Probe", + menu_index++, + wifi_marauder_scene_script_stage_add_probe_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff RAW", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffraw_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Beacon", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffbeacon_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Deauth", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffdeauth_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Esp", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffesp_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff PMKID", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffpmkid_callback, + app); + submenu_add_item( + submenu, + "[+] Sniff Pwnagotchi", + menu_index++, + wifi_marauder_scene_script_stage_add_sniffpwn_callback, + app); + submenu_add_item( + submenu, + "[+] Beacon List", + menu_index++, + wifi_marauder_scene_script_stage_add_beaconlist_callback, + app); + submenu_add_item( + submenu, + "[+] Beacon AP", + menu_index++, + wifi_marauder_scene_script_stage_add_beaconap_callback, + app); + submenu_add_item( + submenu, + "[+] Custom command", + menu_index++, + wifi_marauder_scene_script_stage_add_exec_callback, + app); + submenu_add_item( + submenu, + "[+] Delay", + menu_index++, + wifi_marauder_scene_script_stage_add_delay_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); +} + +bool wifi_marauder_scene_script_stage_add_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_add_on_exit(void* context) { + WifiMarauderApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c new file mode 100644 index 000000000..b8581e3e7 --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c @@ -0,0 +1,203 @@ +#include "../wifi_marauder_app_i.h" + +void wifi_marauder_scene_script_stage_edit_create_list_strings( + WifiMarauderApp* app, + char** strings, + int string_count) { + // Deallocates the existing list + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + + // Create a new list with numbers + WifiMarauderScriptStageListItem* first_item = NULL; + WifiMarauderScriptStageListItem* previous_item = NULL; + for(int i = 0; i < string_count; i++) { + WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem)); + item->value = strdup(strings[i]); + item->next_item = NULL; + + if(previous_item == NULL) { + first_item = item; + } else { + previous_item->next_item = item; + } + previous_item = item; + } + + app->script_stage_edit_first_item = first_item; +} + +void wifi_marauder_scene_script_stage_edit_create_list_numbers( + WifiMarauderApp* app, + int* numbers, + int number_count) { + // Deallocates the existing list + WifiMarauderScriptStageListItem* current_item = app->script_stage_edit_first_item; + while(current_item != NULL) { + WifiMarauderScriptStageListItem* next_item = current_item->next_item; + free(current_item->value); + free(current_item); + current_item = next_item; + } + + // Create a new list with numbers + WifiMarauderScriptStageListItem* first_item = NULL; + WifiMarauderScriptStageListItem* previous_item = NULL; + for(int i = 0; i < number_count; i++) { + char number_str[32]; + snprintf(number_str, sizeof(number_str), "%d", numbers[i]); + + WifiMarauderScriptStageListItem* item = malloc(sizeof(WifiMarauderScriptStageListItem)); + item->value = strdup(number_str); + item->next_item = NULL; + + if(previous_item == NULL) { + first_item = item; + } else { + previous_item->next_item = item; + } + previous_item = item; + } + + app->script_stage_edit_first_item = first_item; +} + +static void + wifi_marauder_scene_script_stage_edit_list_enter_callback(void* context, uint32_t index) { + WifiMarauderApp* app = context; + const WifiMarauderScriptMenuItem* menu_item = &app->script_stage_menu->items[index]; + + // Fixed delete item + if(index == app->script_stage_menu->num_items) { + uint32_t deleted_stage_index = + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptEdit); + if(deleted_stage_index > 0) { + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneScriptEdit, deleted_stage_index - 1); + } + WifiMarauderScriptStage* previous_stage = NULL; + WifiMarauderScriptStage* current_stage = app->script->first_stage; + uint32_t current_stage_index = 0; + + while(current_stage != NULL && current_stage_index < deleted_stage_index) { + previous_stage = current_stage; + current_stage = current_stage->next_stage; + current_stage_index++; + } + + // Delete the stage + if(current_stage != NULL) { + if(previous_stage != NULL) { + if(current_stage->next_stage != NULL) { + previous_stage->next_stage = current_stage->next_stage; + } else { + previous_stage->next_stage = NULL; + app->script->last_stage = previous_stage; + } + } else { + if(current_stage->next_stage != NULL) { + app->script->first_stage = current_stage->next_stage; + } else { + app->script->first_stage = NULL; + app->script->last_stage = NULL; + } + } + } + app->script_edit_selected_stage = NULL; + + scene_manager_previous_scene(app->scene_manager); + return; + } + + if(menu_item->select_callback == NULL) { + return; + } + if(menu_item->type == WifiMarauderScriptMenuItemTypeNumber) { + // Accepts user number input, assigning the value to the reference passed as a parameter + menu_item->select_callback(app); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + app->user_input_type = WifiMarauderUserInputTypeNumber; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeString) { + // Accepts user string input, assigning the value to the reference passed as a parameter + menu_item->select_callback(app); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + app->user_input_type = WifiMarauderUserInputTypeString; + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneUserInput); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeListString) { + // Accepts the strings that compose the list + menu_item->select_callback(app); + wifi_marauder_scene_script_stage_edit_create_list_strings( + app, + *app->script_stage_edit_strings_reference, + *app->script_stage_edit_string_count_reference); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList); + } else if(menu_item->type == WifiMarauderScriptMenuItemTypeListNumber) { + // Accepts the numbers that compose the list + menu_item->select_callback(app); + wifi_marauder_scene_script_stage_edit_create_list_numbers( + app, + *app->script_stage_edit_numbers_reference, + *app->script_stage_edit_number_count_reference); + scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit, index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptStageEditList); + } +} + +void wifi_marauder_scene_script_stage_edit_on_enter(void* context) { + WifiMarauderApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + + variable_item_list_set_enter_callback( + app->var_item_list, wifi_marauder_scene_script_stage_edit_list_enter_callback, app); + app->script_stage_menu = + wifi_marauder_script_stage_menu_create(app->script_edit_selected_stage->type); + + if(app->script_stage_menu->items != NULL) { + for(uint32_t i = 0; i < app->script_stage_menu->num_items; i++) { + WifiMarauderScriptMenuItem* stage_item = &app->script_stage_menu->items[i]; + + // Changes the list item to handle it in callbacks + VariableItem* list_item = variable_item_list_add( + app->var_item_list, + stage_item->name, + stage_item->num_options, + stage_item->change_callback, + app); + + variable_item_list_set_selected_item(app->var_item_list, i); + if(stage_item->setup_callback != NULL) { + stage_item->setup_callback(list_item); + } + if(stage_item->change_callback != NULL) { + stage_item->change_callback(list_item); + } + } + } + + variable_item_list_add(app->var_item_list, "[-] DELETE STAGE", 0, NULL, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneScriptStageEdit)); + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList); +} + +bool wifi_marauder_scene_script_stage_edit_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_script_stage_edit_on_exit(void* context) { + WifiMarauderApp* app = context; + wifi_marauder_script_stage_menu_free(app->script_stage_menu); + app->script_stage_menu = NULL; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c index 2b2ee3a8a..c77543fd0 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c @@ -127,6 +127,7 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { {"Update", {"ota", "sd"}, 2, {"update -w", "update -s"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Reboot", {""}, 1, {"reboot"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, + {"Scripts", {""}, 1, {""}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Save to flipper sdcard", // keep as last entry or change logic in callback below {""}, 1, @@ -143,13 +144,6 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin furi_assert(index < NUM_MENU_ITEMS); const WifiMarauderItem* item = &items[index]; - if(index == NUM_MENU_ITEMS - 1) { - // "Save to flipper sdcard" special case - start SettingsInit widget - view_dispatcher_send_custom_event( - app->view_dispatcher, WifiMarauderEventStartSettingsInit); - return; - } - const int selected_option_index = app->selected_option_index[index]; furi_assert(selected_option_index < item->num_options_menu); app->selected_tx_string = item->actual_commands[selected_option_index]; @@ -167,6 +161,20 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin return; } + // Select automation script + if(index == NUM_MENU_ITEMS - 2) { + view_dispatcher_send_custom_event( + app->view_dispatcher, WifiMarauderEventStartScriptSelect); + return; + } + + if(index == NUM_MENU_ITEMS - 1) { + // "Save to flipper sdcard" special case - start SettingsInit widget + view_dispatcher_send_custom_event( + app->view_dispatcher, WifiMarauderEventStartSettingsInit); + return; + } + bool needs_keyboard = (item->needs_keyboard == TOGGLE_ARGS) ? (selected_option_index != 0) : item->needs_keyboard; if(needs_keyboard) { @@ -242,6 +250,10 @@ bool wifi_marauder_scene_start_on_event(void* context, SceneManagerEvent event) scene_manager_set_scene_state( app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); scene_manager_next_scene(app->scene_manager, WifiMarauderSceneLogViewer); + } else if(event.event == WifiMarauderEventStartScriptSelect) { + scene_manager_set_scene_state( + app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneScriptSelect); } consumed = true; } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c new file mode 100644 index 000000000..3d5697caf --- /dev/null +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c @@ -0,0 +1,155 @@ +#include "../wifi_marauder_app_i.h" + +bool wifi_marauder_scene_user_input_validator_number_callback( + const char* text, + FuriString* error, + void* context) { + UNUSED(context); + for(int i = 0; text[i] != '\0'; i++) { + if(text[i] < '0' || text[i] > '9') { + furi_string_printf(error, "This is not\na valid\nnumber!"); + return false; + } + } + return true; +} + +bool wifi_marauder_scene_user_input_validator_file_callback( + const char* text, + FuriString* error, + void* context) { + UNUSED(context); + if(strlen(text) == 0) { + furi_string_printf(error, "File name\ncannot be\nblank!"); + return false; + } + return true; +} + +void wifi_marauder_scene_user_input_ok_callback(void* context) { + WifiMarauderApp* app = context; + + File* file = NULL; + char* file_path = NULL; + + switch(app->user_input_type) { + // Writes the string value of the reference + case WifiMarauderUserInputTypeString: + if(app->user_input_string_reference != NULL) { + strncpy( + *app->user_input_string_reference, + app->text_input_store, + strlen(app->text_input_store) + 1); + app->user_input_string_reference = NULL; + } + break; + // Writes the numerical value of the reference + case WifiMarauderUserInputTypeNumber: + if(app->user_input_number_reference != NULL) { + *app->user_input_number_reference = atoi(app->text_input_store); + app->user_input_number_reference = NULL; + } + break; + // Creates a file with the name entered by the user, if it does not exist + case WifiMarauderUserInputTypeFileName: + file = storage_file_alloc(app->storage); + // Use application directory if not specified + if(app->user_input_file_dir == NULL) { + app->user_input_file_dir = strdup(MARAUDER_APP_FOLDER); + } + if(app->user_input_file_extension != NULL) { + size_t file_path_len = strlen(app->user_input_file_dir) + + strlen(app->text_input_store) + + strlen(app->user_input_file_extension) + 3; + file_path = (char*)malloc(file_path_len); + snprintf( + file_path, + file_path_len, + "%s/%s.%s", + app->user_input_file_dir, + app->text_input_store, + app->user_input_file_extension); + } else { + size_t file_path_len = + strlen(app->user_input_file_dir) + strlen(app->text_input_store) + 2; + file_path = (char*)malloc(file_path_len); + snprintf( + file_path, file_path_len, "%s/%s", app->user_input_file_dir, app->text_input_store); + } + if(storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_NEW)) { + storage_file_close(file); + } + // Free memory + free(app->user_input_file_dir); + app->user_input_file_dir = NULL; + free(app->user_input_file_extension); + app->user_input_file_extension = NULL; + free(file_path); + storage_file_free(file); + break; + default: + break; + } + + scene_manager_previous_scene(app->scene_manager); +} + +void wifi_marauder_scene_user_input_on_enter(void* context) { + WifiMarauderApp* app = context; + + switch(app->user_input_type) { + // Loads the string value of the reference + case WifiMarauderUserInputTypeString: + wifi_text_input_set_header_text(app->text_input, "Enter value:"); + wifi_text_input_set_validator(app->text_input, NULL, app); + if(app->user_input_string_reference != NULL) { + strncpy( + app->text_input_store, + *app->user_input_string_reference, + strlen(*app->user_input_string_reference) + 1); + } + break; + // Loads the numerical value of the reference + case WifiMarauderUserInputTypeNumber: + wifi_text_input_set_header_text(app->text_input, "Enter a valid number:"); + wifi_text_input_set_validator( + app->text_input, wifi_marauder_scene_user_input_validator_number_callback, app); + if(app->user_input_number_reference != NULL) { + char number_str[32]; + snprintf(number_str, sizeof(number_str), "%d", *app->user_input_number_reference); + strncpy(app->text_input_store, number_str, strlen(number_str) + 1); + } + break; + // File name + case WifiMarauderUserInputTypeFileName: + wifi_text_input_set_header_text(app->text_input, "Enter file name:"); + wifi_text_input_set_validator( + app->text_input, wifi_marauder_scene_user_input_validator_file_callback, app); + break; + default: + scene_manager_previous_scene(app->scene_manager); + return; + } + + wifi_text_input_set_result_callback( + app->text_input, + wifi_marauder_scene_user_input_ok_callback, + app, + app->text_input_store, + WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE, + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewTextInput); +} + +bool wifi_marauder_scene_user_input_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void wifi_marauder_scene_user_input_on_exit(void* context) { + WifiMarauderApp* app = context; + memset(app->text_input_store, 0, sizeof(app->text_input_store)); + wifi_text_input_reset(app->text_input); +} diff --git a/applications/external/wifi_marauder_companion/script/cJSON.c b/applications/external/wifi_marauder_companion/script/cJSON.c new file mode 100644 index 000000000..06341fe38 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/cJSON.c @@ -0,0 +1,2743 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning(push) +/* disable warning about single line comments in system headers */ +#pragma warning(disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0 / 0.0 +#endif +#endif + +typedef struct { + const unsigned char* json; + size_t position; +} error; +static error global_error = {NULL, 0}; + +CJSON_PUBLIC(const char*) cJSON_GetErrorPtr(void) { + return (const char*)(global_error.json + global_error.position); +} + +CJSON_PUBLIC(char*) cJSON_GetStringValue(const cJSON* const item) { + if(!cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON* const item) { + if(!cJSON_IsNumber(item)) { + return (double)NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if(CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15) +#error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) { + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char* string1, const unsigned char* string2) { + if((string1 == NULL) || (string2 == NULL)) { + return 1; + } + + if(string1 == string2) { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { + if(*string1 == '\0') { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks { + void*(CJSON_CDECL* allocate)(size_t size); + void(CJSON_CDECL* deallocate)(void* pointer); + void*(CJSON_CDECL* reallocate)(void* pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void* CJSON_CDECL internal_malloc(size_t size) { + return malloc(size); +} +static void CJSON_CDECL internal_free(void* pointer) { + free(pointer); +} +static void* CJSON_CDECL internal_realloc(void* pointer, size_t size) { + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = {internal_malloc, internal_free, internal_realloc}; + +static unsigned char* + cJSON_strdup(const unsigned char* string, const internal_hooks* const hooks) { + size_t length = 0; + unsigned char* copy = NULL; + + if(string == NULL) { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if(copy == NULL) { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) { + if(hooks == NULL) { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if(hooks->malloc_fn != NULL) { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if(hooks->free_fn != NULL) { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON* cJSON_New_Item(const internal_hooks* const hooks) { + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if(node) { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON* item) { + cJSON* next = NULL; + while(item != NULL) { + next = item->next; + if(!(item->type & cJSON_IsReference) && (item->child != NULL)) { + cJSON_Delete(item->child); + } + if(!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { + global_hooks.deallocate(item->valuestring); + } + if(!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) { +#ifdef ENABLE_LOCALES + struct lconv* lconv = localeconv(); + return (unsigned char)lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct { + const unsigned char* content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) \ + ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) \ + ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Converts an array of characters to double. Alternative implementation of strtod() */ +double string_to_double(const char* str, char** endptr) { + double result = 0.0; + int sign = 1; + const char* p = str; + + while(isspace((unsigned char)*p)) p++; + + if(*p == '-') { + sign = -1; + p++; + } else if(*p == '+') { + p++; + } + + while(isdigit((unsigned char)*p)) { + result = result * (double)(10) + ((double)(*p - '0')); + p++; + } + + if(*p == '.') { + double fraction = 0.1; + p++; + + while(isdigit((unsigned char)p[0])) { + fraction *= 0.1L; + result += (p++[0] - '0') * fraction; + } + } + + if(*p == 'e' || *p == 'E') { + int exponent = 0; + int exp_sign = 1; + p++; + + if(*p == '-') { + exp_sign = -1; + p++; + } else if(*p == '+') { + p++; + } + + while(isdigit((unsigned char)*p)) { + exponent = exponent * 10 + (*p - '0'); + p++; + } + + exponent *= exp_sign; + result *= pow(10, exponent); + } + + *endptr = (char*)p; + + return sign * result; +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON* const item, parse_buffer* const input_buffer) { + double number = 0; + unsigned char* after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for string_to_double) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for(i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) { + switch(buffer_at_offset(input_buffer)[i]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = string_to_double((const char*)number_c_string, (char**)&after_end); + if(number_c_string == after_end) { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if(number >= INT_MAX) { + item->valueint = INT_MAX; + } else if(number <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON* object, double number) { + if(number >= INT_MAX) { + object->valueint = INT_MAX; + } else if(number <= (double)INT_MIN) { + object->valueint = INT_MIN; + } else { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON* object, const char* valuestring) { + char* copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if(!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { + return NULL; + } + if(strlen(valuestring) <= strlen(object->valuestring)) { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*)cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if(copy == NULL) { + return NULL; + } + if(object->valuestring != NULL) { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct { + unsigned char* buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer* const p, size_t needed) { + unsigned char* newbuffer = NULL; + size_t newsize = 0; + + if((p == NULL) || (p->buffer == NULL)) { + return NULL; + } + + if((p->length > 0) && (p->offset >= p->length)) { + /* make sure that offset is valid */ + return NULL; + } + + if(needed > INT_MAX) { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if(needed <= p->length) { + return p->buffer + p->offset; + } + + if(p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if(needed > (INT_MAX / 2)) { + /* overflow of int, use INT_MAX if possible */ + if(needed <= INT_MAX) { + newsize = INT_MAX; + } else { + return NULL; + } + } else { + newsize = needed * 2; + } + + if(p->hooks.reallocate != NULL) { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if(newbuffer == NULL) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } else { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if(!newbuffer) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy(newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer* const buffer) { + const unsigned char* buffer_pointer = NULL; + if((buffer == NULL) || (buffer->buffer == NULL)) { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) { + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if(output_buffer == NULL) { + return false; + } + + /* This checks for NaN and Infinity */ + if(isnan(d) || isinf(d)) { + length = snprintf((char*)number_buffer, sizeof(number_buffer), "null"); + } else { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.15g", d); + + /* Check whether the original double can be recovered */ + if((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { + /* If not, print with 17 decimal places of precision */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if(output_pointer == NULL) { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for(i = 0; i < ((size_t)length); i++) { + if(number_buffer[i] == decimal_point) { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char* const input) { + unsigned int h = 0; + size_t i = 0; + + for(i = 0; i < 4; i++) { + /* parse digit */ + if((input[i] >= '0') && (input[i] <= '9')) { + h += (unsigned int)input[i] - '0'; + } else if((input[i] >= 'A') && (input[i] <= 'F')) { + h += (unsigned int)10 + input[i] - 'A'; + } else if((input[i] >= 'a') && (input[i] <= 'f')) { + h += (unsigned int)10 + input[i] - 'a'; + } else /* invalid */ + { + return 0; + } + + if(i < 3) { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8( + const unsigned char* const input_pointer, + const unsigned char* const input_end, + unsigned char** output_pointer) { + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char* first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if((input_end - first_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if(((first_code >= 0xDC00) && (first_code <= 0xDFFF))) { + goto fail; + } + + /* UTF16 surrogate pair */ + if((first_code >= 0xD800) && (first_code <= 0xDBFF)) { + const unsigned char* second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if((input_end - second_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + if((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if((second_code < 0xDC00) || (second_code > 0xDFFF)) { + /* invalid second half of the surrogate pair */ + goto fail; + } + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } else { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if(codepoint < 0x80) { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } else if(codepoint < 0x800) { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } else if(codepoint < 0x10000) { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } else if(codepoint <= 0x10FFFF) { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } else { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for(utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if(utf8_length > 1) { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } else { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON* const item, parse_buffer* const input_buffer) { + const unsigned char* input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char* input_end = buffer_at_offset(input_buffer) + 1; + unsigned char* output_pointer = NULL; + unsigned char* output = NULL; + + /* not a string */ + if(buffer_at_offset(input_buffer)[0] != '\"') { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while(((size_t)(input_end - input_buffer->content) < input_buffer->length) && + (*input_end != '\"')) { + /* is escape sequence */ + if(input_end[0] == '\\') { + if((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if(((size_t)(input_end - input_buffer->content) >= input_buffer->length) || + (*input_end != '\"')) { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if(output == NULL) { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while(input_pointer < input_end) { + if(*input_pointer != '\\') { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else { + unsigned char sequence_length = 2; + if((input_end - input_pointer) < 1) { + goto fail; + } + + switch(input_pointer[1]) { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if(sequence_length == 0) { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t)(input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if(output != NULL) { + input_buffer->hooks.deallocate(output); + } + + if(input_pointer != NULL) { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool + print_string_ptr(const unsigned char* const input, printbuffer* const output_buffer) { + const unsigned char* input_pointer = NULL; + unsigned char* output = NULL; + unsigned char* output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if(output_buffer == NULL) { + return false; + } + + /* empty string */ + if(input == NULL) { + output = ensure(output_buffer, sizeof("\"\"")); + if(output == NULL) { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for(input_pointer = input; *input_pointer; input_pointer++) { + switch(*input_pointer) { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if(*input_pointer < 32) { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if(output == NULL) { + return false; + } + + /* no characters have to be escaped */ + if(escape_characters == 0) { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for(input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { + if((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { + /* normal character, copy */ + *output_pointer = *input_pointer; + } else { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch(*input_pointer) { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + snprintf((char*)output_pointer, 6, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON* const item, printbuffer* const p) { + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_value(const cJSON* const item, printbuffer* const output_buffer); +static cJSON_bool parse_array(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_array(const cJSON* const item, printbuffer* const output_buffer); +static cJSON_bool parse_object(cJSON* const item, parse_buffer* const input_buffer); +static cJSON_bool print_object(const cJSON* const item, printbuffer* const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer* buffer_skip_whitespace(parse_buffer* const buffer) { + if((buffer == NULL) || (buffer->content == NULL)) { + return NULL; + } + + if(cannot_access_at_index(buffer, 0)) { + return buffer; + } + + while(can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { + buffer->offset++; + } + + if(buffer->offset == buffer->length) { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer* skip_utf8_bom(parse_buffer* const buffer) { + if((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) { + return NULL; + } + + if(can_access_at_index(buffer, 4) && + (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithOpts( + const char* value, + const char** return_parse_end, + cJSON_bool require_null_terminated) { + size_t buffer_length; + + if(NULL == value) { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts( + value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithLengthOpts( + const char* value, + size_t buffer_length, + const char** return_parse_end, + cJSON_bool require_null_terminated) { + parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; + cJSON* item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if(value == NULL || 0 == buffer_length) { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if(item == NULL) /* memory fail */ + { + goto fail; + } + + if(!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if(require_null_terminated) { + buffer_skip_whitespace(&buffer); + if((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { + goto fail; + } + } + if(return_parse_end) { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if(item != NULL) { + cJSON_Delete(item); + } + + if(value != NULL) { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if(buffer.offset < buffer.length) { + local_error.position = buffer.offset; + } else if(buffer.length > 0) { + local_error.position = buffer.length - 1; + } + + if(return_parse_end != NULL) { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON*) cJSON_Parse(const char* value) { + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON*) cJSON_ParseWithLength(const char* value, size_t buffer_length) { + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char* + print(const cJSON* const item, cJSON_bool format, const internal_hooks* const hooks) { + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char* printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*)hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if(buffer->buffer == NULL) { + goto fail; + } + + /* print the value */ + if(!print_value(item, buffer)) { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if(hooks->reallocate != NULL) { + printed = (unsigned char*)hooks->reallocate(buffer->buffer, buffer->offset + 1); + if(printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*)hooks->allocate(buffer->offset + 1); + if(printed == NULL) { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if(buffer->buffer != NULL) { + hooks->deallocate(buffer->buffer); + } + + if(printed != NULL) { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char*) cJSON_Print(const cJSON* item) { + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char*) cJSON_PrintUnformatted(const cJSON* item) { + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char*) cJSON_PrintBuffered(const cJSON* item, int prebuffer, cJSON_bool fmt) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; + + if(prebuffer < 0) { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if(!p.buffer) { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if(!print_value(item, &p)) { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_PrintPreallocated(cJSON* item, char* buffer, const int length, const cJSON_bool format) { + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; + + if((length < 0) || (buffer == NULL)) { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON* const item, parse_buffer* const input_buffer) { + if((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if(can_read(input_buffer, 4) && + (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if(can_read(input_buffer, 5) && + (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if(can_read(input_buffer, 4) && + (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { + return parse_string(item, input_buffer); + } + /* number */ + if(can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || + ((buffer_at_offset(input_buffer)[0] >= '0') && + (buffer_at_offset(input_buffer)[0] <= '9')))) { + return parse_number(item, input_buffer); + } + /* array */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { + return parse_array(item, input_buffer); + } + /* object */ + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output = NULL; + + if((item == NULL) || (output_buffer == NULL)) { + return false; + } + + switch((item->type) & 0xFF) { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if(output == NULL) { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if(output == NULL) { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if(output == NULL) { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: { + size_t raw_length = 0; + if(item->valuestring == NULL) { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if(output == NULL) { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON* const item, parse_buffer* const input_buffer) { + cJSON* head = NULL; /* head of the linked list */ + cJSON* current_item = NULL; + + if(input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if(buffer_at_offset(input_buffer)[0] != '[') { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if(cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON* new_item = cJSON_New_Item(&(input_buffer->hooks)); + if(new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if(head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if(cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if(head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if(head != NULL) { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + size_t length = 0; + cJSON* current_element = item->child; + + if(output_buffer == NULL) { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if(output_pointer == NULL) { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while(current_element != NULL) { + if(!print_value(current_element, output_buffer)) { + return false; + } + update_offset(output_buffer); + if(current_element->next) { + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON* const item, parse_buffer* const input_buffer) { + cJSON* head = NULL; /* linked list head */ + cJSON* current_item = NULL; + + if(input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if(cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON* new_item = cJSON_New_Item(&(input_buffer->hooks)); + if(new_item == NULL) { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if(head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_string(current_item, input_buffer)) { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if(!parse_value(current_item, input_buffer)) { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while(can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if(cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if(head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if(head != NULL) { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON* const item, printbuffer* const output_buffer) { + unsigned char* output_pointer = NULL; + size_t length = 0; + cJSON* current_item = item->child; + + if(output_buffer == NULL) { + return false; + } + + /* Compose the output: */ + length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if(output_buffer->format) { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while(current_item) { + if(output_buffer->format) { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if(output_pointer == NULL) { + return false; + } + for(i = 0; i < output_buffer->depth; i++) { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if(!print_string_ptr((unsigned char*)current_item->string, output_buffer)) { + return false; + } + update_offset(output_buffer); + + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if(output_pointer == NULL) { + return false; + } + *output_pointer++ = ':'; + if(output_buffer->format) { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if(!print_value(current_item, output_buffer)) { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if(output_pointer == NULL) { + return false; + } + if(current_item->next) { + *output_pointer++ = ','; + } + + if(output_buffer->format) { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if(output_pointer == NULL) { + return false; + } + if(output_buffer->format) { + size_t i; + for(i = 0; i < (output_buffer->depth - 1); i++) { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON* array) { + cJSON* child = NULL; + size_t size = 0; + + if(array == NULL) { + return 0; + } + + child = array->child; + + while(child != NULL) { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON* array, size_t index) { + cJSON* current_child = NULL; + + if(array == NULL) { + return NULL; + } + + current_child = array->child; + while((current_child != NULL) && (index > 0)) { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON*) cJSON_GetArrayItem(const cJSON* array, int index) { + if(index < 0) { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON* get_object_item( + const cJSON* const object, + const char* const name, + const cJSON_bool case_sensitive) { + cJSON* current_element = NULL; + + if((object == NULL) || (name == NULL)) { + return NULL; + } + + current_element = object->child; + if(case_sensitive) { + while((current_element != NULL) && (current_element->string != NULL) && + (strcmp(name, current_element->string) != 0)) { + current_element = current_element->next; + } + } else { + while((current_element != NULL) && + (case_insensitive_strcmp( + (const unsigned char*)name, (const unsigned char*)(current_element->string)) != + 0)) { + current_element = current_element->next; + } + } + + if((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON*) cJSON_GetObjectItem(const cJSON* const object, const char* const string) { + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON*) +cJSON_GetObjectItemCaseSensitive(const cJSON* const object, const char* const string) { + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON* object, const char* string) { + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON* prev, cJSON* item) { + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON* create_reference(const cJSON* item, const internal_hooks* const hooks) { + cJSON* reference = NULL; + if(item == NULL) { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if(reference == NULL) { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON* array, cJSON* item) { + cJSON* child = NULL; + + if((item == NULL) || (array == NULL) || (array == item)) { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if(child == NULL) { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } else { + /* append to the end */ + if(child->prev) { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON* array, cJSON* item) { + return add_item_to_array(array, item); +} + +#if defined(__clang__) || \ + (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) { + return (void*)string; +} +#if defined(__clang__) || \ + (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic pop +#endif + +static cJSON_bool add_item_to_object( + cJSON* const object, + const char* const string, + cJSON* const item, + const internal_hooks* const hooks, + const cJSON_bool constant_key) { + char* new_key = NULL; + int new_type = cJSON_Invalid; + + if((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) { + return false; + } + + if(constant_key) { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } else { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if(new_key == NULL) { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if(!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item) { + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item) { + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item) { + if(array == NULL) { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item) { + if((object == NULL) || (string == NULL)) { + return false; + } + + return add_item_to_object( + object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON* const object, const char* const name) { + cJSON* null = cJSON_CreateNull(); + if(add_item_to_object(object, name, null, &global_hooks, false)) { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON* const object, const char* const name) { + cJSON* true_item = cJSON_CreateTrue(); + if(add_item_to_object(object, name, true_item, &global_hooks, false)) { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON* const object, const char* const name) { + cJSON* false_item = cJSON_CreateFalse(); + if(add_item_to_object(object, name, false_item, &global_hooks, false)) { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddBoolToObject(cJSON* const object, const char* const name, const cJSON_bool boolean) { + cJSON* bool_item = cJSON_CreateBool(boolean); + if(add_item_to_object(object, name, bool_item, &global_hooks, false)) { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddNumberToObject(cJSON* const object, const char* const name, const double number) { + cJSON* number_item = cJSON_CreateNumber(number); + if(add_item_to_object(object, name, number_item, &global_hooks, false)) { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddStringToObject(cJSON* const object, const char* const name, const char* const string) { + cJSON* string_item = cJSON_CreateString(string); + if(add_item_to_object(object, name, string_item, &global_hooks, false)) { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) +cJSON_AddRawToObject(cJSON* const object, const char* const name, const char* const raw) { + cJSON* raw_item = cJSON_CreateRaw(raw); + if(add_item_to_object(object, name, raw_item, &global_hooks, false)) { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON* const object, const char* const name) { + cJSON* object_item = cJSON_CreateObject(); + if(add_item_to_object(object, name, object_item, &global_hooks, false)) { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON* const object, const char* const name) { + cJSON* array = cJSON_CreateArray(); + if(add_item_to_object(object, name, array, &global_hooks, false)) { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemViaPointer(cJSON* parent, cJSON* const item) { + if((parent == NULL) || (item == NULL)) { + return NULL; + } + + if(item != parent->child) { + /* not the first element */ + item->prev->next = item->next; + } + if(item->next != NULL) { + /* not the last element */ + item->next->prev = item->prev; + } + + if(item == parent->child) { + /* first element */ + parent->child = item->next; + } else if(item->next == NULL) { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromArray(cJSON* array, int which) { + if(which < 0) { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON* array, int which) { + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObject(cJSON* object, const char* string) { + cJSON* to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObjectCaseSensitive(cJSON* object, const char* string) { + cJSON* to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON* object, const char* string) { + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON* object, const char* string) { + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON* array, int which, cJSON* newitem) { + cJSON* after_inserted = NULL; + + if(which < 0) { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if(after_inserted == NULL) { + return add_item_to_array(array, newitem); + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if(after_inserted == array->child) { + array->child = newitem; + } else { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemViaPointer(cJSON* const parent, cJSON* const item, cJSON* replacement) { + if((parent == NULL) || (replacement == NULL) || (item == NULL)) { + return false; + } + + if(replacement == item) { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if(replacement->next != NULL) { + replacement->next->prev = replacement; + } + if(parent->child == item) { + if(parent->child->prev == parent->child) { + replacement->prev = replacement; + } + parent->child = replacement; + } else { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if(replacement->prev != NULL) { + replacement->prev->next = replacement; + } + if(replacement->next == NULL) { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON* array, int which, cJSON* newitem) { + if(which < 0) { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object( + cJSON* object, + const char* string, + cJSON* replacement, + cJSON_bool case_sensitive) { + if((replacement == NULL) || (string == NULL)) { + return false; + } + + /* replace the name in the replacement */ + if(!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer( + object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObject(cJSON* object, const char* string, cJSON* newitem) { + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObjectCaseSensitive(cJSON* object, const char* string, cJSON* newitem) { + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON*) cJSON_CreateNull(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateTrue(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateFalse(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateBool(cJSON_bool boolean) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateNumber(double num) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if(num >= INT_MAX) { + item->valueint = INT_MAX; + } else if(num <= (double)INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateString(const char* string) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateStringReference(const char* string) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateObjectReference(const cJSON* child) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateArrayReference(const cJSON* child) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateRaw(const char* raw) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateArray(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateObject(void) { + cJSON* item = cJSON_New_Item(&global_hooks); + if(item) { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON*) cJSON_CreateIntArray(const int* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateFloatArray(const float* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateDoubleArray(const double* numbers, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (numbers == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateNumber(numbers[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON*) cJSON_CreateStringArray(const char* const* strings, int count) { + size_t i = 0; + cJSON* n = NULL; + cJSON* p = NULL; + cJSON* a = NULL; + + if((count < 0) || (strings == NULL)) { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) { + n = cJSON_CreateString(strings[i]); + if(!n) { + cJSON_Delete(a); + return NULL; + } + if(!i) { + a->child = n; + } else { + suffix_object(p, n); + } + p = n; + } + + if(a && a->child) { + a->child->prev = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON*) cJSON_Duplicate(const cJSON* item, cJSON_bool recurse) { + cJSON* newitem = NULL; + cJSON* child = NULL; + cJSON* next = NULL; + cJSON* newchild = NULL; + + /* Bail on bad ptr */ + if(!item) { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if(!newitem) { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if(item->valuestring) { + newitem->valuestring = + (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if(!newitem->valuestring) { + goto fail; + } + } + if(item->string) { + newitem->string = (item->type & cJSON_StringIsConst) ? + item->string : + (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if(!newitem->string) { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if(!recurse) { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while(child != NULL) { + newchild = cJSON_Duplicate( + child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if(!newchild) { + goto fail; + } + if(next != NULL) { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } else { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if(newitem && newitem->child) { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if(newitem != NULL) { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char** input) { + *input += static_strlen("//"); + + for(; (*input)[0] != '\0'; ++(*input)) { + if((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char** input) { + *input += static_strlen("/*"); + + for(; (*input)[0] != '\0'; ++(*input)) { + if(((*input)[0] == '*') && ((*input)[1] == '/')) { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char** input, char** output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + for(; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if(((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char* json) { + char* into = json; + + if(json == NULL) { + return; + } + + while(json[0] != '\0') { + switch(json[0]) { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if(json[1] == '/') { + skip_oneline_comment(&json); + } else if(json[1] == '*') { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON* const item) { + if(item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) +cJSON_Compare(const cJSON* const a, const cJSON* const b, const cJSON_bool case_sensitive) { + if((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { + return false; + } + + /* check if type is valid */ + switch(a->type & 0xFF) { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if(a == b) { + return true; + } + + switch(a->type & 0xFF) { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if(compare_double(a->valuedouble, b->valuedouble)) { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if((a->valuestring == NULL) || (b->valuestring == NULL)) { + return false; + } + if(strcmp(a->valuestring, b->valuestring) == 0) { + return true; + } + + return false; + + case cJSON_Array: { + cJSON* a_element = a->child; + cJSON* b_element = b->child; + + for(; (a_element != NULL) && (b_element != NULL);) { + if(!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if(a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: { + cJSON* a_element = NULL; + cJSON* b_element = NULL; + cJSON_ArrayForEach(a_element, a) { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if(b_element == NULL) { + return false; + } + + if(!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) { + a_element = get_object_item(a, b_element->string, case_sensitive); + if(a_element == NULL) { + return false; + } + + if(!cJSON_Compare(b_element, a_element, case_sensitive)) { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void*) cJSON_malloc(size_t size) { + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void* object) { + global_hooks.deallocate(object); +} diff --git a/applications/external/wifi_marauder_companion/script/cJSON.h b/applications/external/wifi_marauder_companion/script/cJSON.h new file mode 100644 index 000000000..14ec83d9d --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/cJSON.h @@ -0,0 +1,321 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__WINDOWS__) && \ + (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && \ + !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if(defined(__GNUC__) || defined(__SUNPRO_CC) || defined(__SUNPRO_C)) && \ + defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 15 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON { + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON* next; + struct cJSON* prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON* child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char* valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char* string; +} cJSON; + +typedef struct cJSON_Hooks { + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void*(CJSON_CDECL* malloc_fn)(size_t sz); + void(CJSON_CDECL* free_fn)(void* ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON*) cJSON_Parse(const char* value); +CJSON_PUBLIC(cJSON*) cJSON_ParseWithLength(const char* value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithOpts( + const char* value, + const char** return_parse_end, + cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON*) +cJSON_ParseWithLengthOpts( + const char* value, + size_t buffer_length, + const char** return_parse_end, + cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char*) cJSON_Print(const cJSON* item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char*) cJSON_PrintUnformatted(const cJSON* item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char*) cJSON_PrintBuffered(const cJSON* item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) +cJSON_PrintPreallocated(cJSON* item, char* buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON* item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON* array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON*) cJSON_GetArrayItem(const cJSON* array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON*) cJSON_GetObjectItem(const cJSON* const object, const char* const string); +CJSON_PUBLIC(cJSON*) +cJSON_GetObjectItemCaseSensitive(const cJSON* const object, const char* const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON* object, const char* string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char*) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char*) cJSON_GetStringValue(const cJSON* const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON* const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON* const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON* const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON*) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON*) cJSON_CreateString(const char* string); +/* raw json */ +CJSON_PUBLIC(cJSON*) cJSON_CreateRaw(const char* raw); +CJSON_PUBLIC(cJSON*) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON*) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON*) cJSON_CreateStringReference(const char* string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON*) cJSON_CreateObjectReference(const cJSON* child); +CJSON_PUBLIC(cJSON*) cJSON_CreateArrayReference(const cJSON* child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON*) cJSON_CreateIntArray(const int* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateFloatArray(const float* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateDoubleArray(const double* numbers, int count); +CJSON_PUBLIC(cJSON*) cJSON_CreateStringArray(const char* const* strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON* array, cJSON* item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON* object, const char* string, cJSON* item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON* object, const char* string, cJSON* item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON* array, cJSON* item); +CJSON_PUBLIC(cJSON_bool) +cJSON_AddItemReferenceToObject(cJSON* object, const char* string, cJSON* item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON*) cJSON_DetachItemViaPointer(cJSON* parent, cJSON* const item); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromArray(cJSON* array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON* array, int which); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObject(cJSON* object, const char* string); +CJSON_PUBLIC(cJSON*) cJSON_DetachItemFromObjectCaseSensitive(cJSON* object, const char* string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON* object, const char* string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON* object, const char* string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) +cJSON_InsertItemInArray( + cJSON* array, + int which, + cJSON* newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemViaPointer(cJSON* const parent, cJSON* const item, cJSON* replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON* array, int which, cJSON* newitem); +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObject(cJSON* object, const char* string, cJSON* newitem); +CJSON_PUBLIC(cJSON_bool) +cJSON_ReplaceItemInObjectCaseSensitive(cJSON* object, const char* string, cJSON* newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON*) cJSON_Duplicate(const cJSON* item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) +cJSON_Compare(const cJSON* const a, const cJSON* const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ +CJSON_PUBLIC(void) cJSON_Minify(char* json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) +cJSON_AddBoolToObject(cJSON* const object, const char* const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) +cJSON_AddNumberToObject(cJSON* const object, const char* const name, const double number); +CJSON_PUBLIC(cJSON*) +cJSON_AddStringToObject(cJSON* const object, const char* const name, const char* const string); +CJSON_PUBLIC(cJSON*) +cJSON_AddRawToObject(cJSON* const object, const char* const name, const char* const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON* const object, const char* const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON* const object, const char* const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) \ + ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON* object, double number); +#define cJSON_SetNumberValue(object, number) \ + ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON* object, const char* valuestring); + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) \ + for(element = (array != NULL) ? (array)->child : NULL; element != NULL; \ + element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void*) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void* object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c new file mode 100644 index 000000000..6fe853eb6 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c @@ -0,0 +1,32 @@ +#include "wifi_marauder_script_stage_menu.h" + +WifiMarauderScriptStageMenu* + wifi_marauder_script_stage_menu_create(WifiMarauderScriptStageType stage_type) { + WifiMarauderScriptStageMenu* script_stage_menu = malloc(sizeof(WifiMarauderScriptStageMenu)); + + switch(stage_type) { +#define ADD_STAGE(name, id) \ + case WifiMarauderScriptStageType##id: \ + wifi_marauder_script_stage_menu_##name##_load(script_stage_menu); \ + break; + +#include "wifi_marauder_script_stage_menu_config.h" +#undef ADD_STAGE + } + return script_stage_menu; +} + +void wifi_marauder_script_stage_menu_free(WifiMarauderScriptStageMenu* stage_menu) { + if(stage_menu == NULL) { + return; + } + for(uint32_t i = 0; i < stage_menu->num_items; i++) { + WifiMarauderScriptMenuItem* item = &(stage_menu->items[i]); + for(int j = 0; j < item->num_options; j++) { + free(item->options[j]); + } + free(item->name); + } + free(stage_menu->items); + free(stage_menu); +} diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h new file mode 100644 index 000000000..f5186526c --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include "../wifi_marauder_script.h" + +#define ITEM_EDIT_MAX_OPTIONS (12) + +typedef void (*VariableItemSetupCallback)(VariableItem* item); +typedef void (*VariableItemSelectCallback)(void* context); + +typedef enum WifiMarauderScriptMenuItemType { + WifiMarauderScriptMenuItemTypeString, + WifiMarauderScriptMenuItemTypeNumber, + WifiMarauderScriptMenuItemTypeOptionsString, + WifiMarauderScriptMenuItemTypeOptionsNumber, + WifiMarauderScriptMenuItemTypeListString, + WifiMarauderScriptMenuItemTypeListNumber +} WifiMarauderScriptMenuItemType; + +typedef struct WifiMarauderScriptMenuItem { + char* name; + WifiMarauderScriptMenuItemType type; + int num_options; + char* options[ITEM_EDIT_MAX_OPTIONS]; + VariableItemSetupCallback setup_callback; + VariableItemChangeCallback change_callback; + VariableItemSelectCallback select_callback; +} WifiMarauderScriptMenuItem; + +typedef struct WifiMarauderScriptStageMenu { + WifiMarauderScriptMenuItem* items; + uint32_t num_items; +} WifiMarauderScriptStageMenu; + +#define ADD_STAGE(name, id) \ + void wifi_marauder_script_stage_menu_##name##_load(WifiMarauderScriptStageMenu*); +#include "wifi_marauder_script_stage_menu_config.h" +#undef ADD_STAGE + +WifiMarauderScriptStageMenu* + wifi_marauder_script_stage_menu_create(WifiMarauderScriptStageType stage_type); +void wifi_marauder_script_stage_menu_free(WifiMarauderScriptStageMenu* list); diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c new file mode 100644 index 000000000..35a74ee3d --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_beaconap_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconAp* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_beaconap_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconAp* stage_beaconap = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconap->timeout; +} + +void wifi_marauder_script_stage_menu_beaconap_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = "Timeout", + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconap_stage_timeout_setup_callback, + .select_callback = wifi_marauder_beaconap_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c new file mode 100644 index 000000000..6f320db3e --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c @@ -0,0 +1,59 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_beaconlist_stage_ssids_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->script_stage_edit_strings_reference = &stage_beaconlist->ssids; + app->script_stage_edit_string_count_reference = &stage_beaconlist->ssid_count; +} + +void wifi_marauder_beaconlist_stage_random_ssids_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconList* stage = app->script_edit_selected_stage->stage; + char random_ssids_str[32]; + snprintf(random_ssids_str, sizeof(random_ssids_str), "%d", stage->random_ssids); + variable_item_set_current_value_text(item, random_ssids_str); +} + +void wifi_marauder_beaconlist_stage_random_ssids_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconlist->random_ssids; +} + +void wifi_marauder_beaconlist_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageBeaconList* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_beaconlist_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageBeaconList* stage_beaconlist = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_beaconlist->timeout; +} + +void wifi_marauder_script_stage_menu_beaconlist_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("SSIDs"), + .type = WifiMarauderScriptMenuItemTypeListString, + .num_options = 1, + .select_callback = wifi_marauder_beaconlist_stage_ssids_select_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Generate random"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconlist_stage_random_ssids_setup_callback, + .select_callback = wifi_marauder_beaconlist_stage_random_ssids_select_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_beaconlist_stage_timeout_setup_callback, + .select_callback = wifi_marauder_beaconlist_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h new file mode 100644 index 000000000..1fd2a314b --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h @@ -0,0 +1,14 @@ +ADD_STAGE(scan, Scan) +ADD_STAGE(select, Select) +ADD_STAGE(deauth, Deauth) +ADD_STAGE(probe, Probe) +ADD_STAGE(sniffraw, SniffRaw) +ADD_STAGE(sniffbeacon, SniffBeacon) +ADD_STAGE(sniffdeauth, SniffDeauth) +ADD_STAGE(sniffesp, SniffEsp) +ADD_STAGE(sniffpmkid, SniffPmkid) +ADD_STAGE(sniffpwn, SniffPwn) +ADD_STAGE(beaconlist, BeaconList) +ADD_STAGE(beaconap, BeaconAp) +ADD_STAGE(exec, Exec) +ADD_STAGE(delay, Delay) \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c new file mode 100644 index 000000000..b15b6f461 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_deauth_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageDeauth* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_deauth_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageDeauth* stage_deauth = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_deauth->timeout; +} + +void wifi_marauder_script_stage_menu_deauth_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_deauth_stage_timeout_setup_callback, + .select_callback = wifi_marauder_deauth_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c new file mode 100644 index 000000000..ffd74f720 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_delay_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageDelay* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_delay_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageDelay* stage_delay = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_delay->timeout; +} + +void wifi_marauder_script_stage_menu_delay_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_delay_stage_timeout_setup_callback, + .select_callback = wifi_marauder_delay_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c new file mode 100644 index 000000000..62afdc2f3 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c @@ -0,0 +1,30 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_exec_stage_filter_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageExec* stage = app->script_edit_selected_stage->stage; + if(stage->command != NULL) { + variable_item_set_current_value_text(item, stage->command); + } +} + +void wifi_marauder_exec_stage_filter_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageExec* stage_select = app->script_edit_selected_stage->stage; + if(stage_select->command == NULL) { + stage_select->command = malloc(128); + } + app->user_input_string_reference = &stage_select->command; +} + +void wifi_marauder_script_stage_menu_exec_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Command"), + .type = WifiMarauderScriptMenuItemTypeString, + .num_options = 1, + .setup_callback = wifi_marauder_exec_stage_filter_setup_callback, + .select_callback = wifi_marauder_exec_stage_filter_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c new file mode 100644 index 000000000..53fa26f47 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_probe_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageProbe* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_probe_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageProbe* stage_probe = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_probe->timeout; +} + +void wifi_marauder_script_stage_menu_probe_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_probe_stage_timeout_setup_callback, + .select_callback = wifi_marauder_probe_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c new file mode 100644 index 000000000..3aab740bb --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c @@ -0,0 +1,93 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_scan_stage_type_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->type); +} + +void wifi_marauder_scan_stage_type_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + stage->type = option_index; +} + +void wifi_marauder_scan_stage_channel_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + if(stage->channel >= 0 && stage->channel < 12) { + variable_item_set_current_value_index(item, stage->channel); + } else { + variable_item_set_current_value_index(item, 0); + } +} + +void wifi_marauder_scan_stage_channel_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + stage->channel = option_index; +} + +void wifi_marauder_scan_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageScan* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_scan_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageScan* stage_scan = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_scan->timeout; +} + +void wifi_marauder_script_stage_menu_scan_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Type"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"ap", "station"}, + .setup_callback = wifi_marauder_scan_stage_type_setup_callback, + .change_callback = wifi_marauder_scan_stage_type_change_callback, + }; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Channel"), + .type = WifiMarauderScriptMenuItemTypeOptionsNumber, + .num_options = 12, + .options = {"none", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, + .setup_callback = wifi_marauder_scan_stage_channel_setup_callback, + .change_callback = wifi_marauder_scan_stage_channel_change_callback, + }; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_scan_stage_timeout_setup_callback, + .select_callback = wifi_marauder_scan_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c new file mode 100644 index 000000000..a6121db95 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c @@ -0,0 +1,95 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_select_stage_type_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->type); +} + +void wifi_marauder_select_stage_type_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + stage->type = option_index; +} + +void wifi_marauder_select_stage_filter_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + + if(stage->filter != NULL) { + variable_item_set_current_value_index(item, 0); + variable_item_set_current_value_text(item, stage->filter); + } else { + variable_item_set_current_value_index(item, 1); + } +} + +void wifi_marauder_select_stage_filter_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSelect* stage = app->script_edit_selected_stage->stage; + + // Clears the filter if you change the option. Flipper input box does not accept blank text + if(variable_item_get_current_value_index(item) == 1) { + stage->filter = NULL; + variable_item_set_current_value_index(item, 0); + variable_item_set_values_count(item, 1); + } + + if(stage->filter != NULL) { + variable_item_set_current_value_text(item, stage->filter); + } else { + variable_item_set_current_value_text(item, ""); + } +} + +void wifi_marauder_select_stage_filter_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSelect* stage_select = app->script_edit_selected_stage->stage; + if(stage_select->filter == NULL) { + stage_select->filter = malloc(128); + } + app->user_input_string_reference = &stage_select->filter; +} + +void wifi_marauder_select_stage_indexes_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSelect* stage_select = app->script_edit_selected_stage->stage; + app->script_stage_edit_numbers_reference = &stage_select->indexes; + app->script_stage_edit_number_count_reference = &stage_select->index_count; +} + +void wifi_marauder_script_stage_menu_select_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Type"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"ap", "station"}, + .setup_callback = wifi_marauder_select_stage_type_setup_callback, + .change_callback = wifi_marauder_select_stage_type_change_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Filter"), + .type = WifiMarauderScriptMenuItemTypeString, + .num_options = 2, + .setup_callback = wifi_marauder_select_stage_filter_setup_callback, + .change_callback = wifi_marauder_select_stage_filter_change_callback, + .select_callback = wifi_marauder_select_stage_filter_select_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Indexes"), + .type = WifiMarauderScriptMenuItemTypeListNumber, + .num_options = 1, + .select_callback = wifi_marauder_select_stage_indexes_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c new file mode 100644 index 000000000..11e7b3297 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffbeacon_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffBeacon* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffbeacon_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffBeacon* stage_sniffbeacon = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffbeacon->timeout; +} + +void wifi_marauder_script_stage_menu_sniffbeacon_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffbeacon_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffbeacon_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c new file mode 100644 index 000000000..935a55936 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffdeauth_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffDeauth* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffdeauth_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffDeauth* stage_sniffdeauth = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffdeauth->timeout; +} + +void wifi_marauder_script_stage_menu_sniffdeauth_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffdeauth_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffdeauth_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c new file mode 100644 index 000000000..e90d6b06c --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffesp_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffEsp* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffesp_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffEsp* stage_sniffesp = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffesp->timeout; +} + +void wifi_marauder_script_stage_menu_sniffesp_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffesp_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffesp_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c new file mode 100644 index 000000000..d4f1f8f36 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c @@ -0,0 +1,91 @@ +#include "../../wifi_marauder_app_i.h" + +static void wifi_marauder_sniffpmkid_stage_force_deauth_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->force_deauth); +} + +static void wifi_marauder_sniffpmkid_stage_force_deauth_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + stage->force_deauth = option_index; +} + +static void wifi_marauder_sniffpmkid_stage_channel_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + if(stage->channel >= 0 && stage->channel < 12) { + variable_item_set_current_value_index(item, stage->channel); + } else { + variable_item_set_current_value_index(item, 0); + } +} + +static void wifi_marauder_sniffpmkid_stage_channel_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + // Get menu item + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + // Defines the text of the selected option + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + // Updates the attribute value of the current stage + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + stage->channel = option_index; +} + +static void wifi_marauder_sniffpmkid_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +static void wifi_marauder_sniffpmkid_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffPmkid* stage_sniffpmkid = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffpmkid->timeout; +} + +void wifi_marauder_script_stage_menu_sniffpmkid_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 3; + stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Force deauth"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"no", "yes"}, + .setup_callback = wifi_marauder_sniffpmkid_stage_force_deauth_setup_callback, + .change_callback = wifi_marauder_sniffpmkid_stage_force_deauth_change_callback}; + stage_menu->items[1] = (WifiMarauderScriptMenuItem){ + .name = strdup("Channel"), + .type = WifiMarauderScriptMenuItemTypeOptionsNumber, + .num_options = 12, + .options = {"none", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, + .setup_callback = wifi_marauder_sniffpmkid_stage_channel_setup_callback, + .change_callback = wifi_marauder_sniffpmkid_stage_channel_change_callback}; + stage_menu->items[2] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffpmkid_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffpmkid_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c new file mode 100644 index 000000000..d0859cd8b --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffpwn_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPwn* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffpwn_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffPwn* stage_sniffpwn = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffpwn->timeout; +} + +void wifi_marauder_script_stage_menu_sniffpwn_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffpwn_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffpwn_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c new file mode 100644 index 000000000..39641f1ee --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c @@ -0,0 +1,27 @@ +#include "../../wifi_marauder_app_i.h" + +void wifi_marauder_sniffraw_stage_timeout_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffRaw* stage = app->script_edit_selected_stage->stage; + char timeout_str[32]; + snprintf(timeout_str, sizeof(timeout_str), "%d", stage->timeout); + variable_item_set_current_value_text(item, timeout_str); +} + +void wifi_marauder_sniffraw_stage_timeout_select_callback(void* context) { + WifiMarauderApp* app = context; + WifiMarauderScriptStageSniffRaw* stage_sniffraw = app->script_edit_selected_stage->stage; + app->user_input_number_reference = &stage_sniffraw->timeout; +} + +void wifi_marauder_script_stage_menu_sniffraw_load(WifiMarauderScriptStageMenu* stage_menu) { + stage_menu->num_items = 1; + stage_menu->items = malloc(1 * sizeof(WifiMarauderScriptMenuItem)); + + stage_menu->items[0] = (WifiMarauderScriptMenuItem){ + .name = strdup("Timeout"), + .type = WifiMarauderScriptMenuItemTypeNumber, + .num_options = 1, + .setup_callback = wifi_marauder_sniffraw_stage_timeout_setup_callback, + .select_callback = wifi_marauder_sniffraw_stage_timeout_select_callback}; +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c new file mode 100644 index 000000000..64dfacef5 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c @@ -0,0 +1,947 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script.h" + +WifiMarauderScript* wifi_marauder_script_alloc() { + WifiMarauderScript* script = (WifiMarauderScript*)malloc(sizeof(WifiMarauderScript)); + if(script == NULL) { + return NULL; + } + script->name = NULL; + script->description = NULL; + script->first_stage = NULL; + script->last_stage = NULL; + script->enable_led = WifiMarauderScriptBooleanUndefined; + script->save_pcap = WifiMarauderScriptBooleanUndefined; + script->repeat = 1; + return script; +} + +WifiMarauderScript* wifi_marauder_script_create(const char* script_name) { + WifiMarauderScript* script = wifi_marauder_script_alloc(); + script->name = strdup(script_name); + return script; +} + +void _wifi_marauder_script_load_meta(WifiMarauderScript* script, cJSON* meta_section) { + if(meta_section != NULL) { + // Script description + cJSON* description = cJSON_GetObjectItem(meta_section, "description"); + if(description != NULL) { + script->description = strdup(description->valuestring); + } + // Enable LED + cJSON* enable_led_json = cJSON_GetObjectItem(meta_section, "enableLed"); + if(cJSON_IsBool(enable_led_json)) { + script->enable_led = enable_led_json->valueint; + } + // Save PCAP + cJSON* save_pcap_json = cJSON_GetObjectItem(meta_section, "savePcap"); + if(cJSON_IsBool(save_pcap_json)) { + script->save_pcap = save_pcap_json->valueint; + } + // Times the script will be repeated + cJSON* repeat = cJSON_GetObjectItem(meta_section, "repeat"); + if(repeat != NULL) { + script->repeat = repeat->valueint; + } + } + if(script->description == NULL) { + script->description = strdup("My script"); + } +} + +WifiMarauderScriptStageScan* _wifi_marauder_script_get_stage_scan(cJSON* stages) { + cJSON* stage_scan = cJSON_GetObjectItem(stages, "scan"); + if(stage_scan == NULL) { + return NULL; + } + cJSON* type = cJSON_GetObjectItem(stage_scan, "type"); + if(type == NULL) { + return NULL; + } + WifiMarauderScriptScanType scan_type; + if(strcmp(type->valuestring, "ap") == 0) { + scan_type = WifiMarauderScriptScanTypeAp; + } else if(strcmp(type->valuestring, "station") == 0) { + scan_type = WifiMarauderScriptScanTypeStation; + } else { + return NULL; + } + cJSON* channel = cJSON_GetObjectItem(stage_scan, "channel"); + int scan_channel = channel != NULL ? (int)cJSON_GetNumberValue(channel) : 0; + cJSON* timeout = cJSON_GetObjectItem(stage_scan, "timeout"); + int scan_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN; + + WifiMarauderScriptStageScan* scan_stage = + (WifiMarauderScriptStageScan*)malloc(sizeof(WifiMarauderScriptStageScan)); + scan_stage->type = scan_type; + scan_stage->channel = scan_channel; + scan_stage->timeout = scan_timeout; + + return scan_stage; +} + +WifiMarauderScriptStageSelect* _wifi_marauder_script_get_stage_select(cJSON* stages) { + cJSON* select_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "select"); + if(select_stage_json == NULL) { + return NULL; + } + + cJSON* type_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "type"); + cJSON* filter_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "filter"); + cJSON* indexes_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "indexes"); + cJSON* allow_repeat_json = cJSON_GetObjectItemCaseSensitive(select_stage_json, "allow_repeat"); + + if(!cJSON_IsString(type_json)) { + return NULL; + } + WifiMarauderScriptSelectType select_type; + if(strcmp(type_json->valuestring, "ap") == 0) { + select_type = WifiMarauderScriptSelectTypeAp; + } else if(strcmp(type_json->valuestring, "station") == 0) { + select_type = WifiMarauderScriptSelectTypeStation; + } else if(strcmp(type_json->valuestring, "ssid") == 0) { + select_type = WifiMarauderScriptSelectTypeSsid; + } else { + return NULL; + } + char* filter_str = cJSON_IsString(filter_json) ? strdup(filter_json->valuestring) : NULL; + + WifiMarauderScriptStageSelect* stage_select = + (WifiMarauderScriptStageSelect*)malloc(sizeof(WifiMarauderScriptStageSelect)); + stage_select->type = select_type; + stage_select->allow_repeat = cJSON_IsBool(allow_repeat_json) ? allow_repeat_json->valueint : + true; + stage_select->filter = filter_str; + + if(cJSON_IsArray(indexes_json)) { + int indexes_size = cJSON_GetArraySize(indexes_json); + int* indexes = (int*)malloc(indexes_size * sizeof(int)); + for(int i = 0; i < indexes_size; i++) { + cJSON* index_item = cJSON_GetArrayItem(indexes_json, i); + if(cJSON_IsNumber(index_item)) { + indexes[i] = index_item->valueint; + } + } + stage_select->indexes = indexes; + stage_select->index_count = indexes_size; + } else { + stage_select->indexes = NULL; + stage_select->index_count = 0; + } + + return stage_select; +} + +WifiMarauderScriptStageDeauth* _wifi_marauder_script_get_stage_deauth(cJSON* stages) { + cJSON* deauth_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "deauth"); + if(deauth_stage_json == NULL) { + return NULL; + } + + cJSON* timeout = cJSON_GetObjectItem(deauth_stage_json, "timeout"); + int deauth_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH; + + WifiMarauderScriptStageDeauth* deauth_stage = + (WifiMarauderScriptStageDeauth*)malloc(sizeof(WifiMarauderScriptStageDeauth)); + deauth_stage->timeout = deauth_timeout; + + return deauth_stage; +} + +WifiMarauderScriptStageProbe* _wifi_marauder_script_get_stage_probe(cJSON* stages) { + cJSON* probe_stage_json = cJSON_GetObjectItemCaseSensitive(stages, "probe"); + if(probe_stage_json == NULL) { + return NULL; + } + + cJSON* timeout = cJSON_GetObjectItem(probe_stage_json, "timeout"); + int probe_timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE; + + WifiMarauderScriptStageProbe* probe_stage = + (WifiMarauderScriptStageProbe*)malloc(sizeof(WifiMarauderScriptStageProbe)); + probe_stage->timeout = probe_timeout; + + return probe_stage; +} + +WifiMarauderScriptStageSniffRaw* _wifi_marauder_script_get_stage_sniff_raw(cJSON* stages) { + cJSON* sniffraw_stage_json = cJSON_GetObjectItem(stages, "sniffraw"); + if(sniffraw_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffraw_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffRaw* sniff_raw_stage = + (WifiMarauderScriptStageSniffRaw*)malloc(sizeof(WifiMarauderScriptStageSniffRaw)); + sniff_raw_stage->timeout = timeout; + + return sniff_raw_stage; +} + +WifiMarauderScriptStageSniffBeacon* _wifi_marauder_script_get_stage_sniff_beacon(cJSON* stages) { + cJSON* sniffbeacon_stage_json = cJSON_GetObjectItem(stages, "sniffbeacon"); + if(sniffbeacon_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffbeacon_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffBeacon* sniff_beacon_stage = + (WifiMarauderScriptStageSniffBeacon*)malloc(sizeof(WifiMarauderScriptStageSniffBeacon)); + sniff_beacon_stage->timeout = timeout; + + return sniff_beacon_stage; +} + +WifiMarauderScriptStageSniffDeauth* _wifi_marauder_script_get_stage_sniff_deauth(cJSON* stages) { + cJSON* sniffdeauth_stage_json = cJSON_GetObjectItem(stages, "sniffdeauth"); + if(sniffdeauth_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffdeauth_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffDeauth* sniff_deauth_stage = + (WifiMarauderScriptStageSniffDeauth*)malloc(sizeof(WifiMarauderScriptStageSniffDeauth)); + sniff_deauth_stage->timeout = timeout; + + return sniff_deauth_stage; +} + +WifiMarauderScriptStageSniffEsp* _wifi_marauder_script_get_stage_sniff_esp(cJSON* stages) { + cJSON* sniffesp_stage_json = cJSON_GetObjectItem(stages, "sniffesp"); + if(sniffesp_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffesp_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffEsp* sniff_esp_stage = + (WifiMarauderScriptStageSniffEsp*)malloc(sizeof(WifiMarauderScriptStageSniffEsp)); + sniff_esp_stage->timeout = timeout; + + return sniff_esp_stage; +} + +WifiMarauderScriptStageSniffPmkid* _wifi_marauder_script_get_stage_sniff_pmkid(cJSON* stages) { + cJSON* sniffpmkid_stage_json = cJSON_GetObjectItem(stages, "sniffpmkid"); + if(sniffpmkid_stage_json == NULL) { + return NULL; + } + + cJSON* channel_json = cJSON_GetObjectItem(sniffpmkid_stage_json, "channel"); + int channel = channel_json != NULL ? (int)cJSON_GetNumberValue(channel_json) : 0; + cJSON* timeout_json = cJSON_GetObjectItem(sniffpmkid_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + cJSON* force_deauth_json = + cJSON_GetObjectItemCaseSensitive(sniffpmkid_stage_json, "forceDeauth"); + bool force_deauth = cJSON_IsBool(force_deauth_json) ? force_deauth_json->valueint : true; + + WifiMarauderScriptStageSniffPmkid* sniff_pmkid_stage = + (WifiMarauderScriptStageSniffPmkid*)malloc(sizeof(WifiMarauderScriptStageSniffPmkid)); + sniff_pmkid_stage->channel = channel; + sniff_pmkid_stage->timeout = timeout; + sniff_pmkid_stage->force_deauth = force_deauth; + + return sniff_pmkid_stage; +} + +WifiMarauderScriptStageSniffPwn* _wifi_marauder_script_get_stage_sniff_pwn(cJSON* stages) { + cJSON* sniffpwn_stage_json = cJSON_GetObjectItem(stages, "sniffpwn"); + if(sniffpwn_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(sniffpwn_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + + WifiMarauderScriptStageSniffPwn* sniff_pwn_stage = + (WifiMarauderScriptStageSniffPwn*)malloc(sizeof(WifiMarauderScriptStageSniffPwn)); + sniff_pwn_stage->timeout = timeout; + + return sniff_pwn_stage; +} + +WifiMarauderScriptStageBeaconList* _wifi_marauder_script_get_stage_beacon_list(cJSON* stages) { + cJSON* stage_beaconlist = cJSON_GetObjectItem(stages, "beaconList"); + if(stage_beaconlist == NULL) { + return NULL; + } + WifiMarauderScriptStageBeaconList* beaconlist_stage = + (WifiMarauderScriptStageBeaconList*)malloc(sizeof(WifiMarauderScriptStageBeaconList)); + if(beaconlist_stage == NULL) { + return NULL; + } + cJSON* ssids = cJSON_GetObjectItem(stage_beaconlist, "ssids"); + if(ssids == NULL) { + return NULL; + } + // SSID count + int ssid_count = cJSON_GetArraySize(ssids); + if(ssid_count == 0) { + return NULL; + } + beaconlist_stage->ssid_count = ssid_count; + // SSIDs + beaconlist_stage->ssids = (char**)malloc(sizeof(char*) * ssid_count); + if(beaconlist_stage->ssids == NULL) { + return NULL; + } + for(int i = 0; i < ssid_count; i++) { + cJSON* ssid = cJSON_GetArrayItem(ssids, i); + if(ssid == NULL) { + continue; + } + char* ssid_string = cJSON_GetStringValue(ssid); + if(ssid_string == NULL) { + continue; + } + beaconlist_stage->ssids[i] = (char*)malloc(sizeof(char) * (strlen(ssid_string) + 1)); + strcpy(beaconlist_stage->ssids[i], ssid_string); + } + // Timeout + cJSON* timeout = cJSON_GetObjectItem(stage_beaconlist, "timeout"); + beaconlist_stage->timeout = timeout != NULL ? (int)cJSON_GetNumberValue(timeout) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + // Random SSIDs + cJSON* random_ssids = cJSON_GetObjectItem(stage_beaconlist, "generate"); + beaconlist_stage->random_ssids = + random_ssids != NULL ? (int)cJSON_GetNumberValue(random_ssids) : 0; + + return beaconlist_stage; +} + +WifiMarauderScriptStageBeaconAp* _wifi_marauder_script_get_stage_beacon_ap(cJSON* stages) { + cJSON* beaconap_stage_json = cJSON_GetObjectItem(stages, "beaconAp"); + if(beaconap_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(beaconap_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : + WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON; + + WifiMarauderScriptStageBeaconAp* beacon_ap_stage = + (WifiMarauderScriptStageBeaconAp*)malloc(sizeof(WifiMarauderScriptStageBeaconAp)); + beacon_ap_stage->timeout = timeout; + + return beacon_ap_stage; +} + +WifiMarauderScriptStageExec* _wifi_marauder_script_get_stage_exec(cJSON* stages) { + cJSON* exec_stage_json = cJSON_GetObjectItem(stages, "exec"); + if(exec_stage_json == NULL) { + return NULL; + } + + cJSON* command_json = cJSON_GetObjectItemCaseSensitive(exec_stage_json, "command"); + char* command_str = cJSON_IsString(command_json) ? strdup(command_json->valuestring) : NULL; + + WifiMarauderScriptStageExec* exec_stage = + (WifiMarauderScriptStageExec*)malloc(sizeof(WifiMarauderScriptStageExec)); + exec_stage->command = command_str; + + return exec_stage; +} + +WifiMarauderScriptStageDelay* _wifi_marauder_script_get_stage_delay(cJSON* stages) { + cJSON* delay_stage_json = cJSON_GetObjectItem(stages, "delay"); + if(delay_stage_json == NULL) { + return NULL; + } + + cJSON* timeout_json = cJSON_GetObjectItem(delay_stage_json, "timeout"); + int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : 0; + + WifiMarauderScriptStageDelay* delay_stage = + (WifiMarauderScriptStageDelay*)malloc(sizeof(WifiMarauderScriptStageDelay)); + delay_stage->timeout = timeout; + + return delay_stage; +} + +WifiMarauderScriptStage* + _wifi_marauder_script_create_stage(WifiMarauderScriptStageType type, void* stage_data) { + WifiMarauderScriptStage* stage = + (WifiMarauderScriptStage*)malloc(sizeof(WifiMarauderScriptStage)); + stage->type = type; + stage->stage = stage_data; + stage->next_stage = NULL; + return stage; +} + +void wifi_marauder_script_add_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type, + void* stage_data) { + if(script == NULL || stage_data == NULL) { + return; + } + WifiMarauderScriptStage* stage = _wifi_marauder_script_create_stage(stage_type, stage_data); + if(script->last_stage != NULL) { + script->last_stage->next_stage = stage; + } else { + script->first_stage = stage; + } + script->last_stage = stage; +} + +void _wifi_marauder_script_load_stages(WifiMarauderScript* script, cJSON* stages) { + // Scan stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeScan, _wifi_marauder_script_get_stage_scan(stages)); + // Select stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeSelect, _wifi_marauder_script_get_stage_select(stages)); + // Deauth stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeDeauth, _wifi_marauder_script_get_stage_deauth(stages)); + // Probe stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeProbe, _wifi_marauder_script_get_stage_probe(stages)); + // Sniff raw stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffRaw, + _wifi_marauder_script_get_stage_sniff_raw(stages)); + // Sniff beacon stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffBeacon, + _wifi_marauder_script_get_stage_sniff_beacon(stages)); + // Sniff deauth stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffDeauth, + _wifi_marauder_script_get_stage_sniff_deauth(stages)); + // Sniff esp stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffEsp, + _wifi_marauder_script_get_stage_sniff_esp(stages)); + // Sniff PMKID stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffPmkid, + _wifi_marauder_script_get_stage_sniff_pmkid(stages)); + // Sniff pwn stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeSniffPwn, + _wifi_marauder_script_get_stage_sniff_pwn(stages)); + // Beacon List stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeBeaconList, + _wifi_marauder_script_get_stage_beacon_list(stages)); + // Beacon Ap stage + wifi_marauder_script_add_stage( + script, + WifiMarauderScriptStageTypeBeaconAp, + _wifi_marauder_script_get_stage_beacon_ap(stages)); + // Exec stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeExec, _wifi_marauder_script_get_stage_exec(stages)); + // Delay stage + wifi_marauder_script_add_stage( + script, WifiMarauderScriptStageTypeDelay, _wifi_marauder_script_get_stage_delay(stages)); +} + +WifiMarauderScript* wifi_marauder_script_parse_raw(const char* json_raw) { + WifiMarauderScript* script = wifi_marauder_script_alloc(); + if(script == NULL) { + return NULL; + } + cJSON* json = cJSON_Parse(json_raw); + if(json == NULL) { + return NULL; + } + cJSON* meta = cJSON_GetObjectItem(json, "meta"); + _wifi_marauder_script_load_meta(script, meta); + + cJSON* stages = cJSON_GetObjectItem(json, "stages"); + if(cJSON_IsArray(stages)) { + cJSON* stage_item = NULL; + cJSON_ArrayForEach(stage_item, stages) { + _wifi_marauder_script_load_stages(script, stage_item); + } + } else { + _wifi_marauder_script_load_stages(script, stages); + } + + return script; +} + +WifiMarauderScript* wifi_marauder_script_parse_json(Storage* storage, const char* file_path) { + WifiMarauderScript* script = NULL; + File* script_file = storage_file_alloc(storage); + FuriString* script_name = furi_string_alloc(); + path_extract_filename_no_ext(file_path, script_name); + + if(storage_file_open(script_file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + uint32_t file_size = storage_file_size(script_file); + char* json_buffer = (char*)malloc(file_size + 1); + uint16_t bytes_read = storage_file_read(script_file, json_buffer, file_size); + json_buffer[bytes_read] = '\0'; + + script = wifi_marauder_script_parse_raw(json_buffer); + } + if(script == NULL) { + script = wifi_marauder_script_create(furi_string_get_cstr(script_name)); + } + script->name = strdup(furi_string_get_cstr(script_name)); + + furi_string_free(script_name); + storage_file_close(script_file); + storage_file_free(script_file); + return script; +} + +cJSON* _wifi_marauder_script_create_json_meta(WifiMarauderScript* script) { + cJSON* meta_json = cJSON_CreateObject(); + if(script->description != NULL) { + cJSON_AddStringToObject(meta_json, "description", script->description); + } else { + cJSON_AddStringToObject(meta_json, "description", "My Script"); + } + if(script->enable_led != WifiMarauderScriptBooleanUndefined) { + cJSON_AddBoolToObject( + meta_json, "enableLed", (script->enable_led == WifiMarauderScriptBooleanTrue)); + } + if(script->save_pcap != WifiMarauderScriptBooleanUndefined) { + cJSON_AddBoolToObject( + meta_json, "savePcap", (script->save_pcap == WifiMarauderScriptBooleanTrue)); + } + cJSON_AddNumberToObject(meta_json, "repeat", script->repeat); + return meta_json; +} + +cJSON* _wifi_marauder_script_create_json_scan(WifiMarauderScriptStageScan* scan_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "scan", cJSON_CreateObject()); + cJSON* scan_json = cJSON_GetObjectItem(stage_json, "scan"); + // Scan type + cJSON_AddStringToObject( + scan_json, "type", scan_stage->type == WifiMarauderScriptScanTypeAp ? "ap" : "station"); + // Channel + if(scan_stage->channel > 0) { + cJSON_AddNumberToObject(scan_json, "channel", scan_stage->channel); + } + // Timeout + if(scan_stage->timeout > 0) { + cJSON_AddNumberToObject(scan_json, "timeout", scan_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_select(WifiMarauderScriptStageSelect* select_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "select", cJSON_CreateObject()); + cJSON* select_json = cJSON_GetObjectItem(stage_json, "select"); + // Select type + cJSON_AddStringToObject( + select_json, + "type", + select_stage->type == WifiMarauderScriptSelectTypeAp ? "ap" : + select_stage->type == WifiMarauderScriptSelectTypeStation ? "station" : + "ssid"); + if(select_stage->filter != NULL) { + cJSON_AddStringToObject(select_json, "filter", select_stage->filter); + } + // Indexes + if(select_stage->indexes != NULL && select_stage->index_count > 0) { + cJSON* indexes_json = cJSON_CreateArray(); + for(int i = 0; i < select_stage->index_count; i++) { + cJSON_AddItemToArray(indexes_json, cJSON_CreateNumber(select_stage->indexes[i])); + } + cJSON_AddItemToObject(select_json, "indexes", indexes_json); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_deauth(WifiMarauderScriptStageDeauth* deauth_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "deauth", cJSON_CreateObject()); + cJSON* deauth_json = cJSON_GetObjectItem(stage_json, "deauth"); + // Timeout + if(deauth_stage->timeout > 0) { + cJSON_AddNumberToObject(deauth_json, "timeout", deauth_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_probe(WifiMarauderScriptStageProbe* probe_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "probe", cJSON_CreateObject()); + cJSON* probe_json = cJSON_GetObjectItem(stage_json, "probe"); + // Timeout + if(probe_stage->timeout > 0) { + cJSON_AddNumberToObject(probe_json, "timeout", probe_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffraw(WifiMarauderScriptStageSniffRaw* sniffraw_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffRaw", cJSON_CreateObject()); + cJSON* sniffraw_json = cJSON_GetObjectItem(stage_json, "sniffRaw"); + // Timeout + if(sniffraw_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffraw_json, "timeout", sniffraw_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffbeacon( + WifiMarauderScriptStageSniffBeacon* sniffbeacon_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffBeacon", cJSON_CreateObject()); + cJSON* sniffbeacon_json = cJSON_GetObjectItem(stage_json, "sniffBeacon"); + // Timeout + if(sniffbeacon_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffbeacon_json, "timeout", sniffbeacon_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffdeauth( + WifiMarauderScriptStageSniffDeauth* sniffdeauth_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffDeauth", cJSON_CreateObject()); + cJSON* sniffdeauth_json = cJSON_GetObjectItem(stage_json, "sniffDeauth"); + // Timeout + if(sniffdeauth_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffdeauth_json, "timeout", sniffdeauth_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffesp(WifiMarauderScriptStageSniffEsp* sniffesp_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffEsp", cJSON_CreateObject()); + cJSON* sniffesp_json = cJSON_GetObjectItem(stage_json, "sniffEsp"); + // Timeout + if(sniffesp_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffesp_json, "timeout", sniffesp_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_sniffpmkid( + WifiMarauderScriptStageSniffPmkid* sniffpmkid_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffPmkid", cJSON_CreateObject()); + cJSON* sniffpmkid_json = cJSON_GetObjectItem(stage_json, "sniffPmkid"); + // Force deauth + cJSON_AddBoolToObject(sniffpmkid_json, "forceDeauth", sniffpmkid_stage->force_deauth); + // Channel + if(sniffpmkid_stage->channel > 0) { + cJSON_AddNumberToObject(sniffpmkid_json, "channel", sniffpmkid_stage->channel); + } + // Timeout + if(sniffpmkid_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffpmkid_json, "timeout", sniffpmkid_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_sniffpwn(WifiMarauderScriptStageSniffPwn* sniffpwn_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "sniffPwn", cJSON_CreateObject()); + cJSON* sniffpwn_json = cJSON_GetObjectItem(stage_json, "sniffPwn"); + // Timeout + if(sniffpwn_stage->timeout > 0) { + cJSON_AddNumberToObject(sniffpwn_json, "timeout", sniffpwn_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_beaconlist( + WifiMarauderScriptStageBeaconList* beaconlist_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "beaconList", cJSON_CreateObject()); + cJSON* beaconlist_json = cJSON_GetObjectItem(stage_json, "beaconList"); + // SSIDs + if(beaconlist_stage->ssids != NULL) { + cJSON* ssids_json = cJSON_CreateStringArray( + (const char**)beaconlist_stage->ssids, beaconlist_stage->ssid_count); + cJSON_AddItemToObject(beaconlist_json, "ssids", ssids_json); + } + // Random SSIDs + if(beaconlist_stage->random_ssids > 0) { + cJSON_AddNumberToObject(beaconlist_json, "generate", beaconlist_stage->random_ssids); + } + // Timeout + if(beaconlist_stage->timeout > 0) { + cJSON_AddNumberToObject(beaconlist_json, "timeout", beaconlist_stage->timeout); + } + return stage_json; +} + +cJSON* + _wifi_marauder_script_create_json_beaconap(WifiMarauderScriptStageBeaconAp* beaconap_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "beaconAp", cJSON_CreateObject()); + cJSON* beaconap_json = cJSON_GetObjectItem(stage_json, "beaconAp"); + // Timeout + if(beaconap_stage->timeout > 0) { + cJSON_AddNumberToObject(beaconap_json, "timeout", beaconap_stage->timeout); + } + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_exec(WifiMarauderScriptStageExec* exec_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "exec", cJSON_CreateObject()); + cJSON* exec_json = cJSON_GetObjectItem(stage_json, "exec"); + // Command + cJSON_AddStringToObject( + exec_json, "command", exec_stage->command != NULL ? exec_stage->command : ""); + return stage_json; +} + +cJSON* _wifi_marauder_script_create_json_delay(WifiMarauderScriptStageDelay* delay_stage) { + cJSON* stage_json = cJSON_CreateObject(); + cJSON_AddItemToObject(stage_json, "delay", cJSON_CreateObject()); + cJSON* delay_json = cJSON_GetObjectItem(stage_json, "delay"); + // Timeout + if(delay_stage->timeout > 0) { + cJSON_AddNumberToObject(delay_json, "timeout", delay_stage->timeout); + } + return stage_json; +} + +void wifi_marauder_script_save_json( + Storage* storage, + const char* file_path, + WifiMarauderScript* script) { + File* script_file = storage_file_alloc(storage); + + if(storage_file_open(script_file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + cJSON* root_json = cJSON_CreateObject(); + + // Meta info + cJSON* meta_json = _wifi_marauder_script_create_json_meta(script); + cJSON_AddItemToObject(root_json, "meta", meta_json); + + // Create array for stages + cJSON* stages_array = cJSON_CreateArray(); + cJSON_AddItemToObject(root_json, "stages", stages_array); + + // Iterate over each stage and create the corresponding JSON object + WifiMarauderScriptStage* stage = script->first_stage; + while(stage != NULL) { + cJSON* stage_json = NULL; + + switch(stage->type) { + case WifiMarauderScriptStageTypeScan: { + WifiMarauderScriptStageScan* scan_stage = + (WifiMarauderScriptStageScan*)stage->stage; + stage_json = _wifi_marauder_script_create_json_scan(scan_stage); + break; + } + case WifiMarauderScriptStageTypeSelect: { + WifiMarauderScriptStageSelect* select_stage = + (WifiMarauderScriptStageSelect*)stage->stage; + stage_json = _wifi_marauder_script_create_json_select(select_stage); + break; + } + case WifiMarauderScriptStageTypeDeauth: { + WifiMarauderScriptStageDeauth* deauth_stage = + (WifiMarauderScriptStageDeauth*)stage->stage; + stage_json = _wifi_marauder_script_create_json_deauth(deauth_stage); + break; + } + case WifiMarauderScriptStageTypeProbe: { + WifiMarauderScriptStageProbe* probe_stage = + (WifiMarauderScriptStageProbe*)stage->stage; + stage_json = _wifi_marauder_script_create_json_probe(probe_stage); + break; + } + case WifiMarauderScriptStageTypeSniffRaw: { + WifiMarauderScriptStageSniffRaw* sniffraw_stage = + (WifiMarauderScriptStageSniffRaw*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffraw(sniffraw_stage); + break; + } + case WifiMarauderScriptStageTypeSniffBeacon: { + WifiMarauderScriptStageSniffBeacon* sniffbeacon_stage = + (WifiMarauderScriptStageSniffBeacon*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffbeacon(sniffbeacon_stage); + break; + } + case WifiMarauderScriptStageTypeSniffDeauth: { + WifiMarauderScriptStageSniffDeauth* sniffdeauth_stage = + (WifiMarauderScriptStageSniffDeauth*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffdeauth(sniffdeauth_stage); + break; + } + case WifiMarauderScriptStageTypeSniffEsp: { + WifiMarauderScriptStageSniffEsp* sniffesp_stage = + (WifiMarauderScriptStageSniffEsp*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffesp(sniffesp_stage); + break; + } + case WifiMarauderScriptStageTypeSniffPmkid: { + WifiMarauderScriptStageSniffPmkid* sniffpmkid_stage = + (WifiMarauderScriptStageSniffPmkid*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffpmkid(sniffpmkid_stage); + break; + } + case WifiMarauderScriptStageTypeSniffPwn: { + WifiMarauderScriptStageSniffPwn* sniffpwn_stage = + (WifiMarauderScriptStageSniffPwn*)stage->stage; + stage_json = _wifi_marauder_script_create_json_sniffpwn(sniffpwn_stage); + break; + } + case WifiMarauderScriptStageTypeBeaconList: { + WifiMarauderScriptStageBeaconList* beaconlist_stage = + (WifiMarauderScriptStageBeaconList*)stage->stage; + stage_json = _wifi_marauder_script_create_json_beaconlist(beaconlist_stage); + break; + } + case WifiMarauderScriptStageTypeBeaconAp: { + WifiMarauderScriptStageBeaconAp* beaconap_stage = + (WifiMarauderScriptStageBeaconAp*)stage->stage; + stage_json = _wifi_marauder_script_create_json_beaconap(beaconap_stage); + break; + } + case WifiMarauderScriptStageTypeExec: { + WifiMarauderScriptStageExec* exec_stage = + (WifiMarauderScriptStageExec*)stage->stage; + stage_json = _wifi_marauder_script_create_json_exec(exec_stage); + break; + } + case WifiMarauderScriptStageTypeDelay: { + WifiMarauderScriptStageDelay* delay_stage = + (WifiMarauderScriptStageDelay*)stage->stage; + stage_json = _wifi_marauder_script_create_json_delay(delay_stage); + break; + } + } + + // Add the stage JSON object to the "stages" array + if(stage_json != NULL) { + cJSON_AddItemToArray(stages_array, stage_json); + } + + stage = stage->next_stage; + } + + // Write JSON to file + char* json_str = cJSON_Print(root_json); + storage_file_write(script_file, json_str, strlen(json_str)); + + //free(json_str); + storage_file_close(script_file); + } + storage_file_free(script_file); +} + +bool wifi_marauder_script_has_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type) { + if(script == NULL) { + return false; + } + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL) { + if(current_stage->type == stage_type) { + return true; + } + current_stage = current_stage->next_stage; + } + return false; +} + +void wifi_marauder_script_free(WifiMarauderScript* script) { + if(script == NULL) { + return; + } + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL) { + WifiMarauderScriptStage* next_stage = current_stage->next_stage; + switch(current_stage->type) { + case WifiMarauderScriptStageTypeScan: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSelect: + if(((WifiMarauderScriptStageSelect*)current_stage->stage)->filter != NULL) { + free(((WifiMarauderScriptStageSelect*)current_stage->stage)->filter); + } + if(((WifiMarauderScriptStageSelect*)current_stage->stage)->indexes != NULL) { + free(((WifiMarauderScriptStageSelect*)current_stage->stage)->indexes); + } + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeDeauth: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeProbe: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffRaw: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffEsp: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeSniffPwn: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeBeaconList: + for(int i = 0; + i < ((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssid_count; + i++) { + free(((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssids[i]); + } + free(((WifiMarauderScriptStageBeaconList*)current_stage->stage)->ssids); + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeBeaconAp: + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeExec: + if(((WifiMarauderScriptStageExec*)current_stage->stage)->command != NULL) { + free(((WifiMarauderScriptStageExec*)current_stage->stage)->command); + } + free(current_stage->stage); + break; + case WifiMarauderScriptStageTypeDelay: + free(current_stage->stage); + break; + } + free(current_stage); + current_stage = next_stage; + } + free(script->name); + free(script->description); + free(script); +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h new file mode 100644 index 000000000..e11ee267f --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h @@ -0,0 +1,257 @@ +/* + * ---------------------------------------------------------------------------------------------------- + * STEPS TO ADD A NEW STAGE: + * + * wifi_marauder_script.h + * - Complement WifiMarauderScriptStageType enum with new stage + * - Create struct WifiMarauderScriptStage???? for the new stage + * + * wifi_marauder_script.c + * - Change _wifi_marauder_script_load_stages() to load new stage + * - Change wifi_marauder_script_save_json() to support the new stage + * - Add case to free memory in wifi_marauder_script_free() + * + * wifi_marauder_script_executor.c + * - Create function "void _wifi_marauder_script_execute_????(WifiMarauderScriptStage????* stage)" + * - Add case in wifi_marauder_script_execute_stage() + * + * wifi_marauder_scene_script_edit.c + * - Add case in wifi_marauder_scene_script_edit_on_enter() + * + * wifi_marauder_scene_script_stage_add.c + * - Create stage creation function and add in wifi_marauder_scene_script_stage_add_on_enter() + * + * wifi_marauder_script_stage_menu_config.h + * - Add the new stage and implement its functions in a new file + * + * ---------------------------------------------------------------------------------------------------- + * SCRIPT SYNTAX (In order of execution): + * { + * "meta": { + * "description": "My script", + * "repeat": times the script will repeat (default 1), + * "enableLed": true (default) | false, + * "savePcap": true (default) | false + * }, + * "stages": { + * "scan": { + * "type": "ap" | "station", + * "timeout": seconds, + * "channel": 1-11 + * }, + * "select": { + * "type": "ap" | "station" | "ssid", + * "filter": "all" | "contains -f '{SSID fragment}' or equals '{SSID}' or ...", + * "indexes": [0, 1, 2, 3...], + * }, + * "deauth": { + * "timeout": seconds + * }, + * "probe": { + * "timeout": seconds + * }, + * "sniffRaw": { + * "timeout": seconds + * }, + * "sniffBeacon": { + * "timeout": seconds + * }, + * "sniffDeauth": { + * "timeout": seconds + * }, + * "sniffEsp": { + * "timeout": seconds + * }, + * "sniffPmkid": { + * "forceDeauth": true (default) | false, + * "channel": 1-11, + * "timeout": seconds + * }, + * "sniffPwn": { + * "timeout": seconds + * }, + * "beaconList": { + * "ssids": [ + * "SSID 1", + * "SSID 2", + * "SSID 3" + * ], + * "generate": number of random SSIDs that will be generated, + * "timeout": seconds + * } + * "beaconAp": { + * "timeout": seconds + * } + * "exec": { + * "command": Command (eg: "clearlist -a") + * } + * "delay": { + * "timeout": seconds + * } + * } + * } + * + * Note: It is possible to inform "stages" as an array, allowing ordering and repetition of stages of the same type: + * "stages": [ + * { + * "beaconList": { "ssids": ["SSID 1", "SSID 2"] } + * }, + * { + * "beaconList": { "generate": 4 } + * }, + * ] + * ---------------------------------------------------------------------------------------------------- + */ + +#pragma once + +#include +#include "cJSON.h" + +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SCAN 15 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_DEAUTH 30 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_PROBE 60 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF 60 +#define WIFI_MARAUDER_DEFAULT_TIMEOUT_BEACON 60 + +typedef enum { + WifiMarauderScriptBooleanFalse = 0, + WifiMarauderScriptBooleanTrue = 1, + WifiMarauderScriptBooleanUndefined = 2 +} WifiMarauderScriptBoolean; + +typedef enum { + WifiMarauderScriptStageTypeScan, + WifiMarauderScriptStageTypeSelect, + WifiMarauderScriptStageTypeDeauth, + WifiMarauderScriptStageTypeProbe, + WifiMarauderScriptStageTypeSniffRaw, + WifiMarauderScriptStageTypeSniffBeacon, + WifiMarauderScriptStageTypeSniffDeauth, + WifiMarauderScriptStageTypeSniffEsp, + WifiMarauderScriptStageTypeSniffPmkid, + WifiMarauderScriptStageTypeSniffPwn, + WifiMarauderScriptStageTypeBeaconList, + WifiMarauderScriptStageTypeBeaconAp, + WifiMarauderScriptStageTypeExec, + WifiMarauderScriptStageTypeDelay, +} WifiMarauderScriptStageType; + +typedef enum { + WifiMarauderScriptScanTypeAp = 0, + WifiMarauderScriptScanTypeStation = 1 +} WifiMarauderScriptScanType; + +typedef enum { + WifiMarauderScriptSelectTypeAp, + WifiMarauderScriptSelectTypeStation, + WifiMarauderScriptSelectTypeSsid +} WifiMarauderScriptSelectType; + +// Stages +typedef struct WifiMarauderScriptStage { + WifiMarauderScriptStageType type; + void* stage; + struct WifiMarauderScriptStage* next_stage; +} WifiMarauderScriptStage; + +typedef struct WifiMarauderScriptStageScan { + WifiMarauderScriptScanType type; + int channel; + int timeout; +} WifiMarauderScriptStageScan; + +typedef struct WifiMarauderScriptStageSelect { + WifiMarauderScriptSelectType type; + char* filter; + int* indexes; + int index_count; + // TODO: Implement a feature to not select the same items in the next iteration of the script + bool allow_repeat; +} WifiMarauderScriptStageSelect; + +typedef struct WifiMarauderScriptStageDeauth { + int timeout; +} WifiMarauderScriptStageDeauth; + +typedef struct WifiMarauderScriptStageProbe { + int timeout; +} WifiMarauderScriptStageProbe; + +typedef struct WifiMarauderScriptStageSniffRaw { + int timeout; +} WifiMarauderScriptStageSniffRaw; + +typedef struct WifiMarauderScriptStageSniffBeacon { + int timeout; +} WifiMarauderScriptStageSniffBeacon; + +typedef struct WifiMarauderScriptStageSniffDeauth { + int timeout; +} WifiMarauderScriptStageSniffDeauth; + +typedef struct WifiMarauderScriptStageSniffEsp { + int timeout; +} WifiMarauderScriptStageSniffEsp; + +typedef struct WifiMarauderScriptStageSniffPmkid { + bool force_deauth; + int channel; + int timeout; +} WifiMarauderScriptStageSniffPmkid; + +typedef struct WifiMarauderScriptStageSniffPwn { + int timeout; +} WifiMarauderScriptStageSniffPwn; + +typedef struct WifiMarauderScriptStageBeaconList { + char** ssids; + int ssid_count; + int random_ssids; + int timeout; +} WifiMarauderScriptStageBeaconList; + +typedef struct WifiMarauderScriptStageBeaconAp { + int timeout; +} WifiMarauderScriptStageBeaconAp; + +typedef struct WifiMarauderScriptStageExec { + char* command; +} WifiMarauderScriptStageExec; + +typedef struct WifiMarauderScriptStageDelay { + int timeout; +} WifiMarauderScriptStageDelay; + +// Script +typedef struct WifiMarauderScript { + char* name; + char* description; + WifiMarauderScriptStage* first_stage; + WifiMarauderScriptStage* last_stage; + WifiMarauderScriptBoolean enable_led; + WifiMarauderScriptBoolean save_pcap; + int repeat; +} WifiMarauderScript; + +typedef struct WifiMarauderScriptStageListItem { + char* value; + struct WifiMarauderScriptStageListItem* next_item; +} WifiMarauderScriptStageListItem; + +WifiMarauderScript* wifi_marauder_script_alloc(); +WifiMarauderScript* wifi_marauder_script_create(const char* script_name); +WifiMarauderScript* wifi_marauder_script_parse_raw(const char* script_raw); +WifiMarauderScript* wifi_marauder_script_parse_json(Storage* storage, const char* file_path); +void wifi_marauder_script_save_json( + Storage* storage, + const char* file_path, + WifiMarauderScript* script); +void wifi_marauder_script_add_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type, + void* stage_data); +bool wifi_marauder_script_has_stage( + WifiMarauderScript* script, + WifiMarauderScriptStageType stage_type); +void wifi_marauder_script_free(WifiMarauderScript* script); diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c new file mode 100644 index 000000000..d7799c300 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c @@ -0,0 +1,307 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script_executor.h" + +void _wifi_marauder_script_delay(WifiMarauderScriptWorker* worker, uint32_t delay_secs) { + for(uint32_t i = 0; i < delay_secs && worker->is_running; i++) furi_delay_ms(1000); +} + +void _send_stop() { + const char stop_command[] = "stopscan\n"; + wifi_marauder_uart_tx((uint8_t*)(stop_command), strlen(stop_command)); +} + +void _send_line_break() { + wifi_marauder_uart_tx((uint8_t*)("\n"), 1); +} + +void _send_channel_select(int channel) { + char command[30]; + wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + snprintf(command, sizeof(command), "channel -s %d\n", channel); + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); +} + +void _wifi_marauder_script_execute_scan( + WifiMarauderScriptStageScan* stage, + WifiMarauderScriptWorker* worker) { + char command[15]; + // Set channel + if(stage->channel > 0) { + _send_channel_select(stage->channel); + } + // Start scan + if(stage->type == WifiMarauderScriptScanTypeAp) { + snprintf(command, sizeof(command), "scanap\n"); + } else { + snprintf(command, sizeof(command), "scansta\n"); + } + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_select(WifiMarauderScriptStageSelect* stage) { + const char* select_type = NULL; + switch(stage->type) { + case WifiMarauderScriptSelectTypeAp: + select_type = "-a"; + break; + case WifiMarauderScriptSelectTypeStation: + select_type = "-c"; + break; + case WifiMarauderScriptSelectTypeSsid: + select_type = "-s"; + break; + default: + return; // invalid stage + } + + char command[256]; + size_t command_length = 0; + + if(stage->indexes != NULL && stage->index_count > 0) { + command_length = snprintf(command, sizeof(command), "select %s ", select_type); + + for(int i = 0; i < stage->index_count; i++) { + int index = stage->indexes[i]; + command_length += snprintf( + command + command_length, sizeof(command) - command_length, "%d, ", index); + } + + // Remove the trailing comma and space + command_length -= 2; + command[command_length] = '\n'; + command_length++; + } else if(stage->filter == NULL || strcmp(stage->filter, "all") == 0) { + command_length = snprintf(command, sizeof(command), "select %s all\n", select_type); + } else { + command_length = snprintf( + command, sizeof(command), "select %s -f \"%s\"\n", select_type, stage->filter); + } + + wifi_marauder_uart_tx((uint8_t*)command, command_length); +} + +void _wifi_marauder_script_execute_deauth( + WifiMarauderScriptStageDeauth* stage, + WifiMarauderScriptWorker* worker) { + const char attack_command[] = "attack -t deauth\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_probe( + WifiMarauderScriptStageProbe* stage, + WifiMarauderScriptWorker* worker) { + const char attack_command[] = "attack -t probe\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_raw( + WifiMarauderScriptStageSniffRaw* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffraw\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_beacon( + WifiMarauderScriptStageSniffBeacon* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffbeacon\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_deauth( + WifiMarauderScriptStageSniffDeauth* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffdeauth\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_esp( + WifiMarauderScriptStageSniffEsp* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffesp\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_pmkid( + WifiMarauderScriptStageSniffPmkid* stage, + WifiMarauderScriptWorker* worker) { + char attack_command[50] = "sniffpmkid"; + int len = strlen(attack_command); + + if(stage->channel > 0) { + len += + snprintf(attack_command + len, sizeof(attack_command) - len, " -c %d", stage->channel); + } + + if(stage->force_deauth) { + len += snprintf(attack_command + len, sizeof(attack_command) - len, " -d"); + } + + len += snprintf(attack_command + len, sizeof(attack_command) - len, "\n"); + + wifi_marauder_uart_tx((uint8_t*)attack_command, len); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_sniff_pwn( + WifiMarauderScriptStageSniffPwn* stage, + WifiMarauderScriptWorker* worker) { + const char sniff_command[] = "sniffpwn\n"; + wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_beacon_list( + WifiMarauderScriptStageBeaconList* stage, + WifiMarauderScriptWorker* worker) { + const char clearlist_command[] = "clearlist -s\n"; + wifi_marauder_uart_tx((uint8_t*)(clearlist_command), strlen(clearlist_command)); + + char command[100]; + char* ssid; + + for(int i = 0; i < stage->ssid_count; i++) { + ssid = stage->ssids[i]; + snprintf(command, sizeof(command), "ssid -a -n \"%s\"", ssid); + wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); + _send_line_break(); + } + if(stage->random_ssids > 0) { + char add_random_command[50]; + snprintf( + add_random_command, + sizeof(add_random_command), + "ssid -a -r -g %d\n", + stage->random_ssids); + wifi_marauder_uart_tx((uint8_t*)add_random_command, strlen(add_random_command)); + } + const char attack_command[] = "attack -t beacon -l\n"; + wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_beacon_ap( + WifiMarauderScriptStageBeaconAp* stage, + WifiMarauderScriptWorker* worker) { + const char command[] = "attack -t beacon -a\n"; + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(); +} + +void _wifi_marauder_script_execute_exec(WifiMarauderScriptStageExec* stage) { + if(stage->command != NULL) { + wifi_marauder_uart_tx((uint8_t*)stage->command, strlen(stage->command)); + } +} + +void _wifi_marauder_script_execute_delay( + WifiMarauderScriptStageDelay* stage, + WifiMarauderScriptWorker* worker) { + _wifi_marauder_script_delay(worker, stage->timeout); +} + +void wifi_marauder_script_execute_start(void* context) { + furi_assert(context); + WifiMarauderScriptWorker* worker = context; + WifiMarauderScript* script = worker->script; + char command[100]; + + // Enables or disables the LED according to script settings + if(script->enable_led != WifiMarauderScriptBooleanUndefined) { + snprintf( + command, + sizeof(command), + "settings -s EnableLED %s", + script->enable_led ? "enable" : "disable"); + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _send_line_break(); + } + + // Enables or disables PCAP saving according to script settings + if(script->save_pcap != WifiMarauderScriptBooleanUndefined) { + snprintf( + command, + sizeof(command), + "settings -s SavePCAP %s", + script->save_pcap ? "enable" : "disable"); + wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + _send_line_break(); + } +} + +void wifi_marauder_script_execute_stage(WifiMarauderScriptStage* stage, void* context) { + furi_assert(context); + WifiMarauderScriptWorker* worker = context; + void* stage_data = stage->stage; + + switch(stage->type) { + case WifiMarauderScriptStageTypeScan: + _wifi_marauder_script_execute_scan((WifiMarauderScriptStageScan*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSelect: + _wifi_marauder_script_execute_select((WifiMarauderScriptStageSelect*)stage_data); + break; + case WifiMarauderScriptStageTypeDeauth: + _wifi_marauder_script_execute_deauth((WifiMarauderScriptStageDeauth*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeProbe: + _wifi_marauder_script_execute_probe((WifiMarauderScriptStageProbe*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffRaw: + _wifi_marauder_script_execute_sniff_raw( + (WifiMarauderScriptStageSniffRaw*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffBeacon: + _wifi_marauder_script_execute_sniff_beacon( + (WifiMarauderScriptStageSniffBeacon*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffDeauth: + _wifi_marauder_script_execute_sniff_deauth( + (WifiMarauderScriptStageSniffDeauth*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffEsp: + _wifi_marauder_script_execute_sniff_esp( + (WifiMarauderScriptStageSniffEsp*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffPmkid: + _wifi_marauder_script_execute_sniff_pmkid( + (WifiMarauderScriptStageSniffPmkid*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeSniffPwn: + _wifi_marauder_script_execute_sniff_pwn( + (WifiMarauderScriptStageSniffPwn*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeBeaconList: + _wifi_marauder_script_execute_beacon_list( + (WifiMarauderScriptStageBeaconList*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeBeaconAp: + _wifi_marauder_script_execute_beacon_ap( + (WifiMarauderScriptStageBeaconAp*)stage_data, worker); + break; + case WifiMarauderScriptStageTypeExec: + _wifi_marauder_script_execute_exec((WifiMarauderScriptStageExec*)stage_data); + break; + case WifiMarauderScriptStageTypeDelay: + _wifi_marauder_script_execute_delay((WifiMarauderScriptStageDelay*)stage_data, worker); + break; + } +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h new file mode 100644 index 000000000..654712849 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h @@ -0,0 +1,6 @@ +#pragma once + +#include "wifi_marauder_script.h" + +void wifi_marauder_script_execute_start(void* context); +void wifi_marauder_script_execute_stage(WifiMarauderScriptStage* stage, void* context); diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c new file mode 100644 index 000000000..45c5b56ba --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c @@ -0,0 +1,74 @@ +#include "../wifi_marauder_app_i.h" +#include "wifi_marauder_script_worker.h" + +WifiMarauderScriptWorker* wifi_marauder_script_worker_alloc() { + WifiMarauderScriptWorker* worker = malloc(sizeof(WifiMarauderScriptWorker)); + if(worker == NULL) { + return NULL; + } + worker->callback_start = NULL; + worker->callback_stage = NULL; + worker->worker_thread = NULL; + worker->is_running = false; + return worker; +} + +int32_t _wifi_marauder_script_worker_task(void* worker) { + WifiMarauderScriptWorker* script_worker = worker; + WifiMarauderScript* script = script_worker->script; + if(script == NULL) { + return WifiMarauderScriptWorkerStatusInvalidScript; + } + + // Setup + script_worker->callback_start(script_worker->context); + if(!script_worker->is_running) { + return WifiMarauderScriptWorkerStatusForceExit; + } + + // Stages + for(int i = 0; i < script->repeat; i++) { + WifiMarauderScriptStage* current_stage = script->first_stage; + while(current_stage != NULL && script_worker->is_running) { + script_worker->callback_stage(current_stage, script_worker->context); + current_stage = current_stage->next_stage; + } + if(!script_worker->is_running) { + return WifiMarauderScriptWorkerStatusForceExit; + } + } + + script_worker->is_running = false; + return WifiMarauderScriptWorkerStatusSuccess; +} + +bool wifi_marauder_script_worker_start( + WifiMarauderScriptWorker* instance, + WifiMarauderScript* script) { + if(!instance || !script) { + return false; + } + instance->callback_start = wifi_marauder_script_execute_start; + instance->callback_stage = wifi_marauder_script_execute_stage; + instance->script = script; + instance->context = instance; + instance->is_running = true; + instance->worker_thread = furi_thread_alloc_ex( + "WifiMarauderScriptWorker", 1024, _wifi_marauder_script_worker_task, instance); + if(!instance->worker_thread) { + return false; + } + furi_thread_start(instance->worker_thread); + return true; +} + +void wifi_marauder_script_worker_free(WifiMarauderScriptWorker* worker) { + if(worker != NULL) { + if(worker->worker_thread != NULL) { + worker->is_running = false; + furi_thread_join(worker->worker_thread); + furi_thread_free(worker->worker_thread); + } + free(worker); + } +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h new file mode 100644 index 000000000..76ff070d2 --- /dev/null +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h @@ -0,0 +1,43 @@ +#pragma once + +#include "wifi_marauder_script.h" + +typedef enum { + WifiMarauderScriptWorkerStatusSuccess = 0, + WifiMarauderScriptWorkerStatusInvalidScript = 1, + WifiMarauderScriptWorkerStatusForceExit = 2 +} WifiMarauderScriptWorkerStatus; + +typedef struct WifiMarauderScriptWorker { + WifiMarauderScript* script; + FuriThread* worker_thread; + void (*callback_start)(void*); + void (*callback_stage)(WifiMarauderScriptStage*, void*); + void* context; + bool is_running; +} WifiMarauderScriptWorker; + +/** + * @brief Allocates a new instance of WifiMarauderScriptWorker. + * + * @return A pointer to the allocated instance or NULL if allocation fails. + */ +WifiMarauderScriptWorker* wifi_marauder_script_worker_alloc(); + +/** + * @brief Starts the execution of the worker and sets the callback function to be called after each stage is executed. + * + * @param instance A pointer to the instance of WifiMarauderScriptWorker to start. + * @param script Script to be executed + * @return True if the worker was successfully started, false otherwise. + */ +bool wifi_marauder_script_worker_start( + WifiMarauderScriptWorker* instance, + WifiMarauderScript* script); + +/** + * @brief Frees the memory used by the instance of WifiMarauderScriptWorker. + * + * @param script A pointer to the instance of WifiMarauderScriptWorker to free. + */ +void wifi_marauder_script_worker_free(WifiMarauderScriptWorker* script); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c index 9958b09eb..97b1d9715 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.c @@ -81,6 +81,11 @@ WifiMarauderApp* wifi_marauder_app_alloc() { (!storage_file_exists(app->storage, SAVE_PCAP_SETTING_FILEPATH) || !storage_file_exists(app->storage, SAVE_LOGS_SETTING_FILEPATH)); + // Submenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, WifiMarauderAppViewSubmenu, submenu_get_view(app->submenu)); + scene_manager_next_scene(app->scene_manager, WifiMarauderSceneStart); return app; @@ -100,6 +105,10 @@ void wifi_marauder_make_app_folder(WifiMarauderApp* app) { if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER_LOGS)) { dialog_message_show_storage_error(app->dialogs, "Cannot create\npcaps folder"); } + + if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER_SCRIPTS)) { + dialog_message_show_storage_error(app->dialogs, "Cannot create\nscripts folder"); + } } void wifi_marauder_load_settings(WifiMarauderApp* app) { @@ -134,10 +143,14 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput); view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewTextInput); view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewWidget); + view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); + widget_free(app->widget); text_box_free(app->text_box); furi_string_free(app->text_box_store); wifi_text_input_free(app->text_input); + submenu_free(app->submenu); + variable_item_list_free(app->var_item_list); storage_file_free(app->capture_file); storage_file_free(app->log_file); storage_file_free(app->save_pcap_setting_file); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h index 1bc8f78fe..2a16522bb 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h @@ -6,20 +6,27 @@ #include "scenes/wifi_marauder_scene.h" #include "wifi_marauder_custom_event.h" #include "wifi_marauder_uart.h" -#include "wifi_marauder_pcap.h" +#include "file/sequential_file.h" +#include "script/wifi_marauder_script.h" +#include "script/wifi_marauder_script_worker.h" +#include "script/wifi_marauder_script_executor.h" +#include "script/menu/wifi_marauder_script_stage_menu.h" #include #include #include #include +#include #include #include #include "wifi_marauder_text_input.h" +#include #include +#include #include -#define NUM_MENU_ITEMS (17) +#define NUM_MENU_ITEMS (18) #define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096) #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512) @@ -30,9 +37,17 @@ #define MARAUDER_APP_FOLDER_LOGS MARAUDER_APP_FOLDER "/logs" #define MARAUDER_APP_FOLDER_USER_PCAPS MARAUDER_APP_FOLDER_USER "/pcaps" #define MARAUDER_APP_FOLDER_USER_LOGS MARAUDER_APP_FOLDER_USER "/logs" +#define MARAUDER_APP_FOLDER_SCRIPTS MARAUDER_APP_FOLDER "/scripts" +#define MARAUDER_APP_SCRIPT_PATH(file_name) MARAUDER_APP_FOLDER_SCRIPTS "/" file_name ".json" #define SAVE_PCAP_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_pcaps_here.setting" #define SAVE_LOGS_SETTING_FILEPATH MARAUDER_APP_FOLDER "/save_logs_here.setting" +typedef enum WifiMarauderUserInputType { + WifiMarauderUserInputTypeString, + WifiMarauderUserInputTypeNumber, + WifiMarauderUserInputTypeFileName +} WifiMarauderUserInputType; + struct WifiMarauderApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -58,6 +73,7 @@ struct WifiMarauderApp { VariableItemList* var_item_list; Widget* widget; + Submenu* submenu; int open_log_file_page; int open_log_file_num_pages; @@ -73,6 +89,26 @@ struct WifiMarauderApp { bool is_writing_pcap; bool is_writing_log; + // User input + WifiMarauderUserInputType user_input_type; + char** user_input_string_reference; + int* user_input_number_reference; + char* user_input_file_dir; + char* user_input_file_extension; + + // Automation script + WifiMarauderScript* script; + WifiMarauderScriptWorker* script_worker; + FuriString** script_list; + int script_list_count; + WifiMarauderScriptStage* script_edit_selected_stage; + WifiMarauderScriptStageMenu* script_stage_menu; + WifiMarauderScriptStageListItem* script_stage_edit_first_item; + char*** script_stage_edit_strings_reference; + int* script_stage_edit_string_count_reference; + int** script_stage_edit_numbers_reference; + int* script_stage_edit_number_count_reference; + // For input source and destination MAC in targeted deauth attack int special_case_input_step; char special_case_input_src_addr[20]; @@ -105,4 +141,5 @@ typedef enum { WifiMarauderAppViewConsoleOutput, WifiMarauderAppViewTextInput, WifiMarauderAppViewWidget, + WifiMarauderAppViewSubmenu, } WifiMarauderAppView; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h index 79f96b107..5acdfa38e 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h @@ -7,5 +7,6 @@ typedef enum { WifiMarauderEventSaveSourceMac, WifiMarauderEventSaveDestinationMac, WifiMarauderEventStartSettingsInit, - WifiMarauderEventStartLogViewer + WifiMarauderEventStartLogViewer, + WifiMarauderEventStartScriptSelect } WifiMarauderCustomEvent; From f48a2713bc171af2ad30187f391937e712d11c56 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 00:18:12 +0300 Subject: [PATCH 134/282] Add log files icon in wifi marauder --- .../wifi_marauder_companion/assets/Text_10x10.png | Bin 0 -> 158 bytes .../scenes/wifi_marauder_scene_log_viewer.c | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 applications/external/wifi_marauder_companion/assets/Text_10x10.png diff --git a/applications/external/wifi_marauder_companion/assets/Text_10x10.png b/applications/external/wifi_marauder_companion/assets/Text_10x10.png new file mode 100644 index 0000000000000000000000000000000000000000..8e8a6183dd50535729dc9c9b4f220a12dd4c600f GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&u?6^qxB}__|Nk$&IsYz@rRC}3 z7*a7OIiZ2U&CSi=;0cBn1vTatM&Z;3u7g(^G9`qQn09G2aWeNXaKC0S=Q~tg57Z@F z;u=vBoS#-wo>-L1;E+?AmspUPnOCA;ke9BToS%}K{MA`f4ycg9)78&qol`;+00Iau A9smFU literal 0 HcmV?d00001 diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c index f4e84ccc8..6edb4a49d 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c @@ -148,8 +148,10 @@ bool wifi_marauder_scene_log_viewer_on_event(void* context, SceneManagerEvent ev // Browse FuriString* predefined_filepath = furi_string_alloc_set_str(MARAUDER_APP_FOLDER_LOGS); FuriString* selected_filepath = furi_string_alloc(); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".log", &I_Text_10x10); if(dialog_file_browser_show( - app->dialogs, selected_filepath, predefined_filepath, NULL)) { + app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { strncpy( app->log_file_path, furi_string_get_cstr(selected_filepath), From 0a32cd252820d6a32ff7ffebb45bf918e507150a Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 27 Apr 2023 00:27:57 +0300 Subject: [PATCH 135/282] I was outplayed by the C programming language --- lib/nfc/nfc_worker.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 28a1f6827..5a0145bf4 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -661,7 +661,8 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { if(mf_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyA, !deactivated, cuid)) { mf_classic_set_key_found(data, i, MfClassicKeyA, key); - FURI_LOG_D(TAG, "Key A found"); + FURI_LOG_D( + TAG, "Key A found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); uint64_t found_key; @@ -683,8 +684,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key A is marked as found and matches the searching key, invalidate it + uint8_t found_key[6]; + memcpy(found_key, data->block[i].value, 6); + + uint8_t current_key[6]; + memcpy(current_key, &key, 6); + if(mf_classic_is_key_found(data, i, MfClassicKeyA) && - data->block[i].value[0] == key) { + found_key == current_key) { mf_classic_set_key_not_found(data, i, MfClassicKeyA); is_key_a_found = false; FURI_LOG_D(TAG, "Key %dA not found in attack", i); @@ -694,7 +701,8 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); if(mf_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) { - FURI_LOG_D(TAG, "Key B found"); + FURI_LOG_D( + TAG, "Key B found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); mf_classic_set_key_found(data, i, MfClassicKeyB, key); nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1); @@ -702,8 +710,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key B is marked as found and matches the searching key, invalidate it + uint8_t found_key[6]; + memcpy(found_key, data->block[i].value + 10, 6); + + uint8_t current_key[6]; + memcpy(current_key, &key, 6); + if(mf_classic_is_key_found(data, i, MfClassicKeyB) && - data->block[i].value[10] == key) { + found_key == current_key) { mf_classic_set_key_not_found(data, i, MfClassicKeyB); is_key_b_found = false; FURI_LOG_D(TAG, "Key %dB not found in attack", i); From 102dd690b7ec6b679bfd56d75bfe682af6c81e00 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 27 Apr 2023 00:28:21 +0300 Subject: [PATCH 136/282] Fix emulating empty keys as 0s --- lib/nfc/protocols/mifare_classic.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index d2d7467dc..204e3a5eb 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -892,11 +892,25 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ MfClassicSectorTrailer* sector_trailer = (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD) { - key = nfc_util_bytes2num(sector_trailer->key_a, 6); - access_key = MfClassicKeyA; + if(mf_classic_is_key_found( + &emulator->data, mf_classic_get_sector_by_block(block), MfClassicKeyA)) { + key = nfc_util_bytes2num(sector_trailer->key_a, 6); + access_key = MfClassicKeyA; + } else { + FURI_LOG_D(TAG, "Key not known"); + command_processed = true; + break; + } } else { - key = nfc_util_bytes2num(sector_trailer->key_b, 6); - access_key = MfClassicKeyB; + if(mf_classic_is_key_found( + &emulator->data, mf_classic_get_sector_by_block(block), MfClassicKeyB)) { + key = nfc_util_bytes2num(sector_trailer->key_b, 6); + access_key = MfClassicKeyB; + } else { + FURI_LOG_D(TAG, "Key not known"); + command_processed = true; + break; + } } uint32_t nonce = prng_successor(DWT->CYCCNT, 32) ^ 0xAA; From 6119d6e1022adb03f2985fdf4c87c64026f93062 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 02:02:27 +0300 Subject: [PATCH 137/282] OFW PR 2616: Picopass: remove spaces in CSN by bettse --- .../external/picopass/scenes/picopass_scene_read_card_success.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/picopass/scenes/picopass_scene_read_card_success.c b/applications/external/picopass/scenes/picopass_scene_read_card_success.c index 198b21d98..cc18ac066 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card_success.c @@ -34,7 +34,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(csn_str, "%02X ", csn[i]); + furi_string_cat_printf(csn_str, "%02X", csn[i]); } bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); From 243edf7e13ed92f1d703d1ccd345622d6be5abed Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 02:40:33 +0300 Subject: [PATCH 138/282] Confirm when removing t5577 password add extra scene --- .../lfrfid/scenes/lfrfid_scene_clear_t5577.c | 9 ++-- .../scenes/lfrfid_scene_clear_t5577_confirm.c | 46 +++++++++++++++++++ .../main/lfrfid/scenes/lfrfid_scene_config.h | 1 + .../scenes/lfrfid_scene_extra_actions.c | 2 +- 4 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577_confirm.c diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c index c57373a3a..3b9b41909 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c @@ -83,10 +83,11 @@ bool lfrfid_scene_clear_t5577_on_event(void* context, SceneManagerEvent event) { LfRfid* app = context; bool consumed = false; - const uint32_t prev_scene = LfRfidSceneExtraActions; - - if(event.type == SceneManagerEventTypeCustom && event.event == LfRfidEventPopupClosed) { - scene_manager_search_and_switch_to_previous_scene(app->scene_manager, prev_scene); + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom && event.event == LfRfidEventPopupClosed) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, LfRfidSceneExtraActions); consumed = true; } return consumed; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577_confirm.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577_confirm.c new file mode 100644 index 000000000..ca0d43176 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577_confirm.c @@ -0,0 +1,46 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_clear_t5577_confirm_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeRight, "Start", lfrfid_widget_callback, app); + widget_add_string_multiline_element( + widget, 64, 22, AlignCenter, AlignBottom, FontPrimary, "Apply tag to\nFlipper's back"); + widget_add_string_multiline_element( + widget, + 64, + 45, + AlignCenter, + AlignBottom, + FontSecondary, + "And don't move it\nwhile process is running"); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); +} + +bool lfrfid_scene_clear_t5577_confirm_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, LfRfidSceneExtraActions); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(scene_manager, LfRfidSceneClearT5577); + } + } + + return consumed; +} + +void lfrfid_scene_clear_t5577_confirm_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_config.h b/applications/main/lfrfid/scenes/lfrfid_scene_config.h index 54b840844..7789e133e 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_config.h +++ b/applications/main/lfrfid/scenes/lfrfid_scene_config.h @@ -16,6 +16,7 @@ ADD_SCENE(lfrfid, save_data, SaveData) ADD_SCENE(lfrfid, save_type, SaveType) ADD_SCENE(lfrfid, saved_info, SavedInfo) ADD_SCENE(lfrfid, clear_t5577, ClearT5577) +ADD_SCENE(lfrfid, clear_t5577_confirm, ClearT5577Confirm) ADD_SCENE(lfrfid, delete_success, DeleteSuccess) ADD_SCENE(lfrfid, extra_actions, ExtraActions) ADD_SCENE(lfrfid, raw_info, RawInfo) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index bf73eccd3..d2bf20680 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -80,7 +80,7 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexClearT5577) { - scene_manager_next_scene(app->scene_manager, LfRfidSceneClearT5577); + scene_manager_next_scene(app->scene_manager, LfRfidSceneClearT5577Confirm); consumed = true; } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); From a2b2e486eacc89c57cee56f386544b6521379db1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 04:02:00 +0300 Subject: [PATCH 139/282] Update changelog, docs, readme --- CHANGELOG.md | 59 +++++++++++---------------------- ReadMe.md | 4 +-- documentation/SubGHzSettings.md | 2 ++ 3 files changed, 24 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbc2bdc13..f0fa58cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,44 +1,25 @@ ### New changes -* Note: Due to latest official changes this release was delayed - release was tested by group of users, and some of them reported getting one crash on "furi_check failed", but this can not be reproduced in any way we tried, so, please if you found any issues with BLE (+ Mobile app) that results in crash, create issue with all details how you got it and how many times, and detailed steps on repeating such issue, if you got one crash and can't get it again, collect information how it happened and create issue with as much details as possible -> Thanks! -* SubGHz: New option to use timestamps + protocol name when you saving file, instead of random name - Enable in `Radio Settings -> Time in names = ON` -* SubGHz: Read mode UI improvements (scrolling text, + shows time when signal was received) (by @wosk | PR #429) -* SubGHz: New options to ignore Magellan, Cars(ScherKhan, Kia)(no you can't send that signals) -* SubGHz: Fix keeloq custom buttons bugs -* SubGhz: Nero Radio 57bit **experimental** support + encoder improvements and decoder changes -* SubGhz: Fix RAW recording and reading, (bug where raw file plays endlessly) (Fixes issue #431) -* SubGHz Remote: Add Alutech AT4N Support, fix some issues -* Power GUI: Changing battery style doesnt require reboot (Added API to trigger UI change from different place) (Inspired by @ESurge work) -* Plugins: BLE Remote -> Keynote with vertical layout (by @Kami-no | PR #428) -* Plugins: Improve wifi marauder keyboard (added extra symbols!) (Port uart terminal keyboard into wifi marauder) -* Infrared: Update universal remote assets (by @amec0e | PR #421) -* Docs: Update build docs (by @PhoenixSheppy | PR #425) -* OFW: cubewb: updated to v1.16.0 -> **Part 2 of "Various stop mode fixes"** -* OFW: github: testing SDK with ufbt action -* OFW: Raw RFID documentation -* OFW: Introduce stealth mode and auto-selective lock -* OFW: Active RPC session icon -> **Breaking API change, api was changed from 22.x to 23.x** -* OFW: Various stop mode fixes -> **Should fix known issues with BLE (Random freezes, menu freeze, BT Remote plugin freeze) and other similar issues** -* OFW: Picopass: Correctly aborts when correct key is found -> Fixes Bug (Picopass app not reading elite keyed cards anymore. #413) -### Pre-release changes -* If you have copied apps into `apps` folder - remove `apps` folder on your microSD before installing this release to avoid issues! -* SubGHz: (Bug that I decided to keep as a feature) You can change default button (Ok) for remote by holding custom button and pressing back at same time (same can be used to restore your button if you changed it accidentally) - Be careful, it might be unstable, I will make proper option to change button in next releases -* SubGHz: Fixes for custom button bugs in SubGHz Remote app -* SubGHz: Add alutech table to enviroment alloc and free -* Docs: Fix and update docs - thanks to @lesterrry -* Plugins: Bluetooth Remote - implemented YouTube Shorts Remote (may be unstable) -* Plugins: Bluetooth Remote - improvements and fixes for TikTok remote (by @krolchonok | PR #420 and #432) -* Plugins: Implement an array for baudrates on GPS UART app (+ add 19200 baud) (by @p0ns | PR #416) -* Plugins: Remove UART Echo from releases since it is locked on 115200 baud, and we have **UART Terminal** with ability to set baudrate +* Power + BLE: DeepSleep + required ble stack upgrade added back, all known issues was fixed in OFW, no issues was found during our tests +* Desktop: Allow locking without pin using Up menu on desktop (Short click on `Lock` = Without PIN / Long = With PIN) +* RFID: Add confirmation message before running `Clear T5577 Password` +* RFID: Add more user friendly RAW emulation via UI [(by Dan Caprita)](https://forum.flipperzero.one/t/electra-intercom/6368/43) +* SubGHz: Fixed `Frequency Analyzer` issues, fixed `Read` mode issues +* SubGHz: Fix NFC crash when using external CC1101 radio module +* SubGHz: Fix multiple external CC1101 radio module issues, (int callbacks, SPI handlers init/reinit) +* SubGHz: Using scene manager function in add manually (by @gid9798 | PR #437) +* Plugins: ESP32: WiFi Marauder - add icon for log files in logs browser +* Plugins: Update **ESP32: WiFi Marauder companion** plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) merged [PR by @tcpassos](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/11) * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) -* Plugins: Update **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) -* OFW: Deep Sleep Idle - **Improves battery usage!!!** -> **Breaking API change, api was changed from 21.x to 22.x** -* OFW: FuriHal: pwr pulls for some pins -* OFW: Bugfix: ISP Programmer and SubGhz -* OFW: AVR_ISP: fix NULL pointer dereference -* OFW: Fix gpio state isp programmer -* OFW: ufbt: project & debugging updates -* OFW: FuriHal: fix gpio naming and add explicit pulls for vibro, speaker and ir_tx -> **Breaking API change, api was changed from 20.x to 21.x** -**(this will make your manually copied plugins not work, update them in same way you installed them, or delete `apps` folder and then install firmware, if you using extra pack builds (with `e` in version) all apps in _Extra will be updated automatically)** +* Plugins: Fix RFID Fuzzer and iButton Fuzzer crashes +* Infrared: Updated infrared assets (by @amec0e | PR #441) +* Docs: Update **How To Install** images (by @krolchonok | PR #436) +* OFW PR 2620: NFC: Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys (by Astrrra) +* OFW PR 2616: Picopass: remove spaces in CSN (by bettse) +* OFW PR 2604: WS: add protocol "Wendox W6726" (by Skorpionm) +* OFW PR 2607: BadUSB: command parser fix (by nminaylov) +* OFW: FuriHal: use proper divider for core2 when transition to sleep, remove extra stop mode transition checks, cleanup code. Furi: proper assert and check messages. +* OFW: Don't reboot on crash in debug builds +* OFW: cubewb: downgraded to v1.15.0 #### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) diff --git a/ReadMe.md b/ReadMe.md index fea644f92..1d42e52a3 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -69,12 +69,12 @@ Our Discord Community: Also check the changelog in releases for latest updates! ### Current modified and new Sub-GHz protocols list: -Thanks to Official team (to their SubGHz Developer, Skorp) for implementing decoders for these protocols. +Thanks to Official team (to their SubGHz Developer, Skorp) for implementing decoders for these protocols in OFW. Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported manufacturers list](https://0bin.net/paste/VwR2lNJY#WH9vnPgvcp7w6zVKucFCuNREKAcOij8KsJ6vqLfMn3b) Encoders or sending made by @xMasterX: -- Nero Radio 57bit (experimental) (+ 56bit encoder improvements) +- Nero Radio 57bit (+ 56bit encoder improvements) - Keeloq: HCS101 - Keeloq: AN-Motors - Keeloq: JCM Tech diff --git a/documentation/SubGHzSettings.md b/documentation/SubGHzSettings.md index 41cf0d6bc..adf72067b 100644 --- a/documentation/SubGHzSettings.md +++ b/documentation/SubGHzSettings.md @@ -76,6 +76,8 @@ Change that line to `Add_standard_frequencies: false` +**You need to have custom frequencies added in both lists! in main frequency list and in hopping list! Replacing only hopping freqs will not work with that setting set on false, you need to add something in main list since it will be empty** + ### To add your own frequency to user list Just add new line `Frequency: 928000000` - where `928000000` is your frequency, keep it in that format! it should be 9 digits! From 408edb3e99751b5374571340c9517027845a65fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 27 Apr 2023 23:01:13 +0900 Subject: [PATCH 140/282] Keep HSI16 working in stop mode. (#2621) --- firmware/targets/f7/furi_hal/furi_hal_clock.c | 3 ++- firmware/targets/f7/furi_hal/furi_hal_power.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index a4df4877e..a76fbfbca 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -144,6 +144,7 @@ void furi_hal_clock_init() { LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48); LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1); LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_PLLSAI1); + LL_RCC_HSI_EnableInStopMode(); // Ensure that MR is capable of work in STOP0 LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); LL_RCC_SetSMPSPrescaler(LL_RCC_SMPS_DIV_1); LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); @@ -207,8 +208,8 @@ void furi_hal_clock_switch_to_hsi() { while(!LL_RCC_HSI_IsReady()) ; - LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) ; diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 7d9334c2c..e380de7fa 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -94,6 +94,7 @@ void furi_hal_power_init() { LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN); + LL_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); LL_C2_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); From 1afa3916acb465e6f68929f0a9b7b259b28ef148 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 20:21:04 +0300 Subject: [PATCH 141/282] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0fa58cac..197bd29f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ * OFW PR 2616: Picopass: remove spaces in CSN (by bettse) * OFW PR 2604: WS: add protocol "Wendox W6726" (by Skorpionm) * OFW PR 2607: BadUSB: command parser fix (by nminaylov) +* OFW: Keep HSI16 working in stop mode. * OFW: FuriHal: use proper divider for core2 when transition to sleep, remove extra stop mode transition checks, cleanup code. Furi: proper assert and check messages. * OFW: Don't reboot on crash in debug builds * OFW: cubewb: downgraded to v1.15.0 From 6ed182013d42bfc46789fd25820fc80838e0d0cf Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 21:12:02 +0300 Subject: [PATCH 142/282] Sync ibutton fuzzer code PR with key updates from @team-orangeBlue --- CHANGELOG.md | 1 + .../scene/ibtnfuzzer_scene_run_attack.c | 19 +++++++------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 197bd29f0..f613f6eda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Plugins: Update **ESP32: WiFi Marauder companion** plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) merged [PR by @tcpassos](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/11) * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) * Plugins: Fix RFID Fuzzer and iButton Fuzzer crashes +* Plugins: iButton Fuzzer default keys update (by @team-orangeBlue) * Infrared: Updated infrared assets (by @amec0e | PR #441) * Docs: Update **How To Install** images (by @krolchonok | PR #436) * OFW PR 2620: NFC: Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys (by Astrrra) diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c index 1cab8b04e..92975a427 100644 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c +++ b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c @@ -3,7 +3,7 @@ uint8_t counter = 0; -uint8_t id_list_ds1990[25][8] = { +uint8_t id_list_ds1990[18][8] = { {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает @@ -19,16 +19,9 @@ uint8_t id_list_ds1990[25][8] = { {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF + {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 + {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni }; uint8_t id_list_metakom[17][4] = { @@ -51,7 +44,7 @@ uint8_t id_list_metakom[17][4] = { {0xCA, 0xCA, 0xCA, 0xCA}, // ?? }; -uint8_t id_list_cyfral[14][2] = { +uint8_t id_list_cyfral[16][2] = { {0x00, 0x00}, // Null bytes {0xFF, 0xFF}, // Only FF {0x11, 0x11}, // Only 11 @@ -66,6 +59,8 @@ uint8_t id_list_cyfral[14][2] = { {0x12, 0x34}, // Incremental UID {0x56, 0x34}, // Decremental UID {0xCA, 0xCA}, // ?? + {0x8E, 0xC9}, // Elevator code + {0x6A, 0x50}, // VERY fresh code from smartkey }; void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context) { From d85e097ee5c9bcaf70f78519eeefbc72477fedb8 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 27 Apr 2023 21:45:30 +0300 Subject: [PATCH 143/282] Fix ibutton fuzzer stop values --- .../external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c index 92975a427..13ec6e6be 100644 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c +++ b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c @@ -125,7 +125,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { context->payload[6] = id_list_ds1990[context->attack_step][6]; context->payload[7] = id_list_ds1990[context->attack_step][7]; - if(context->attack_step == 24) { + if(context->attack_step == 17) { context->attack_step = 0; counter = 0; context->is_attacking = false; @@ -155,7 +155,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { context->payload[0] = id_list_cyfral[context->attack_step][0]; context->payload[1] = id_list_cyfral[context->attack_step][1]; - if(context->attack_step == 13) { + if(context->attack_step == 15) { context->attack_step = 0; counter = 0; context->is_attacking = false; From 41895118bd5a73f5358d60ce89f337bec57f1d4d Mon Sep 17 00:00:00 2001 From: ushastoe <40743392+krolchonok@users.noreply.github.com> Date: Fri, 28 Apr 2023 01:28:40 +0300 Subject: [PATCH 144/282] Update HowToInstall.md 46 version --- documentation/HowToInstall.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/documentation/HowToInstall.md b/documentation/HowToInstall.md index a650d97ee..abbca0830 100644 --- a/documentation/HowToInstall.md +++ b/documentation/HowToInstall.md @@ -23,7 +23,7 @@ after that on web updater page - press `Connect` button - And wait, if all flashed successfully - you will have all needed assets pre installed - Done -![web](https://user-images.githubusercontent.com/40743392/233779040-596ad9a9-87cd-4b19-a831-16fdc678ba35.png) +![web](https://user-images.githubusercontent.com/40743392/235004244-30a59649-fa52-4db0-bf8f-ef5290951aa0.png)
@@ -40,7 +40,7 @@ after that on web updater page - press `Connect` button - Error in ios app will show up, but flipper will be updated successfully - And if all flashed successfully - you will have all needed assets pre installed - Done -![ios](https://user-images.githubusercontent.com/40743392/233779071-10e37af5-a7d9-41d6-81cd-9bb660352b4d.png) +![ios](https://user-images.githubusercontent.com/40743392/235004287-bf739fb9-ca77-4843-a235-4614832e9ae5.png)

@@ -56,7 +56,7 @@ after that on web updater page - press `Connect` button - Wait until update is finished - And if all flashed successfully - you will have all needed assets pre installed - Done -![andro_tgz](https://user-images.githubusercontent.com/40743392/233782806-e81c9634-4694-4faf-88ce-08b89a8b6fa0.png) +![andro_tgz](https://user-images.githubusercontent.com/40743392/235004340-325a97da-7e3d-4821-892c-705f661bfddc.png)

@@ -71,7 +71,7 @@ after that on web updater page - press `Connect` button - Wait until update is finished - And if all flashed successfully - you will have all needed assets pre installed - Done -![androweb](https://user-images.githubusercontent.com/40743392/233782906-1f8f1ebf-c488-4d9f-9a6f-67a4e693cbda.png) +![androweb](https://user-images.githubusercontent.com/40743392/235004374-bef68a66-1b10-4688-a506-8753660c2ebc.png)
@@ -90,7 +90,7 @@ after that on web updater page - press `Connect` button - And wait, if all flashed successfully - you will have all needed assets pre installed - Done -![qflip](https://user-images.githubusercontent.com/40743392/233779331-3f21c662-6e77-42e5-a928-f5441bd85bd4.png) +![qflip](https://user-images.githubusercontent.com/40743392/235004392-8e824589-d0fa-444c-a592-098ff629f8c6.png) @@ -111,7 +111,7 @@ after that on web updater page - press `Connect` button - Update will start, wait for all stages - Done -![manual](https://user-images.githubusercontent.com/40743392/233779363-faf5b35b-e136-4dca-b1fb-15188b26eb6a.png) +![manual](https://user-images.githubusercontent.com/40743392/235004403-cc82a02b-06a3-4cbe-9420-8eb0ad1f4659.png) From b31e955744d329601b087c0099d7c1734f0980cc Mon Sep 17 00:00:00 2001 From: ushastoe <40743392+krolchonok@users.noreply.github.com> Date: Fri, 28 Apr 2023 01:39:58 +0300 Subject: [PATCH 145/282] Update HowToInstall.md --- documentation/HowToInstall.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/documentation/HowToInstall.md b/documentation/HowToInstall.md index abbca0830..9b762ea11 100644 --- a/documentation/HowToInstall.md +++ b/documentation/HowToInstall.md @@ -23,7 +23,7 @@ after that on web updater page - press `Connect` button - And wait, if all flashed successfully - you will have all needed assets pre installed - Done -![web](https://user-images.githubusercontent.com/40743392/235004244-30a59649-fa52-4db0-bf8f-ef5290951aa0.png) +![web](https://user-images.githubusercontent.com/40743392/235005830-98ceda39-a143-47ef-ad4d-5489bc3df98b.png)
@@ -40,7 +40,7 @@ after that on web updater page - press `Connect` button - Error in ios app will show up, but flipper will be updated successfully - And if all flashed successfully - you will have all needed assets pre installed - Done -![ios](https://user-images.githubusercontent.com/40743392/235004287-bf739fb9-ca77-4843-a235-4614832e9ae5.png) +![ios](https://user-images.githubusercontent.com/40743392/235005844-bea8f2fd-f50d-41b1-9191-e3842d8658d2.png)

@@ -56,7 +56,8 @@ after that on web updater page - press `Connect` button - Wait until update is finished - And if all flashed successfully - you will have all needed assets pre installed - Done -![andro_tgz](https://user-images.githubusercontent.com/40743392/235004340-325a97da-7e3d-4821-892c-705f661bfddc.png) +![andro_tgz](https://user-images.githubusercontent.com/40743392/235005877-d4f5f73c-241c-4a7b-a51d-b8407983856c.png) +

@@ -71,7 +72,7 @@ after that on web updater page - press `Connect` button - Wait until update is finished - And if all flashed successfully - you will have all needed assets pre installed - Done -![androweb](https://user-images.githubusercontent.com/40743392/235004374-bef68a66-1b10-4688-a506-8753660c2ebc.png) +![androweb](https://user-images.githubusercontent.com/40743392/235005891-19ef6bb6-094f-437d-afcd-75d60921e3c4.png)
@@ -89,8 +90,7 @@ after that on web updater page - press `Connect` button - Update will start - And wait, if all flashed successfully - you will have all needed assets pre installed - Done - -![qflip](https://user-images.githubusercontent.com/40743392/235004392-8e824589-d0fa-444c-a592-098ff629f8c6.png) +![qflip](https://user-images.githubusercontent.com/40743392/235005910-819abd34-65d4-4aaa-a11c-9c28bea737e9.png) @@ -110,8 +110,8 @@ after that on web updater page - press `Connect` button `update/f7-update-(CURRENT VERSION)/update.fuf` - Update will start, wait for all stages - Done +![manual](https://user-images.githubusercontent.com/40743392/235005942-c4debf85-b251-41c1-ad0b-c7bd94b999fb.png) -![manual](https://user-images.githubusercontent.com/40743392/235004403-cc82a02b-06a3-4cbe-9420-8eb0ad1f4659.png) From 7b21dd7082fddf58542e1a06ebcd1a0ad711e845 Mon Sep 17 00:00:00 2001 From: ushastoe <40743392+krolchonok@users.noreply.github.com> Date: Fri, 28 Apr 2023 01:41:03 +0300 Subject: [PATCH 146/282] Update HowToInstall.md --- documentation/HowToInstall.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/HowToInstall.md b/documentation/HowToInstall.md index 9b762ea11..d4dcc3bbc 100644 --- a/documentation/HowToInstall.md +++ b/documentation/HowToInstall.md @@ -110,7 +110,8 @@ after that on web updater page - press `Connect` button `update/f7-update-(CURRENT VERSION)/update.fuf` - Update will start, wait for all stages - Done -![manual](https://user-images.githubusercontent.com/40743392/235005942-c4debf85-b251-41c1-ad0b-c7bd94b999fb.png) +![manual](https://user-images.githubusercontent.com/40743392/235006093-5f76f28c-6159-4785-a7ca-a06354dffab6.png) + From c0de75367f7b4209585648a2506aaeec165c0b38 Mon Sep 17 00:00:00 2001 From: ushastoe <40743392+krolchonok@users.noreply.github.com> Date: Fri, 28 Apr 2023 01:43:27 +0300 Subject: [PATCH 147/282] Update HowToInstall.md --- documentation/HowToInstall.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/HowToInstall.md b/documentation/HowToInstall.md index d4dcc3bbc..656d4cc4b 100644 --- a/documentation/HowToInstall.md +++ b/documentation/HowToInstall.md @@ -110,7 +110,7 @@ after that on web updater page - press `Connect` button `update/f7-update-(CURRENT VERSION)/update.fuf` - Update will start, wait for all stages - Done -![manual](https://user-images.githubusercontent.com/40743392/235006093-5f76f28c-6159-4785-a7ca-a06354dffab6.png) +![manual](https://user-images.githubusercontent.com/40743392/235006410-19eaf58e-2425-4e8e-8ec9-973bda362c47.png) From 96375e8244a1e90bf6359e040677425dbc36968d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 28 Apr 2023 14:00:40 +0300 Subject: [PATCH 148/282] Version instead of branch --- firmware/targets/f7/ble_glue/dev_info_service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c index 8bdb2eea8..d24058632 100644 --- a/firmware/targets/f7/ble_glue/dev_info_service.c +++ b/firmware/targets/f7/ble_glue/dev_info_service.c @@ -33,7 +33,7 @@ void dev_info_svc_start() { dev_info_svc->version_string = furi_string_alloc_printf( "%s %s %s %s", version_get_githash(NULL), - version_get_gitbranch(NULL), + version_get_version(NULL), version_get_gitbranchnum(NULL), version_get_builddate(NULL)); snprintf( From e87256e01f3e289c6eb60badd4ae88e190369da5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 28 Apr 2023 14:04:16 +0300 Subject: [PATCH 149/282] Update TOTP --- .../services/config/token_info_iterator.c | 16 ++++++-------- .../external/totp/services/hmac/hmac_common.h | 1 - .../external/totp/services/hmac/hmac_sha256.c | 1 + .../external/totp/services/hmac/sha1.c | 8 +++---- .../external/totp/services/hmac/sha256.c | 7 ++----- .../external/totp/services/hmac/sha512.c | 7 ++----- .../totp/services/hmac/sha_pad_buffer.c | 11 ++++++++++ .../totp/services/hmac/sha_pad_buffer.h | 4 ++++ applications/external/totp/types/token_info.c | 21 +++++++++++++++++++ applications/external/totp/types/token_info.h | 12 +++++++++-- 10 files changed, 60 insertions(+), 28 deletions(-) create mode 100644 applications/external/totp/services/hmac/sha_pad_buffer.c create mode 100644 applications/external/totp/services/hmac/sha_pad_buffer.h diff --git a/applications/external/totp/services/config/token_info_iterator.c b/applications/external/totp/services/config/token_info_iterator.c index 9b7dd5550..f8cd3c64e 100644 --- a/applications/external/totp/services/config/token_info_iterator.c +++ b/applications/external/totp/services/config/token_info_iterator.c @@ -68,7 +68,9 @@ static bool seek_to_token(size_t token_index, TokenInfoIteratorContext* context) direction = StreamDirectionBackward; } - stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart); + if(!stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart)) { + return false; + } if(token_index_diff != 0) { long i = 0; @@ -89,10 +91,6 @@ static bool seek_to_token(size_t token_index, TokenInfoIteratorContext* context) context->last_seek_offset = stream_tell(stream); context->last_seek_index = token_index; - } else { - if(!stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart)) { - return false; - } } return true; @@ -495,11 +493,9 @@ bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t to } uint32_t temp_data32; - if(flipper_format_read_uint32( - context->config_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &temp_data32, 1) && - temp_data32 <= STEAM) { - tokenInfo->algo = (TokenHashAlgo)temp_data32; - } else { + if(!flipper_format_read_uint32( + context->config_file, TOTP_CONFIG_KEY_TOKEN_ALGO, &temp_data32, 1) || + !token_info_set_algo_from_int(tokenInfo, temp_data32)) { tokenInfo->algo = SHA1; } diff --git a/applications/external/totp/services/hmac/hmac_common.h b/applications/external/totp/services/hmac/hmac_common.h index 0cd56ed99..3499cb800 100644 --- a/applications/external/totp/services/hmac/hmac_common.h +++ b/applications/external/totp/services/hmac/hmac_common.h @@ -1,5 +1,4 @@ #include -#include "sha256.h" #include "memxor.h" #define IPAD 0x36 diff --git a/applications/external/totp/services/hmac/hmac_sha256.c b/applications/external/totp/services/hmac/hmac_sha256.c index c51f24b4d..00ac2a177 100644 --- a/applications/external/totp/services/hmac/hmac_sha256.c +++ b/applications/external/totp/services/hmac/hmac_sha256.c @@ -15,6 +15,7 @@ along with this program. If not, see . */ #include "hmac_sha256.h" +#include "sha256.h" #define GL_HMAC_NAME 256 #define GL_HMAC_BLOCKSIZE 64 diff --git a/applications/external/totp/services/hmac/sha1.c b/applications/external/totp/services/hmac/sha1.c index ecf22fc97..29f22e3c3 100644 --- a/applications/external/totp/services/hmac/sha1.c +++ b/applications/external/totp/services/hmac/sha1.c @@ -27,6 +27,8 @@ #include #include +#include "sha_pad_buffer.h" + #ifdef WORDS_BIGENDIAN #define SWAP(n) (n) #else @@ -34,10 +36,6 @@ #define SWAP(n) swap_uint32(n) #endif -/* This array contains the bytes used to pad the buffer to the next - 64-byte boundary. (RFC 1321, 3.1: Step 1) */ -static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */}; - /* Take a pointer to a 160 bit block of data (five 32 bit ints) and initialize it to the start constants of the SHA1 algorithm. This must be called before using hash in the call to sha1_hash. */ @@ -87,7 +85,7 @@ void* sha1_finish_ctx(struct sha1_ctx* ctx, void* resbuf) { ctx->buffer[size - 2] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29)); ctx->buffer[size - 1] = SWAP(ctx->total[0] << 3); - memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes); /* Process last bytes. */ sha1_process_block(ctx->buffer, size * 4, ctx); diff --git a/applications/external/totp/services/hmac/sha256.c b/applications/external/totp/services/hmac/sha256.c index 89ca67c2b..09ba272e7 100644 --- a/applications/external/totp/services/hmac/sha256.c +++ b/applications/external/totp/services/hmac/sha256.c @@ -25,6 +25,7 @@ #include #include +#include "sha_pad_buffer.h" #ifdef WORDS_BIGENDIAN #define SWAP(n) (n) @@ -33,10 +34,6 @@ #define SWAP(n) swap_uint32(n) #endif -/* This array contains the bytes used to pad the buffer to the next - 64-byte boundary. */ -static const unsigned char fillbuf[64] = {0x80, 0 /* , 0, 0, ... */}; - /* Takes a pointer to a 256 bit block of data (eight 32 bit ints) and initializes it to the start constants of the SHA256 algorithm. This @@ -91,7 +88,7 @@ static void sha256_conclude_ctx(struct sha256_ctx* ctx) { set_uint32((char*)&ctx->buffer[size - 2], SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29))); set_uint32((char*)&ctx->buffer[size - 1], SWAP(ctx->total[0] << 3)); - memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); + sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes); /* Process last bytes. */ sha256_process_block(ctx->buffer, size * 4, ctx); diff --git a/applications/external/totp/services/hmac/sha512.c b/applications/external/totp/services/hmac/sha512.c index b56dd0f2e..ffe2864fb 100644 --- a/applications/external/totp/services/hmac/sha512.c +++ b/applications/external/totp/services/hmac/sha512.c @@ -27,13 +27,10 @@ #include #include "byteswap.h" +#include "sha_pad_buffer.h" #define SWAP(n) swap_uint64(n) -/* This array contains the bytes used to pad the buffer to the next - 128-byte boundary. */ -static const unsigned char fillbuf[128] = {0x80, 0 /* , 0, 0, ... */}; - /* Takes a pointer to a 512 bit block of data (eight 64 bit ints) and initializes it to the start constants of the SHA512 algorithm. This @@ -90,7 +87,7 @@ static void sha512_conclude_ctx(struct sha512_ctx* ctx) { SWAP(u64or(u64shl(ctx->total[1], 3), u64shr(ctx->total[0], 61)))); set_uint64((char*)&ctx->buffer[size - 1], SWAP(u64shl(ctx->total[0], 3))); - memcpy(&((char*)ctx->buffer)[bytes], fillbuf, (size - 2) * 8 - bytes); + sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 8 - bytes); /* Process last bytes. */ sha512_process_block(ctx->buffer, size * 8, ctx); diff --git a/applications/external/totp/services/hmac/sha_pad_buffer.c b/applications/external/totp/services/hmac/sha_pad_buffer.c new file mode 100644 index 000000000..badedbcc7 --- /dev/null +++ b/applications/external/totp/services/hmac/sha_pad_buffer.c @@ -0,0 +1,11 @@ +#include "sha_pad_buffer.h" +#include + +void sha_pad_buffer(uint8_t* buffer, size_t size) { + if(size > 0) { + buffer[0] = 0x80; + if(size > 1) { + memset(&buffer[1], 0, size - 1); + } + } +} \ No newline at end of file diff --git a/applications/external/totp/services/hmac/sha_pad_buffer.h b/applications/external/totp/services/hmac/sha_pad_buffer.h new file mode 100644 index 000000000..7dba40fa9 --- /dev/null +++ b/applications/external/totp/services/hmac/sha_pad_buffer.h @@ -0,0 +1,4 @@ +#include +#include + +void sha_pad_buffer(uint8_t* buffer, size_t size); \ No newline at end of file diff --git a/applications/external/totp/types/token_info.c b/applications/external/totp/types/token_info.c index 2f108033b..6810d0211 100644 --- a/applications/external/totp/types/token_info.c +++ b/applications/external/totp/types/token_info.c @@ -117,6 +117,27 @@ bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str) return false; } +bool token_info_set_algo_from_int(TokenInfo* token_info, uint8_t algo_code) { + switch(algo_code) { + case SHA1: + token_info->algo = SHA1; + break; + case SHA256: + token_info->algo = SHA256; + break; + case SHA512: + token_info->algo = SHA512; + break; + case STEAM: + token_info->algo = STEAM; + break; + default: + return false; + } + + return true; +} + char* token_info_get_algo_as_cstr(const TokenInfo* token_info) { switch(token_info->algo) { case SHA1: diff --git a/applications/external/totp/types/token_info.h b/applications/external/totp/types/token_info.h index 138ad32b1..0d73dd061 100644 --- a/applications/external/totp/types/token_info.h +++ b/applications/external/totp/types/token_info.h @@ -168,7 +168,7 @@ void token_info_free(TokenInfo* token_info); /** * @brief Encrypts & sets plain token secret to the given instance of \c TokenInfo * @param token_info instance where secret should be updated - * @param base32_token_secret plain token secret in Base32 format + * @param plain_token_secret plain token secret * @param token_secret_length plain token secret length * @param plain_token_secret_encoding plain token secret encoding * @param iv initialization vecor (IV) to be used for encryption @@ -201,10 +201,18 @@ bool token_info_set_duration_from_int(TokenInfo* token_info, uint8_t duration); * @brief Sets token hashing algorithm from \c str value * @param token_info instance whichs token hashing algorithm should be updated * @param str desired token algorithm - * @return \c true if token hahsing algorithm has been updated; \c false otherwise + * @return \c true if token hashing algorithm has been updated; \c false otherwise */ bool token_info_set_algo_from_str(TokenInfo* token_info, const FuriString* str); +/** + * @brief Sets token hashing algorithm from \c algo_code code + * @param token_info instance whichs token hashing algorithm should be updated + * @param algo_code desired token algorithm code + * @return \c true if token hashing algorithm has been updated; \c false otherwise + */ +bool token_info_set_algo_from_int(TokenInfo* token_info, uint8_t algo_code); + /** * @brief Gets token hahsing algorithm name as C-string * @param token_info instance which token hahsing algorithm name should be returned From e42aec68c533e6120f0e776e8bdc5d7d89e99c8b Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Fri, 28 Apr 2023 17:25:20 +0300 Subject: [PATCH 150/282] Disable ci/cd on release* branches (#2624) --- .github/workflows/build.yml | 7 +++---- .github/workflows/lint_and_submodule_check.yml | 5 ++--- .github/workflows/merge_report.yml | 2 +- .github/workflows/pvs_studio.yml | 3 +-- .github/workflows/unit_tests.yml | 2 +- .github/workflows/updater_test.yml | 6 +++--- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dfeb8d83f..0934eec76 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,6 @@ on: push: branches: - dev - - "release*" tags: - '*' pull_request: @@ -19,7 +18,7 @@ jobs: runs-on: [self-hosted,FlipperZeroShell] steps: - name: 'Wipe workspace' - run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - name: 'Checkout code' uses: actions/checkout@v3 @@ -167,7 +166,7 @@ jobs: target: [f7, f18] steps: - name: 'Wipe workspace' - run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - name: 'Checkout code' uses: actions/checkout@v3 @@ -207,7 +206,7 @@ jobs: cd testapp ufbt create APPID=testapp ufbt - + - name: Build example & external apps with uFBT run: | for appdir in 'applications/external' 'applications/examples'; do diff --git a/.github/workflows/lint_and_submodule_check.yml b/.github/workflows/lint_and_submodule_check.yml index 999111cc9..22ca7d893 100644 --- a/.github/workflows/lint_and_submodule_check.yml +++ b/.github/workflows/lint_and_submodule_check.yml @@ -4,7 +4,6 @@ on: push: branches: - dev - - "release*" tags: - '*' pull_request: @@ -19,7 +18,7 @@ jobs: runs-on: [self-hosted,FlipperZeroShell] steps: - name: 'Wipe workspace' - run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - name: 'Checkout code' uses: actions/checkout@v3 @@ -64,7 +63,7 @@ jobs: else echo "Python Lint: all good ✨" >> $GITHUB_STEP_SUMMARY; fi - + - name: 'Check C++ code formatting' id: syntax_check_cpp if: always() diff --git a/.github/workflows/merge_report.yml b/.github/workflows/merge_report.yml index 5b7d5fcbf..020166666 100644 --- a/.github/workflows/merge_report.yml +++ b/.github/workflows/merge_report.yml @@ -13,7 +13,7 @@ jobs: runs-on: [self-hosted,FlipperZeroShell] steps: - name: 'Wipe workspace' - run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - name: 'Checkout code' uses: actions/checkout@v3 diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index b8c4d7a36..cb5b50278 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -4,7 +4,6 @@ on: push: branches: - dev - - "release*" tags: - '*' pull_request: @@ -20,7 +19,7 @@ jobs: runs-on: [self-hosted, FlipperZeroShell] steps: - name: 'Wipe workspace' - run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - name: 'Checkout code' uses: actions/checkout@v3 diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 6a824fac3..81f0e0d05 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -13,7 +13,7 @@ jobs: runs-on: [self-hosted, FlipperZeroUnitTest] steps: - name: 'Wipe workspace' - run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - name: Checkout code uses: actions/checkout@v3 diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml index 2861529d8..bd8372979 100644 --- a/.github/workflows/updater_test.yml +++ b/.github/workflows/updater_test.yml @@ -13,13 +13,13 @@ jobs: runs-on: [self-hosted, FlipperZeroUpdaterTest] steps: - name: 'Wipe workspace' - run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - name: Checkout code uses: actions/checkout@v3 with: fetch-depth: 1 - submodules: false + submodules: false ref: ${{ github.event.pull_request.head.sha }} - name: 'Get flipper from device manager (mock)' @@ -50,7 +50,7 @@ jobs: echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT - name: 'Wipe workspace' - run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - name: 'Checkout latest release' uses: actions/checkout@v3 From 6a0e813b6c6599d30d4d23ef572c0e73455b90ad Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 28 Apr 2023 16:53:09 +0100 Subject: [PATCH 151/282] Add option to show seconds on lockscreen --- .../xtreme_app_scene_interface_lockscreen.c | 17 ++++++++++++ .../desktop/views/desktop_view_locked.c | 26 +++++++++++------- applications/services/xtreme/settings.c | 1 + applications/services/xtreme/settings.h | 3 +- assets/icons/Interface/Lockscreen.png | Bin 5453 -> 12122 bytes 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c index 5544ee941..59ac287fc 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c @@ -18,6 +18,14 @@ static void xtreme_app_scene_interface_lockscreen_show_time_changed(VariableItem app->save_settings = true; } +static void xtreme_app_scene_interface_lockscreen_show_seconds_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->lockscreen_seconds = value; + app->save_settings = true; +} + static void xtreme_app_scene_interface_lockscreen_show_date_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); @@ -57,6 +65,15 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->lockscreen_time); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_time ? "ON" : "OFF"); + item = variable_item_list_add( + var_item_list, + "Show Seconds", + 2, + xtreme_app_scene_interface_lockscreen_show_seconds_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->lockscreen_seconds); + variable_item_set_current_value_text(item, xtreme_settings->lockscreen_seconds ? "ON" : "OFF"); + item = variable_item_list_add( var_item_list, "Show Date", diff --git a/applications/services/desktop/views/desktop_view_locked.c b/applications/services/desktop/views/desktop_view_locked.c index 9a24414f7..6d3aeb819 100644 --- a/applications/services/desktop/views/desktop_view_locked.c +++ b/applications/services/desktop/views/desktop_view_locked.c @@ -67,22 +67,24 @@ void desktop_view_locked_draw_lockscreen(Canvas* canvas, void* m) { DesktopViewLockedModel* model = m; int y = model->cover_offset; char time_str[9]; + char second_str[5]; char date_str[14]; char meridian_str[3]; FuriHalRtcDateTime datetime; furi_hal_rtc_get_datetime(&datetime); LocaleTimeFormat time_format = locale_get_time_format(); LocaleDateFormat date_format = locale_get_date_format(); + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + bool pm; if(time_format == LocaleTimeFormat24h) { - snprintf(time_str, 9, "%.2d:%.2d", datetime.hour, datetime.minute); + pm = false; } else { - bool pm = datetime.hour > 12; - bool pm12 = datetime.hour >= 12; - snprintf( - time_str, 9, "%.2d:%.2d", pm ? datetime.hour - 12 : datetime.hour, datetime.minute); - snprintf(meridian_str, 3, pm12 ? "PM" : "AM"); + pm = datetime.hour > 12; + snprintf(meridian_str, 3, datetime.hour >= 12 ? "PM" : "AM"); } + snprintf(time_str, 9, "%.2d:%.2d", pm ? datetime.hour - 12 : datetime.hour, datetime.minute); + snprintf(second_str, 5, ":%.2d", datetime.second); if(date_format == LocaleDateFormatYMD) { snprintf(date_str, 14, "%.4d-%.2d-%.2d", datetime.year, datetime.month, datetime.day); @@ -92,15 +94,19 @@ void desktop_view_locked_draw_lockscreen(Canvas* canvas, void* m) { snprintf(date_str, 14, "%.2d-%.2d-%.4d", datetime.day, datetime.month, datetime.year); } - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); canvas_draw_icon(canvas, 0, 0 + y, XTREME_ASSETS()->I_Lockscreen); if(xtreme_settings->lockscreen_time) { canvas_set_font(canvas, FontBigNumbers); canvas_draw_str(canvas, 0, 64 + y, time_str); - if(time_format == LocaleTimeFormat12h) { - int meridian_offset = canvas_string_width(canvas, time_str) + 2; + int offset = canvas_string_width(canvas, time_str) + 2; + if(xtreme_settings->lockscreen_seconds) { canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 0 + meridian_offset, 64 + y, meridian_str); + canvas_draw_str(canvas, 0 + offset, 64 + y, second_str); + offset += canvas_string_width(canvas, ":00") + 2; + } + if(time_format == LocaleTimeFormat12h) { + canvas_set_font(canvas, FontKeyboard); + canvas_draw_str(canvas, 0 + offset, 64 + y, meridian_str); } } if(xtreme_settings->lockscreen_date) { diff --git a/applications/services/xtreme/settings.c b/applications/services/xtreme/settings.c index a5b7ee6cb..ec1f89ecf 100644 --- a/applications/services/xtreme/settings.c +++ b/applications/services/xtreme/settings.c @@ -51,6 +51,7 @@ void XTREME_SETTINGS_LOAD() { xtreme_settings->fallback_anim = true; // ON xtreme_settings->wii_menu = true; // ON xtreme_settings->lockscreen_time = true; // ON + xtreme_settings->lockscreen_seconds = false; // ON xtreme_settings->lockscreen_date = true; // ON xtreme_settings->lockscreen_statusbar = true; // ON xtreme_settings->lockscreen_prompt = true; // ON diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h index ea4838e5a..0261b68ae 100644 --- a/applications/services/xtreme/settings.h +++ b/applications/services/xtreme/settings.h @@ -13,7 +13,7 @@ extern "C" { #define MAX_PACK_NAME_LEN 32 -#define XTREME_SETTINGS_VERSION (10) +#define XTREME_SETTINGS_VERSION (11) #define XTREME_SETTINGS_MAGIC (0x69) #define XTREME_SETTINGS_OLD_INT_PATH INT_PATH(".xtreme.settings") #define XTREME_SETTINGS_OLD_PATH EXT_PATH(".xtreme.settings") @@ -29,6 +29,7 @@ typedef struct { bool fallback_anim; bool wii_menu; bool lockscreen_time; + bool lockscreen_seconds; bool lockscreen_date; bool lockscreen_statusbar; bool lockscreen_prompt; diff --git a/assets/icons/Interface/Lockscreen.png b/assets/icons/Interface/Lockscreen.png index c9aa8bcd7e906e0fcd650f1437884709a7b2ee28..ab4f0a739ca48280c52537153d9f84655f808828 100644 GIT binary patch delta 7828 zcmV;F9&6#vD%xIaB^>EX>4U6ba`-PAZ2)IW&i+q+P#};k|VdW ztp8&bcL|9ZSPn+ByMwp<`#F%T9@MR))-iikYLZ1Jkw6R{?g7ZG|M$P!{0~3byT^o> zOR6b7{Dc~+YrH7W{b}dXKH)r`pU+49{lohvd_Hiz<$3n+7k|=w{pWrAHNbspyo%oq zRL1*3|M7$R-ocfh26}JgH6zE1`3_XyfxNpP*mr<-m-VDt^7l*V>psuVO3M9b^x*Ru z`)iP&{|*`J&B&YHx#g{Yc=X=g33&bU#y4IB9-S{-Xno?fKkr2UnF$8@m7TtRDl^)e zYWU?vKX~Qqm4E%U7tPP!3huqc|77I!_592Cjg7zd$}#y6VRs-YJ-kJ= zEg4hkmG5RjKvo`WET8t=^+|Ii+gpxvt{IoJskXVx=*z~{ZNt1C!`nS;&SS5bW&de_ z+g-t8{18gA(}l60+_#@h$*+Nd`vUX}?|)(MXm=^L4L@mQ`@v2(|^y>MXABWm9)^niWUvOHnfRTrHOEMyxZZQC8S4 ztekdJ?ce#Ard-E{ts7=aOui<}CF(vs`!VKTnF-{P(`4P=_PloSz5AM&D>Ld)n|~av zIA30-)ZIho6V}PB(`-HQ9+pgQ_dZ4+yTePc>ax`5?KERw;WW#)ls#XV#Rn5++F_B! ztL!JW*}|0V+(;R>#nHR1o=GLwvgjZSruLO4s`Y)880_O0UxOwc=gv;H_JQ5WEVxqG zHIg@EERU|^_7t~|*;|s4B+lG7k$+wW+!)6MaXwm;4Sm?YoM#Ttba1tq@|u((zt(n( z^|~=?@2S<~M`ww?5TD=?;nKg&l1yf?ca9=&gq(8>c;?tD&JMF=6l=G4o!;V9Xl`oGtHcS4@N=HdIyHPH z0JkA;2%e$z+l|~=DS()fS%0Us;=^STe!5`{>%o6iEyrvD_`Ts-XI3Ekz}Q-4S1yGU zSo)iVkYS+rCe27xQm!q3EHt4LB0U&`s+%{|kPoVhCZ7*RT10`C&^90FD{%@pusPtu zJgOF?`+Jazq$GyOK|8l)op{&u$H!-P5`yf?b?#m%iI^X5K$bSbHGflN!Vb)Om18H( zzni8{mrvOFR*Qj$=)&tMZ zeP%KD>47Ov#xm~I-+%TbKd~C21b|2F5Us!zpNNHS0F1T3VjnfBh`nTv1pqx_pFTMR;Lh3Wo899m_A~$G9UjZ{!hoA^$ENe#izWBsQXo zSS&yiV@NMCDCLUs{`cxowCm}(=MR*3)`&@&)p2Lt;QDVxdsE96%(i;%NM(Wcy5 z0LIwmLr>G~i+{`e1Y_#NX$q{EDmp?Xwg!1j`BHVtJ8D_>IoZMTSwu1L&aJxZPOw*K zD*#0Z9y@SnNPwU~Nz;iCeYPr#B-lv*JyMw=qccqNokE^HrUu)SBN)01aRKF2o{Z1< zii6z3QlJf^Qg8^BCcGI31i&v4z>qqyQ8=Uu7kHglBY$b|7{(=6tlev&NqGq=Ak+Hv z@XxisSQ8(h6_X*Hi}i}>*n2z$pSw~Mfv!+4XYx?fPTI?h1Z+H+Hu)Bnk#PZyQVbl1 zq_s-yrP)y$m|Fr{xI4wn!C+(Qkn!-ara(G(R-j_qu+|h32!&1Tp%Q_etA|KK2TZ&p zFPaDu{C`&dcD_Q_RN4xFB;1Znr&09!wodkv%FZMvB-tzUwr`ZkO09q>C5VRthL0w) zkYeJ8w<>qfAvolu>_VMny+&Z+2k1biX(G200RRvpkTW-tpo7+Xl#@iTF@gZYA0|cA z6#O<$6&V7=ohoaCdjQvoqV6v|Y{Hp&K|#Re6Mt(gM%j_&1kf2{92hAfLr5y+F`qAD zE-^2_vf_X~nHzIH1Gwj;D-?%I5N3qtTWzx>gjh0FgmBD&jyS=Zr4hH`~JJg)_>r0hhc(6ZO+*Z0EN6nD^qh?xyV#J5rM(Pj*;g{yeKj{;R^Gm_Djf9e97U+ zmw!%KFsCv>%Cp=l^kI63i&jYOp!5^+;$n+}XHQ*dJA(F<2{QKEyv;wGxE0FDY2RFS z944BB%eK7s7l%>1VH3eG@ABo&7)@5>1EvGj--P?zmm3v`6*U4{LyGPY4u7^=9wgYr zPLi;T!31lWxGZ72ZkhX*YUV2BlM|c$PFaNw)>7dHxUsIT%Fy|#DR$WU2rX<1(Rp&m-o7ev;nTmWIa@&hxD60)hi#BIpz(CA5BWBrh^{+3}#w_8V?Ocl$%3N=Cu zgK7ei*bT-ssGP9cr1qN7Bq^MX+6Nu_E;a}bbOjT}RGUr=5yzpdmlctu9JBBYq7gX# zRBSmD(n1w9?#MmXiGN4basrJ+EcB=2OhzqIYQX&hR$mrP0bRBnpBvd#PD*7{HIifZAsj#EU8yeFT9G19zNNoR^5as&LO215UdGO&Spu}YM04vTq#cd!0y==73;@6hU9e4ksR5o!5P)Hm7Gyy=d_#%D~;51!A5EliHu}bm0)`lu)(;{1?tR& zg#sWndkcOtm^u#SR5Q}?2uX{Xqa)v6&b*R1Il{^aabC)teNgu>#+Nb!H{Oaf*CXn9 zGppSV$^p(SnjD!~YFchIK5GV!08X*139)PGJ%1vFYVlPlUA)w2`%Opm(-1W$WKN6e zKh@ITQIS98kcW;0ZL;(?MBUS?5C+%*kLKUC3aoZbk=K+AvCPhlIaP5lY#-ZG1;WmhRF$15p-4a+BIoZSBXu=J?85V1&dy z6o2c@T=G_JF)#@>-A0ZDVil8Go-ZD7OYKX5!OGo)iM=O74JaPkR?0$)uw~9b-Pet? zDp!GeX(!@SxO79uO6*PTF6JIg)xMfjgC2w=zy(iXPwlsfrC(fvxolbR{DrIfcwP}G znsyOV%~%GUBnnM;%)k{zQTKfcH<>eewUA_J400 z8Q&7Xd@`U3?3+s`p6nK=kupJZ?5qHHa26hGfm19@*j zLNN#_{$YlFOEB}xb^Qx-o3nxO7jrY|{AU1cevQEYT*SBMPT%}x{y)zC`%wAS)FH+~ zUWA`)@gVW+-8>M}Cor)$*qg-$o3gFfpsm$96ua&%bU`P5=4xIW@MeEgCx29$B(f=$ zqJl2BLt9{pBS2~VKpbRheZhdYwee6RS3f9fk}CRX7@(or5Y0PDc^dKl#BZv|7R1PD z4vADH9K2=ug&qM869b~M2X#_d^6uoR1u|d-tT97c!G5$00V3WHs*}2zFhxPADW}2= zJONo0$oSmy1tTTE6VeBqQ-3?MHnUlgRz-N|2!|RwUcMbRzSMkQg@1hZQ33USYTeyN zRhRu}ML!KASUkS`Be^Nc7uwk)AR8reM*i#(Xw%bck!Onlc?e~K9h@zKkPM+3wMP)o zCZ6B}U%}E*@hEHOk=0_e?G#FA!iZj5Q!3XVHX34B)HNxAf?O+hBwrV`h_oy2>e@G^ zw5`FPEa~i0sgW`zvwtuaEOlKiBBh9;ol{bjX+Z(t_hb(9JuT=NrB>SADSjd{<20cy z)pX!eX(RL0wG6@lCWDY6b6Xl+@`lN}iQ5oV zOQ)HJAw3XLX@3-i4M`BAG9g`v4}Wm*#{A96n?HIspBEx?=$$Ab1ehZ^0Bjv&-bj!7 z*(=w9EF6LEppu}xiUN5>q?nO`uzOj2qYBd2Jw$O11?;R)AYlom9?yQkITN5J@%AcD zA;H3^T+TcRgzUvB&*}^@Ya7C=$`S`)6P&?{buX2j?tf>&k}y}YX)BVsK43$Kgiy~a z2iPxV0a8SAr!i54>p{xUlG&}k``HDP-8kAm8ZI6$4${rcQ)CC zN-p0mQ@~7}H!#PMp*lgtTEk$4i^7I~4NR>lPQh25`k-`b#6Kse=4GeX@vnBfn*-}{ zrvve{RDZ-CYIEApV&(0$fZ#2v0V6wj#DMvC6ewg%pe=$1AA?x0@`re94&Z*_?Rzlo zyoP&wd(otJ4sKECF;3CJttZ1bSKBvCwQD&JBhg~l zZQYPARB@fTN)$55YHLnYLg(=2j^r%ywGMDcHGh9@I1vK@NPtl$Ed1`2LOXP*hcHLM zd0z~WHd;F=3uhpb%jv|<_MA$24vIbH@>h!4?UWG--;Q}v)?C=8Ym-8aj2*nT=eLe5 zKI*Wj-6Q`~$4{S^s_I)!XFo@{45$JC=wxl-Clk^G*Lg7bIRl7uUR$eUDW{#b3M)ZM z!+)Ip32s>@tLk0?KL%dw1Q+jUH+QIC71h@AH=34=yb&iQ`nQjvyoFh;d(<<>>e7~) zTc!Z(Ug0clsQnRbs*CuBtdn`HX`Qy)o{!o;>L3Nxz&4<;X|fd1)~TbFkg6dg71xcU z`FsV^pFErI-#FV3D*U;B`P`8V61&IehkvWp^7`CSyXxcJak(%XSSHQ-t9tW2XlixD zTK)MpyeP%c(L_9Ai?^hKql70n;nJ=`LSr%>YdM`wAt9iZveiCkfr?5Kn7`JkQ2++P z)K%#YL51Y0Yuu`fYFTarNrGwj9<)P)(HTTdN*~W5fIQ@_ypT0w;`MY8b;{&(cz;!7 z60C!;NA8~AGm_pn9eE*~y%?b~~Jx_<9I- zP{EP;YAAwtDU0tT4c?x_?+!khe{}Lm?Nq0lj@Bp5SoZ0P!s6>axnVa@i&iA7kP13( z2=>|JpLoIx>IB)%Ib7}Xw`JG-&3~~cf5qnKAfk5T%bDeS7?J9D&Wx#n`xu==%k7-{ zRTB@qD)8M+22}^7Djz5p+R+Hl2ljMO@pLcF6puD|_eG-GnnxU{LTHLd96ZgJs8(=* zsXmSJfSt9q+IJE69_PsHJ?3LRht}>nAA;eWS-S^)KH@BDC*eOh#N2ZQ1%IM8&t|X! zyJIV-M(-`r!VFAENwnIv#rmGY^bo%t_fvr^&`4d=<{Nl3PR+k{ksFg@tXf(QRwfRj zlL%3Yarm>_H1t0YbRF(HC(aF8m}1Kyr%_)&5scC$XVEy`r7!)29?ylcD_&t`R;sW z9TBB~eLItU+jH$OjZ+`9xW>_QXkl$afw@PeXn#=sd*Yr7M`=)TAab(y0Ooj)%Bmpf zOsk#w><*m_49yw;Ia2#y{x1XOpIzub2Y0{Q3Hh;^X08t&g!*mgpns{bxlCA8)pd|; z>s+#W8L*PcAW;$#oJR12p1rO@oiWc<={G^=JxzK9NzZpP`6#x!?`hjwbg*ZkB5e|6A|JJ;BxK<6z_fYKvb!V?pF2`i^?womA3KvzMv<0maFOmHf741u zDjn<~r9+15WET~2tW_vNg-|QB>R@u|7c^-}Qd}Gb*Mfr|i&X~~XI&j!1wrrw#MQ+~ z(M3x9Us7lh9qpE$xQvPyhTJYmoUi66PHxctVs z?6ANyBStPePaGi@%YCf$F{>CF@icK<)pW`i@-C~Kw>WFHChOjlzc5tPSC+X>Gln!4 zu>^@aQqV*dHCRZ{s*_?OOZzDgf7J0yd+uNe(*o|-K|-gf0}fYLMdS2 z#kN1jfZ#6BY1#Jov2Axw0RJ;^rT6^xHZc21daI|!kAUztaB7$cF4l zewsq51iYWoHx+=9TVQC-?XA0y(+40&U1e^7gF|4vOxf!m?;h;$?cX!q{(b?zX#01?7zR9JLaO-wptvx@?*0h4J2Dw8<`Fq4x6=Oi*>GiG6BWn?WfW@Kb7G%;Z~ zEjc(bW-T~3HDfbjGGSpgG+~pT1yGX*20cMIGB!CjFgY|cFgP$cHX;fjARtFcO;9>k zWpZoV=Xr@Ghr<>V=`eaVlpx|EjVUoV>x9vIX5w5WRnmH z9g`mlD-bj-GB{K*HaasjIyE#aFfp@Q3JVD%uDLs?00006P)t-s|Ns900033O(|!N| z00Cl4M??UK1szC}dlMgj1RM_{HcO6M`2YX`k4Z#9R5;7slfO&DKorNnNs3XB#zp85 zn$DdHE*(lO_!kIz~QO_yf?r}4B$%IOtT=Rtzr7;0am7UbHjswCizf&-kE4{Vbfi} zPV5{U(W;I8GQjr)WHivgA#S}$oG}=yjDV}IWsGTpff8b>SUzP7VdknZmkb_envlOE`~z~^rC z4N&JNG!5Xgzh?g2b>r+Zuq1i)4M0tf(Kf*ljlB@o26SmNl-eesi9$)krB%`hXdGYy mJ?~ZEOez55DuDWnKkXN_gpku}Rxz#s0000aB^>EX>4U6ba`-PAZ2)IW&i+q+HFwV5d$Fz z{HKbPAdmB~94}{P(m~1(3TDnEJ0q-%t_ZZ(^8vRGLIK%!%_Zk#kRpYu5lxOdA|@Ge zV3usZ=peH|Cb4CX3pvhD9X|%1sPY~aZkw4@Qq1L7 z#okv0F@y->2+7Pc<|HW%-|=;i0AKIoJj?&wpJPBRSPTe=#0h_9m^SeS@yw=eaNZ}5 zva+lapA%1*bV1@rt}7nDaW1(m@XV;0&CC-=iN#VED_zXWrbawX98)!&@`aqoD(5ZE zTD8vF_v9}O7xa~7uG1Vv3X52R1Q81AsGtg)`l>)D#X^SmlRo|t*DsMvAy);A91EyH zgY5dj|KNAGR&jr7(n|^_fzXTNe2f9TyFjbqIN!&P(>eiy&%l-5@z?i4sjutxt z2DgEW>yD=E0hc?#@RK2%vMU8?3dJJuen#Jv2O_sX|C-lZdmpC{K$f~n-v9@Pz<7zW z*L~jI*WKH{XWIS!0DM4ls>-C0^8f%4-)U4>bXZMHI%Bh|8?FJ9gB>uFSsf~qvmNIo zF*7(bW@IurEoEggH7ztZHD)bhWnp72WjAFpVPZHmWMMXAlfE8M6l7yMW^ZzBVRStr zX>DO=WiN1UXOjsZLMS&mF*h(WHaIvlGB+|fA_^cNAV*0}P&!s+a&u{KZapG0E-^4J zGc=QoAIUy5Ix#p@GB7$ZF*-0fD=;uRFfbwtARr)kZE!kGZ)9m^c|>7!Wj!J?Ffubb zFf%$aI8-t)Ix#UiFgPnPFtd{&@&co1BncMjB(JOh000J1OjJex|Nj6009C@%ev^wW zAAbW96blH9B|ZxP00ELoL_t(I%jJ{5OT$1E$G=I6ryz}s&>=LPI~80ylv?mF5DZyF zGCLJqx@Ze-{v*N_?Ak%({sjlYG2$S&7?48I*z>)lq?e4YKDf*A$v5xbuK@qT(q#o4 zt^g-Cfcr+9sS=8|h5`Hpuu{`Uk9=r$B7bsE2TeNq>||$>feRa`jXgTqIXL3&dr}70 zivl?qWWXVAsR(Bb#yTY+^tEbX8e^cPoNBgD(g&%~L2fBH45_~)S(`*L{0AW9_s6`# z3B;3zT-%MjrCZ)5*53!=58a2$AX7}wrJSwq3G{Qt$$k)=y3bst0JBxDm^+-Pkbf9C z3%(;vkZ}BuP;lAJJVJ-F`{1+|q@b*_dS+GjrM$xkH9kjK=2|DXg2TsQQR7#D`_L(X zQfSemy8)f7SDQ|$ZHhC2SAdultkRslTjM&AH+4xwoP!Ty?g zxa-E*Wnd}t>KTBV9HDK3AsTxDtTqkk(kQh}KofRLsAe;9pa3&RiaTP%S b;m7&~9D|XzFsLxu00000NkvXXu0mjfq+c*Z From 72a4fb6cb6ab7195af962cb8e638cfaeff73357f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 28 Apr 2023 16:53:49 +0100 Subject: [PATCH 152/282] Begone, goofy ahh else-if chain --- applications/services/gui/canvas.c | 31 ++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index fc4064282..193218c0b 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -140,18 +140,25 @@ void canvas_invert_color(Canvas* canvas) { void canvas_set_font(Canvas* canvas, Font font) { furi_assert(canvas); u8g2_SetFontMode(&canvas->fb, 1); - if(font == FontPrimary) { - u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr); - } else if(font == FontSecondary) { - u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr); - } else if(font == FontKeyboard) { - u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mr); - } else if(font == FontBigNumbers) { - u8g2_SetFont(&canvas->fb, u8g2_font_profont22_tn); - } else if(font == FontBatteryPercent) { - u8g2_SetFont(&canvas->fb, u8g2_font_5x7_tf); //u8g2_font_micro_tr); - } else { - furi_crash(NULL); + switch(font) { + case FontPrimary: + u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr); + break; + case FontSecondary: + u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr); + break; + case FontKeyboard: + u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mr); + break; + case FontBigNumbers: + u8g2_SetFont(&canvas->fb, u8g2_font_profont22_tn); + break; + case FontBatteryPercent: + u8g2_SetFont(&canvas->fb, u8g2_font_5x7_tf); //u8g2_font_micro_tr); + break; + default: + furi_crash(NULL); + break; } } From 8a5d88fc9a73ac66f8607f3c51116f5321f99095 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 28 Apr 2023 16:57:17 +0100 Subject: [PATCH 153/282] Format --- .../scenes/xtreme_app_scene_misc_rename.c | 3 +- .../desktop/views/desktop_view_lock_menu.c | 3 +- applications/services/gui/canvas.c | 36 +++++++++---------- .../services/gui/modules/text_input.c | 20 +++++------ .../services/gui/modules/text_input.h | 4 +-- applications/settings/about/about.c | 4 +-- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c index 9f034ea56..47a0fb607 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_rename.c @@ -12,7 +12,8 @@ static void xtreme_app_scene_misc_rename_text_input_callback(void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); } -static bool xtreme_app_scene_misc_rename_validator(const char* text, FuriString* error, void* context) { +static bool + xtreme_app_scene_misc_rename_validator(const char* text, FuriString* error, void* context) { UNUSED(context); for(; *text; ++text) { diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 23079bcae..334cf9376 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -303,7 +303,8 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { desktop_event = DesktopLockMenuEventXtreme; break; case DesktopLockMenuIndexVolume: - desktop_event = stealth_mode ? DesktopLockMenuEventStealthModeOff : DesktopLockMenuEventStealthModeOn; + desktop_event = stealth_mode ? DesktopLockMenuEventStealthModeOff : + DesktopLockMenuEventStealthModeOn; break; default: break; diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 193218c0b..98fd96e8c 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -141,24 +141,24 @@ void canvas_set_font(Canvas* canvas, Font font) { furi_assert(canvas); u8g2_SetFontMode(&canvas->fb, 1); switch(font) { - case FontPrimary: - u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr); - break; - case FontSecondary: - u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr); - break; - case FontKeyboard: - u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mr); - break; - case FontBigNumbers: - u8g2_SetFont(&canvas->fb, u8g2_font_profont22_tn); - break; - case FontBatteryPercent: - u8g2_SetFont(&canvas->fb, u8g2_font_5x7_tf); //u8g2_font_micro_tr); - break; - default: - furi_crash(NULL); - break; + case FontPrimary: + u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr); + break; + case FontSecondary: + u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr); + break; + case FontKeyboard: + u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mr); + break; + case FontBigNumbers: + u8g2_SetFont(&canvas->fb, u8g2_font_profont22_tn); + break; + case FontBatteryPercent: + u8g2_SetFont(&canvas->fb, u8g2_font_5x7_tf); //u8g2_font_micro_tr); + break; + default: + furi_crash(NULL); + break; } } diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 569213fa2..520526e48 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -373,10 +373,12 @@ static void text_input_handle_up(TextInput* text_input, TextInputModel* model) { if(model->selected_row > 0) { model->selected_row--; if(model->selected_row == 0 && - model->selected_column > get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 6) { + model->selected_column > + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 6) { model->selected_column = model->selected_column + 1; } - if(model->selected_row == 1 && model->selected_keyboard == symbol_keyboard.keyboard_index) { + if(model->selected_row == 1 && + model->selected_keyboard == symbol_keyboard.keyboard_index) { if(model->selected_column > 5) model->selected_column += 2; else if(model->selected_column > 1) @@ -395,10 +397,12 @@ static void text_input_handle_down(TextInput* text_input, TextInputModel* model) } else if(model->selected_row < keyboard_row_count - 1) { model->selected_row++; if(model->selected_row == 1 && - model->selected_column > get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 4) { + model->selected_column > + get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 4) { model->selected_column = model->selected_column - 1; } - if(model->selected_row == 2 && model->selected_keyboard == symbol_keyboard.keyboard_index) { + if(model->selected_row == 2 && + model->selected_keyboard == symbol_keyboard.keyboard_index) { if(model->selected_column > 7) model->selected_column -= 2; else if(model->selected_column > 1) @@ -688,15 +692,11 @@ void text_input_set_result_callback( true); } -void text_input_set_minimum_length( - TextInput* text_input, - size_t minimum_length) { +void text_input_set_minimum_length(TextInput* text_input, size_t minimum_length) { with_view_model( text_input->view, TextInputModel * model, - { - model->minimum_length = minimum_length; - }, + { model->minimum_length = minimum_length; }, true); } diff --git a/applications/services/gui/modules/text_input.h b/applications/services/gui/modules/text_input.h index 5c5d8365b..65207532e 100644 --- a/applications/services/gui/modules/text_input.h +++ b/applications/services/gui/modules/text_input.h @@ -70,9 +70,7 @@ void text_input_set_validator( TextInputValidatorCallback callback, void* callback_context); -void text_input_set_minimum_length( - TextInput* text_input, - size_t minimum_length); +void text_input_set_minimum_length(TextInput* text_input, size_t minimum_length); TextInputValidatorCallback text_input_get_validator_callback(TextInput* text_input); diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index e932b58ce..20263e887 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -228,8 +228,8 @@ static void draw_battery(Canvas* canvas, PowerInfo* info, int x, int y) { value, sizeof(value), "%lu.%luV", - (uint32_t)(info->voltage_battery_charge_limit), - (uint32_t)(info->voltage_battery_charge_limit * 10) % 10); + (uint32_t)(info->voltage_battery_charge_limit), + (uint32_t)(info->voltage_battery_charge_limit * 10) % 10); } else { snprintf(header, sizeof(header), "Charged!"); } From 7adb7e2ded86f57a853632ed00efbd46b18df60b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 28 Apr 2023 19:50:04 +0100 Subject: [PATCH 154/282] Fix internal storage bugs with restoring U2F keys --- .../services/storage/storages/storage_int.c | 100 ++++++++++-------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/applications/services/storage/storages/storage_int.c b/applications/services/storage/storages/storage_int.c index adf016f4e..59e4ea7f1 100644 --- a/applications/services/storage/storages/storage_int.c +++ b/applications/services/storage/storages/storage_int.c @@ -193,57 +193,71 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) { was_fingerprint_outdated; if(need_format) { - err = lfs_mount(lfs, &lfs_data->config); - if(err == 0) { + // Backup U2F keys + lfs_file_t file; + uint8_t* cnt = NULL; + uint32_t cnt_size; + uint8_t* key = NULL; + uint32_t key_size; + if(lfs_mount(lfs, &lfs_data->config) == 0) { FURI_LOG_I(TAG, "Factory reset: Mounted for backup"); - // Backup U2F keys - lfs_file_t file; - lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_RDWR | LFS_O_CREAT); - uint8_t cnt[file.ctz.size]; - lfs_file_read(lfs, &file, cnt, sizeof(cnt)); - lfs_file_close(lfs, &file); - lfs_file_open(lfs, &file, ".key.u2f", LFS_O_RDWR | LFS_O_CREAT); - uint8_t key[file.ctz.size]; - lfs_file_read(lfs, &file, key, sizeof(key)); - lfs_file_close(lfs, &file); - - err = lfs_unmount(lfs); - if(err == 0) { - FURI_LOG_E(TAG, "Factory reset: Unmounted after backup"); - // Format storage - err = lfs_format(lfs, &lfs_data->config); - if(err == 0) { - FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount"); - furi_hal_rtc_reset_flag(FuriHalRtcFlagFactoryReset); - err = lfs_mount(lfs, &lfs_data->config); - if(err == 0) { - FURI_LOG_I(TAG, "Factory reset: Mounted"); - - // Restore U2F keys - lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_RDWR | LFS_O_CREAT); - lfs_file_write(lfs, &file, cnt, sizeof(cnt)); - lfs_file_close(lfs, &file); - lfs_file_open(lfs, &file, ".key.u2f", LFS_O_RDWR | LFS_O_CREAT); - lfs_file_write(lfs, &file, key, sizeof(key)); - lfs_file_close(lfs, &file); - - storage->status = StorageStatusOK; - } else { - FURI_LOG_E(TAG, "Factory reset: Mount after format failed"); - storage->status = StorageStatusNotMounted; - } - } else { - FURI_LOG_E(TAG, "Factory reset: Format failed"); - storage->status = StorageStatusNoFS; + if(lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_RDONLY) == 0) { + cnt_size = file.ctz.size; + cnt = malloc(cnt_size); + if(lfs_file_read(lfs, &file, cnt, cnt_size) != (int32_t)cnt_size) { + free(cnt); + cnt = NULL; } + lfs_file_close(lfs, &file); + if(lfs_file_open(lfs, &file, ".key.u2f", LFS_O_RDONLY) == 0) { + key_size = file.ctz.size; + key = malloc(key_size); + if(lfs_file_read(lfs, &file, key, key_size) != (int32_t)key_size) { + free(key); + key = NULL; + } + lfs_file_close(lfs, &file); + } + } + + if(lfs_unmount(lfs) == 0) { + FURI_LOG_E(TAG, "Factory reset: Unmounted after backup"); } else { FURI_LOG_E(TAG, "Factory reset: Unmount after backup failed"); - storage->status = StorageStatusNotMounted; } } else { FURI_LOG_E(TAG, "Factory reset: Mount for backup failed"); - storage->status = StorageStatusNotMounted; + } + + // Format storage + if(lfs_format(lfs, &lfs_data->config) == 0) { + FURI_LOG_I(TAG, "Factory reset: Format successful, trying to mount"); + furi_hal_rtc_reset_flag(FuriHalRtcFlagFactoryReset); + if(lfs_mount(lfs, &lfs_data->config) == 0) { + FURI_LOG_I(TAG, "Factory reset: Mounted"); + storage->status = StorageStatusOK; + + // Restore U2F keys + if(cnt != NULL && key != NULL) { + if(lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_WRONLY | LFS_O_CREAT) == 0) { + lfs_file_write(lfs, &file, cnt, cnt_size); + lfs_file_close(lfs, &file); + if(lfs_file_open(lfs, &file, ".key.u2f", LFS_O_WRONLY | LFS_O_CREAT) == 0) { + lfs_file_write(lfs, &file, key, key_size); + lfs_file_close(lfs, &file); + } + } + } + if(cnt != NULL) free(cnt); + if(key != NULL) free(key); + } else { + FURI_LOG_E(TAG, "Factory reset: Mount after format failed"); + storage->status = StorageStatusNotMounted; + } + } else { + FURI_LOG_E(TAG, "Factory reset: Format failed"); + storage->status = StorageStatusNoFS; } } else { // Normal From 601e82d210eb41a87ff5eebf8ea144dc5b254979 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 28 Apr 2023 19:52:56 +0100 Subject: [PATCH 155/282] Please FBT, snort some copium --- applications/services/storage/storages/storage_int.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/services/storage/storages/storage_int.c b/applications/services/storage/storages/storage_int.c index 59e4ea7f1..c1cf2c7de 100644 --- a/applications/services/storage/storages/storage_int.c +++ b/applications/services/storage/storages/storage_int.c @@ -243,7 +243,8 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) { if(lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_WRONLY | LFS_O_CREAT) == 0) { lfs_file_write(lfs, &file, cnt, cnt_size); lfs_file_close(lfs, &file); - if(lfs_file_open(lfs, &file, ".key.u2f", LFS_O_WRONLY | LFS_O_CREAT) == 0) { + if(lfs_file_open(lfs, &file, ".key.u2f", LFS_O_WRONLY | LFS_O_CREAT) == + 0) { lfs_file_write(lfs, &file, key, key_size); lfs_file_close(lfs, &file); } From 5085a17bcb6fbe74fc8d874ef371aedc45d71d2f Mon Sep 17 00:00:00 2001 From: ushastoe Date: Sat, 29 Apr 2023 18:16:53 +0300 Subject: [PATCH 156/282] HID app refactor --- .../external/hid_app/assets/OutCircles.png | Bin 0 -> 2469 bytes .../hid_app/assets/Pause_icon_9x9.png | Bin 4559 -> 1743 bytes .../hid_app/assets/Pin_back_arrow_10x10.png | Bin 0 -> 4575 bytes .../hid_app/assets/Pressed_Button_19x19.png | Bin 0 -> 1790 bytes .../external/hid_app/assets/S_DOWN.png | Bin 0 -> 1893 bytes .../external/hid_app/assets/S_LEFT.png | Bin 0 -> 1906 bytes .../external/hid_app/assets/S_RIGHT.png | Bin 0 -> 1902 bytes applications/external/hid_app/assets/S_UP.png | Bin 0 -> 1886 bytes .../external/hid_app/assets/Voldwn_6x6.png | Bin 3593 -> 4556 bytes .../external/hid_app/assets/Volup_8x6.png | Bin 3595 -> 4564 bytes .../external/hid_app/views/hid_media.c | 51 +++++++++++------- .../external/hid_app/views/hid_mouse.c | 26 ++++----- .../external/hid_app/views/hid_tiktok.c | 42 ++++++++------- .../external/hid_app/views/hid_ytshorts.c | 33 +++++++----- 14 files changed, 86 insertions(+), 66 deletions(-) create mode 100644 applications/external/hid_app/assets/OutCircles.png create mode 100644 applications/external/hid_app/assets/Pin_back_arrow_10x10.png create mode 100644 applications/external/hid_app/assets/Pressed_Button_19x19.png create mode 100644 applications/external/hid_app/assets/S_DOWN.png create mode 100644 applications/external/hid_app/assets/S_LEFT.png create mode 100644 applications/external/hid_app/assets/S_RIGHT.png create mode 100644 applications/external/hid_app/assets/S_UP.png diff --git a/applications/external/hid_app/assets/OutCircles.png b/applications/external/hid_app/assets/OutCircles.png new file mode 100644 index 0000000000000000000000000000000000000000..f34d2687a6b1c03b312899572ce3dc6c2fb7af13 GIT binary patch literal 2469 zcmcIm32+lt7*6Xzu>~1H$_+b8NG&vlD9qT+?j~JHvLU;rX|*a) zs8AWFWu$-{au?(nK~W9`idGl|vC5${fH*Bh1Um@jC`WypCIu9bVaCjC_U-0--~YY; z|Ni$fJ3DJ+m-s&M8jYrlEz>d%+(&?m>=X;WSFikiIk-I?$b3cCXcD@sSBz%S;$9j} z94tDViqk%Z;Uu4yFbEgq-OM=4h)6K- zWP+4~o+?jba2!5C}jZa*i-+|E7;{iy z6)EVDByY4*+0lp)L1}R)d4lNYr9wGHRTP;ZSXf0c!Juj+f>8*eb6_|q8AH;k7|CE* z8`KVrz_SYb53tdJVQPs<-F;w*7u`jV1GU>3n_pH~KPT8MCK!m)iXzYOdW>dy%7xGb z>qc}2%7D0Vy&Dl&(q$kyo~ML1U0}XN;tJGEsPo%=%S#;KXwN1?=}3-rQ5-_hm>bbk zlpdj7f&rl@N=K8Fo7ItIc$I+d<)8>!hxOjes;c4vBgPqd4%3qeM;ToR##1z6Bv=kH zP;MjPa=Qo&*TK-;xMs;K1?>_KvJ3cmj&Vyt9}D~=`dE*E2K*k?&43-m%@k!&x}r#b zU9&$sYMoeIPzM2K=vL_XB%x{z5qxcQT#TTEV-3JHYj9xK&JoG zT%{}<_zv~u~Z-y?vX z6NR6AF^}is2iD==ef1=Dc<^FSb-!$L82V51-8FBt0Wv^qs@9YvH66}++N+x$?F_>R z%t%?xdYzffz;UZN!%C3DjTns^F{1&)N2G;gdPOiZJZd9B9~~`>yl0<}P6mgEI&43- zz(f1NC-_0lg5X%|-nVX+MiZN2vt&5(ryYHD@0NW~r=P~$X{{v9UJ zdf=towjG;u{Pm3o>7m!thi*)$?)>%1^j6&0!?R;?-?M*ATQTqFhTa`*eRl1AN>_F+ zhE1?-=v8JWvz89&vZ!0x1#O1|302ABk?#AKl-+v&06cZgsxSN1^eF#+&sfv;qQzz< znHt+~PII6`-P}qhXEHM1wzgA!O9eIXo#%5e*vfy~kz^Y@bkYaY2i7E=?e^JAxub8D zY%;Tz&XXNac|V*~J}I6jrZokYjNY=aS4wRoeQa>ogEO5MQ!h1V`p$gZ^~CKh>xtd5 ziNPUPW;o#UDI+&rABkip5^*tj!_hPEZV+wzbH&&U^pa=Hl>QTh^w6I>500v@z0_Ej zI;*;6!K&EmmZhtpthvp(HtYCi)@IEX=GT8wku$J(>#)K7L$}4$8z1$&IpgwL z^Lv}U*LpiIp%u#uD^84ESJqHrxtUw}4oI0O_OR;l3sWd-MV>MdBcL;m5JV#(iu|X&A6Jzl|RhB?A;}_+@tDm{t;n}&l*S8-} z{k)|9o7MrN&Yga44D{82+r#TRUranZF@JsMrmlJ7$o_ra>QpxYS-Y@$_3~vU-yU!( zNrz&$TG6XzdBw%I$a52GoBp~p@k)6u(;8R&$%fv@*_6ZTA1RwP%d%;B-t@l#cgkqJ literal 0 HcmV?d00001 diff --git a/applications/external/hid_app/assets/Pause_icon_9x9.png b/applications/external/hid_app/assets/Pause_icon_9x9.png index d72d712bbaa47429793bddbab0096de8d95045b4..fe16dc03e2629da128e37a7b9e1ccb11b4c92b78 100644 GIT binary patch delta 682 zcma)4y>HV%6nFS6X&kpgf(j6_9AKeG?#|!FDI~E=vs6%(p}9FOj);hTDlIcz zl-bn|?O0xTmUzz9qosfHCqh+QgQt{A$3Ahjt7Op5|F;Bj`0Y!I2YYSW0s7_q#3@j@8D z$i`R)kPsc%mZky{TNs4MfN^YUoEEcj`Sg;i$WiFT$)HE_n$qo&Hm#=pw!(HKd^DVP z#$HS3{aN@FIm`X~x$~ey`~RPWxZoD5?3rZyGxQtI!neVe7tcLPJ4v2oog{UK zg^(?ZgtC(?V+mP%z1Q>nuIu;y@m}vA=iKL<&$;i<`P}FHae8=q5cJ!Y=Egi+;#>d# z@R;BY2?xme@5TW+=)YKOzz+cGp=YKC02PVcyF@ks;4<+vFt7|H80$-$7#XOeG}YCS zXeAT?;BV(xhd3}cMRZ0t$$WPDgUc1^KGy&^g2~aw?^wWp4Oyvap%iU?QHh!Za1xmFY1VZeDX|z z7obUq(ZUT%C0gK{+v}ph!uT<$H2+M;I}f%N09c05LL;f=r*}cm`~WcE->)Pq&^rDn zy2eu89*|B3#^3_8+@K@^(57V5CIqz6MS*hb7j9fY3IO=f!u8?66CPmUmx&G=Fj&GE zfdPXBM@u+>co2|r+*lT@`v!2ay+%+3H@*Y%n7VK!Xk!^D%JCK27#vl{1_UQKB=PCG z08ttGik*No77Q316YPRWn?mwb`WnVcoc&{}@gShFBeg62T%W2?bt?U%c*yGV*o4f) zJD&=DBJ2deJLeQUJ3R{_h@XX+b{_@+@vYvs``FV-vollUGw!oKtY0k!``*iba2=VI z(f!)HU?{+E-oqMlo}M0n__u<}gBrX`E&xMzz~#{`C)Hujyz^Wi!?xmR*j?RYx%rlD zT9QY(xVabQT=VNx=#z0ee^kf)#^NG34#NAxVavdkOx=vq#=_{mD@5PmGD5;^2UH*B zyE%*4Z;hevC7kG;z*JmR6uz_KaJ$+PZq`bBO-MGBX-}OHn4_atq|){{86i9X^QJ|k z`78F99X^ebOCWF@uR~ujpc}b{yerO~4Dfqk(6mhepfeh_5_J_oKv#OvBmmU@5WV;~ z1!nr50{{&3uAMB`V?X>>h}p=S{Z_KJ5e6n6GSodR+o;8_e`qS|C|AB{nx2?VBW@7- zvK%6wcO-M1zw#{#%QHJBaPiu_KkSf;Xzn}F+MAqEy*3U(q6{wfh-<^Y-vpx*rjXFRs3lnWEsY7TY9RU|o_VahMb9FxhFyYv^Ypfdw6%iDf$%A89^E(vYI zZQ_j|#v_ZYi*)+a=aVSy-={d9M=_<-ldF>Wk}U4+v^Pyv3E^5|CrmjDWC=y| z6oIp9&g-_=qN%K_J7Ru z^zhugtUO1p;rteT%qab1dY|_X_PN=)=wBg+d2aGVx8hxoi+PJ(6)P645>w7FeZ=-D zJ)_M=;1hSId$N1Z%#|(>@B50eWzDbj z?6ci6-8|h_kr3o*WW|72se5T;sm4&)Q2mhWV!)!v;`|UR$UjFnM=@s|N5O3dan`8U zSk=VT36XW&S}07%o#}Vfm5KRI9`V*9x_Y9Ai<*mC zrs_|0rFdt$*Ys9?qIPz+DUL7)ne7V+n*G_{xy1UfwZpsdF%ONhTh7L*$F-&nrGND< zShw~aLM`kra4lpm99q=eAa2NQP&RrbJC6!T4xFN%3J;(Lbq4$R*UlG>W132uOPc2F zJtREx19R5YV2{ovbqJ&fr^Bo%#dnH*AB1(JPKz(q^u7GFUYPYJYozom<@2{r?UsnA zJ7!8*675b++AO_a%bN}pS2`!@ImKDU@79uLl`jQfa=f%tcg*5NwtVh@3Z-qh`NCLT z$5j`cjy8F_dk&)WUI+Xj?7^-2NA>Vg?RrGyKx9TF`)=;esqK!v#NQ3$v{jK6b2b;y zThKJ9?wa_u7Z7iV6f^_+kX?-}p6$&c<-UeZ)gCvsd^7 zM~?7$iEWimdDCM9M-p!KaE$Ut`aRk*o$GwX5j$q=6I(6iDV71-iV!++68%K!r&Xmj z^$K-Uaez7HxZ_cmu##AE_w}h^uU7%V9!xJEf)8mSt2(OsN%ghRTfdD~(gmo3vYd;z zruZXGgV};NQq8CD|@pbs~pH)ZQ_kV67xUY+Z*Cmx(R+qmRtPXWt z8G|<%y%9mgS;Invzw@}aJzKD*?9naJDEGHZYwyR#Wk<|o3bl<~=1`5~hF{vdf z?H#^lzs|oV2@_M@Y*}=#^!^KW^pmhuXQn^%aoO+kGu1N__U6w0;p;TY(9rj@RUa1T z@$*IVzRXR>hG%E%y!84%-dyG>l~{?NmT;z={Z7pf^IA{ns_gpE1yLiaEnet+#T;JE zS1;TM%b`1U*zJ`*t*ZYjKQz$rE#&GNZ?vTFsqd_mh1!=2q4wVNI-`hn-@c{m-x9@L zC2AUGBEMA|hgh`aCSHAbm3_mAHSb0B^BH;8(D0m-LQs!ChR69&XGQr9Wi4igy!1bR zlJ}k7^xMBiABA6?wEU6Z-89nFHJU-MTIZO@(-it}4M9v~Atg^czmE0eZefn(B}wD@ zsy~q|)FdgWRV~q;o|~6*Tzh3}>N;bx*sVA>$NaSR=Zq2Brs-~mx097qorghB?r9ix z$+XIOvwiLRY@*Y0t^Y6hrt7d=)wxpbGHv5X|GjbivkQLn++&SF`>*cw-S3!nI^NLU#&F41Km+KeYFgeVgwm zy>h>T+(;Sw?b7Jd)8xp<;^n-RyPxvs)2;D7<4)s##2G|y!d&s zV9$q0yFaw~z^AjLdlNh7dK`8Km$|p%dK$`TC2Ik!rzOvDt%z2s+HRWm%A?)V?%@bl zHLGT{wN5JI_{Ck_eap4pZBlFTM~h9J+3@{c<;@#wJ4j<}&%w`AnuI5q0YI2M07TsY zfbD~&s2>0jqyhjxT>t=g9{^zf*)>*L2LVn?QyZfL+>ZqQL;};=&+aA5h=TGfaFHMg zf$YxM2Z=CxK_bwDF@jMBGG?3vA7`w9)EES?G_R@>O8U4Nk>u%1^$Wbrhy+V;qtG@e zRh)`CP8GxG1xs_IFg7YE914X~Rc91|;r}t;1;Y_;D6$5|4Woj@U`VP+6?F_5NkpMl zkZu}knkw#Cl7_k(Ll2^W&_t<{$f_7uq^p{yCKBa(z#yW?Sfq-p8`%|$a>Htp8Ho@% zX#-6Z#y}m5MX8*@pioAJdWHsAeM7Xqfd*O?rHR&HEJI*)Y5&W9|30ywKhD6<$1lLl z&yB1NN5gfbwL&TWW@b1;a)4(r+3ie#A4NKldQdyg(>IVx^d*r^47K40dz3sq-EjZp zQu}YY;Qw&l{769*vM=@jxQucL6#frc{lAix8SGFrV*@H9>xM-Wi73^BiisL(NFoNK zj?^T(qmdYQR}|6B6{Vu-N@7GoMgB4Oz;9J7BOj{8xX&iSkcA5WE%LvO{{JF>-`L+G zXolS(?X$}mEc4)@D47`Q*>GeSbyn~+s_-{8P9k;^j}})*+Cf1jifts}@3F+YO+SW4 f2VUWZmqyuD&Dlloaost3zywT;%ni%*U9SHNN*V#K diff --git a/applications/external/hid_app/assets/Pin_back_arrow_10x10.png b/applications/external/hid_app/assets/Pin_back_arrow_10x10.png new file mode 100644 index 0000000000000000000000000000000000000000..e7510fd5dc0084fe0f9b6fdeabf969ed3cfd6466 GIT binary patch literal 4575 zcmb_f2{@E(+kR|iiDV7Q81Yt&S#4AHZ4}YiD=}tan5~(yj7nOxDNDAj2_-d@UPZE1 zin2wKP$Wx2Ly|=GKU#e6cl^Eo@&4cO&2c>Q+|PZV*L7d#b)ENpKhFu5ZT8ZVs*(Tz zNIT$c@Zenvyd=a$!2c%|IvU_jG{Dly5&%l#q!tKb03hi=wYGNQ;O(s-4z|{YNE1Ut z7)l=r0Jsxbu3=t@vr1-tv*eW?R$Y@NskDOtREsa(AnTngdj=pJk(IN!AAMZXLqTy> zCeFR?P=_Qg>-a#<`tktFlgD?&xbHH4r_oz*V}FETVq*T;eC0^y$U+ORb!F5lIh};z z+#tXNAH5mdr4i?ht9w`#C9H_+7lp_UH{J~pyAJ@9BE0ZO?ltoTp{qpFbXjlzgbN!Pf2_yjkjknJV3S5>3#y>cii2+@O ziM`4|SMHiZap1HNkhb1_ov_7iz|Z|4UQf98E|9~wfa;6Z77Imr-$dC9M^%Xdp|M`^ zD=qwhs5C3RCIDhA3|Oy~Zx(?#isT^LYx)a)S<&SILrWevBI4Mx0svI!+U|TcHjf_}9(*-S8KDV2+|T_QJjsNb zX-@Thtvn?x3dnA26?FR!4RwmJ>V>X_)C3pq1iC$dz`i*jbdN;N4#~$6b1^*Q1&g)W z=Uo~$tFMuilA6%=KVOA-9b@(l{fgNi6ZsJw{n`^T3G7L?NGqz%JN#u2fe~7aj~!_g zwL&sxN3_1yM<4hSyP<6WQ?g4>@#K`(iEk7#4u@q zf7H2l+s)-S8fmqW?}UV7WW3r#0gK3K*eO-11VA9Nc)#a`}oo3jA7`%sc9pwaUVTWi}Qo*41v7wOTe9wMO#%>J&>A zw_0qM=#6V4syVCDU&)r zapkmFQ78e2ITMu+89lDB9eTfkoiAKy6_ntE(|QkME0~<#W$`(_rvZXGxp1=59+`CT z`gW10!XXy7E@`Nqe2~Lw<6>6&M5W{gx2cw{HI2HNThO-kO$Zm*e=?RB)rORzoO({! zb?TU{-w7{Ooq8qWke7i+oB>hY%P3S)tu~t=5ML)86D2<`zWa#mUD~1eczZ8LFY8O% z<65P172=)}hmRod{sB$AT0MI~?dap)PROV}f_Y*;5W-hM@A2S2wNe2RQhl|&VRj1u*zPQg4Jaz z@HEGZVoy@j8r%@iP-xN)Ci!Xvq4Y_dmkWb(*mH+PP^c?a*Zl-m{e zl~;Cq?7wK|{-?|9LW!qJS2_yS-ES8f7PsKT@Nq7!+kNw@eZL&~Oq9NUG}W4uv`nMX4+qc7U*XXqdDW|ZFwIt6VSMhJ|!VY~_rX-u4K ziYLAaK1(fcn>_OB(yGS5iiEnryf%ltKgxC99SeAE5Uy-S;WO9x^D!^Uy}_Fa{!~;? zeqH|k!RigoNx|uqinh`x+@_`my<)8I1^Wb^Rcs%1fbnY6{8>4r6(p=O$Ggf3^Ga7GRD#|FT3(cVSDGVsY zZD`*xQz(s9IhOwlbFZ+j z@ZP9rfLg$LPS&)6^2M$3jdH1>smiYOf|CV|a}kZnL#pp8+HX9W$;-H%(OdMK{`5tY z>HYmz=AHC2)E@fWGZk2Vn4I+**wgent01G`?sWVJe0S(>@7?oNYn}hh)XDBd&>MQ{ zcJ@~_?)Af(nUX)ZjEa~&FcUr(aqd|4#cF7uX|+~lXJlIB@`ddAO`jXT#C@uH#e1(b zwN1=V=#J2kP}M=zgMW$yi)e_ZiC$Q4Aa+{p_A>YiGexTv64Krp>_ld*@_V&8BDyy~ zFTG9ik$9Hk4z3lEe!2W$__0s6Q>k)X z`E?Q#CkE@f>P%0(<_M3_($SfN>24`pV)0OK?k(lv(U!*Su+82E-tqg4qtD8vUN;{) z46$;7uXY|PU^uDEzdw=@?QC0}dpdV}Nm==p&1Z(QJd!^ezu2_j`g7n>XSm;Bb}d!EBgk}{Jr^YGHtlv3Sih_dx%&JYd@`xGLO^r|3S)*SB+8QiqiGRmER(DZln- zS9!SiR6n%F_O_B%jH_~(KGFwYK?}yySra* zDSI?Ah8xQtWAJCaYp!mo3bO2Yd~{N}NNwu$keV-j%S-P0h@hF+=F;Xz%_0T_gNdC@ zH~Bs9&l~1_jL16R)9CS~=t^1jbDi$anipYvr)3VSD{guzNE)xap&RZQTxGj|hSC1+ z(2F=#f3=F5;m8-|_F>NVv*Y$&*t`6rtzayO%cS*Qt*N;#LB|`OUW>!BhBBg<-5C?< zVK-PiHp<+!9J=#I-^G1GW^UTi4T5mcvH+U%Vbpfd$J8PS{>C?1c-mK$7TCmS{pQ-@s z)@*ZS?mNda-`R)LFGu2hCM#JVptJrx+GSgdOp8tJBUxWw;jZpvj*({@Cp`B!X`PPC($G+oR;_wZ zYDs)maJ@jj^--k9H{KZM%1Garf%&biUJG55Qgbn_HO0KbX|~`>;Z9)_r`hv&Kvt}dvzAnILk4(?Q?2TK&mrMu|hj#*i z2ps^3IsyQn!LFz`01$!zfVX}CfISNU%B;+a?VG`YWiF0xw%;Pb7O>pXu?W0LuyEcS z@YPj*@e%^kGgpJCa;lpb*UQNfOJvgZ2_&XJSwEc4TC@T-n}@Rq#9%TP;!h5sGEAX@ z+ci)Km1GL_G;l&Vv8>5~R9pm`>>9DnjTjM3G$BFF%^;h@v7iGwnM;6#(`gJ2Hry2Y z-7gkwFLuMBkna#~uqo7XaYBfflMBR}$tFWg^bs&30)c=Sndl>pkVKRbMGt~Ppippx zF&t?KLmFcdC@cmI`T2u_)3HeuEZ)ZM=VaiMDKwDFWntm)u&^-wFtk3C9RNp~n3%v3 zC^!lQ0}(I|kHIB`!x$W`Umk4893q>_;!>Fm$f8GrKQn}D3I#*`!GX?Na?9ZS%oCU~ zcsPLtN9rRMIsF1UIsM;II(-Sv;o`~viubpUIc_`_8IC7&m?3N;8H8*7nu^1<4FyB| zN%kKY+lG=E+$D1oamkhy!lr#s6Nv~X)5vrX#sOI&m*QjvGPz7nAoFjMur$1s3KrIy zO(t-eY&Ry8_N&Y;zq~+DXniDPy%T{*Wi0yLut@2r3E76gC7VLQqN8C53=C!DhC*Y( zx}%Z02s9Rf_yKidlBg8k@1P(WgZMWnC^-^=OZX>XCnu}}gTo~-h-3#FQz#geK9x!W z#b9DYLV>zLVbBy9(GWv{`5RM=U}R%|Lk!vDg^i9pz*txXI}e%U&+!PiFyf%c#IAQ7Nf}R0fF|#sQ`O!-yD2U<8oCy%i2!q`9~U{r}1Q zGok*N@V8|Cx_JJ3WZ;WzeiRA*Lrnjw-}kYnUJ3<8>dQ+g*;fb?R zZ==}o-*hmG8;G!gHpX$v(SmUD^f*O$lNMq?`%&Ube_sAc5+mYD=bAq7(>5JM%exs} z-(Bh9-A!y0>GUb_+tLHu_B2RNU`Zkt+JrF;jfBqmQm4e z02u=SMAL3S6Jpg2UlUcJstT|aXvolVfa>U)Sp6hkE+e6{<}Fpl_?;^aiXwFsWi%Q! zMp}bqeFfUKtpHU~Rhc8?d?zW;SWfceHG@ZUoJDC7u|zBxQNXqeSK?BqbBNO!ZIV|$ zamSP~N)^}u<(z7u?;i|};~6?HI@`QTrT3x4d2c7B${NktR)%S3n!131!eoeilVAFpm^e3ln^$` zUoI6@1E_}z>PkE{O$NHGHhVlWXgaX@D$pDNrcj?Z0zm~j9GH$wsa)rcbEXrzgW6{s>X5Rp|=Z^{;eK-P4qTDIM^P1~I4;^RGrw@N3<@zcH~40Dc| zn@#`?099hhx?vE`p##~5fXIXdhGAoB>!x0_oyj8p>7e1gtfeZ#8Et5VCJYVP2oRM~ zpqp}lFqT8020@_NmJtS$c(-nw4O!+h!rj#*kzgleS&Vr9L^0}9B~AKeH*h7svbuVaqn+G&kk57fJ@Vw!>F!4B~cEpHqm*{(pV4X>uq^C zTj}}DY_FgRpJv8)0{`lh1HOx#>3Xf6_36z^a83}P+Q0tyov*I^{LS@GKDuxuTz|&< z-s!$@e)Pt(Z~ywr-r1+!y%%p>8~!nW;#0B+@wys;lfr* z&u%TY$<`XNec{Y$erDhRK}2)R529Y2IfJS&Onl#Z-uD2B&xxsj7@DS*eJM!5B zFZDHvr5#zZZCggFtg0d)#B4Lp@j#5T=`lltW+V-hJY+FnFk+9b=T!luj^+?06WTZ{ zeFDei0ZwE^LdBfMK-b+L8buRymNz$`N~w2G;;g-yP;}et`nmMdbk6z&Wi7&^1j&x~N)hgdA0K6#FrxIS7rQE(F2HIX)Pe`C+hm5UA@qtvJhZ zOlYf76+n~}hQ8w=6ZxK@iK+>wprRuYBTW&hs;S7Rn3@?_LtQYw!N_{yL@|DpTb~i& zn7HXETA=w}#SlGgV$rm9Q^dMri*_YYk*^V5LCTozsWgO72lsZf7OH$;R6U{^*fK>E zS4>g2bs~BSkok&bnM60KZGftsx^>oLsmpwDR}*-jH~~u|4EYHo+@W$3cZxIvJIrDs z%%F9f<@a%xzEIxvLVg%J2a_V}Re=;uroK?V4)<{HU^+*%{VoO4eVRXlWh}`1IHk1? zIQ^gDl5ZzI!yEfcKUm}cN9m=lq>JMYg_Tnlipj{u5;JOw{O^A4^n09{8l<`CW>(rT zi#roTPVXI2-Oz~UL7-F{1!6_lE28aD2$`-Mwx$Qz(6o_?Ubw?G4yHMWEWJ^$7#qwpu1V*)xB1oYKi$4>>hY(WkG#Rn#lIh`|8RZg z;|srCI`PiC^Q{M+>tB5P-6?+hql+)!{7{VSjJ`O3tNHb3e{Mh2U;Xj&&&_LBzWVZs lm1llBdNS$U^e@tF?tx!srfzgTf2r6ATg}DBC$rDJ@;C6_S*-v7 literal 0 HcmV?d00001 diff --git a/applications/external/hid_app/assets/S_LEFT.png b/applications/external/hid_app/assets/S_LEFT.png new file mode 100644 index 0000000000000000000000000000000000000000..13c9f51b4ee428f92a995efb2bb961c76f7dc247 GIT binary patch literal 1906 zcmcIlONbmr819IM7&n_k4$)j}ItL9}(^cI){bDD(voo{qKxSPwgINzks;jDJ+MVg@ zbocDcE(ks_5Qv}=1w|C`D7g3lbt6Pn#GHIU4iUUY52APw)Pq_*uU%!c?!|$getq@* zUw!}ARTmZ)9yoOH*g;8>4pryNOX7T39CshMMf|_-N$17s_Gtd$M3U|}k{$b`mtVP4 zlJ>tI)R)uc+9{Xtb`DeSv0Sem3A7|lP4^;9R#+-~tPz9-<>$*+6gi*;(Meia6;+0McO*{gYY|hISi(CoVU<-DrpHIc zq9#vy(&RVMHh%pNg_Mj7MxD6Tw}%pywOCtlCjyK5`XaX!Go13c&UtIpsl`!6Su=7- zzV}QJQr=BYWQv9ZSQ)3Rpop&N02)BE>kzt{;~M6DPz>ibKMx<$@wa<${2^3|q*|TNn^eqX7B_0uE+6z{I1FBGWP%>gy8Y%bavFC$jN_ z+)_>i$Jk95X^c=4+JJeM2Otq_n0g*Sp3XF%>b{S?5!-`tAU+-3+RmD(qJmMtnMsiW zu#GHXl;Hq}Il?upQOkxpAyn{e)h+QBk87L?cXdUk#PxaH#v(sK8#kC5g^euDf?dpF zK1f9C)`s85X?AXS)e7W(=v+*)tQQoLHJMRm_&C_Xy}jui&~}?lknYm_0W9Hu+Ql&| zHiXmv87}o^;uE~OyY#&^{(qD{w3Re**kEGiRF!NpYPQ4-nxg)@Ut9eiWu_?7oOLr9 z+Axb7V?$2v98n{$6Wuci&>ZLk(=bfnc+3KZVdNd%@Nr((2P#^@7S|}4)*Q5ra>>k> z^ddr)Qn8}xvo>^)4Q&gexoso+GZ_d{cURs=Zd+zYHi)%5yBzNt;%2<>uuxbQi(Td7 zPjpJ`NZo3=Sht_Kwp5(_Se|(F_rG4djxV4W9=iP5ryo?GID4)7?X6ww-I>XmcQzVt zyjuC@aqlB-v+B=0Vq7$@fBh_2x$WxtKi<1~wDacE&z(AY&r=(}{`vOizDvz3Uw!!7 zjg3$6OP^2Qb?(6TvzuQ|p7>(_rK0)bcj@y#9y|Qlk;=p`Hzt0_cExICq5RhD*(d)7 DzvyO% literal 0 HcmV?d00001 diff --git a/applications/external/hid_app/assets/S_RIGHT.png b/applications/external/hid_app/assets/S_RIGHT.png new file mode 100644 index 0000000000000000000000000000000000000000..e0ba2afd1aefe680ef730165fab4c73892d67709 GIT binary patch literal 1902 zcmcIlONbmr7;Z(DWOWf=APd1x=i(zZUHzEut|pT>J2UGJY_FTmglzN>tE;PLnw{=y zyL)$LS49Xx2@>%j9()`k7{r6%B>|PqMK5{~!3TIXiiqe%4CWwK&-Bc$vRU`yKtJcJ z@BixizpgsJxbWE2nFOH2JQ=4e4Ua;zU?vO+V_rR^Z9NWXn_RT2ZQBAqHb(1~lb z6U=QS^u)$Oi)^eATnKW zJf@A(!YA&S+{dx3lwdxm5zujVhlb%8oo0nl@C|6Vrpmm(lip0C70jLAjYC&i#x~*D|K+T452z7W6Je=XZfteTh>wAGZ zj@^vX3`c}E>lKNMDg}XOYo6gNu8Dw7u>}y-Ens<&0*n;uB7QbaI?%<(*BM#Ooyf-z zbL%m}ImT|Q@Q zO`utZ2~0yb05UKFL?dpQdJqv;k;JXKHP&T`!#wV;CW#)geU^ln=O+kpo62$2&eP1< z`7Gvwl(%lZ_&!e1r;EF8APz!jW0GgRB9Xkw)RT(W!4B^2P3Mrd*P)zrm*x*)Df6=) zPH45wo&L{o$u|?9;GhgX zu4z?8Y1FC>RhzLOQY>hhP?_B}axjyjAbEG?1LU@4c4PxzyYt)euE8J1`woq`WeML^ zl9S)|`Hqw}>(!QZ;fGhNlaC(US6}`0`Sz3d9=vqv*(aXsAvST>`Np}AU0+fkzWevX zFMjjx2d8h$e|_Lu$7-I9_J8`-$uB3azVgx^*ALwH-JchJdF>*)F8}n`=9z1ooA3Sn z*8Iec=!rusBo2rlA)$&3ESyk<+5_T%Km~3H4&2I#BEbc?mT}@_7qngWLL|q3Z{GLj zea|zmwHBT~bny5=Nszj6H1pW^gDJbyNoqz50#kA2eH?;ew+ z``!=Q%h|H`f=hU)V#+&A9fq+$OVY`iVT{QN%j6F022n-%{l@2t9MFogWO>kwYpfT{ zZzSx@#zLEHtPqDPGpFQ}Lst-lEW`3J>_@3PtSBR1S6t_hHANmlvXzQb%?rxQUQ4d= zgvpKy0f7+8Wk=P^Ix))rv$6pVLxZ-amw;}&&~T9{7e7UmOQ`QI*5`_1;;y3fvMhEr zZ7>+915@QmSJNHG(V(Fj1`r64u16Ujf+#&PVW_i|Bte`7Jd$%p+~KQPMG;cRa|q)p zZIl*13CFY{jx}9{`J5&|&)XduhEsH!HP%FxLhp{mX?s0p+9FH&YC>3JjYZjsNwK)c zGoJSNU9?SK|3e`q(}MA8(jVDFiN^XY6x^x6>LY!zTT2+uc+%#)Kj~C!Qc*ULs>@F< z1rg7?F@V)^#wv>Fnh78RhTVqHg(VlEXQ1gqIEH#62oz^{Z(vRXe|;}d&vTnm zn&F7BX1$^aQECuSmonSYZ3+P``wSQaQGj*IfNwe2LIe^UnIm0de4Ufk+=+brIJcA& z!7+8yrM_GA_CxQ5MaDO{%u8IoA4zUc!)G>f2 zB7l_;1|8kPfUpjR4)J{hV@2MoTjPD6c$^A%g<48npC=&}`3XYYWm+6{^E3-~F^l;i z6|Gw@zK=8PmEx)&$fMA?nB-ZnC?sz(qe}5O*ulNM=^WDzdQ6b+()=+j<$gB639EL6 z)BhPR?QY^zytcday*2)SlwR0MdN}Gbv2tokJ{c`vV#ZC;{@t&weorz}6=}}9nHDz8 z;_lRtQ#(f#Stc<%B2|V1eNaNE1e^{l1B8&}n8?SLX^vI&gDtK}Fw1k$M)g|Bs+mIx&3d(MU;gs@FOEt_4&2-N>CT<>@ZpDEx@=s3>-NW&_Pq!`m0nlRe|7D<#)WS; z&wuvB)nBWFcOL&GyZ+ZlS2RQU=ITZN=J4weZoGF4x*hA*ADj2z^TW9xXVMvR?izUR dLThDn|C>wR&!;bbcqQKfn~jC~C$lfV`Zrq^TDbrK literal 0 HcmV?d00001 diff --git a/applications/external/hid_app/assets/Voldwn_6x6.png b/applications/external/hid_app/assets/Voldwn_6x6.png index d7a82a2df8262667a9a03419f437ff9b350e645f..ce487b546812e471bfaf380f3849f954e6c757ac 100644 GIT binary patch delta 1123 zcmbtTO=uHA6rSKu+hki0wNyl8*;c9OW@mr0e>T!I2~D7>u|`F_bhERYEF{@=ciUPI zZ51ztg2;NX(z^#iD0-5DMNeYEgI+v|C&g1iyoeAdrm40HDjwdx+4sJA?|bvjEF4-5t8TqR10U(^UlF7U`J~B*Z z(@Bw4M3LsAEC88>6#&ZB%Jqp154FJC2(EJAgCLsVHCje0I0MP3! zKRz>v6MOTcg>=1M$IEk1U(I6I;bbm)07SYQ76Nyd4`9^&U@5#fphO|9LkLMpiLw%_ zbCP+2W@Qy}s=z-Sjf6rSm71q|etOCe)*^=kiRc48k0P&B8#$!M zsj}}oD#JLFZY65FB?jq?Q8B6*<}qxPWm;ILZ2PuXwu2{;-mcbF z`c0PSB_x})E@=v_7=lJCMHHhAN#+$))FoM$f=5JrUxP(1r=E7Ll2y|x!733uPa<74 zZMUlVamuP{C4+HlC5qe{RZZFz+r`uy^Zgvp;v3=Grd7d-Uv5UQLEMTryJalTx80dj zJTSwdM=Gmq{t+_^`gtd-S31k-yGQX*^LvlitB?m58 zHDphFCy%v?>t(H0GEkgiDCQsd|BW)O7Qf~HALKFjXmjKpba2S0?xN^Q_{8+SWro;(PWD#)E8nEcIY` G;`%p^?M_($ delta 269 zcmX@3+$m$x8Q|y6%O%Cdz`(%k>ERLtq}f22gBeI(7m}}HVBm7jbaoE#baqxKD9TUE z%t>WnsJOM!&xA|2!BSNas7$iNHKHUqKdq!Zu_%?nF(p4KRlzeiF+DXXH8G{K@MNkD z0|T?-ZW>Ty2*wnMhfPZdWL2OmPRHz3PuKo=K2Qa`i92328LD!W>&_QnJ0dj1BPl)UP|Nnu^ z&_kE&fRwDKi(^Q|t)v7XNMT@fb2DIEB9SN(BCv=>;Q#|;4TIpasE#Q>g$$mqelF{r G5}E+EnMbPt diff --git a/applications/external/hid_app/assets/Volup_8x6.png b/applications/external/hid_app/assets/Volup_8x6.png index 4b7ec66d65178e808c672576150f5773b04c31a4..ee9045f7fde3e4c5e7104bdee4919d7694e34d7c 100644 GIT binary patch delta 1123 zcmb7D&ubGw6rNz!Hc4BdLZuX0HXtp!*_qAe*Vw2@8k#^;W37sK>2zn3EF{@=ciUPI z{UKh8g2*1U(!W7077rpQ;;jb{ir`5P9z+lXZz9BrX{xP)_3(DyzWLsJ-^};sM*r); z*m7ZPq|@8y1psvBvcuzi?clb*eGmVB&fIAP+DgfM5`eW^zVBo&0N$LQP8aO)k(7|j zq$4OEiAYKi0gzo@i>cmA(n#zj&l0rkcpiYOM*vv74!}1aTKovWGz8$&Bmnp>0KMkw z<1<5iV_#u(B2%x|xqI&Ut9ib3nAwXq0KT4vdBDBZLmaj4u6S<_#Dh@MAcR6R9z;>3 zDbeCFL4k@Q!C|ih{#&tiq!(gduEHGjZOadU0+CG(6E@9>Gdv8Vy$a!cC(G5GfOO%EODt5Vv-tDOek!MYzm4RM8d!Yy+(~$ z`}kn1xK<{$5~B%G7Uf;@Ke(_9VOkyjO8@t%_<#C?eC(Q8JLt>5l#sWHu+-#FF@hLfwFgM=*^?A?|g0Tpl_ffrE)mPWyXf@ IrzWrc08aHw1^@s6 delta 274 zcmcbj+%03#8Q|y6%O%Cdz`(%k>ERLtq&a|?je{9Tp1iGpl7WHCIn&uWz|+}Tp`a)~ zEi)&TfuZ8oMn4lS-3CilL7+0p64!{5;QX|b^2DN42FH~Aq*MjZ+{ElzqZ8JJlaTW+4n z&B!>}h}U59Q(g&n7bi0d0|V#D`utLp@-$N^k%G diff --git a/applications/external/hid_app/views/hid_media.c b/applications/external/hid_app/views/hid_media.c index 468529d56..0028ac596 100644 --- a/applications/external/hid_app/views/hid_media.c +++ b/applications/external/hid_app/views/hid_media.c @@ -21,6 +21,7 @@ typedef struct { bool down_pressed; bool ok_pressed; bool connected; + bool back_pressed; HidTransport transport; } HidMediaModel; @@ -55,61 +56,72 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontSecondary); // Keypad circles - canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47); + canvas_draw_icon(canvas, 58, 3, &I_OutCircles); // Up if(model->up_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 6, &I_S_UP); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 96, 12, &I_Volup_8x6); + canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6); canvas_set_color(canvas, ColorBlack); // Down if(model->down_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 96, 45, &I_Voldwn_6x6); + canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6); canvas_set_color(canvas, ColorBlack); // Left if(model->left_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); - hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); + hid_media_draw_arrow(canvas, 65, 28, CanvasDirectionRightToLeft); + hid_media_draw_arrow(canvas, 70, 28, CanvasDirectionRightToLeft); canvas_set_color(canvas, ColorBlack); // Right if(model->right_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); - hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 101, 28, CanvasDirectionLeftToRight); canvas_set_color(canvas, ColorBlack); // Ok if(model->ok_pressed) { - canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13); + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); - canvas_draw_line(canvas, 100, 29, 100, 33); - canvas_draw_line(canvas, 102, 29, 102, 33); + hid_media_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight); + canvas_draw_line(canvas, 84, 26, 84, 30); + canvas_draw_line(canvas, 86, 26, 86, 30); canvas_set_color(canvas, ColorBlack); // Exit + if(model->back_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); @@ -135,6 +147,8 @@ static void hid_media_process_press(HidMedia* hid_media, InputEvent* event) { } else if(event->key == InputKeyOk) { model->ok_pressed = true; hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = true; } }, true); @@ -160,6 +174,8 @@ static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) { } else if(event->key == InputKeyOk) { model->ok_pressed = false; hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = false; } }, true); @@ -176,12 +192,7 @@ static bool hid_media_input_callback(InputEvent* event, void* context) { } else if(event->type == InputTypeRelease) { hid_media_process_release(hid_media, event); consumed = true; - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - hid_hal_consumer_key_release_all(hid_media->hid); - } } - return consumed; } diff --git a/applications/external/hid_app/views/hid_mouse.c b/applications/external/hid_app/views/hid_mouse.c index ca299c463..75df53dd1 100644 --- a/applications/external/hid_app/views/hid_mouse.c +++ b/applications/external/hid_app/views/hid_mouse.c @@ -49,66 +49,66 @@ static void hid_mouse_draw_callback(Canvas* canvas, void* context) { } // Keypad circles - canvas_draw_icon(canvas, 64, 8, &I_Circles_47x47); + canvas_draw_icon(canvas, 58, 3, &I_OutCircles); // Up if(model->up_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 81, 9, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 6, &I_S_UP); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up_7x9); + canvas_draw_icon(canvas, 80, 8, &I_Pin_arrow_up_7x9); canvas_set_color(canvas, ColorBlack); // Down if(model->down_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 84, 43, &I_Pin_arrow_down_7x9); + canvas_draw_icon(canvas, 80, 40, &I_Pin_arrow_down_7x9); canvas_set_color(canvas, ColorBlack); // Left if(model->left_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 67, 28, &I_Pin_arrow_left_9x7); + canvas_draw_icon(canvas, 63, 25, &I_Pin_arrow_left_9x7); canvas_set_color(canvas, ColorBlack); // Right if(model->right_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 99, 28, &I_Pin_arrow_right_9x7); + canvas_draw_icon(canvas, 95, 25, &I_Pin_arrow_right_9x7); canvas_set_color(canvas, ColorBlack); // Ok if(model->left_mouse_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 81, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x9); + canvas_draw_icon(canvas, 79, 24, &I_Left_mouse_icon_9x9); canvas_set_color(canvas, ColorBlack); // Back if(model->right_mouse_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 108, 48, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x9); + canvas_draw_icon(canvas, 112, 38, &I_Right_mouse_icon_9x9); canvas_set_color(canvas, ColorBlack); } diff --git a/applications/external/hid_app/views/hid_tiktok.c b/applications/external/hid_app/views/hid_tiktok.c index 9b15d812b..bfa0dbc89 100644 --- a/applications/external/hid_app/views/hid_tiktok.c +++ b/applications/external/hid_app/views/hid_tiktok.c @@ -41,64 +41,68 @@ static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontSecondary); // Keypad circles - canvas_draw_icon(canvas, 66, 8, &I_Circles_47x47); + canvas_draw_icon(canvas, 58, 3, &I_OutCircles); // Pause if(model->back_mouse_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 106, 46, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 108, 48, &I_Pause_icon_9x9); + canvas_draw_icon(canvas, 113, 37, &I_Pause_icon_9x9); canvas_set_color(canvas, ColorBlack); // Up if(model->up_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 83, 9, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 6, &I_S_UP); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 86, 11, &I_Arr_up_7x9); + canvas_draw_icon(canvas, 80, 8, &I_Arr_up_7x9); canvas_set_color(canvas, ColorBlack); // Down if(model->down_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 83, 41, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 86, 44, &I_Arr_dwn_7x9); + canvas_draw_icon(canvas, 80, 40, &I_Arr_dwn_7x9); canvas_set_color(canvas, ColorBlack); // Left if(model->left_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 67, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 71, 29, &I_Voldwn_6x6); + canvas_draw_icon(canvas, 64, 25, &I_Voldwn_6x6); canvas_set_color(canvas, ColorBlack); // Right if(model->right_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 99, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 101, 29, &I_Volup_8x6); + canvas_draw_icon(canvas, 95, 25, &I_Volup_8x6); canvas_set_color(canvas, ColorBlack); // Ok if(model->ok_pressed) { - canvas_draw_icon(canvas, 81, 23, &I_Like_pressed_17x17); - } else { - canvas_draw_icon(canvas, 84, 27, &I_Like_def_11x9); + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); } + canvas_draw_icon(canvas, 78, 25, &I_Like_def_11x9); + canvas_set_color(canvas, ColorBlack); + // Exit canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); canvas_set_font(canvas, FontSecondary); @@ -187,17 +191,17 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) { consumed = true; } else if(event->key == InputKeyDown) { // Swipe to new video - hid_hal_mouse_scroll(hid_tiktok->hid, 19); + hid_hal_mouse_scroll(hid_tiktok->hid, -19); consumed = true; } else if(event->key == InputKeyUp) { // Swipe to previous video - hid_hal_mouse_scroll(hid_tiktok->hid, -19); + hid_hal_mouse_scroll(hid_tiktok->hid, 19); consumed = true; } else if(event->key == InputKeyBack) { // Pause - hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_RIGHT); - furi_delay_ms(25); - hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_RIGHT); + hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); consumed = true; } } else if(event->type == InputTypeLong) { diff --git a/applications/external/hid_app/views/hid_ytshorts.c b/applications/external/hid_app/views/hid_ytshorts.c index 44ad02ff0..9be2f853c 100644 --- a/applications/external/hid_app/views/hid_ytshorts.c +++ b/applications/external/hid_app/views/hid_ytshorts.c @@ -41,70 +41,75 @@ static void hid_ytshorts_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontSecondary); // Keypad circles - canvas_draw_icon(canvas, 66, 8, &I_Circles_47x47); + canvas_draw_icon(canvas, 58, 3, &I_OutCircles); // Pause if(model->back_mouse_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 106, 46, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 108, 48, &I_Pause_icon_9x9); + canvas_draw_icon(canvas, 113, 37, &I_Pause_icon_9x9); canvas_set_color(canvas, ColorBlack); // Up if(model->up_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 83, 9, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 6, &I_S_UP); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 86, 11, &I_Arr_up_7x9); + canvas_draw_icon(canvas, 80, 8, &I_Arr_up_7x9); canvas_set_color(canvas, ColorBlack); // Down if(model->down_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 83, 41, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 86, 44, &I_Arr_dwn_7x9); + canvas_draw_icon(canvas, 80, 40, &I_Arr_dwn_7x9); canvas_set_color(canvas, ColorBlack); // Left if(model->left_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 67, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 71, 29, &I_Voldwn_6x6); + canvas_draw_icon(canvas, 64, 25, &I_Voldwn_6x6); canvas_set_color(canvas, ColorBlack); // Right if(model->right_pressed) { canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 99, 25, &I_Pressed_Button_13x13); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT); canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - canvas_draw_icon(canvas, 101, 29, &I_Volup_8x6); + canvas_draw_icon(canvas, 95, 25, &I_Volup_8x6); canvas_set_color(canvas, ColorBlack); // Ok if(model->ok_pressed) { - canvas_draw_icon(canvas, 81, 23, &I_Like_pressed_17x17); - } else { - canvas_draw_icon(canvas, 84, 27, &I_Like_def_11x9); + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); } + canvas_draw_icon(canvas, 78, 25, &I_Like_def_11x9); + canvas_set_color(canvas, ColorBlack); + // Exit canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } + static void hid_ytshorts_reset_cursor(HidYTShorts* hid_ytshorts) { // Set cursor to the phone's left up corner // Delays to guarantee one packet per connection interval From 947dd1b75444696def1cd0f558d3dd05179e5301 Mon Sep 17 00:00:00 2001 From: ushastoe Date: Sat, 29 Apr 2023 18:20:54 +0300 Subject: [PATCH 157/282] Change UP&DOWN swipe --- applications/external/hid_app/views/hid_tiktok.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/external/hid_app/views/hid_tiktok.c b/applications/external/hid_app/views/hid_tiktok.c index bfa0dbc89..4dfbde4eb 100644 --- a/applications/external/hid_app/views/hid_tiktok.c +++ b/applications/external/hid_app/views/hid_tiktok.c @@ -190,13 +190,13 @@ static bool hid_tiktok_input_callback(InputEvent* event, void* context) { hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); consumed = true; } else if(event->key == InputKeyDown) { - // Swipe to new video - hid_hal_mouse_scroll(hid_tiktok->hid, -19); - consumed = true; - } else if(event->key == InputKeyUp) { // Swipe to previous video hid_hal_mouse_scroll(hid_tiktok->hid, 19); consumed = true; + } else if(event->key == InputKeyUp) { + // Swipe to new video + hid_hal_mouse_scroll(hid_tiktok->hid, -19); + consumed = true; } else if(event->key == InputKeyBack) { // Pause hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); From b86d155e8e67a866afe37eed44bfd4e1607e8011 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 29 Apr 2023 21:41:09 +0300 Subject: [PATCH 158/282] Remove ko-fi --- .github/FUNDING.yml | 1 - CHANGELOG.md | 1 - ReadMe.md | 1 - 3 files changed, 3 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 6a4a770bf..9187b0748 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,3 @@ -ko_fi: masterx custom: [ "https://boosty.to/mmxdev", diff --git a/CHANGELOG.md b/CHANGELOG.md index f613f6eda..8dfbf46cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,6 @@ ## Please support development of the project * **Boosty** (patreon alternative): https://boosty.to/mmxdev -* Ko-Fi: https://ko-fi.com/masterx * cloudtips (only RU payments accepted): https://pay.cloudtips.ru/p/7b3e9d65 * YooMoney (only RU payments accepted): https://yoomoney.ru/fundraise/XA49mgQLPA0.221209 * USDT(TRC20): `TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs` diff --git a/ReadMe.md b/ReadMe.md index 1d42e52a3..7ba70bc33 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -118,7 +118,6 @@ The amount of work done on this project is huge and we need your support, no mat Also, regarding our releases, every build has and always will be free and open-source. There will be no paywall releases or closed-source apps within the firmware. As long as I am working on this project it will never happen. You can support us by using links or addresses below: * **Boosty** (patreon alternative): https://boosty.to/mmxdev -* Ko-Fi: https://ko-fi.com/masterx * cloudtips (only RU payments accepted): https://pay.cloudtips.ru/p/7b3e9d65 * YooMoney (only RU payments accepted): https://yoomoney.ru/fundraise/XA49mgQLPA0.221209 * USDT(TRC20): `TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs` From 569edc9e9a3800141d56df570b472e59ee3497db Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 30 Apr 2023 12:22:10 +0100 Subject: [PATCH 159/282] Format --- applications/external/hid_app/views/hid_ytshorts.c | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/external/hid_app/views/hid_ytshorts.c b/applications/external/hid_app/views/hid_ytshorts.c index 9be2f853c..359091640 100644 --- a/applications/external/hid_app/views/hid_ytshorts.c +++ b/applications/external/hid_app/views/hid_ytshorts.c @@ -109,7 +109,6 @@ static void hid_ytshorts_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } - static void hid_ytshorts_reset_cursor(HidYTShorts* hid_ytshorts) { // Set cursor to the phone's left up corner // Delays to guarantee one packet per connection interval From a85a754aa35bc0ac4504f7273d0ea0f743d63268 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 13:27:47 +0200 Subject: [PATCH 160/282] Update ReadMe.md --- ReadMe.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 87e6df32e..024edfa61 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -32,16 +32,22 @@ Note, the below mentioned changes are only a few things we did. For a full list We wrote a powerful yet easy-to-use application specifically for our Firmware, that gives you easy-access to all the fancy things we implemented: - + -Misc: -All the other options that don't fit elsewhere. Change your Flipper's name, change xp level, and manage settings for RGB backlight. + + + + +- Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style, statusbar items and screen options like dark mode and lefty mode. + +- Protocols: Here you can toggle USB & Bluetooth mode for our Bad-Keyboard app, and manage Subghz settings like custom frequencies and extend options. + +- Misc: All the other options that don't fit elsewhere. Change your Flipper's name, change xp level, and manage settings for RGB backlight.
From acd8f1ac9c275272861fc51ed4de9d9fca0c26ee Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 13:29:29 +0200 Subject: [PATCH 161/282] Update ReadMe.md --- ReadMe.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 024edfa61..35e34dbe3 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -43,11 +43,11 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f -- Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style, statusbar items and screen options like dark mode and lefty mode. +- Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style, statusbar items and screen options like dark mode and lefty mode. -- Protocols: Here you can toggle USB & Bluetooth mode for our Bad-Keyboard app, and manage Subghz settings like custom frequencies and extend options. +- Protocols: Here you can toggle USB & Bluetooth mode for our Bad-Keyboard app, and manage Subghz settings like custom frequencies and extend options. -- Misc: All the other options that don't fit elsewhere. Change your Flipper's name, change xp level, and manage settings for RGB backlight. +- Misc: All the other options that don't fit elsewhere. Change your Flipper's name, change xp level, and manage settings for RGB backlight.
From 9f5ebe1055e6d0ba049291906867474e48e1a7dd Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 13:35:44 +0200 Subject: [PATCH 162/282] testing some more --- ReadMe.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 35e34dbe3..cdeba7166 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -39,15 +39,20 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - + +
+Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style, statusbar items and screen options like dark mode and lefty mode. +
-- Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style, statusbar items and screen options like dark mode and lefty mode. +
+Protocols: Here you can toggle USB & Bluetooth mode for our Bad-Keyboard app, and manage Subghz settings like custom frequencies and extend options. +
-- Protocols: Here you can toggle USB & Bluetooth mode for our Bad-Keyboard app, and manage Subghz settings like custom frequencies and extend options. - -- Misc: All the other options that don't fit elsewhere. Change your Flipper's name, change xp level, and manage settings for RGB backlight. +
+Misc: All the other options that don't fit elsewhere. Change your Flipper's name, change xp level, and manage settings for RGB backlight. +

From 7df5ea9bd414c7d435867b159eb80f723c49f42e Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 13:39:23 +0200 Subject: [PATCH 163/282] maybe now? --- ReadMe.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index cdeba7166..52add461e 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -39,19 +39,19 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - - + +
-Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style, statusbar items and screen options like dark mode and lefty mode. +Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. Everything can be modified.
-Protocols: Here you can toggle USB & Bluetooth mode for our Bad-Keyboard app, and manage Subghz settings like custom frequencies and extend options. +Protocols: Here you can toggle between USB & Bluetooth mode for [BadKB](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki/Generic-Guides#badbt--kb), and manage custom Subghz frequencies.
-Misc: All the other options that don't fit elsewhere. Change your Flipper's name, change xp level, and manage settings for RGB backlight. +Misc: All the other options that don't fit elsewhere. Change your Flipper's name, xp level, and manage settings for RGB backlight.

From d4fa794a2d52139a2917cc53a9664f4a1b7e1d15 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 13:41:12 +0200 Subject: [PATCH 164/282] i hate readmes lol --- ReadMe.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 52add461e..9803c1604 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -39,15 +39,15 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - - + +
Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. Everything can be modified.
-Protocols: Here you can toggle between USB & Bluetooth mode for [BadKB](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki/Generic-Guides#badbt--kb), and manage custom Subghz frequencies. +Protocols: Here you can toggle between USB & Bluetooth mode for BadKB, and manage custom Subghz frequencies.
From e8cc9e6f69b2686c4cf46ac41310e55281ea78a5 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 13:44:02 +0200 Subject: [PATCH 165/282] listening to a clown --- ReadMe.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 9803c1604..22d3fec1d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -39,8 +39,8 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - - + +
Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. Everything can be modified. From 3c1799dcc9003ab0ca59318901cae1ccf26ab791 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 13:47:45 +0200 Subject: [PATCH 166/282] meow? --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 22d3fec1d..18d604cc0 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -37,9 +37,9 @@ We wrote a powerful yet easy-to-use application specifically for our Firmware, t This image needs to be updated! Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it fits all our bulletpoints without issues + --> -
From 607b9debcaa6195d6887276e723b84c8402a57fa Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 13:51:24 +0200 Subject: [PATCH 167/282] listening some more to a clown that thinks he knows what hes doing but usually just breaks things and makes me fix it bc hes too dumb for it. Can someone please take him already? I'm fucking tired. k thx bye --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 18d604cc0..f1cf40a85 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -40,7 +40,7 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - +
Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. Everything can be modified. From 8d7259b1ce2ae27385248125d65b57d7eb1a8e4f Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 13:57:53 +0200 Subject: [PATCH 168/282] un-clowning it --- ReadMe.md | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index f1cf40a85..7f1c0e096 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -37,22 +37,15 @@ We wrote a powerful yet easy-to-use application specifically for our Firmware, t This image needs to be updated! Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it fits all our bulletpoints without issues - --> - + + -
-Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. Everything can be modified. -
+- Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. Everything can be modified. -
-Protocols: Here you can toggle between USB & Bluetooth mode for BadKB, and manage custom Subghz frequencies. -
- -
-Misc: All the other options that don't fit elsewhere. Change your Flipper's name, xp level, and manage settings for RGB backlight. -
+- Protocols: Here you can toggle between USB & Bluetooth mode for BadKB, and manage custom Subghz frequencies. +- Misc: All the other options that don't fit elsewhere. Change your Flipper's name, xp level, and manage settings for RGB backlight.
From a0b1f2ae3f23a8e4f73bf834ed7d87486b032f70 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 13:58:28 +0200 Subject: [PATCH 169/282] Update ReadMe.md --- ReadMe.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 7f1c0e096..2e409ee57 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -39,8 +39,8 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - - + + - Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. Everything can be modified. From 512fe8aeada2d38194518e44765e1404d00b5f27 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 30 Apr 2023 14:59:38 +0300 Subject: [PATCH 170/282] Fix readme --- ReadMe.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReadMe.md b/ReadMe.md index 7ba70bc33..1611bed67 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -161,6 +161,7 @@ You can support us by using links or addresses below: - **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) - **ProtoView** [(by antirez)](https://github.com/antirez/protoview) - **SWD Probe** [(by g3gg0)](https://github.com/g3gg0/flipper-swd_probe) +- IR Scope [(by kallanreed)](https://github.com/DarkFlippers/unleashed-firmware/pull/407) Games: - DOOM (fixed) [(by p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/) From aa2ff5a89a3268a9b69325c7fe203dafe22d0258 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 14:00:05 +0200 Subject: [PATCH 171/282] Update ReadMe.md --- ReadMe.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 2e409ee57..11775d3ab 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -39,15 +39,16 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - - + + -- Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. Everything can be modified. +- Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. - Protocols: Here you can toggle between USB & Bluetooth mode for BadKB, and manage custom Subghz frequencies. + - Misc: All the other options that don't fit elsewhere. Change your Flipper's name, xp level, and manage settings for RGB backlight. -
+
-----
From edc2af4955af182967776abde0565227b831e900 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 14:01:08 +0200 Subject: [PATCH 172/282] idfk what im doing.. readmes suck --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 11775d3ab..9c193a4a0 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -40,7 +40,7 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - + - Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. From 56263ace849a50f1e907ecc9e1f3d09347f0d7c1 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 14:02:14 +0200 Subject: [PATCH 173/282] perfection --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 9c193a4a0..987e83fed 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -40,7 +40,7 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - + - Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. From 27825d7cf96953dfb5f8275612ddb7429c84434f Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 14:03:59 +0200 Subject: [PATCH 174/282] Update ReadMe.md --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 987e83fed..9c31e18f1 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -40,7 +40,7 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - + - Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. From 19a2501fee94b4716d47962f6e2622e5d3a674bc Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 14:04:46 +0200 Subject: [PATCH 175/282] Update ReadMe.md --- ReadMe.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 9c31e18f1..67d2d96ac 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -39,8 +39,8 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - - + + - Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. From 81f79a19e4f1c8f1a2d3b44d3a41897f70f11e96 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 14:06:10 +0200 Subject: [PATCH 176/282] Update ReadMe.md --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 67d2d96ac..5e6026da0 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -40,7 +40,7 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - + - Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. From d2d6e7e48801fa654bb354856e3094709700428c Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 14:07:01 +0200 Subject: [PATCH 177/282] Update ReadMe.md --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 5e6026da0..e1ecc43bc 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -40,7 +40,7 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f --> - + - Interface: Customize every bit of your Flipper, from the desktop animations, to the main menu apps, lockscreen style etc. From b4d671770602e08f3d602e0088cf1ddb9092dfc1 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 30 Apr 2023 14:10:30 +0200 Subject: [PATCH 178/282] Update ReadMe.md --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index e1ecc43bc..6934b45c1 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -46,7 +46,7 @@ Also, perhaps a bigger height, with set width (yes distrotion issues ik) so it f - Protocols: Here you can toggle between USB & Bluetooth mode for BadKB, and manage custom Subghz frequencies. -- Misc: All the other options that don't fit elsewhere. Change your Flipper's name, xp level, and manage settings for RGB backlight. +- Misc: All the other options that don't fit elsewhere. Change your Flipper's name, xp level, and configure the RGB backlight.
From 3846e811579ed0586ba641a5e5863747d715f760 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 1 Dec 2022 01:37:50 +0100 Subject: [PATCH 179/282] added DigitalSequence to chain multiple DigitalSignals added PulseReader for hardware assisted digital signal sampling --- lib/SConscript | 1 + lib/digital_signal/digital_signal.c | 451 +++++++++++++++++++++++----- lib/digital_signal/digital_signal.h | 43 ++- lib/pulse_reader/pulse_reader.c | 202 +++++++++++++ lib/pulse_reader/pulse_reader.h | 140 +++++++++ 5 files changed, 761 insertions(+), 76 deletions(-) create mode 100644 lib/pulse_reader/pulse_reader.c create mode 100644 lib/pulse_reader/pulse_reader.h diff --git a/lib/SConscript b/lib/SConscript index f5d4689f1..51f6f7544 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -4,6 +4,7 @@ env.Append( LINT_SOURCES=[ Dir("app-scened-template"), Dir("digital_signal"), + Dir("pulse_reader"), Dir("drivers"), Dir("flipper_format"), Dir("infrared"), diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 46ca307a7..c7e8bbca5 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -1,23 +1,46 @@ #include "digital_signal.h" #include -#include -#include +#include #include -#pragma GCC optimize("O3,unroll-loops,Ofast") +#define TAG "DigitalSignal" + + +#define F_TIM (64000000.0) +#define T_TIM 1562 /* 15.625 ns *100 */ +#define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ -#define F_TIM (64000000.0) -#define T_TIM 1562 //15.625 ns *100 -#define T_TIM_DIV2 781 //15.625 ns / 2 *100 DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { DigitalSignal* signal = malloc(sizeof(DigitalSignal)); signal->start_level = true; signal->edges_max_cnt = max_edges_cnt; - signal->edge_timings = malloc(max_edges_cnt * sizeof(uint32_t)); - signal->reload_reg_buff = malloc(max_edges_cnt * sizeof(uint32_t)); + signal->edge_timings = malloc(signal->edges_max_cnt * sizeof(uint32_t)); signal->edge_cnt = 0; + signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); + signal->reload_reg_entries = 0; + signal->reload_reg_remainder = 0; + + signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_gpio.NbData = 2; + signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + + signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->ARR); + signal->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + signal->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; + signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + signal->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; return signal; } @@ -37,7 +60,10 @@ bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) { if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) { return false; } - + /* in case there are no edges in our target signal, the signal to append makes the rules */ + if(!signal_a->edge_cnt) { + signal_a->start_level = signal_b->start_level; + } bool end_level = signal_a->start_level; if(signal_a->edge_cnt) { end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2); @@ -72,6 +98,32 @@ uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal) { return signal->edge_cnt; } +void digital_signal_add(DigitalSignal* signal, uint32_t ticks) { + furi_assert(signal); + furi_assert(signal->edge_cnt < signal->edges_max_cnt); + + signal->edge_timings[signal->edge_cnt++] = ticks; +} + +void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level) { + furi_assert(signal); + furi_assert(signal->edge_cnt < signal->edges_max_cnt); + + /* virgin signal? add it as the only level */ + if(signal->edge_cnt == 0) { + signal->start_level = level; + signal->edge_timings[signal->edge_cnt++] = ticks; + } else { + bool end_level = signal->start_level ^ !(signal->edge_cnt % 2); + + if(level != end_level) { + signal->edge_timings[signal->edge_cnt++] = ticks; + } else { + signal->edge_timings[signal->edge_cnt - 1] += ticks; + } + } +} + uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { furi_assert(signal); furi_assert(edge_num < signal->edge_cnt); @@ -79,76 +131,76 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { return signal->edge_timings[edge_num]; } -void digital_signal_prepare_arr(DigitalSignal* signal) { - uint32_t t_signal_rest = signal->edge_timings[0]; - uint32_t r_count_tick_arr = 0; - uint32_t r_rest_div = 0; +void digital_signal_prepare(DigitalSignal* signal) { + furi_assert(signal); + furi_assert(signal->gpio); + furi_assert(signal->gpio->pin); + + /* set up signal polarities */ + uint32_t bit_set = signal->gpio->pin; + uint32_t bit_reset = signal->gpio->pin << 16; - for(size_t i = 0; i < signal->edge_cnt - 1; i++) { - r_count_tick_arr = t_signal_rest / T_TIM; - r_rest_div = t_signal_rest % T_TIM; - t_signal_rest = signal->edge_timings[i + 1] + r_rest_div; + if(signal->start_level) { + signal->gpio_buff[0] = bit_set; + signal->gpio_buff[1] = bit_reset; + } else { + signal->gpio_buff[0] = bit_reset; + signal->gpio_buff[1] = bit_set; + } - if(r_rest_div < T_TIM_DIV2) { - signal->reload_reg_buff[i] = r_count_tick_arr - 1; - } else { - signal->reload_reg_buff[i] = r_count_tick_arr; - t_signal_rest -= T_TIM; + /* set up edge timings */ + signal->reload_reg_entries = 0; + + for(size_t pos = 0; pos < signal->edge_cnt; pos++) { + uint32_t pulse_duration = signal->edge_timings[pos] + signal->reload_reg_remainder; + uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; + signal->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); + + if(pulse_ticks > 1) { + signal->reload_reg_buff[signal->reload_reg_entries++] = pulse_ticks - 1; } } } -void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { +static void digital_signal_stop_dma() { + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); +} + +static void digital_signal_stop_timer() { + LL_TIM_DisableCounter(TIM2); + LL_TIM_SetCounter(TIM2, 0); +} + +static bool digital_signal_setup_dma(DigitalSignal* signal) { furi_assert(signal); - furi_assert(gpio); - // Configure gpio as output - furi_hal_gpio_init(gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - - // Init gpio buffer and DMA channel - uint16_t gpio_reg = gpio->port->ODR; - uint16_t gpio_buff[2]; - if(signal->start_level) { - gpio_buff[0] = gpio_reg | gpio->pin; - gpio_buff[1] = gpio_reg & ~(gpio->pin); - } else { - gpio_buff[0] = gpio_reg & ~(gpio->pin); - gpio_buff[1] = gpio_reg | gpio->pin; + if(!signal->reload_reg_entries) { + return false; } - LL_DMA_InitTypeDef dma_config = {}; - dma_config.MemoryOrM2MDstAddress = (uint32_t)gpio_buff; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->ODR); - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; - dma_config.NbData = 2; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - // Init timer arr register buffer and DMA channel - digital_signal_prepare_arr(signal); - dma_config.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_NORMAL; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = signal->edge_cnt - 2; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_HIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->edge_cnt - 2); + signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buff; + signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->BSRR); + signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; + signal->dma_config_timer.NbData = signal->reload_reg_entries; + + /* set up DMA channel 1 and 2 for GPIO and timer copy operations */ + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &signal->dma_config_gpio); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &signal->dma_config_timer); + + /* enable both DMA channels */ + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - // Set up timer + return true; +} + +static void digital_signal_setup_timer() { + + digital_signal_stop_timer(); + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); LL_TIM_SetPrescaler(TIM2, 0); @@ -156,18 +208,267 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableUpdateEvent(TIM2); LL_TIM_EnableDMAReq_UPDATE(TIM2); +} - // Start transactions - LL_TIM_GenerateEvent_UPDATE(TIM2); // Do we really need it? +static void digital_signal_start_timer() { + LL_TIM_GenerateEvent_UPDATE(TIM2); LL_TIM_EnableCounter(TIM2); +} - while(!LL_DMA_IsActiveFlag_TC2(DMA1)) - ; +void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { + furi_assert(signal); + + if(!signal->edge_cnt) { + return; + } + + /* Configure gpio as output */ + signal->gpio = gpio; + furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + /* single signal, add a temporary, terminating edge at the end */ + signal->edge_timings[signal->edge_cnt++] = 10; + digital_signal_prepare(signal); + + digital_signal_setup_dma(signal); + digital_signal_setup_timer(); + digital_signal_start_timer(); + + while(!LL_DMA_IsActiveFlag_TC2(DMA1)) { + } + + digital_signal_stop_timer(); + digital_signal_stop_dma(); + + signal->edge_cnt--; +} + +void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) { + sequence->signals_size = size; + sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*)); + sequence->signals_prolonged = malloc(sequence->signals_size * sizeof(bool)); +} + +void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { + sequence->sequence_used = 0; + sequence->sequence_size = size; + sequence->sequence = malloc(sequence->sequence_size); +} + +DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { + + DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); + + sequence->gpio = gpio; + sequence->bake = false; + + digital_sequence_alloc_signals(sequence, 32); + digital_sequence_alloc_sequence(sequence, size); + + return sequence; +} + +void digital_sequence_free(DigitalSequence* sequence) { + furi_assert(sequence); + + free(sequence->signals); + free(sequence->sequence); + free(sequence); +} + +void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index, DigitalSignal* signal) { + furi_assert(sequence); + furi_assert(signal); + furi_assert(signal_index < sequence->signals_size); + + sequence->signals[signal_index] = signal; + signal->gpio = sequence->gpio; + signal->reload_reg_remainder = 0; + + digital_signal_prepare(signal); +} + +void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { + furi_assert(sequence); + furi_assert(signal_index < sequence->signals_size); + + if(sequence->sequence_used >= sequence->sequence_size) { + sequence->sequence_size += 256; + sequence->sequence = realloc(sequence->sequence, sequence->sequence_size); + } + + sequence->sequence[sequence->sequence_used++] = signal_index; +} + +void digital_signal_update_dma(DigitalSignal* signal) { + + volatile uint32_t dma1_data[] = { + /* R6 */ (uint32_t)&(DMA1_Channel1->CCR), + /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, + /* R8 */ 2, + /* R9 */ (uint32_t)&(signal->gpio->port->BSRR), + /* R10 */ (uint32_t)signal->gpio_buff, + /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN }; + + volatile uint32_t dma2_data[] = { + /* R0 */ (uint32_t)&(DMA1_Channel2->CCR), + /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, + /* R2 */ (uint32_t)signal->reload_reg_entries, + /* R3 */ (uint32_t)&(TIM2->ARR), + /* R4 */ (uint32_t)signal->reload_reg_buff, + /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN }; + + + /* hurry when setting up next transfer */ + asm volatile("\t" + "MOV r6, %[data1]\n\t" + "MOV r7, %[data2]\n\t" + + "PUSH {r0-r12}\n\t" + + "LDM r7, {r0-r5}\n\t" + "LDM r6, {r6-r11}\n\t" + + "loop:\n\t" + "LDR r12, [r0, #4]\n\t" + "CMP r12, #0\n\t" + "BNE loop\n\t" + + "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ + "STR r11, [r6, #0]\n\t" /* enable channel again */ + "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ + "STR r5, [r0, #0]\n\t" /* enable channel again */ + + "POP {r0-r12}\n\t" + + : /* no outputs*/ + : /* inputs */ + [data1] "r" (dma1_data), + [data2] "r" (dma2_data) + : "r6", "r7" ); + LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC2(DMA1); - LL_TIM_DisableCounter(TIM2); - LL_TIM_SetCounter(TIM2, 0); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); +} + +static bool digital_sequence_send_signal(DigitalSignal* signal) { + furi_assert(signal); + + /* the first iteration has to set up the whole machinery */ + if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { + if(!digital_signal_setup_dma(signal)) { + FURI_LOG_D(TAG, "digital_sequence_send_signal: Signal has no entries, aborting"); + return false; + } + digital_signal_setup_timer(); + digital_signal_start_timer(); + } else { + /* configure next polarities and timings */ + digital_signal_update_dma(signal); + } + + return true; +} + +DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { + + uint32_t edges = 0; + + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { + uint8_t signal_index = sequence->sequence[pos]; + DigitalSignal *sig = sequence->signals[signal_index]; + + edges += sig->edge_cnt; + } + + DigitalSignal* ret = digital_signal_alloc(edges); + + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { + uint8_t signal_index = sequence->sequence[pos]; + DigitalSignal *sig = sequence->signals[signal_index]; + + digital_signal_append(ret, sig); + } + + return ret; +} + +bool digital_sequence_send(DigitalSequence* sequence) { + furi_assert(sequence); + + furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + if(sequence->bake) { + DigitalSignal* sig = digital_sequence_bake(sequence); + + digital_signal_send(sig, sequence->gpio); + digital_signal_free(sig); + return true; + } + + int32_t remainder = 0; + FURI_CRITICAL_ENTER(); + + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { + uint8_t signal_index = sequence->sequence[pos]; + DigitalSignal *sig = sequence->signals[signal_index]; + + if(!sig) { + FURI_LOG_D(TAG, "digital_sequence_send: Signal at index %u, used at pos %lu is NULL, aborting", signal_index, pos); + break; + } + + /* when we are too late more than half a tick, make the first edge temporarily longer */ + bool needs_prolongation = false; + + if(remainder >= T_TIM_DIV2) { + remainder -= T_TIM; + needs_prolongation = true; + } + + /* update the total remainder */ + remainder += sig->reload_reg_remainder; + + /* do we need to update the prolongation? */ + if(needs_prolongation != sequence->signals_prolonged[signal_index]) { + if(needs_prolongation) { + sig->edge_timings[0]++; + } else { + sig->edge_timings[0]--; + } + sequence->signals_prolonged[signal_index] = needs_prolongation; + } + + bool success = digital_sequence_send_signal(sig); + + if(!success) { + break; + } + } + FURI_CRITICAL_EXIT(); + + while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { + } + + digital_signal_stop_timer(); + digital_signal_stop_dma(); + + /* undo previously prolonged edges */ + for(uint32_t pos = 0; pos < sequence->signals_size; pos++) { + DigitalSignal *sig = sequence->signals[pos]; + + if(sig && sequence->signals_prolonged[pos]) { + sig->edge_timings[0]--; + sequence->signals_prolonged[pos] = false; + } + } + + return true; +} + +void digital_sequence_clear(DigitalSequence* sequence) { + furi_assert(sequence); + + sequence->sequence_used = 0; } diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 90905d74b..8f6142258 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -5,26 +5,57 @@ #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif +/* helper for easier signal generation */ +#define DIGITAL_SIGNAL_MS(x) (x*100000000UL) +#define DIGITAL_SIGNAL_US(x) (x*100000UL) +#define DIGITAL_SIGNAL_NS(x) (x*100UL) +#define DIGITAL_SIGNAL_PS(x) (x/10UL) + + typedef struct { bool start_level; uint32_t edge_cnt; uint32_t edges_max_cnt; uint32_t* edge_timings; uint32_t* reload_reg_buff; + uint32_t reload_reg_entries; + uint32_t reload_reg_remainder; + uint32_t gpio_buff[2]; + const GpioPin* gpio; + LL_DMA_InitTypeDef dma_config_gpio; + LL_DMA_InitTypeDef dma_config_timer; } DigitalSignal; +typedef struct { + uint8_t signals_size; + bool bake; + uint32_t sequence_used; + uint32_t sequence_size; + DigitalSignal** signals; + bool* signals_prolonged; + uint8_t* sequence; + const GpioPin* gpio; +} DigitalSequence; + + DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); void digital_signal_free(DigitalSignal* signal); +void digital_signal_add(DigitalSignal* signal, uint32_t ticks); + +void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level); + bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b); -void digital_signal_prepare_arr(DigitalSignal* signal); +void digital_signal_prepare(DigitalSignal* signal); bool digital_signal_get_start_level(DigitalSignal* signal); @@ -34,6 +65,16 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num); void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); + +DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio); +void digital_sequence_free(DigitalSequence* sequence); +void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index, DigitalSignal* signal); +void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index); +bool digital_sequence_send(DigitalSequence* sequence); +void digital_sequence_clear(DigitalSequence* sequence); + + + #ifdef __cplusplus } #endif diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c new file mode 100644 index 000000000..18e7ee98d --- /dev/null +++ b/lib/pulse_reader/pulse_reader.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include + +#include "pulse_reader.h" + + +#define GPIO_PIN_MAP(pin, prefix) \ + (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ + ((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \ + ((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \ + ((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \ + ((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \ + ((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \ + ((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \ + ((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \ + ((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \ + ((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \ + ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \ + ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \ + ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \ + ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \ + ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \ + prefix##15) + +#define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE) + + + +PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { + + PulseReader* signal = malloc(sizeof(PulseReader)); + signal->timer_buffer = malloc(size * sizeof(uint32_t)); + signal->gpio_buffer = malloc(size * sizeof(uint32_t)); + signal->dma_channel = LL_DMA_CHANNEL_4; + signal->gpio = gpio; + signal->size = size; + signal->timer_value = 0; + signal->pos = 0; + + pulse_reader_set_timebase(signal, PulseReaderUnit64MHz); + pulse_reader_set_bittime(signal, 1); + + signal->dma_config_timer.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->CNT); + signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t) signal->timer_buffer; + signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR; + signal->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_timer.Priority = LL_DMA_PRIORITY_VERYHIGH; + + signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + + return signal; +} + +void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit) { + switch(unit) { + case PulseReaderUnit64MHz: + signal->unit_multiplier = 1; + signal->unit_divider = 1; + break; + case PulseReaderUnitPicosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1; + break; + case PulseReaderUnitNanosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000; + break; + case PulseReaderUnitMicrosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000000; + break; + } +} + +void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) { + signal->bit_time = bit_time; +} + +void pulse_reader_free(PulseReader* signal) { + free(signal->timer_buffer); + free(signal->gpio_buffer); + free(signal); +} + +uint32_t pulse_reader_samples(PulseReader* signal) { + uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); + + return ((signal->pos + signal->size) - dma_pos) % signal->size; +} + +void pulse_reader_stop(PulseReader* signal) { + LL_DMA_DisableChannel(DMA1, signal->dma_channel); + LL_DMA_DisableChannel(DMA1, signal->dma_channel+1); + LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); + LL_TIM_DisableCounter(TIM2); +} + +void pulse_reader_start(PulseReader* signal) { + /* configure DMA to read from a timer peripheral */ + signal->dma_config_timer.NbData = signal->size; + + signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->IDR); + signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buffer; + signal->dma_config_gpio.NbData = signal->size; + + /* start counter */ + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetPrescaler(TIM2, 0); + LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); + + /* generator 0 gets fed by EXTI_LINEn */ + LL_DMAMUX_SetRequestSignalID(NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin)); + /* trigger on rising edge of the interrupt */ + LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); + /* now enable request generation again */ + LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); + + /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */ + furi_hal_gpio_init_ex(signal->gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused); + + /* capture current timer */ + signal->pos = 0; + signal->start_level = furi_hal_gpio_read(signal->gpio); + signal->timer_value = TIM2->CNT; + signal->gpio_mask = signal->gpio->pin; + + /* now set up DMA with these settings */ + LL_DMA_Init(DMA1, signal->dma_channel, &signal->dma_config_timer); + LL_DMA_Init(DMA1, signal->dma_channel + 1, &signal->dma_config_gpio); + LL_DMA_EnableChannel(DMA1, signal->dma_channel); + LL_DMA_EnableChannel(DMA1, signal->dma_channel + 1); +} + +uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { + + uint32_t start_time = DWT->CYCCNT; + uint32_t timeout_ticks = timeout_us * (F_TIM2/1000000); + + do { + /* get the DMA's next write position by reading "remaining length" register */ + uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); + + /* the DMA has advanced in the ringbuffer */ + if(dma_pos != signal->pos) { + + uint32_t delta = signal->timer_buffer[signal->pos] - signal->timer_value; + uint32_t last_gpio_value = signal->gpio_value; + + signal->gpio_value = signal->gpio_buffer[signal->pos]; + + /* check if the GPIO really toggled. if not, we lost an edge :( */ + if(((last_gpio_value ^ signal->gpio_value) & signal->gpio_mask) != signal->gpio_mask) { + signal->gpio_value ^= signal->gpio_mask; + return PULSE_READER_LOST_EDGE; + } + signal->timer_value = signal->timer_buffer[signal->pos]; + + signal->pos++; + signal->pos %= signal->size; + + uint32_t delta_unit = 0; + + /* probably larger values, so choose a wider data type */ + if(signal->unit_divider > 1) { + delta_unit = (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider); + } else { + delta_unit = delta * signal->unit_multiplier; + } + + /* if to be scaled to bit times, save a few instructions. should be faster */ + if(signal->bit_time > 1) { + return (delta_unit + signal->bit_time / 2) / signal->bit_time; + } + + return delta_unit; + } + + /* check for timeout */ + uint32_t elapsed = DWT->CYCCNT - start_time; + + if(elapsed > timeout_ticks) { + return PULSE_READER_NO_EDGE; + } + } while(true); +} diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h new file mode 100644 index 000000000..f55a65931 --- /dev/null +++ b/lib/pulse_reader/pulse_reader.h @@ -0,0 +1,140 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PULSE_READER_NO_EDGE 0xFFFFFFFFUL +#define PULSE_READER_LOST_EDGE 0xFFFFFFFEUL +#define F_TIM2 64000000UL + +/** + * unit of the edge durations to return + */ +typedef enum { + PulseReaderUnit64MHz, + PulseReaderUnitPicosecond, + PulseReaderUnitNanosecond, + PulseReaderUnitMicrosecond, +} PulseReaderUnit; + + +typedef struct { + bool start_level; + uint32_t* timer_buffer; + uint32_t* gpio_buffer; + uint32_t size; + uint32_t pos; + uint32_t timer_value; + uint32_t gpio_value; + uint32_t gpio_mask; + uint32_t unit_multiplier; + uint32_t unit_divider; + uint32_t bit_time; + uint32_t dma_channel; + const GpioPin* gpio; + LL_DMA_InitTypeDef dma_config_timer; + LL_DMA_InitTypeDef dma_config_gpio; +} PulseReader; + + +/** Allocate a PulseReader object + * + * Allocates memory for a ringbuffer and initalizes the object + * + * @param[in] gpio the GPIO to use. will get configured as input. + * @param[in] size number of edges to buffer + */ +PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size); + + +/** Free a PulseReader object + * + * Frees all memory of the given object + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_free(PulseReader* signal); + + +/** Start signal capturing + * + * Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_start(PulseReader* signal); + + +/** Stop signal capturing + * + * Frees DMA1, TIM2 and DMAMUX_REQ_GEN_0 + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_stop(PulseReader* signal); + + +/** Recevie a sample from ringbuffer + * + * Waits for the specified time until a new edge gets detected. + * If not configured otherwise, the pulse duration will be in picosecond resolution. + * If a bittime was configured, the return value will contain the properly rounded + * number of bit times measured. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] timeout_us time to wait for a signal [µs] + * + * @returns the scaled value of the pulse duration + */ +uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us); + + +/** Get available samples + * + * Get the number of available samples in the ringbuffer + * + * @param[in] signal previously allocated PulseReader object. + * + * @returns the number of samples in buffer + */ +uint32_t pulse_reader_samples(PulseReader* signal); + + +/** Set timebase + * + * Set the timebase to be used when returning pulse duration. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] unit PulseReaderUnit64MHz or PulseReaderUnitPicosecond + */ +void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit); + + +/** Set bit time + * + * Set the number of timebase units per bit. + * When set, the pulse_reader_receive() will return an already rounded + * bit count value instead of the raw duration. + * + * Set to 1 to return duration again. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] bit_time + */ +void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time); + + +#ifdef __cplusplus +} +#endif From 26c4e886777e23d39218102d695a43f99d30aaa9 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 1 Dec 2022 18:43:22 +0100 Subject: [PATCH 180/282] added send_time option to start a signal at a specific DWT->CYCCNT value --- lib/digital_signal/digital_signal.c | 21 +++++++++++++++++++-- lib/digital_signal/digital_signal.h | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index c7e8bbca5..727b64ca4 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -253,6 +253,7 @@ void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { sequence->sequence_used = 0; sequence->sequence_size = size; sequence->sequence = malloc(sequence->sequence_size); + sequence->send_time = 0; } DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { @@ -288,6 +289,10 @@ void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index digital_signal_prepare(signal); } +void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time) { + sequence->send_time = send_time; +} + void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { furi_assert(sequence); furi_assert(signal_index < sequence->signals_size); @@ -352,7 +357,8 @@ void digital_signal_update_dma(DigitalSignal* signal) { LL_DMA_ClearFlag_TC2(DMA1); } -static bool digital_sequence_send_signal(DigitalSignal* signal) { +static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { + furi_assert(sequence); furi_assert(signal); /* the first iteration has to set up the whole machinery */ @@ -362,6 +368,17 @@ static bool digital_sequence_send_signal(DigitalSignal* signal) { return false; } digital_signal_setup_timer(); + + /* if the send time is specified, wait till the core timer passed beyond that time */ + if(sequence->send_time != 0) { + while(true) { + uint32_t delta = sequence->send_time - DWT->CYCCNT; + /* yeah, it's making use of underflows... */ + if(delta > 0x80000000) { + break; + } + } + } digital_signal_start_timer(); } else { /* configure next polarities and timings */ @@ -440,7 +457,7 @@ bool digital_sequence_send(DigitalSequence* sequence) { sequence->signals_prolonged[signal_index] = needs_prolongation; } - bool success = digital_sequence_send_signal(sig); + bool success = digital_sequence_send_signal(sequence, sig); if(!success) { break; diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 8f6142258..94df13a60 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -42,6 +42,7 @@ typedef struct { bool* signals_prolonged; uint8_t* sequence; const GpioPin* gpio; + uint32_t send_time; } DigitalSequence; @@ -69,6 +70,7 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio); void digital_sequence_free(DigitalSequence* sequence); void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index, DigitalSignal* signal); +void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time); void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index); bool digital_sequence_send(DigitalSequence* sequence); void digital_sequence_clear(DigitalSequence* sequence); From 0a714fc1648cb70bfdc8985d33494650dbf7eb41 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 5 Dec 2022 09:36:45 +0100 Subject: [PATCH 181/282] fixed linter errors and undone function renaming --- lib/digital_signal/digital_signal.c | 104 ++++++++++++++-------------- lib/digital_signal/digital_signal.h | 18 +++-- lib/pulse_reader/pulse_reader.c | 68 +++++++++--------- lib/pulse_reader/pulse_reader.h | 16 +---- 4 files changed, 96 insertions(+), 110 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 727b64ca4..b78daab3d 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -6,11 +6,9 @@ #define TAG "DigitalSignal" - -#define F_TIM (64000000.0) -#define T_TIM 1562 /* 15.625 ns *100 */ -#define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ - +#define F_TIM (64000000.0) +#define T_TIM 1562 /* 15.625 ns *100 */ +#define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { DigitalSignal* signal = malloc(sizeof(DigitalSignal)); @@ -21,7 +19,7 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); signal->reload_reg_entries = 0; signal->reload_reg_remainder = 0; - + signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; @@ -32,7 +30,7 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; - signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->ARR); + signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); signal->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; signal->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; @@ -131,11 +129,11 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { return signal->edge_timings[edge_num]; } -void digital_signal_prepare(DigitalSignal* signal) { +void digital_signal_prepare_arr(DigitalSignal* signal) { furi_assert(signal); furi_assert(signal->gpio); furi_assert(signal->gpio->pin); - + /* set up signal polarities */ uint32_t bit_set = signal->gpio->pin; uint32_t bit_reset = signal->gpio->pin << 16; @@ -181,8 +179,8 @@ static bool digital_signal_setup_dma(DigitalSignal* signal) { return false; } - signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buff; - signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->BSRR); + signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buff; + signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (signal->gpio->port->BSRR); signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; signal->dma_config_timer.NbData = signal->reload_reg_entries; @@ -198,7 +196,6 @@ static bool digital_signal_setup_dma(DigitalSignal* signal) { } static void digital_signal_setup_timer() { - digital_signal_stop_timer(); LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); @@ -228,7 +225,7 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { /* single signal, add a temporary, terminating edge at the end */ signal->edge_timings[signal->edge_cnt++] = 10; - digital_signal_prepare(signal); + digital_signal_prepare_arr(signal); digital_signal_setup_dma(signal); digital_signal_setup_timer(); @@ -257,7 +254,6 @@ void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { } DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { - DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); sequence->gpio = gpio; @@ -277,7 +273,10 @@ void digital_sequence_free(DigitalSequence* sequence) { free(sequence); } -void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index, DigitalSignal* signal) { +void digital_sequence_set_signal( + DigitalSequence* sequence, + uint8_t signal_index, + DigitalSignal* signal) { furi_assert(sequence); furi_assert(signal); furi_assert(signal_index < sequence->signals_size); @@ -286,7 +285,7 @@ void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index signal->gpio = sequence->gpio; signal->reload_reg_remainder = 0; - digital_signal_prepare(signal); + digital_signal_prepare_arr(signal); } void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time) { @@ -306,52 +305,48 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { } void digital_signal_update_dma(DigitalSignal* signal) { - volatile uint32_t dma1_data[] = { - /* R6 */ (uint32_t)&(DMA1_Channel1->CCR), + /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, /* R8 */ 2, - /* R9 */ (uint32_t)&(signal->gpio->port->BSRR), + /* R9 */ (uint32_t) & (signal->gpio->port->BSRR), /* R10 */ (uint32_t)signal->gpio_buff, - /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN }; + /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; volatile uint32_t dma2_data[] = { - /* R0 */ (uint32_t)&(DMA1_Channel2->CCR), + /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* R2 */ (uint32_t)signal->reload_reg_entries, - /* R3 */ (uint32_t)&(TIM2->ARR), + /* R3 */ (uint32_t) & (TIM2->ARR), /* R4 */ (uint32_t)signal->reload_reg_buff, - /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN }; + /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN}; - /* hurry when setting up next transfer */ asm volatile("\t" - "MOV r6, %[data1]\n\t" - "MOV r7, %[data2]\n\t" + "MOV r6, %[data1]\n\t" + "MOV r7, %[data2]\n\t" - "PUSH {r0-r12}\n\t" + "PUSH {r0-r12}\n\t" - "LDM r7, {r0-r5}\n\t" - "LDM r6, {r6-r11}\n\t" + "LDM r7, {r0-r5}\n\t" + "LDM r6, {r6-r11}\n\t" - "loop:\n\t" - "LDR r12, [r0, #4]\n\t" - "CMP r12, #0\n\t" - "BNE loop\n\t" + "loop:\n\t" + "LDR r12, [r0, #4]\n\t" + "CMP r12, #0\n\t" + "BNE loop\n\t" - "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ - "STR r11, [r6, #0]\n\t" /* enable channel again */ - "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ - "STR r5, [r0, #0]\n\t" /* enable channel again */ + "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ + "STR r11, [r6, #0]\n\t" /* enable channel again */ + "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ + "STR r5, [r0, #0]\n\t" /* enable channel again */ - "POP {r0-r12}\n\t" + "POP {r0-r12}\n\t" - : /* no outputs*/ - : /* inputs */ - [data1] "r" (dma1_data), - [data2] "r" (dma2_data) - : "r6", "r7" ); - + : /* no outputs*/ + : /* inputs */ + [data1] "r"(dma1_data), [data2] "r"(dma2_data) + : "r6", "r7"); LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC2(DMA1); @@ -360,7 +355,7 @@ void digital_signal_update_dma(DigitalSignal* signal) { static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { furi_assert(sequence); furi_assert(signal); - + /* the first iteration has to set up the whole machinery */ if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { if(!digital_signal_setup_dma(signal)) { @@ -389,21 +384,20 @@ static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSigna } DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { - uint32_t edges = 0; for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { uint8_t signal_index = sequence->sequence[pos]; - DigitalSignal *sig = sequence->signals[signal_index]; + DigitalSignal* sig = sequence->signals[signal_index]; edges += sig->edge_cnt; } DigitalSignal* ret = digital_signal_alloc(edges); - + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { uint8_t signal_index = sequence->sequence[pos]; - DigitalSignal *sig = sequence->signals[signal_index]; + DigitalSignal* sig = sequence->signals[signal_index]; digital_signal_append(ret, sig); } @@ -429,10 +423,14 @@ bool digital_sequence_send(DigitalSequence* sequence) { for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { uint8_t signal_index = sequence->sequence[pos]; - DigitalSignal *sig = sequence->signals[signal_index]; + DigitalSignal* sig = sequence->signals[signal_index]; if(!sig) { - FURI_LOG_D(TAG, "digital_sequence_send: Signal at index %u, used at pos %lu is NULL, aborting", signal_index, pos); + FURI_LOG_D( + TAG, + "digital_sequence_send: Signal at index %u, used at pos %lu is NULL, aborting", + signal_index, + pos); break; } @@ -470,10 +468,10 @@ bool digital_sequence_send(DigitalSequence* sequence) { digital_signal_stop_timer(); digital_signal_stop_dma(); - + /* undo previously prolonged edges */ for(uint32_t pos = 0; pos < sequence->signals_size; pos++) { - DigitalSignal *sig = sequence->signals[pos]; + DigitalSignal* sig = sequence->signals[pos]; if(sig && sequence->signals_prolonged[pos]) { sig->edge_timings[0]--; diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 94df13a60..2e1a05522 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -13,11 +13,10 @@ extern "C" { #endif /* helper for easier signal generation */ -#define DIGITAL_SIGNAL_MS(x) (x*100000000UL) -#define DIGITAL_SIGNAL_US(x) (x*100000UL) -#define DIGITAL_SIGNAL_NS(x) (x*100UL) -#define DIGITAL_SIGNAL_PS(x) (x/10UL) - +#define DIGITAL_SIGNAL_MS(x) (x * 100000000UL) +#define DIGITAL_SIGNAL_US(x) (x * 100000UL) +#define DIGITAL_SIGNAL_NS(x) (x * 100UL) +#define DIGITAL_SIGNAL_PS(x) (x / 10UL) typedef struct { bool start_level; @@ -45,7 +44,6 @@ typedef struct { uint32_t send_time; } DigitalSequence; - DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); void digital_signal_free(DigitalSignal* signal); @@ -66,17 +64,17 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num); void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); - DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio); void digital_sequence_free(DigitalSequence* sequence); -void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index, DigitalSignal* signal); +void digital_sequence_set_signal( + DigitalSequence* sequence, + uint8_t signal_index, + DigitalSignal* signal); void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time); void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index); bool digital_sequence_send(DigitalSequence* sequence); void digital_sequence_clear(DigitalSequence* sequence); - - #ifdef __cplusplus } #endif diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 18e7ee98d..20f993844 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -5,7 +5,6 @@ #include "pulse_reader.h" - #define GPIO_PIN_MAP(pin, prefix) \ (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ ((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \ @@ -26,10 +25,7 @@ #define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE) - - PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { - PulseReader* signal = malloc(sizeof(PulseReader)); signal->timer_buffer = malloc(size * sizeof(uint32_t)); signal->gpio_buffer = malloc(size * sizeof(uint32_t)); @@ -43,14 +39,15 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { pulse_reader_set_bittime(signal, 1); signal->dma_config_timer.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; - signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->CNT); + signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->CNT); signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t) signal->timer_buffer; + signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->timer_buffer; signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; signal->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR; - signal->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_timer.PeriphRequest = + LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ signal->dma_config_timer.Priority = LL_DMA_PRIORITY_VERYHIGH; signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; @@ -59,7 +56,8 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; - signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_gpio.PeriphRequest = + LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; return signal; @@ -67,22 +65,22 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit) { switch(unit) { - case PulseReaderUnit64MHz: - signal->unit_multiplier = 1; - signal->unit_divider = 1; - break; - case PulseReaderUnitPicosecond: - signal->unit_multiplier = 15625; - signal->unit_divider = 1; - break; - case PulseReaderUnitNanosecond: - signal->unit_multiplier = 15625; - signal->unit_divider = 1000; - break; - case PulseReaderUnitMicrosecond: - signal->unit_multiplier = 15625; - signal->unit_divider = 1000000; - break; + case PulseReaderUnit64MHz: + signal->unit_multiplier = 1; + signal->unit_divider = 1; + break; + case PulseReaderUnitPicosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1; + break; + case PulseReaderUnitNanosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000; + break; + case PulseReaderUnitMicrosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000000; + break; } } @@ -104,7 +102,7 @@ uint32_t pulse_reader_samples(PulseReader* signal) { void pulse_reader_stop(PulseReader* signal) { LL_DMA_DisableChannel(DMA1, signal->dma_channel); - LL_DMA_DisableChannel(DMA1, signal->dma_channel+1); + LL_DMA_DisableChannel(DMA1, signal->dma_channel + 1); LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); LL_TIM_DisableCounter(TIM2); } @@ -113,8 +111,8 @@ void pulse_reader_start(PulseReader* signal) { /* configure DMA to read from a timer peripheral */ signal->dma_config_timer.NbData = signal->size; - signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->IDR); - signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buffer; + signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (signal->gpio->port->IDR); + signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buffer; signal->dma_config_gpio.NbData = signal->size; /* start counter */ @@ -126,14 +124,16 @@ void pulse_reader_start(PulseReader* signal) { LL_TIM_EnableCounter(TIM2); /* generator 0 gets fed by EXTI_LINEn */ - LL_DMAMUX_SetRequestSignalID(NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin)); + LL_DMAMUX_SetRequestSignalID( + NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin)); /* trigger on rising edge of the interrupt */ LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); /* now enable request generation again */ LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */ - furi_hal_gpio_init_ex(signal->gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused); + furi_hal_gpio_init_ex( + signal->gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused); /* capture current timer */ signal->pos = 0; @@ -149,17 +149,16 @@ void pulse_reader_start(PulseReader* signal) { } uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { - uint32_t start_time = DWT->CYCCNT; - uint32_t timeout_ticks = timeout_us * (F_TIM2/1000000); + uint32_t timeout_ticks = timeout_us * (F_TIM2 / 1000000); do { /* get the DMA's next write position by reading "remaining length" register */ - uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); + uint32_t dma_pos = + signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); /* the DMA has advanced in the ringbuffer */ if(dma_pos != signal->pos) { - uint32_t delta = signal->timer_buffer[signal->pos] - signal->timer_value; uint32_t last_gpio_value = signal->gpio_value; @@ -179,7 +178,8 @@ uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { /* probably larger values, so choose a wider data type */ if(signal->unit_divider > 1) { - delta_unit = (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider); + delta_unit = + (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider); } else { delta_unit = delta * signal->unit_multiplier; } diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h index f55a65931..564bf1063 100644 --- a/lib/pulse_reader/pulse_reader.h +++ b/lib/pulse_reader/pulse_reader.h @@ -14,9 +14,9 @@ extern "C" { #endif -#define PULSE_READER_NO_EDGE 0xFFFFFFFFUL -#define PULSE_READER_LOST_EDGE 0xFFFFFFFEUL -#define F_TIM2 64000000UL +#define PULSE_READER_NO_EDGE 0xFFFFFFFFUL +#define PULSE_READER_LOST_EDGE 0xFFFFFFFEUL +#define F_TIM2 64000000UL /** * unit of the edge durations to return @@ -28,7 +28,6 @@ typedef enum { PulseReaderUnitMicrosecond, } PulseReaderUnit; - typedef struct { bool start_level; uint32_t* timer_buffer; @@ -47,7 +46,6 @@ typedef struct { LL_DMA_InitTypeDef dma_config_gpio; } PulseReader; - /** Allocate a PulseReader object * * Allocates memory for a ringbuffer and initalizes the object @@ -57,7 +55,6 @@ typedef struct { */ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size); - /** Free a PulseReader object * * Frees all memory of the given object @@ -66,7 +63,6 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size); */ void pulse_reader_free(PulseReader* signal); - /** Start signal capturing * * Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values @@ -75,7 +71,6 @@ void pulse_reader_free(PulseReader* signal); */ void pulse_reader_start(PulseReader* signal); - /** Stop signal capturing * * Frees DMA1, TIM2 and DMAMUX_REQ_GEN_0 @@ -84,7 +79,6 @@ void pulse_reader_start(PulseReader* signal); */ void pulse_reader_stop(PulseReader* signal); - /** Recevie a sample from ringbuffer * * Waits for the specified time until a new edge gets detected. @@ -99,7 +93,6 @@ void pulse_reader_stop(PulseReader* signal); */ uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us); - /** Get available samples * * Get the number of available samples in the ringbuffer @@ -110,7 +103,6 @@ uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us); */ uint32_t pulse_reader_samples(PulseReader* signal); - /** Set timebase * * Set the timebase to be used when returning pulse duration. @@ -120,7 +112,6 @@ uint32_t pulse_reader_samples(PulseReader* signal); */ void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit); - /** Set bit time * * Set the number of timebase units per bit. @@ -134,7 +125,6 @@ void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit); */ void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time); - #ifdef __cplusplus } #endif From 9a6a796804ac07999216fcb02b182147ebf7439a Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 5 Dec 2022 10:32:44 +0100 Subject: [PATCH 182/282] fixed renaming --- lib/digital_signal/digital_signal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 2e1a05522..9a91c8b45 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -54,7 +54,7 @@ void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level) bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b); -void digital_signal_prepare(DigitalSignal* signal); +void digital_signal_prepare_arr(DigitalSignal* signal); bool digital_signal_get_start_level(DigitalSignal* signal); From b24c0cd97fbaa1c41758e5a4e9af6185c33472df Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 5 Dec 2022 10:36:15 +0100 Subject: [PATCH 183/282] flagged functions in api_symbols.csv --- firmware/targets/f7/api_symbols.csv | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d0c6b36ad..63fb64a00 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -736,6 +736,15 @@ Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, u Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*" Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" Function,-,difftime,double,"time_t, time_t" +Function,-,digital_sequence_add,void,"DigitalSequence*, uint8_t" +Function,-,digital_sequence_alloc,DigitalSequence*,"uint32_t, const GpioPin*" +Function,-,digital_sequence_clear,void,DigitalSequence* +Function,-,digital_sequence_free,void,DigitalSequence* +Function,-,digital_sequence_send,_Bool,DigitalSequence* +Function,-,digital_sequence_set_sendtime,void,"DigitalSequence*, uint32_t" +Function,-,digital_sequence_set_signal,void,"DigitalSequence*, uint8_t, DigitalSignal*" +Function,-,digital_signal_add,void,"DigitalSignal*, uint32_t" +Function,-,digital_signal_add_pulse,void,"DigitalSignal*, uint32_t, _Bool" Function,-,digital_signal_alloc,DigitalSignal*,uint32_t Function,-,digital_signal_append,_Bool,"DigitalSignal*, DigitalSignal*" Function,-,digital_signal_free,void,DigitalSignal* From 302739d543ebb95f17056b3cfaed6ac92d5beea3 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 5 Dec 2022 12:19:05 +0100 Subject: [PATCH 184/282] allow gpio field to stay uninitialized in digital_signal_prepare_arr() --- lib/digital_signal/digital_signal.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index b78daab3d..9ba5cc901 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -131,19 +131,19 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { void digital_signal_prepare_arr(DigitalSignal* signal) { furi_assert(signal); - furi_assert(signal->gpio); - furi_assert(signal->gpio->pin); /* set up signal polarities */ - uint32_t bit_set = signal->gpio->pin; - uint32_t bit_reset = signal->gpio->pin << 16; + if(signal->gpio) { + uint32_t bit_set = signal->gpio->pin; + uint32_t bit_reset = signal->gpio->pin << 16; - if(signal->start_level) { - signal->gpio_buff[0] = bit_set; - signal->gpio_buff[1] = bit_reset; - } else { - signal->gpio_buff[0] = bit_reset; - signal->gpio_buff[1] = bit_set; + if(signal->start_level) { + signal->gpio_buff[0] = bit_set; + signal->gpio_buff[1] = bit_reset; + } else { + signal->gpio_buff[0] = bit_reset; + signal->gpio_buff[1] = bit_set; + } } /* set up edge timings */ From 035d630ff90f9bf29f29336fc78465b3517fa106 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 15 Dec 2022 15:29:16 +0100 Subject: [PATCH 185/282] fix test cases to match (expected) implementation --- assets/unit_tests/nfc/nfc_nfca_signal_long.nfc | 2 +- assets/unit_tests/nfc/nfc_nfca_signal_short.nfc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc b/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc index fae69cb5c..dd6a2ff8e 100644 --- a/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc +++ b/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc @@ -3,4 +3,4 @@ Version: 1 Data length: 18 Plain data: f1 99 41 43 a1 2f 23 01 de f3 c5 8d 91 4b 1e 50 4a c9 Timings length: 1304 -Timings: 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 640 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 0 +Timings: 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 640 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 diff --git a/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc b/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc index 3b7e2d9e9..f447fca26 100644 --- a/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc +++ b/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc @@ -3,4 +3,4 @@ Version: 1 Data length: 4 Plain data: 14 d8 a0 c9 Timings length: 296 -Timings: 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 36 37 37 37 37 36 37 0 +Timings: 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 36 37 37 37 37 36 37 37 From b5b5abac273d50d2b886c5e119ec16e3f55609ca Mon Sep 17 00:00:00 2001 From: gornekich Date: Tue, 20 Dec 2022 18:49:29 +0400 Subject: [PATCH 186/282] pulse_reader: build as static library Signed-off-by: g3gg0.de --- firmware/targets/f7/api_symbols.csv | 9 +++++++++ firmware/targets/f7/target.json | 1 + lib/SConscript | 2 ++ lib/pulse_reader/SConscript | 19 +++++++++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 lib/pulse_reader/SConscript diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 63fb64a00..7b80eb8aa 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -173,6 +173,7 @@ Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_slave.h,, Header,+,lib/print/wrappers.h,, +Header,+,lib/pulse_reader/pulse_reader.h,, Header,+,lib/subghz/blocks/const.h,, Header,+,lib/subghz/blocks/decoder.h,, Header,+,lib/subghz/blocks/encoder.h,, @@ -2166,6 +2167,14 @@ Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, siz Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*" +Function,+,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" +Function,+,pulse_reader_free,void,PulseReader* +Function,+,pulse_reader_receive,uint32_t,"PulseReader*, int" +Function,+,pulse_reader_samples,uint32_t,PulseReader* +Function,+,pulse_reader_set_bittime,void,"PulseReader*, uint32_t" +Function,+,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit" +Function,+,pulse_reader_start,void,PulseReader* +Function,+,pulse_reader_stop,void,PulseReader* Function,-,putc,int,"int, FILE*" Function,-,putc_unlocked,int,"int, FILE*" Function,-,putchar,int,int diff --git a/firmware/targets/f7/target.json b/firmware/targets/f7/target.json index 14bb1cd0c..0d2367b3a 100644 --- a/firmware/targets/f7/target.json +++ b/firmware/targets/f7/target.json @@ -28,6 +28,7 @@ "flipperformat", "toolbox", "nfc", + "pulse_reader", "microtar", "usb_stm32", "st25rfal002", diff --git a/lib/SConscript b/lib/SConscript index 51f6f7544..23c74b334 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -15,6 +15,7 @@ env.Append( Dir("u8g2"), Dir("update_util"), Dir("print"), + Dir("pulse_reader"), ], ) @@ -94,6 +95,7 @@ libs = env.BuildModules( "mbedtls", "subghz", "nfc", + "pulse_reader", "appframe", "misc", "lfrfid", diff --git a/lib/pulse_reader/SConscript b/lib/pulse_reader/SConscript new file mode 100644 index 000000000..416ad4a27 --- /dev/null +++ b/lib/pulse_reader/SConscript @@ -0,0 +1,19 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/pulse_reader", + ], + SDK_HEADERS=[ + File("pulse_reader.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="pulse_reader") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") \ No newline at end of file From 396ebe1ad80c8bbb7728b14fa6d95b43b6103f06 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 01:01:16 +0100 Subject: [PATCH 187/282] fix starting level detection in pulse_reader --- lib/pulse_reader/pulse_reader.c | 2 +- lib/pulse_reader/pulse_reader.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 20f993844..64fde7fc9 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -137,9 +137,9 @@ void pulse_reader_start(PulseReader* signal) { /* capture current timer */ signal->pos = 0; - signal->start_level = furi_hal_gpio_read(signal->gpio); signal->timer_value = TIM2->CNT; signal->gpio_mask = signal->gpio->pin; + signal->gpio_value = signal->gpio->port->IDR & signal->gpio_mask; /* now set up DMA with these settings */ LL_DMA_Init(DMA1, signal->dma_channel, &signal->dma_config_timer); diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h index 564bf1063..b4e6bca57 100644 --- a/lib/pulse_reader/pulse_reader.h +++ b/lib/pulse_reader/pulse_reader.h @@ -29,7 +29,6 @@ typedef enum { } PulseReaderUnit; typedef struct { - bool start_level; uint32_t* timer_buffer; uint32_t* gpio_buffer; uint32_t size; @@ -65,7 +64,8 @@ void pulse_reader_free(PulseReader* signal); /** Start signal capturing * - * Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values + * Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values. + * Ensure that interrupts are always enabled, as the used EXTI line is handled as one. * * @param[in] signal previously allocated PulseReader object. */ From e56cd5165afb38ee36373d0d205bf954ad4605b0 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 01:06:22 +0100 Subject: [PATCH 188/282] added unit test for pulse_reader --- applications/debug/unit_tests/nfc/nfc_test.c | 150 +++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 54bdd5909..3b3a44431 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -1,10 +1,12 @@ #include #include +#include #include #include #include #include #include +#include #include #include @@ -179,6 +181,153 @@ MU_TEST(nfc_digital_signal_test) { "NFC long digital signal test failed\r\n"); } +static bool nfc_test_pulse_reader_toggle( + uint32_t usec_low, + uint32_t usec_high, + uint32_t period_count, + uint32_t tolerance) { + furi_assert(nfc_test); + + bool success = false; + uint32_t pulses = 0; + const GpioPin* gpio_in = &gpio_ext_pa6; + const GpioPin* gpio_out = &gpio_ext_pa7; + PulseReader* reader = NULL; + + do { + reader = pulse_reader_alloc(gpio_in, 512); + + if(!reader) { + FURI_LOG_E(TAG, "failed to allocate pulse reader"); + break; + } + + /* use TIM1 to create a specific number of pulses with defined duty cycle + but first set the IO to high, so the low/high pulse can get detected */ + furi_hal_gpio_init(gpio_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(gpio_out, true); + + LL_TIM_DeInit(TIM1); + + LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetRepetitionCounter(TIM1, 0); + LL_TIM_SetClockDivision(TIM1, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM1); + + LL_TIM_OC_DisablePreload(TIM1, LL_TIM_CHANNEL_CH1); + LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM2); + LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1); + LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N); + + LL_TIM_EnableAllOutputs(TIM1); + + /* now calculate the TIM1 period and compare values */ + uint32_t freq_div = 64 * (usec_low + usec_high); + uint32_t prescaler = freq_div / 0x10000LU; + uint32_t period = freq_div / (prescaler + 1); + uint32_t compare = 64 * usec_low / (prescaler + 1); + + LL_TIM_SetPrescaler(TIM1, prescaler); + LL_TIM_SetAutoReload(TIM1, period - 1); + LL_TIM_SetCounter(TIM1, period - 1); + LL_TIM_OC_SetCompareCH1(TIM1, compare); + + /* timer is ready to launch, now start the pulse reader */ + pulse_reader_set_timebase(reader, PulseReaderUnitMicrosecond); + pulse_reader_start(reader); + + /* and quickly enable and switch over the GPIO to the generated signal */ + LL_TIM_EnableCounter(TIM1); + furi_hal_gpio_init_ex( + gpio_out, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedVeryHigh, GpioAltFn1TIM1); + + /* now it's time to parse the pulses received by the reader */ + uint32_t timer_pulses = period_count; + uint32_t prev_cnt = 0; + + while(timer_pulses > 0) { + /* whenever the counter gets reset, we went through a full period */ + uint32_t cur_cnt = LL_TIM_GetCounter(TIM1); + if(cur_cnt < prev_cnt) { + timer_pulses--; + } + prev_cnt = cur_cnt; + } + /* quickly halt the counter to keep a static signal */ + LL_TIM_DisableCounter(TIM1); + + do { + /* as all edges were sampled asynchronously, the timeout can be zero */ + uint32_t length = pulse_reader_receive(reader, 0); + + /* in the last pulse, we expect a "no edge" return value. if seen that, test succeeded. */ + if(pulses > period_count * 2) { + if(length != PULSE_READER_NO_EDGE) { + FURI_LOG_E( + TAG, + "last pulse expected to be PULSE_READER_NO_EDGE, but was %lu.", + length); + break; + } + success = true; + break; + } + + /* else we shall never see "no edge" or "lost edge" */ + if(length == PULSE_READER_NO_EDGE) { + FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_NO_EDGE", pulses); + break; + } + if(length == PULSE_READER_LOST_EDGE) { + FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_LOST_EDGE", pulses); + break; + } + + if(pulses > 0) { + /* throw away the first pulse, which is the 1->0 from the first start and will be irrelevant for our test */ + bool phase = ((pulses - 1) % 2) == 1; + uint32_t expected = phase ? usec_high : usec_low; + uint32_t deviation = abs((int32_t)length - (int32_t)expected); + + if(deviation > tolerance) { + FURI_LOG_E( + TAG, + "%lu. pulse expected %lu, but pulse was %lu.", + pulses, + expected, + length); + break; + } + } + pulses++; + } while(true); + } while(false); + + if(reader != NULL) { + pulse_reader_stop(reader); + pulse_reader_free(reader); + } + + LL_TIM_DeInit(TIM1); + furi_hal_gpio_init_simple(gpio_in, GpioModeAnalog); + furi_hal_gpio_init_simple(gpio_out, GpioModeAnalog); + + return success; +} + +MU_TEST(nfc_pulse_reader_test) { + mu_assert(nfc_test_pulse_reader_toggle(1500, 2500, 50, 10), "1 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(10000, 10000, 10, 10), "10 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(100000, 100000, 5, 50), "100 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(100, 900, 50, 10), "1 ms asymmetric signal failed\r\n"); + mu_assert( + nfc_test_pulse_reader_toggle(3333, 6666, 10, 10), "10 ms asymmetric signal failed\r\n"); + mu_assert( + nfc_test_pulse_reader_toggle(25000, 75000, 5, 10), "100 ms asymmetric signal failed\r\n"); +} + MU_TEST(mf_classic_dict_test) { MfClassicDict* instance = NULL; uint64_t key = 0; @@ -513,6 +662,7 @@ MU_TEST(mf_classic_4k_7b_file_test) { MU_TEST_SUITE(nfc) { nfc_test_alloc(); + MU_RUN_TEST(nfc_pulse_reader_test); MU_RUN_TEST(nfca_file_test); MU_RUN_TEST(mf_mini_file_test); MU_RUN_TEST(mf_classic_1k_4b_file_test); From 9cf16bdfe4a9ccf81b6fa6ca282bbf81443853ba Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 01:15:22 +0100 Subject: [PATCH 189/282] change pulse reader test timings to 1, 10 and 100 ms --- applications/debug/unit_tests/nfc/nfc_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 3b3a44431..98b996212 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -318,9 +318,9 @@ static bool nfc_test_pulse_reader_toggle( } MU_TEST(nfc_pulse_reader_test) { - mu_assert(nfc_test_pulse_reader_toggle(1500, 2500, 50, 10), "1 ms signal failed\r\n"); - mu_assert(nfc_test_pulse_reader_toggle(10000, 10000, 10, 10), "10 ms signal failed\r\n"); - mu_assert(nfc_test_pulse_reader_toggle(100000, 100000, 5, 50), "100 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(500, 500, 50, 10), "1 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(5000, 5000, 10, 10), "10 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(50000, 50000, 5, 50), "100 ms signal failed\r\n"); mu_assert(nfc_test_pulse_reader_toggle(100, 900, 50, 10), "1 ms asymmetric signal failed\r\n"); mu_assert( nfc_test_pulse_reader_toggle(3333, 6666, 10, 10), "10 ms asymmetric signal failed\r\n"); From 146061e631fa6d446db1a76c9f8141b5d96ffca6 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 01:26:16 +0100 Subject: [PATCH 190/282] fine tuned timings for pulse_reader test --- applications/debug/unit_tests/nfc/nfc_test.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 98b996212..87622f3d3 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -255,7 +255,9 @@ static bool nfc_test_pulse_reader_toggle( } prev_cnt = cur_cnt; } - /* quickly halt the counter to keep a static signal */ + /* quickly halt the counter to keep a static signal. we might get some delay here due to scheduling, + causing the timer to continue racing. there is currently no workaround, as disabling interrupts will + defunct the pulse_reader */ LL_TIM_DisableCounter(TIM1); do { @@ -318,12 +320,10 @@ static bool nfc_test_pulse_reader_toggle( } MU_TEST(nfc_pulse_reader_test) { - mu_assert(nfc_test_pulse_reader_toggle(500, 500, 50, 10), "1 ms signal failed\r\n"); mu_assert(nfc_test_pulse_reader_toggle(5000, 5000, 10, 10), "10 ms signal failed\r\n"); mu_assert(nfc_test_pulse_reader_toggle(50000, 50000, 5, 50), "100 ms signal failed\r\n"); - mu_assert(nfc_test_pulse_reader_toggle(100, 900, 50, 10), "1 ms asymmetric signal failed\r\n"); mu_assert( - nfc_test_pulse_reader_toggle(3333, 6666, 10, 10), "10 ms asymmetric signal failed\r\n"); + nfc_test_pulse_reader_toggle(3333, 6667, 10, 10), "10 ms asymmetric signal failed\r\n"); mu_assert( nfc_test_pulse_reader_toggle(25000, 75000, 5, 10), "100 ms asymmetric signal failed\r\n"); } From a34f2d290382bc6dac61859a2eb92efa1fb160c5 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 15:38:03 +0100 Subject: [PATCH 191/282] pulse_reader_stop now deinits GPIO as recommended by @gornekich --- lib/pulse_reader/pulse_reader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 64fde7fc9..8b35f6d18 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -105,6 +105,7 @@ void pulse_reader_stop(PulseReader* signal) { LL_DMA_DisableChannel(DMA1, signal->dma_channel + 1); LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); LL_TIM_DisableCounter(TIM2); + furi_hal_gpio_init_simple(signal->gpio, GpioModeAnalog); } void pulse_reader_start(PulseReader* signal) { From f56835cb655fa15e7895fb84f65f4f45f20d9d03 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 17:36:51 +0100 Subject: [PATCH 192/282] ran format_py --- lib/pulse_reader/SConscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pulse_reader/SConscript b/lib/pulse_reader/SConscript index 416ad4a27..5a72b953f 100644 --- a/lib/pulse_reader/SConscript +++ b/lib/pulse_reader/SConscript @@ -16,4 +16,4 @@ sources = libenv.GlobRecursive("*.c*") lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) -Return("lib") \ No newline at end of file +Return("lib") From 4d09a50fbb4d444b1327cc35a12196cd1fe0644e Mon Sep 17 00:00:00 2001 From: gornekich Date: Wed, 21 Dec 2022 23:17:06 +0400 Subject: [PATCH 193/282] pulse_reader: remove from API, allow to link with faps Signed-off-by: g3gg0.de --- firmware/targets/f7/api_symbols.csv | 16 ++++++++-------- lib/pulse_reader/SConscript | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 7b80eb8aa..bfd87214c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2167,14 +2167,14 @@ Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, siz Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*" -Function,+,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" -Function,+,pulse_reader_free,void,PulseReader* -Function,+,pulse_reader_receive,uint32_t,"PulseReader*, int" -Function,+,pulse_reader_samples,uint32_t,PulseReader* -Function,+,pulse_reader_set_bittime,void,"PulseReader*, uint32_t" -Function,+,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit" -Function,+,pulse_reader_start,void,PulseReader* -Function,+,pulse_reader_stop,void,PulseReader* +Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" +Function,-,pulse_reader_free,void,PulseReader* +Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int" +Function,-,pulse_reader_samples,uint32_t,PulseReader* +Function,-,pulse_reader_set_bittime,void,"PulseReader*, uint32_t" +Function,-,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit" +Function,-,pulse_reader_start,void,PulseReader* +Function,-,pulse_reader_stop,void,PulseReader* Function,-,putc,int,"int, FILE*" Function,-,putc_unlocked,int,"int, FILE*" Function,-,putchar,int,int diff --git a/lib/pulse_reader/SConscript b/lib/pulse_reader/SConscript index 5a72b953f..f00851a20 100644 --- a/lib/pulse_reader/SConscript +++ b/lib/pulse_reader/SConscript @@ -12,6 +12,14 @@ env.Append( libenv = env.Clone(FW_LIB_NAME="pulse_reader") libenv.ApplyLibFlags() +libenv.AppendUnique( + CCFLAGS=[ + # Required for lib to be linkable with .faps + "-mword-relocations", + "-mlong-calls", + ], +) + sources = libenv.GlobRecursive("*.c*") lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) From 67b02b7785cebc7f39ceac4705d21088b854cd49 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 21:54:33 +0100 Subject: [PATCH 194/282] remove unit test for pulse_reader again --- applications/debug/unit_tests/nfc/nfc_test.c | 150 ------------------- 1 file changed, 150 deletions(-) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 87622f3d3..54bdd5909 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -1,12 +1,10 @@ #include #include -#include #include #include #include #include #include -#include #include #include @@ -181,153 +179,6 @@ MU_TEST(nfc_digital_signal_test) { "NFC long digital signal test failed\r\n"); } -static bool nfc_test_pulse_reader_toggle( - uint32_t usec_low, - uint32_t usec_high, - uint32_t period_count, - uint32_t tolerance) { - furi_assert(nfc_test); - - bool success = false; - uint32_t pulses = 0; - const GpioPin* gpio_in = &gpio_ext_pa6; - const GpioPin* gpio_out = &gpio_ext_pa7; - PulseReader* reader = NULL; - - do { - reader = pulse_reader_alloc(gpio_in, 512); - - if(!reader) { - FURI_LOG_E(TAG, "failed to allocate pulse reader"); - break; - } - - /* use TIM1 to create a specific number of pulses with defined duty cycle - but first set the IO to high, so the low/high pulse can get detected */ - furi_hal_gpio_init(gpio_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(gpio_out, true); - - LL_TIM_DeInit(TIM1); - - LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP); - LL_TIM_SetRepetitionCounter(TIM1, 0); - LL_TIM_SetClockDivision(TIM1, LL_TIM_CLOCKDIVISION_DIV1); - LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_DisableARRPreload(TIM1); - - LL_TIM_OC_DisablePreload(TIM1, LL_TIM_CHANNEL_CH1); - LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM2); - LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH); - LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1); - LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N); - - LL_TIM_EnableAllOutputs(TIM1); - - /* now calculate the TIM1 period and compare values */ - uint32_t freq_div = 64 * (usec_low + usec_high); - uint32_t prescaler = freq_div / 0x10000LU; - uint32_t period = freq_div / (prescaler + 1); - uint32_t compare = 64 * usec_low / (prescaler + 1); - - LL_TIM_SetPrescaler(TIM1, prescaler); - LL_TIM_SetAutoReload(TIM1, period - 1); - LL_TIM_SetCounter(TIM1, period - 1); - LL_TIM_OC_SetCompareCH1(TIM1, compare); - - /* timer is ready to launch, now start the pulse reader */ - pulse_reader_set_timebase(reader, PulseReaderUnitMicrosecond); - pulse_reader_start(reader); - - /* and quickly enable and switch over the GPIO to the generated signal */ - LL_TIM_EnableCounter(TIM1); - furi_hal_gpio_init_ex( - gpio_out, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedVeryHigh, GpioAltFn1TIM1); - - /* now it's time to parse the pulses received by the reader */ - uint32_t timer_pulses = period_count; - uint32_t prev_cnt = 0; - - while(timer_pulses > 0) { - /* whenever the counter gets reset, we went through a full period */ - uint32_t cur_cnt = LL_TIM_GetCounter(TIM1); - if(cur_cnt < prev_cnt) { - timer_pulses--; - } - prev_cnt = cur_cnt; - } - /* quickly halt the counter to keep a static signal. we might get some delay here due to scheduling, - causing the timer to continue racing. there is currently no workaround, as disabling interrupts will - defunct the pulse_reader */ - LL_TIM_DisableCounter(TIM1); - - do { - /* as all edges were sampled asynchronously, the timeout can be zero */ - uint32_t length = pulse_reader_receive(reader, 0); - - /* in the last pulse, we expect a "no edge" return value. if seen that, test succeeded. */ - if(pulses > period_count * 2) { - if(length != PULSE_READER_NO_EDGE) { - FURI_LOG_E( - TAG, - "last pulse expected to be PULSE_READER_NO_EDGE, but was %lu.", - length); - break; - } - success = true; - break; - } - - /* else we shall never see "no edge" or "lost edge" */ - if(length == PULSE_READER_NO_EDGE) { - FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_NO_EDGE", pulses); - break; - } - if(length == PULSE_READER_LOST_EDGE) { - FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_LOST_EDGE", pulses); - break; - } - - if(pulses > 0) { - /* throw away the first pulse, which is the 1->0 from the first start and will be irrelevant for our test */ - bool phase = ((pulses - 1) % 2) == 1; - uint32_t expected = phase ? usec_high : usec_low; - uint32_t deviation = abs((int32_t)length - (int32_t)expected); - - if(deviation > tolerance) { - FURI_LOG_E( - TAG, - "%lu. pulse expected %lu, but pulse was %lu.", - pulses, - expected, - length); - break; - } - } - pulses++; - } while(true); - } while(false); - - if(reader != NULL) { - pulse_reader_stop(reader); - pulse_reader_free(reader); - } - - LL_TIM_DeInit(TIM1); - furi_hal_gpio_init_simple(gpio_in, GpioModeAnalog); - furi_hal_gpio_init_simple(gpio_out, GpioModeAnalog); - - return success; -} - -MU_TEST(nfc_pulse_reader_test) { - mu_assert(nfc_test_pulse_reader_toggle(5000, 5000, 10, 10), "10 ms signal failed\r\n"); - mu_assert(nfc_test_pulse_reader_toggle(50000, 50000, 5, 50), "100 ms signal failed\r\n"); - mu_assert( - nfc_test_pulse_reader_toggle(3333, 6667, 10, 10), "10 ms asymmetric signal failed\r\n"); - mu_assert( - nfc_test_pulse_reader_toggle(25000, 75000, 5, 10), "100 ms asymmetric signal failed\r\n"); -} - MU_TEST(mf_classic_dict_test) { MfClassicDict* instance = NULL; uint64_t key = 0; @@ -662,7 +513,6 @@ MU_TEST(mf_classic_4k_7b_file_test) { MU_TEST_SUITE(nfc) { nfc_test_alloc(); - MU_RUN_TEST(nfc_pulse_reader_test); MU_RUN_TEST(nfca_file_test); MU_RUN_TEST(mf_mini_file_test); MU_RUN_TEST(mf_classic_1k_4b_file_test); From f08c8d6a1dee43199f6a78047d323258f1561696 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 28 Dec 2022 14:40:29 +0100 Subject: [PATCH 195/282] pulse_reader: add call to set GPIO pull direction --- firmware/targets/f7/api_symbols.csv | 1 + lib/pulse_reader/pulse_reader.c | 7 ++++++- lib/pulse_reader/pulse_reader.h | 11 +++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index bfd87214c..2edbdb16d 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2172,6 +2172,7 @@ Function,-,pulse_reader_free,void,PulseReader* Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int" Function,-,pulse_reader_samples,uint32_t,PulseReader* Function,-,pulse_reader_set_bittime,void,"PulseReader*, uint32_t" +Function,-,pulse_reader_set_pull,void,"PulseReader*, GpioPull" Function,-,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit" Function,-,pulse_reader_start,void,PulseReader* Function,-,pulse_reader_stop,void,PulseReader* diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 8b35f6d18..3d9508f9a 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -31,6 +31,7 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { signal->gpio_buffer = malloc(size * sizeof(uint32_t)); signal->dma_channel = LL_DMA_CHANNEL_4; signal->gpio = gpio; + signal->pull = GpioPullNo; signal->size = size; signal->timer_value = 0; signal->pos = 0; @@ -88,6 +89,10 @@ void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) { signal->bit_time = bit_time; } +void pulse_reader_set_pull(PulseReader* signal, GpioPull pull) { + signal->pull = pull; +} + void pulse_reader_free(PulseReader* signal) { free(signal->timer_buffer); free(signal->gpio_buffer); @@ -134,7 +139,7 @@ void pulse_reader_start(PulseReader* signal) { /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */ furi_hal_gpio_init_ex( - signal->gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused); + signal->gpio, GpioModeInterruptRiseFall, signal->pull, GpioSpeedVeryHigh, GpioAltFnUnused); /* capture current timer */ signal->pos = 0; diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h index b4e6bca57..be0ad7012 100644 --- a/lib/pulse_reader/pulse_reader.h +++ b/lib/pulse_reader/pulse_reader.h @@ -41,6 +41,7 @@ typedef struct { uint32_t bit_time; uint32_t dma_channel; const GpioPin* gpio; + GpioPull pull; LL_DMA_InitTypeDef dma_config_timer; LL_DMA_InitTypeDef dma_config_gpio; } PulseReader; @@ -125,6 +126,16 @@ void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit); */ void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time); +/** Set GPIO pull direction + * + * Some GPIOs need pulldown, others don't. By default the + * pull direction is GpioPullNo. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] pull GPIO pull direction + */ +void pulse_reader_set_pull(PulseReader* signal, GpioPull pull); + #ifdef __cplusplus } #endif From c18cf71646ed1fa3e167efc470dae568dc96c2f9 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 28 Dec 2022 22:43:39 +0100 Subject: [PATCH 196/282] make structures private, add C implementation of digital_signal_update_dma() --- lib/digital_signal/digital_signal.c | 179 ++++++++++++++++++---------- lib/digital_signal/digital_signal.h | 29 ++--- lib/pulse_reader/pulse_reader.c | 25 +++- lib/pulse_reader/pulse_reader.h | 23 +--- 4 files changed, 147 insertions(+), 109 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 9ba5cc901..12b543273 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -4,6 +4,30 @@ #include #include +#include +#include + +struct DigitalSequence { + uint8_t signals_size; + bool bake; + uint32_t sequence_used; + uint32_t sequence_size; + DigitalSignal** signals; + bool* signals_prolonged; + uint8_t* sequence; + const GpioPin* gpio; + uint32_t send_time; +}; + +struct DigitalSignalInternals { + uint32_t reload_reg_entries; + uint32_t reload_reg_remainder; + uint32_t gpio_buff[2]; + const GpioPin* gpio; + LL_DMA_InitTypeDef dma_config_gpio; + LL_DMA_InitTypeDef dma_config_timer; +}; + #define TAG "DigitalSignal" #define F_TIM (64000000.0) @@ -17,28 +41,29 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->edge_timings = malloc(signal->edges_max_cnt * sizeof(uint32_t)); signal->edge_cnt = 0; signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); - signal->reload_reg_entries = 0; - signal->reload_reg_remainder = 0; - signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; - signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - signal->dma_config_gpio.NbData = 2; - signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + signal->internals = malloc(sizeof(DigitalSignalInternals)); + signal->internals->reload_reg_entries = 0; + signal->internals->reload_reg_remainder = 0; + signal->internals->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + signal->internals->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + signal->internals->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->internals->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->internals->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->internals->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->internals->dma_config_gpio.NbData = 2; + signal->internals->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + signal->internals->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; - signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); - signal->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - signal->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; - signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - signal->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - signal->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; + signal->internals->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); + signal->internals->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + signal->internals->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; + signal->internals->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->internals->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->internals->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->internals->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->internals->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + signal->internals->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; return signal; } @@ -48,6 +73,7 @@ void digital_signal_free(DigitalSignal* signal) { free(signal->edge_timings); free(signal->reload_reg_buff); + free(signal->internals); free(signal); } @@ -133,29 +159,30 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { furi_assert(signal); /* set up signal polarities */ - if(signal->gpio) { - uint32_t bit_set = signal->gpio->pin; - uint32_t bit_reset = signal->gpio->pin << 16; + if(signal->internals->gpio) { + uint32_t bit_set = signal->internals->gpio->pin; + uint32_t bit_reset = signal->internals->gpio->pin << 16; if(signal->start_level) { - signal->gpio_buff[0] = bit_set; - signal->gpio_buff[1] = bit_reset; + signal->internals->gpio_buff[0] = bit_set; + signal->internals->gpio_buff[1] = bit_reset; } else { - signal->gpio_buff[0] = bit_reset; - signal->gpio_buff[1] = bit_set; + signal->internals->gpio_buff[0] = bit_reset; + signal->internals->gpio_buff[1] = bit_set; } } /* set up edge timings */ - signal->reload_reg_entries = 0; + signal->internals->reload_reg_entries = 0; for(size_t pos = 0; pos < signal->edge_cnt; pos++) { - uint32_t pulse_duration = signal->edge_timings[pos] + signal->reload_reg_remainder; + uint32_t pulse_duration = + signal->edge_timings[pos] + signal->internals->reload_reg_remainder; uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; - signal->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); + signal->internals->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); if(pulse_ticks > 1) { - signal->reload_reg_buff[signal->reload_reg_entries++] = pulse_ticks - 1; + signal->reload_reg_buff[signal->internals->reload_reg_entries++] = pulse_ticks - 1; } } } @@ -175,18 +202,20 @@ static void digital_signal_stop_timer() { static bool digital_signal_setup_dma(DigitalSignal* signal) { furi_assert(signal); - if(!signal->reload_reg_entries) { + if(!signal->internals->reload_reg_entries) { return false; } - signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buff; - signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (signal->gpio->port->BSRR); - signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; - signal->dma_config_timer.NbData = signal->reload_reg_entries; + signal->internals->dma_config_gpio.MemoryOrM2MDstAddress = + (uint32_t)signal->internals->gpio_buff; + signal->internals->dma_config_gpio.PeriphOrM2MSrcAddress = + (uint32_t) & (signal->internals->gpio->port->BSRR); + signal->internals->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; + signal->internals->dma_config_timer.NbData = signal->internals->reload_reg_entries; /* set up DMA channel 1 and 2 for GPIO and timer copy operations */ - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &signal->dma_config_gpio); - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &signal->dma_config_timer); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &signal->internals->dma_config_gpio); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &signal->internals->dma_config_timer); /* enable both DMA channels */ LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); @@ -220,8 +249,9 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { } /* Configure gpio as output */ - signal->gpio = gpio; - furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + signal->internals->gpio = gpio; + furi_hal_gpio_init( + signal->internals->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); /* single signal, add a temporary, terminating edge at the end */ signal->edge_timings[signal->edge_cnt++] = 10; @@ -282,8 +312,8 @@ void digital_sequence_set_signal( furi_assert(signal_index < sequence->signals_size); sequence->signals[signal_index] = signal; - signal->gpio = sequence->gpio; - signal->reload_reg_remainder = 0; + signal->internals->gpio = sequence->gpio; + signal->internals->reload_reg_remainder = 0; digital_signal_prepare_arr(signal); } @@ -304,54 +334,71 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { sequence->sequence[sequence->sequence_used++] = signal_index; } -void digital_signal_update_dma(DigitalSignal* signal) { +#if defined(DIGITAL_SIGNAL_PORTABLE_CODE) + +static void digital_signal_update_dma(DigitalSignal* signal) { + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)signal->internals->gpio_buff); + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->internals->reload_reg_entries); + + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); +} + +#else + +static void digital_signal_update_dma(DigitalSignal* signal) { volatile uint32_t dma1_data[] = { /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, /* R8 */ 2, - /* R9 */ (uint32_t) & (signal->gpio->port->BSRR), - /* R10 */ (uint32_t)signal->gpio_buff, + /* R9 */ (uint32_t) & (signal->internals->gpio->port->BSRR), + /* R10 */ (uint32_t)signal->internals->gpio_buff, /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; volatile uint32_t dma2_data[] = { /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, - /* R2 */ (uint32_t)signal->reload_reg_entries, + /* R2 */ (uint32_t)signal->internals->reload_reg_entries, /* R3 */ (uint32_t) & (TIM2->ARR), /* R4 */ (uint32_t)signal->reload_reg_buff, /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN}; /* hurry when setting up next transfer */ - asm volatile("\t" - "MOV r6, %[data1]\n\t" - "MOV r7, %[data2]\n\t" + asm volatile( + "\t" + "MOV r6, %[data1]\n\t" + "MOV r7, %[data2]\n\t" - "PUSH {r0-r12}\n\t" + "PUSH {r0-r12}\n\t" - "LDM r7, {r0-r5}\n\t" - "LDM r6, {r6-r11}\n\t" + "LDM r7, {r0-r5}\n\t" /* prepare registers with values to write into DMA config */ + "LDM r6, {r6-r11}\n\t" - "loop:\n\t" - "LDR r12, [r0, #4]\n\t" - "CMP r12, #0\n\t" - "BNE loop\n\t" + "loop:\n\t" + "LDR r12, [r0, #4]\n\t" /* read DMA_CNDTRx to get remaining transfers */ + "CMP r12, #0\n\t" + "BNE loop\n\t" - "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ - "STR r11, [r6, #0]\n\t" /* enable channel again */ - "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ - "STR r5, [r0, #0]\n\t" /* enable channel again */ + "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ + "STR r11, [r6, #0]\n\t" /* enable channel again */ + "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ + "STR r5, [r0, #0]\n\t" /* enable channel again */ - "POP {r0-r12}\n\t" + "POP {r0-r12}\n\t" - : /* no outputs*/ - : /* inputs */ - [data1] "r"(dma1_data), [data2] "r"(dma2_data) - : "r6", "r7"); + : /* no outputs*/ + : /* inputs */ + [data1] "r"(dma1_data), [data2] "r"(dma2_data) + : "r6", "r7"); LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC2(DMA1); } +#endif + static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { furi_assert(sequence); furi_assert(signal); @@ -443,7 +490,7 @@ bool digital_sequence_send(DigitalSequence* sequence) { } /* update the total remainder */ - remainder += sig->reload_reg_remainder; + remainder += sig->internals->reload_reg_remainder; /* do we need to update the prolongation? */ if(needs_prolongation != sequence->signals_prolonged[signal_index]) { diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 9a91c8b45..2cb107486 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -5,8 +5,6 @@ #include #include -#include -#include #ifdef __cplusplus extern "C" { @@ -18,31 +16,20 @@ extern "C" { #define DIGITAL_SIGNAL_NS(x) (x * 100UL) #define DIGITAL_SIGNAL_PS(x) (x / 10UL) -typedef struct { +/* using an anonymous type for the internals */ +typedef struct DigitalSignalInternals DigitalSignalInternals; + +/* and a public one for accessing user-side fields */ +typedef struct DigitalSignal { bool start_level; uint32_t edge_cnt; uint32_t edges_max_cnt; uint32_t* edge_timings; - uint32_t* reload_reg_buff; - uint32_t reload_reg_entries; - uint32_t reload_reg_remainder; - uint32_t gpio_buff[2]; - const GpioPin* gpio; - LL_DMA_InitTypeDef dma_config_gpio; - LL_DMA_InitTypeDef dma_config_timer; + uint32_t* reload_reg_buff; /* internal, but used by unit tests */ + DigitalSignalInternals* internals; } DigitalSignal; -typedef struct { - uint8_t signals_size; - bool bake; - uint32_t sequence_used; - uint32_t sequence_size; - DigitalSignal** signals; - bool* signals_prolonged; - uint8_t* sequence; - const GpioPin* gpio; - uint32_t send_time; -} DigitalSequence; +typedef struct DigitalSequence DigitalSequence; DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 3d9508f9a..84ce2ff23 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -1,9 +1,32 @@ +#include "pulse_reader.h" + #include #include #include #include -#include "pulse_reader.h" +#include +#include +#include +#include + +struct PulseReader { + uint32_t* timer_buffer; + uint32_t* gpio_buffer; + uint32_t size; + uint32_t pos; + uint32_t timer_value; + uint32_t gpio_value; + uint32_t gpio_mask; + uint32_t unit_multiplier; + uint32_t unit_divider; + uint32_t bit_time; + uint32_t dma_channel; + const GpioPin* gpio; + GpioPull pull; + LL_DMA_InitTypeDef dma_config_timer; + LL_DMA_InitTypeDef dma_config_gpio; +}; #define GPIO_PIN_MAP(pin, prefix) \ (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h index be0ad7012..08056a0ac 100644 --- a/lib/pulse_reader/pulse_reader.h +++ b/lib/pulse_reader/pulse_reader.h @@ -3,10 +3,6 @@ #include #include #include -#include -#include -#include -#include #include @@ -28,23 +24,8 @@ typedef enum { PulseReaderUnitMicrosecond, } PulseReaderUnit; -typedef struct { - uint32_t* timer_buffer; - uint32_t* gpio_buffer; - uint32_t size; - uint32_t pos; - uint32_t timer_value; - uint32_t gpio_value; - uint32_t gpio_mask; - uint32_t unit_multiplier; - uint32_t unit_divider; - uint32_t bit_time; - uint32_t dma_channel; - const GpioPin* gpio; - GpioPull pull; - LL_DMA_InitTypeDef dma_config_timer; - LL_DMA_InitTypeDef dma_config_gpio; -} PulseReader; +/* using an anonymous type */ +typedef struct PulseReader PulseReader; /** Allocate a PulseReader object * From bc80ddd0a7c13a81c190b84c2b3815ed21969439 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Thu, 29 Dec 2022 22:36:30 +0100 Subject: [PATCH 197/282] digital_signal/pulse_reader: allow parameters for free to be NULL --- lib/digital_signal/digital_signal.c | 8 ++++++++ lib/pulse_reader/pulse_reader.c | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 12b543273..ae87a09e4 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -71,6 +71,10 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { void digital_signal_free(DigitalSignal* signal) { furi_assert(signal); + if(!signal) { + return; + } + free(signal->edge_timings); free(signal->reload_reg_buff); free(signal->internals); @@ -298,6 +302,10 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { void digital_sequence_free(DigitalSequence* sequence) { furi_assert(sequence); + if(!sequence) { + return; + } + free(sequence->signals); free(sequence->sequence); free(sequence); diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 84ce2ff23..c8d0e3ff7 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -117,6 +117,12 @@ void pulse_reader_set_pull(PulseReader* signal, GpioPull pull) { } void pulse_reader_free(PulseReader* signal) { + furi_assert(signal); + + if(!signal) { + return; + } + free(signal->timer_buffer); free(signal->gpio_buffer); free(signal); From cd082c9d90911c3d65f9b98f41eba25445cb2420 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 30 Dec 2022 10:50:55 +0100 Subject: [PATCH 198/282] digital_signal: show unoptimized and optimized code for digital_signal_update_dma() next to each other --- lib/digital_signal/digital_signal.c | 62 ++++++++++++++++++----------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index ae87a09e4..1be81f135 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -342,36 +342,42 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { sequence->sequence[sequence->sequence_used++] = signal_index; } -#if defined(DIGITAL_SIGNAL_PORTABLE_CODE) +bool digital_signal_optimization = true; -static void digital_signal_update_dma(DigitalSignal* signal) { - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)signal->internals->gpio_buff); +static void digital_signal_update_dma_c(DigitalSignal* signal) { + /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ + while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { + } + + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->internals->reload_reg_entries); - - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)signal->internals->gpio_buff); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); } -#else - -static void digital_signal_update_dma(DigitalSignal* signal) { +static void digital_signal_update_dma_asm(DigitalSignal* signal) { volatile uint32_t dma1_data[] = { - /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), - /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, - /* R8 */ 2, - /* R9 */ (uint32_t) & (signal->internals->gpio->port->BSRR), - /* R10 */ (uint32_t)signal->internals->gpio_buff, - /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; + /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), /* base address of DMA channel 1 */ + /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, /* CCR value to write first */ + /* R8 */ 2, /* CNDTR to write */ + /* R9 */ (uint32_t) & (signal->internals->gpio->port->BSRR), /* CPAR to write */ + /* R10 */ (uint32_t)signal->internals->gpio_buff, /* CMAR to write */ + /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; /* and CCR again to write after finished */ volatile uint32_t dma2_data[] = { - /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), - /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, - /* R2 */ (uint32_t)signal->internals->reload_reg_entries, - /* R3 */ (uint32_t) & (TIM2->ARR), - /* R4 */ (uint32_t)signal->reload_reg_buff, - /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN}; + /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* base address of DMA channel 2 */ + /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ + /* R2 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ + /* R3 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ + /* R4 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ + /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN}; /* and CCR again to write after finished */ /* hurry when setting up next transfer */ asm volatile( @@ -389,10 +395,12 @@ static void digital_signal_update_dma(DigitalSignal* signal) { "CMP r12, #0\n\t" "BNE loop\n\t" + /* no transfers left, the DMA has finished. now quickly re-enable with new settings + the next 4 instructions are the critical part */ "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ - "STR r11, [r6, #0]\n\t" /* enable channel again */ + "STR r11, [r6, #0]\n\t" /* enable channel again by writing CCR */ "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ - "STR r5, [r0, #0]\n\t" /* enable channel again */ + "STR r5, [r0, #0]\n\t" /* enable channel again by writing CCR */ "POP {r0-r12}\n\t" @@ -405,7 +413,13 @@ static void digital_signal_update_dma(DigitalSignal* signal) { LL_DMA_ClearFlag_TC2(DMA1); } -#endif +void digital_signal_update_dma(DigitalSignal* signal) { + if(digital_signal_optimization) { + digital_signal_update_dma_asm(signal); + } else { + digital_signal_update_dma_c(signal); + } +} static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { furi_assert(sequence); From 064a34e681235587135ba083c47e046eb1823061 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 30 Dec 2022 12:21:43 +0100 Subject: [PATCH 199/282] pulse_reader: further optimize assembly code --- lib/digital_signal/digital_signal.c | 37 +++++++++++++---------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 1be81f135..073a69eb8 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -363,7 +363,17 @@ static void digital_signal_update_dma_c(DigitalSignal* signal) { } static void digital_signal_update_dma_asm(DigitalSignal* signal) { - volatile uint32_t dma1_data[] = { + /* this is an "already-prepared" buffer of all DMA channel configs to write */ + const volatile uint32_t dma_data[] = { + /* DMA channel 2 data */ + /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* base address of DMA channel 2 */ + /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ + /* R2 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ + /* R3 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ + /* R4 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ + /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN, /* and CCR again to write after finished */ + + /* DMA channel 1 data */ /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), /* base address of DMA channel 1 */ /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, /* CCR value to write first */ /* R8 */ 2, /* CNDTR to write */ @@ -371,31 +381,19 @@ static void digital_signal_update_dma_asm(DigitalSignal* signal) { /* R10 */ (uint32_t)signal->internals->gpio_buff, /* CMAR to write */ /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; /* and CCR again to write after finished */ - volatile uint32_t dma2_data[] = { - /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* base address of DMA channel 2 */ - /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ - /* R2 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ - /* R3 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ - /* R4 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ - /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN}; /* and CCR again to write after finished */ - - /* hurry when setting up next transfer */ + /* now wait for the DMA finishing and instantly reconfigure it with as few instructions as possible */ asm volatile( "\t" - "MOV r6, %[data1]\n\t" - "MOV r7, %[data2]\n\t" - "PUSH {r0-r12}\n\t" - "LDM r7, {r0-r5}\n\t" /* prepare registers with values to write into DMA config */ - "LDM r6, {r6-r11}\n\t" + "LDM %[data], {r0-r11}\n\t" /* prepare registers with values to write into DMA config */ - "loop:\n\t" + "wait_for_dma_finished:\n\t" "LDR r12, [r0, #4]\n\t" /* read DMA_CNDTRx to get remaining transfers */ "CMP r12, #0\n\t" - "BNE loop\n\t" + "BNE wait_for_dma_finished\n\t" - /* no transfers left, the DMA has finished. now quickly re-enable with new settings + /* no transfers left, the DMA has finished. now quickly re-enable with new settings. the next 4 instructions are the critical part */ "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ "STR r11, [r6, #0]\n\t" /* enable channel again by writing CCR */ @@ -406,8 +404,7 @@ static void digital_signal_update_dma_asm(DigitalSignal* signal) { : /* no outputs*/ : /* inputs */ - [data1] "r"(dma1_data), [data2] "r"(dma2_data) - : "r6", "r7"); + [data] "r"(dma_data)); LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC2(DMA1); From 17f7f0e637e2aa3714c8a491700bcb81ab2cab16 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 30 Dec 2022 16:18:55 +0100 Subject: [PATCH 200/282] digital_signal: reduce code complexity of digital_signal_update_dma() by only reconfiguring DMA2 --- lib/digital_signal/digital_signal.c | 53 +++++++++-------------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 073a69eb8..4772b654f 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -350,64 +350,45 @@ static void digital_signal_update_dma_c(DigitalSignal* signal) { } LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->internals->reload_reg_entries); + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)signal->internals->gpio_buff); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - - LL_DMA_ClearFlag_TC1(DMA1); - LL_DMA_ClearFlag_TC2(DMA1); } static void digital_signal_update_dma_asm(DigitalSignal* signal) { - /* this is an "already-prepared" buffer of all DMA channel configs to write */ + /* this is an "already-prepared" buffer of DMA channel 2 config to write */ const volatile uint32_t dma_data[] = { - /* DMA channel 2 data */ + /* base addresses of DMA channel register */ /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* base address of DMA channel 2 */ - /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ - /* R2 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ - /* R3 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ - /* R4 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ - /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN, /* and CCR again to write after finished */ - - /* DMA channel 1 data */ - /* R6 */ (uint32_t) & (DMA1_Channel1->CCR), /* base address of DMA channel 1 */ - /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, /* CCR value to write first */ - /* R8 */ 2, /* CNDTR to write */ - /* R9 */ (uint32_t) & (signal->internals->gpio->port->BSRR), /* CPAR to write */ - /* R10 */ (uint32_t)signal->internals->gpio_buff, /* CMAR to write */ - /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN}; /* and CCR again to write after finished */ + /* R1 */ DMA1_Channel2->CCR | DMA_CCR_EN, /* CCR to write after finished */ + /* R2 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ + /* R3 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ + /* R4 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ + /* R5 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ + }; /* now wait for the DMA finishing and instantly reconfigure it with as few instructions as possible */ asm volatile( "\t" - "PUSH {r0-r12}\n\t" + "PUSH {r0-r6}\n\t" - "LDM %[data], {r0-r11}\n\t" /* prepare registers with values to write into DMA config */ + "LDM %[data], {r0-r5}\n\t" /* prepare registers with values to write into DMA config */ "wait_for_dma_finished:\n\t" - "LDR r12, [r0, #4]\n\t" /* read DMA_CNDTRx to get remaining transfers */ - "CMP r12, #0\n\t" + "LDR r6, [r0, #4]\n\t" /* read DMA_CNDTRx of DMA1 chan 2 to get remaining transfers */ + "CMP r6, #0\n\t" "BNE wait_for_dma_finished\n\t" /* no transfers left, the DMA has finished. now quickly re-enable with new settings. - the next 4 instructions are the critical part */ - "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ - "STR r11, [r6, #0]\n\t" /* enable channel again by writing CCR */ - "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ - "STR r5, [r0, #0]\n\t" /* enable channel again by writing CCR */ + these next 2 instructions are the critical part */ + "STM r0, {r2-r5}\n\t" /* disable channel and set up new parameters */ + "STR r1, [r0, #0]\n\t" /* enable channel again by writing CCR */ - "POP {r0-r12}\n\t" + "POP {r0-r6}\n\t" : /* no outputs*/ : /* inputs */ [data] "r"(dma_data)); - - LL_DMA_ClearFlag_TC1(DMA1); - LL_DMA_ClearFlag_TC2(DMA1); } void digital_signal_update_dma(DigitalSignal* signal) { From 4c3a5e9c125e61bc05f44903f0bbcf704675fa1a Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 30 Dec 2022 16:34:52 +0100 Subject: [PATCH 201/282] digital_signal: remove assembly code, limiting the performance but increasing portability --- lib/digital_signal/digital_signal.c | 54 ++++------------------------- 1 file changed, 6 insertions(+), 48 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 4772b654f..f420652f4 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -342,63 +342,21 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { sequence->sequence[sequence->sequence_used++] = signal_index; } -bool digital_signal_optimization = true; +static void digital_signal_update_dma(DigitalSignal* signal) { + /* keep them prepared in registers so there is less delay when writing */ + register volatile uint16_t len = signal->internals->reload_reg_entries; + register volatile uint32_t addr = (uint32_t)signal->reload_reg_buff; -static void digital_signal_update_dma_c(DigitalSignal* signal) { /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { } LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->internals->reload_reg_entries); - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, len); + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, addr); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); } -static void digital_signal_update_dma_asm(DigitalSignal* signal) { - /* this is an "already-prepared" buffer of DMA channel 2 config to write */ - const volatile uint32_t dma_data[] = { - /* base addresses of DMA channel register */ - /* R0 */ (uint32_t) & (DMA1_Channel2->CCR), /* base address of DMA channel 2 */ - /* R1 */ DMA1_Channel2->CCR | DMA_CCR_EN, /* CCR to write after finished */ - /* R2 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, /* CCR value to write first */ - /* R3 */ (uint32_t)signal->internals->reload_reg_entries, /* CNDTR to write */ - /* R4 */ (uint32_t) & (TIM2->ARR), /* CPAR to write */ - /* R5 */ (uint32_t)signal->reload_reg_buff, /* CMAR to write */ - }; - - /* now wait for the DMA finishing and instantly reconfigure it with as few instructions as possible */ - asm volatile( - "\t" - "PUSH {r0-r6}\n\t" - - "LDM %[data], {r0-r5}\n\t" /* prepare registers with values to write into DMA config */ - - "wait_for_dma_finished:\n\t" - "LDR r6, [r0, #4]\n\t" /* read DMA_CNDTRx of DMA1 chan 2 to get remaining transfers */ - "CMP r6, #0\n\t" - "BNE wait_for_dma_finished\n\t" - - /* no transfers left, the DMA has finished. now quickly re-enable with new settings. - these next 2 instructions are the critical part */ - "STM r0, {r2-r5}\n\t" /* disable channel and set up new parameters */ - "STR r1, [r0, #0]\n\t" /* enable channel again by writing CCR */ - - "POP {r0-r6}\n\t" - - : /* no outputs*/ - : /* inputs */ - [data] "r"(dma_data)); -} - -void digital_signal_update_dma(DigitalSignal* signal) { - if(digital_signal_optimization) { - digital_signal_update_dma_asm(signal); - } else { - digital_signal_update_dma_c(signal); - } -} - static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { furi_assert(sequence); furi_assert(signal); From 7e42c9c3f0df43d683ed2002f7b0bb6b1d29df0c Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Tue, 3 Jan 2023 02:10:30 +0100 Subject: [PATCH 202/282] added recovery if the timer already expired --- lib/digital_signal/digital_signal.c | 50 +++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index f420652f4..e07235ece 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -1,6 +1,7 @@ #include "digital_signal.h" #include +#include #include #include @@ -17,6 +18,7 @@ struct DigitalSequence { uint8_t* sequence; const GpioPin* gpio; uint32_t send_time; + bool send_time_active; }; struct DigitalSignalInternals { @@ -31,7 +33,7 @@ struct DigitalSignalInternals { #define TAG "DigitalSignal" #define F_TIM (64000000.0) -#define T_TIM 1562 /* 15.625 ns *100 */ +#define T_TIM 1562 /* 15.625 ns *100 */ #define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { @@ -285,9 +287,12 @@ void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { sequence->sequence_size = size; sequence->sequence = malloc(sequence->sequence_size); sequence->send_time = 0; + sequence->send_time_active = false; } DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { + furi_assert(gpio); + DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); sequence->gpio = gpio; @@ -327,7 +332,10 @@ void digital_sequence_set_signal( } void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time) { + furi_assert(sequence); + sequence->send_time = send_time; + sequence->send_time_active = true; } void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { @@ -344,23 +352,40 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { static void digital_signal_update_dma(DigitalSignal* signal) { /* keep them prepared in registers so there is less delay when writing */ + register bool restart_needed = false; register volatile uint16_t len = signal->internals->reload_reg_entries; register volatile uint32_t addr = (uint32_t)signal->reload_reg_buff; - /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ - while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { + /* first make sure it will still count down, else we will risk waiting infinitely */ + const uint32_t wait_ms = 10; + const uint32_t wait_ticks = wait_ms * 1000 * furi_hal_cortex_instructions_per_microsecond(); + uint16_t prev_remain = LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); + uint32_t prev_timer = DWT->CYCCNT; + + while(prev_remain == LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { + if(DWT->CYCCNT - prev_timer > wait_ticks) { + restart_needed = true; + break; + } + } + + if(!restart_needed) { + /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ + while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { + } } LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, len); LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, addr); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + + if(restart_needed) { + LL_TIM_GenerateEvent_UPDATE(TIM2); + } } static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { - furi_assert(sequence); - furi_assert(signal); - /* the first iteration has to set up the whole machinery */ if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { if(!digital_signal_setup_dma(signal)) { @@ -370,13 +395,9 @@ static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSigna digital_signal_setup_timer(); /* if the send time is specified, wait till the core timer passed beyond that time */ - if(sequence->send_time != 0) { - while(true) { - uint32_t delta = sequence->send_time - DWT->CYCCNT; - /* yeah, it's making use of underflows... */ - if(delta > 0x80000000) { - break; - } + if(sequence->send_time_active) { + sequence->send_time_active = false; + while(sequence->send_time - DWT->CYCCNT < 0x80000000) { } } digital_signal_start_timer(); @@ -389,6 +410,8 @@ static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSigna } DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { + furi_assert(sequence); + uint32_t edges = 0; for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { @@ -468,6 +491,7 @@ bool digital_sequence_send(DigitalSequence* sequence) { } FURI_CRITICAL_EXIT(); + /* wait until last dma transaction was finished */ while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { } From 4f076485a31027f72f1b9affb0c3d370d5bac391 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Mon, 16 Jan 2023 10:49:01 +0100 Subject: [PATCH 203/282] digital_signal: fix memory leak --- lib/digital_signal/digital_signal.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index e07235ece..91f5b2173 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -292,7 +292,7 @@ void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { furi_assert(gpio); - + DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); sequence->gpio = gpio; @@ -313,6 +313,7 @@ void digital_sequence_free(DigitalSequence* sequence) { free(sequence->signals); free(sequence->sequence); + free(sequence->signals_prolonged); free(sequence); } @@ -333,7 +334,7 @@ void digital_sequence_set_signal( void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time) { furi_assert(sequence); - + sequence->send_time = send_time; sequence->send_time_active = true; } @@ -365,7 +366,7 @@ static void digital_signal_update_dma(DigitalSignal* signal) { while(prev_remain == LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { if(DWT->CYCCNT - prev_timer > wait_ticks) { restart_needed = true; - break; + break; } } @@ -411,7 +412,7 @@ static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSigna DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { furi_assert(sequence); - + uint32_t edges = 0; for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { From e2b77fcfb2918b58645491b0da3006d6199a385d Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Mon, 16 Jan 2023 18:09:35 +0100 Subject: [PATCH 204/282] digital_signal: keep lock until all DMA transfers have finished --- lib/digital_signal/digital_signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 91f5b2173..474e840a0 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -490,7 +490,6 @@ bool digital_sequence_send(DigitalSequence* sequence) { break; } } - FURI_CRITICAL_EXIT(); /* wait until last dma transaction was finished */ while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { @@ -498,6 +497,7 @@ bool digital_sequence_send(DigitalSequence* sequence) { digital_signal_stop_timer(); digital_signal_stop_dma(); + FURI_CRITICAL_EXIT(); /* undo previously prolonged edges */ for(uint32_t pos = 0; pos < sequence->signals_size; pos++) { From 826e4d1eda4de0d8f224ce4052db30eb8da98b4e Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 1 Feb 2023 23:25:23 +0100 Subject: [PATCH 205/282] DigitalSequence: fix issues with concatenation of same levels and spurious bit flips --- lib/digital_signal/digital_signal.c | 282 +++++++++++++++++++--------- 1 file changed, 195 insertions(+), 87 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 474e840a0..4ceca2ead 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -8,17 +8,26 @@ #include #include + +struct ReloadBuffers { + uint32_t** buffers; /* pointers to the shadow buffers, either one or two. NULL if none */ + uint32_t count; /* number of allocated buffers, 0, 1 or 2 */ + uint32_t size; /* maximum entry count of a single buffer */ + uint32_t current; /* current buffer index, the other one is most likely being used */ + uint32_t entries; /* entries in the current buffer */ +}; + struct DigitalSequence { uint8_t signals_size; bool bake; uint32_t sequence_used; uint32_t sequence_size; DigitalSignal** signals; - bool* signals_prolonged; uint8_t* sequence; const GpioPin* gpio; uint32_t send_time; bool send_time_active; + struct ReloadBuffers* reload; }; struct DigitalSignalInternals { @@ -28,6 +37,7 @@ struct DigitalSignalInternals { const GpioPin* gpio; LL_DMA_InitTypeDef dma_config_gpio; LL_DMA_InitTypeDef dma_config_timer; + struct ReloadBuffers* reload; }; #define TAG "DigitalSignal" @@ -45,27 +55,30 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); signal->internals = malloc(sizeof(DigitalSignalInternals)); - signal->internals->reload_reg_entries = 0; - signal->internals->reload_reg_remainder = 0; - signal->internals->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - signal->internals->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; - signal->internals->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - signal->internals->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - signal->internals->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - signal->internals->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - signal->internals->dma_config_gpio.NbData = 2; - signal->internals->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - signal->internals->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + DigitalSignalInternals* internals = signal->internals; + internals->reload = NULL; + internals->reload_reg_entries = 0; + internals->reload_reg_remainder = 0; - signal->internals->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); - signal->internals->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - signal->internals->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; - signal->internals->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - signal->internals->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - signal->internals->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - signal->internals->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - signal->internals->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - signal->internals->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; + internals->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + internals->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + internals->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + internals->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + internals->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + internals->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + internals->dma_config_gpio.NbData = 2; + internals->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + internals->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + + internals->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); + internals->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + internals->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; + internals->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + internals->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + internals->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + internals->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + internals->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + internals->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; return signal; } @@ -79,6 +92,12 @@ void digital_signal_free(DigitalSignal* signal) { free(signal->edge_timings); free(signal->reload_reg_buff); + if(signal->internals->reload) { + if(signal->internals->reload->buffers) { + free(signal->internals->reload->buffers); + } + free(signal->internals->reload); + } free(signal->internals); free(signal); } @@ -164,33 +183,55 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { void digital_signal_prepare_arr(DigitalSignal* signal) { furi_assert(signal); + DigitalSignalInternals* internals = signal->internals; + /* set up signal polarities */ - if(signal->internals->gpio) { - uint32_t bit_set = signal->internals->gpio->pin; - uint32_t bit_reset = signal->internals->gpio->pin << 16; + if(internals->gpio) { + uint32_t bit_set = internals->gpio->pin; + uint32_t bit_reset = internals->gpio->pin << 16; + +#ifdef DEBUG_OUTPUT + bit_set |= gpio_ext_pb3.pin; + bit_reset |= gpio_ext_pb3.pin << 16; +#endif if(signal->start_level) { - signal->internals->gpio_buff[0] = bit_set; - signal->internals->gpio_buff[1] = bit_reset; + internals->gpio_buff[0] = bit_set; + internals->gpio_buff[1] = bit_reset; } else { - signal->internals->gpio_buff[0] = bit_reset; - signal->internals->gpio_buff[1] = bit_set; + internals->gpio_buff[0] = bit_reset; + internals->gpio_buff[1] = bit_set; } } /* set up edge timings */ - signal->internals->reload_reg_entries = 0; + internals->reload_reg_entries = 0; for(size_t pos = 0; pos < signal->edge_cnt; pos++) { - uint32_t pulse_duration = - signal->edge_timings[pos] + signal->internals->reload_reg_remainder; + uint32_t pulse_duration = signal->edge_timings[pos] + internals->reload_reg_remainder; uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; - signal->internals->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); + internals->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); if(pulse_ticks > 1) { - signal->reload_reg_buff[signal->internals->reload_reg_entries++] = pulse_ticks - 1; + signal->reload_reg_buff[internals->reload_reg_entries++] = pulse_ticks - 1; } } + + /* in case there are no shadow buffers defined, allocate and use the precalced data */ + if(!internals->reload || !internals->reload->count) { + if(internals->reload) { + free(internals->reload); + } + internals->reload = malloc(sizeof(struct ReloadBuffers)); + internals->reload->count = 1; + internals->reload->size = signal->edges_max_cnt; + internals->reload->buffers = malloc(sizeof(uint32_t*)); + internals->reload->buffers[0] = malloc(internals->reload->size * sizeof(uint32_t)); + memcpy( + internals->reload->buffers[0], + signal->reload_reg_buff, + internals->reload_reg_entries * sizeof(uint32_t)); + } } static void digital_signal_stop_dma() { @@ -202,32 +243,8 @@ static void digital_signal_stop_dma() { static void digital_signal_stop_timer() { LL_TIM_DisableCounter(TIM2); - LL_TIM_SetCounter(TIM2, 0); -} - -static bool digital_signal_setup_dma(DigitalSignal* signal) { - furi_assert(signal); - - if(!signal->internals->reload_reg_entries) { - return false; - } - - signal->internals->dma_config_gpio.MemoryOrM2MDstAddress = - (uint32_t)signal->internals->gpio_buff; - signal->internals->dma_config_gpio.PeriphOrM2MSrcAddress = - (uint32_t) & (signal->internals->gpio->port->BSRR); - signal->internals->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; - signal->internals->dma_config_timer.NbData = signal->internals->reload_reg_entries; - - /* set up DMA channel 1 and 2 for GPIO and timer copy operations */ - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &signal->internals->dma_config_gpio); - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &signal->internals->dma_config_timer); - - /* enable both DMA channels */ - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - - return true; + LL_TIM_DisableUpdateEvent(TIM2); + LL_TIM_DisableDMAReq_UPDATE(TIM2); } static void digital_signal_setup_timer() { @@ -236,15 +253,45 @@ static void digital_signal_setup_timer() { LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); LL_TIM_SetPrescaler(TIM2, 0); - LL_TIM_SetAutoReload(TIM2, 10); + LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableUpdateEvent(TIM2); - LL_TIM_EnableDMAReq_UPDATE(TIM2); } static void digital_signal_start_timer() { - LL_TIM_GenerateEvent_UPDATE(TIM2); LL_TIM_EnableCounter(TIM2); + LL_TIM_EnableUpdateEvent(TIM2); + LL_TIM_EnableDMAReq_UPDATE(TIM2); + LL_TIM_GenerateEvent_UPDATE(TIM2); +} + +static bool digital_signal_setup_dma(DigitalSignal* signal) { + furi_assert(signal); + DigitalSignalInternals* internals = signal->internals; + + uint32_t buffer_entries = internals->reload->entries; + if(!buffer_entries || !internals->reload || !internals->reload->buffers) { + return false; + } + digital_signal_stop_dma(); + + internals->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)internals->gpio_buff; + internals->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (internals->gpio->port->BSRR); + internals->dma_config_timer.MemoryOrM2MDstAddress = + (uint32_t)internals->reload->buffers[internals->reload->current]; + internals->dma_config_timer.NbData = buffer_entries; + + /* set up DMA channel 1 and 2 for GPIO and timer copy operations */ + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &internals->dma_config_gpio); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &internals->dma_config_timer); + + /* enable both DMA channels */ + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + + /* buffer is used now by DMA, skip to next */ + internals->reload->current = (internals->reload->current + 1) % internals->reload->count; + + return true; } void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { @@ -279,7 +326,6 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) { sequence->signals_size = size; sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*)); - sequence->signals_prolonged = malloc(sequence->signals_size * sizeof(bool)); } void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { @@ -298,6 +344,13 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { sequence->gpio = gpio; sequence->bake = false; + sequence->reload = malloc(sizeof(struct ReloadBuffers)); + sequence->reload->count = 2; + sequence->reload->size = 512; + sequence->reload->buffers = malloc(sizeof(uint32_t*)); + sequence->reload->buffers[0] = malloc(sequence->reload->size * sizeof(uint32_t)); + sequence->reload->buffers[1] = malloc(sequence->reload->size * sizeof(uint32_t)); + digital_sequence_alloc_signals(sequence, 32); digital_sequence_alloc_sequence(sequence, size); @@ -311,9 +364,17 @@ void digital_sequence_free(DigitalSequence* sequence) { return; } + /* de-assign the shared reload buffer */ + for(int pos = 0; pos < sequence->signals_size; pos++) { + if(sequence->signals[pos]) { + sequence->signals[pos]->internals->reload = NULL; + } + } + free(sequence->signals); free(sequence->sequence); - free(sequence->signals_prolonged); + free(sequence->reload->buffers); + free(sequence->reload); free(sequence); } @@ -325,10 +386,38 @@ void digital_sequence_set_signal( furi_assert(signal); furi_assert(signal_index < sequence->signals_size); + /* if there is already a signal, unassign the shared reload buffer */ + if(sequence->signals[signal_index]) { + sequence->signals[signal_index]->internals->reload = NULL; + } + sequence->signals[signal_index] = signal; signal->internals->gpio = sequence->gpio; signal->internals->reload_reg_remainder = 0; + /* free the original reload buffer */ + if(signal->internals->reload) { + if(signal->internals->reload->buffers) { + for(uint32_t pos = 0; pos < signal->internals->reload->count; pos++) { + free(signal->internals->reload->buffers[pos]); + } + free(signal->internals->reload->buffers); + } + free(signal->internals->reload); + } + + /* assign the sequence's shared reload buffer */ + signal->internals->reload = sequence->reload; + + /* ensure it is big enough and reallocate if not */ + if(sequence->reload->size < signal->edges_max_cnt) { + free(sequence->reload->buffers); + + sequence->reload->size = signal->edges_max_cnt; + sequence->reload->buffers[0] = malloc(sequence->reload->size * sizeof(uint32_t)); + sequence->reload->buffers[1] = malloc(sequence->reload->size * sizeof(uint32_t)); + } + digital_signal_prepare_arr(signal); } @@ -352,10 +441,11 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { } static void digital_signal_update_dma(DigitalSignal* signal) { + struct ReloadBuffers* reload = signal->internals->reload; /* keep them prepared in registers so there is less delay when writing */ register bool restart_needed = false; - register volatile uint16_t len = signal->internals->reload_reg_entries; - register volatile uint32_t addr = (uint32_t)signal->reload_reg_buff; + register volatile uint16_t len = reload->entries; + register volatile uint32_t addr = (uint32_t)reload->buffers[reload->current]; /* first make sure it will still count down, else we will risk waiting infinitely */ const uint32_t wait_ms = 10; @@ -374,6 +464,8 @@ static void digital_signal_update_dma(DigitalSignal* signal) { /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { } + } else { + FURI_LOG_D(TAG, "digital_sequence_send_signal: DMA hung, restart needed"); } LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); @@ -384,6 +476,8 @@ static void digital_signal_update_dma(DigitalSignal* signal) { if(restart_needed) { LL_TIM_GenerateEvent_UPDATE(TIM2); } + + reload->current = (reload->current + 1) % reload->count; } static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { @@ -437,7 +531,12 @@ DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { bool digital_sequence_send(DigitalSequence* sequence) { furi_assert(sequence); + struct ReloadBuffers* reload = sequence->reload; + furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DEBUG_OUTPUT + furi_hal_gpio_init(&gpio_ext_pb3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +#endif if(sequence->bake) { DigitalSignal* sig = digital_sequence_bake(sequence); @@ -450,9 +549,16 @@ bool digital_sequence_send(DigitalSequence* sequence) { int32_t remainder = 0; FURI_CRITICAL_ENTER(); + bool traded_first = false; + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { uint8_t signal_index = sequence->sequence[pos]; DigitalSignal* sig = sequence->signals[signal_index]; + DigitalSignal* sig_next = NULL; + + if(pos + 1 < sequence->sequence_used) { + sig_next = sequence->signals[sequence->sequence[pos + 1]]; + } if(!sig) { FURI_LOG_D( @@ -463,27 +569,39 @@ bool digital_sequence_send(DigitalSequence* sequence) { break; } - /* when we are too late more than half a tick, make the first edge temporarily longer */ - bool needs_prolongation = false; + /* if the first edge is handled by prolonging the last pulse of the previous signal, skip it here */ + reload->entries = sig->edge_cnt - (traded_first ? 1 : 0); + memcpy( + reload->buffers[reload->current], + &sig->reload_reg_buff[traded_first ? 1 : 0], + reload->entries * sizeof(uint32_t)); + traded_first = false; + + /* when we are too late more than half a tick, make the first edge temporarily longer */ if(remainder >= T_TIM_DIV2) { remainder -= T_TIM; - needs_prolongation = true; + reload->buffers[reload->current][0] += 1; } /* update the total remainder */ remainder += sig->internals->reload_reg_remainder; - /* do we need to update the prolongation? */ - if(needs_prolongation != sequence->signals_prolonged[signal_index]) { - if(needs_prolongation) { - sig->edge_timings[0]++; - } else { - sig->edge_timings[0]--; + /* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */ + if(sig_next) { + /* beware, we do not want the level after the last edge, but the last level before that edge */ + bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); + + /* take from the next, add it to the first */ + if(end_level == sig_next->start_level) { + /* add the traded prolongation to the last pulse */ + reload->buffers[reload->current][reload->entries - 1] += + sig_next->reload_reg_buff[0]; + traded_first = true; } - sequence->signals_prolonged[signal_index] = needs_prolongation; } + /* transmit */ bool success = digital_sequence_send_signal(sequence, sig); if(!success) { @@ -499,16 +617,6 @@ bool digital_sequence_send(DigitalSequence* sequence) { digital_signal_stop_dma(); FURI_CRITICAL_EXIT(); - /* undo previously prolonged edges */ - for(uint32_t pos = 0; pos < sequence->signals_size; pos++) { - DigitalSignal* sig = sequence->signals[pos]; - - if(sig && sequence->signals_prolonged[pos]) { - sig->edge_timings[0]--; - sequence->signals_prolonged[pos] = false; - } - } - return true; } From 3cb1b1f6c17fe9da185a73584166c28c7eb6693c Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Sat, 4 Feb 2023 01:39:56 +0100 Subject: [PATCH 206/282] DigitalSignal: use cyclic DMA buffer for sequences --- lib/digital_signal/digital_signal.c | 435 +++++++++++++++------------- lib/digital_signal/digital_signal.h | 1 + 2 files changed, 234 insertions(+), 202 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 4ceca2ead..59278277c 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -8,13 +8,15 @@ #include #include +/* must be on bank B */ +#define DEBUG_OUTPUT gpio_ext_pb3 -struct ReloadBuffers { - uint32_t** buffers; /* pointers to the shadow buffers, either one or two. NULL if none */ - uint32_t count; /* number of allocated buffers, 0, 1 or 2 */ - uint32_t size; /* maximum entry count of a single buffer */ - uint32_t current; /* current buffer index, the other one is most likely being used */ - uint32_t entries; /* entries in the current buffer */ +struct ReloadBuffer { + uint32_t* buffer; /* DMA ringbuffer */ + uint32_t size; /* maximum entry count of the ring buffer */ + uint32_t write_pos; /* current buffer write index */ + uint32_t read_pos; /* current buffer read index */ + bool dma_active; }; struct DigitalSequence { @@ -27,17 +29,20 @@ struct DigitalSequence { const GpioPin* gpio; uint32_t send_time; bool send_time_active; - struct ReloadBuffers* reload; + LL_DMA_InitTypeDef dma_config_gpio; + LL_DMA_InitTypeDef dma_config_timer; + uint32_t* gpio_buff; + struct ReloadBuffer* dma_buffer; }; struct DigitalSignalInternals { + uint64_t factor; uint32_t reload_reg_entries; uint32_t reload_reg_remainder; uint32_t gpio_buff[2]; const GpioPin* gpio; LL_DMA_InitTypeDef dma_config_gpio; LL_DMA_InitTypeDef dma_config_timer; - struct ReloadBuffers* reload; }; #define TAG "DigitalSignal" @@ -56,9 +61,8 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->internals = malloc(sizeof(DigitalSignalInternals)); DigitalSignalInternals* internals = signal->internals; - internals->reload = NULL; - internals->reload_reg_entries = 0; - internals->reload_reg_remainder = 0; + + internals->factor = 1024 * 1024; internals->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; internals->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; @@ -92,12 +96,6 @@ void digital_signal_free(DigitalSignal* signal) { free(signal->edge_timings); free(signal->reload_reg_buff); - if(signal->internals->reload) { - if(signal->internals->reload->buffers) { - free(signal->internals->reload->buffers); - } - free(signal->internals->reload); - } free(signal->internals); free(signal); } @@ -191,8 +189,8 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { uint32_t bit_reset = internals->gpio->pin << 16; #ifdef DEBUG_OUTPUT - bit_set |= gpio_ext_pb3.pin; - bit_reset |= gpio_ext_pb3.pin << 16; + bit_set |= DEBUG_OUTPUT.pin; + bit_reset |= DEBUG_OUTPUT.pin << 16; #endif if(signal->start_level) { @@ -208,7 +206,17 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { internals->reload_reg_entries = 0; for(size_t pos = 0; pos < signal->edge_cnt; pos++) { - uint32_t pulse_duration = signal->edge_timings[pos] + internals->reload_reg_remainder; + uint32_t edge_scaled = (internals->factor * signal->edge_timings[pos]) / (1024 * 1024); + uint32_t pulse_duration = edge_scaled + internals->reload_reg_remainder; + if(pulse_duration < 10 || pulse_duration > 10000000) { + FURI_LOG_D( + TAG, + "[prepare] pulse_duration out of range: %lu = %lu * %llu", + pulse_duration, + signal->edge_timings[pos], + internals->factor); + pulse_duration = 100; + } uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; internals->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); @@ -216,22 +224,6 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { signal->reload_reg_buff[internals->reload_reg_entries++] = pulse_ticks - 1; } } - - /* in case there are no shadow buffers defined, allocate and use the precalced data */ - if(!internals->reload || !internals->reload->count) { - if(internals->reload) { - free(internals->reload); - } - internals->reload = malloc(sizeof(struct ReloadBuffers)); - internals->reload->count = 1; - internals->reload->size = signal->edges_max_cnt; - internals->reload->buffers = malloc(sizeof(uint32_t*)); - internals->reload->buffers[0] = malloc(internals->reload->size * sizeof(uint32_t)); - memcpy( - internals->reload->buffers[0], - signal->reload_reg_buff, - internals->reload_reg_entries * sizeof(uint32_t)); - } } static void digital_signal_stop_dma() { @@ -268,17 +260,15 @@ static bool digital_signal_setup_dma(DigitalSignal* signal) { furi_assert(signal); DigitalSignalInternals* internals = signal->internals; - uint32_t buffer_entries = internals->reload->entries; - if(!buffer_entries || !internals->reload || !internals->reload->buffers) { + if(!signal->internals->reload_reg_entries) { return false; } digital_signal_stop_dma(); internals->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)internals->gpio_buff; internals->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (internals->gpio->port->BSRR); - internals->dma_config_timer.MemoryOrM2MDstAddress = - (uint32_t)internals->reload->buffers[internals->reload->current]; - internals->dma_config_timer.NbData = buffer_entries; + internals->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; + internals->dma_config_timer.NbData = signal->internals->reload_reg_entries; /* set up DMA channel 1 and 2 for GPIO and timer copy operations */ LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &internals->dma_config_gpio); @@ -288,9 +278,6 @@ static bool digital_signal_setup_dma(DigitalSignal* signal) { LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - /* buffer is used now by DMA, skip to next */ - internals->reload->current = (internals->reload->current + 1) % internals->reload->count; - return true; } @@ -323,12 +310,12 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { signal->edge_cnt--; } -void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) { +static void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) { sequence->signals_size = size; sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*)); } -void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { +static void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { sequence->sequence_used = 0; sequence->sequence_size = size; sequence->sequence = malloc(sequence->sequence_size); @@ -344,12 +331,31 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { sequence->gpio = gpio; sequence->bake = false; - sequence->reload = malloc(sizeof(struct ReloadBuffers)); - sequence->reload->count = 2; - sequence->reload->size = 512; - sequence->reload->buffers = malloc(sizeof(uint32_t*)); - sequence->reload->buffers[0] = malloc(sequence->reload->size * sizeof(uint32_t)); - sequence->reload->buffers[1] = malloc(sequence->reload->size * sizeof(uint32_t)); + sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer)); + sequence->dma_buffer->size = 32; + sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t)); + + sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + sequence->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + sequence->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + sequence->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + sequence->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + sequence->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + sequence->dma_config_gpio.NbData = 2; + sequence->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + sequence->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + + sequence->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + sequence->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR; + sequence->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + sequence->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + sequence->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + sequence->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + sequence->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); + sequence->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)sequence->dma_buffer->buffer; + sequence->dma_config_timer.NbData = sequence->dma_buffer->size; + sequence->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + sequence->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; digital_sequence_alloc_signals(sequence, 32); digital_sequence_alloc_sequence(sequence, size); @@ -364,17 +370,10 @@ void digital_sequence_free(DigitalSequence* sequence) { return; } - /* de-assign the shared reload buffer */ - for(int pos = 0; pos < sequence->signals_size; pos++) { - if(sequence->signals[pos]) { - sequence->signals[pos]->internals->reload = NULL; - } - } - free(sequence->signals); free(sequence->sequence); - free(sequence->reload->buffers); - free(sequence->reload); + free(sequence->dma_buffer->buffer); + free(sequence->dma_buffer); free(sequence); } @@ -386,38 +385,10 @@ void digital_sequence_set_signal( furi_assert(signal); furi_assert(signal_index < sequence->signals_size); - /* if there is already a signal, unassign the shared reload buffer */ - if(sequence->signals[signal_index]) { - sequence->signals[signal_index]->internals->reload = NULL; - } - sequence->signals[signal_index] = signal; signal->internals->gpio = sequence->gpio; signal->internals->reload_reg_remainder = 0; - /* free the original reload buffer */ - if(signal->internals->reload) { - if(signal->internals->reload->buffers) { - for(uint32_t pos = 0; pos < signal->internals->reload->count; pos++) { - free(signal->internals->reload->buffers[pos]); - } - free(signal->internals->reload->buffers); - } - free(signal->internals->reload); - } - - /* assign the sequence's shared reload buffer */ - signal->internals->reload = sequence->reload; - - /* ensure it is big enough and reallocate if not */ - if(sequence->reload->size < signal->edges_max_cnt) { - free(sequence->reload->buffers); - - sequence->reload->size = signal->edges_max_cnt; - sequence->reload->buffers[0] = malloc(sequence->reload->size * sizeof(uint32_t)); - sequence->reload->buffers[1] = malloc(sequence->reload->size * sizeof(uint32_t)); - } - digital_signal_prepare_arr(signal); } @@ -440,71 +411,26 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { sequence->sequence[sequence->sequence_used++] = signal_index; } -static void digital_signal_update_dma(DigitalSignal* signal) { - struct ReloadBuffers* reload = signal->internals->reload; - /* keep them prepared in registers so there is less delay when writing */ - register bool restart_needed = false; - register volatile uint16_t len = reload->entries; - register volatile uint32_t addr = (uint32_t)reload->buffers[reload->current]; +static bool digital_sequence_setup_dma(DigitalSequence* sequence) { + furi_assert(sequence); - /* first make sure it will still count down, else we will risk waiting infinitely */ - const uint32_t wait_ms = 10; - const uint32_t wait_ticks = wait_ms * 1000 * furi_hal_cortex_instructions_per_microsecond(); - uint16_t prev_remain = LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); - uint32_t prev_timer = DWT->CYCCNT; + digital_signal_stop_dma(); - while(prev_remain == LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { - if(DWT->CYCCNT - prev_timer > wait_ticks) { - restart_needed = true; - break; - } - } + sequence->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)sequence->gpio_buff; + sequence->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (sequence->gpio->port->BSRR); - if(!restart_needed) { - /* if transfer was already active, wait till DMA is done and the last timer ticks are running */ - while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { - } - } else { - FURI_LOG_D(TAG, "digital_sequence_send_signal: DMA hung, restart needed"); - } + /* set up DMA channel 1 and 2 for GPIO and timer copy operations */ + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &sequence->dma_config_gpio); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &sequence->dma_config_timer); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, len); - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, addr); + /* enable both DMA channels */ + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - if(restart_needed) { - LL_TIM_GenerateEvent_UPDATE(TIM2); - } - - reload->current = (reload->current + 1) % reload->count; -} - -static bool digital_sequence_send_signal(DigitalSequence* sequence, DigitalSignal* signal) { - /* the first iteration has to set up the whole machinery */ - if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { - if(!digital_signal_setup_dma(signal)) { - FURI_LOG_D(TAG, "digital_sequence_send_signal: Signal has no entries, aborting"); - return false; - } - digital_signal_setup_timer(); - - /* if the send time is specified, wait till the core timer passed beyond that time */ - if(sequence->send_time_active) { - sequence->send_time_active = false; - while(sequence->send_time - DWT->CYCCNT < 0x80000000) { - } - } - digital_signal_start_timer(); - } else { - /* configure next polarities and timings */ - digital_signal_update_dma(signal); - } - return true; } -DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { +static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { furi_assert(sequence); uint32_t edges = 0; @@ -528,14 +454,94 @@ DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { return ret; } +static void digital_sequence_update_pos(DigitalSequence* sequence) { + struct ReloadBuffer* dma_buffer = sequence->dma_buffer; + + dma_buffer->read_pos = dma_buffer->size - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); +} + +static const uint32_t wait_ms = 10; +static const uint32_t wait_ticks = wait_ms * 1000 * 64; + +static void digital_sequence_finish(DigitalSequence* sequence) { + struct ReloadBuffer* dma_buffer = sequence->dma_buffer; + + if(dma_buffer->dma_active) { + uint32_t prev_timer = DWT->CYCCNT; + uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; + do { + uint32_t last_pos = dma_buffer->read_pos; + + digital_sequence_update_pos(sequence); + + /* we are finished, when the DMA transferred the 0xFFFFFFFF-timer which is the current write_pos */ + if(dma_buffer->read_pos == end_pos) { + break; + } + + if(last_pos != dma_buffer->read_pos) { + prev_timer = DWT->CYCCNT; + } + if(DWT->CYCCNT - prev_timer > wait_ticks) { + FURI_LOG_D( + TAG, + "[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)", + wait_ms, + TIM2->ARR, + dma_buffer->read_pos, + dma_buffer->write_pos); + break; + } + } while(1); + } + + digital_signal_stop_timer(); + digital_signal_stop_dma(); +} + +static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t length) { + struct ReloadBuffer* dma_buffer = sequence->dma_buffer; + + if(dma_buffer->dma_active) { + uint32_t prev_timer = DWT->CYCCNT; + uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; + do { + uint32_t last_pos = dma_buffer->read_pos; + digital_sequence_update_pos(sequence); + + if(dma_buffer->read_pos != end_pos) { + break; + } + + if(last_pos != dma_buffer->read_pos) { + prev_timer = DWT->CYCCNT; + } + if(DWT->CYCCNT - prev_timer > wait_ticks) { + FURI_LOG_D( + TAG, + "[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)", + wait_ms, + TIM2->ARR, + dma_buffer->read_pos, + dma_buffer->write_pos); + break; + } + } while(1); + } + + dma_buffer->buffer[dma_buffer->write_pos] = length; + dma_buffer->write_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; + dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF; +} + bool digital_sequence_send(DigitalSequence* sequence) { furi_assert(sequence); - struct ReloadBuffers* reload = sequence->reload; + struct ReloadBuffer* dma_buffer = sequence->dma_buffer; furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); #ifdef DEBUG_OUTPUT - furi_hal_gpio_init(&gpio_ext_pb3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&DEBUG_OUTPUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); #endif if(sequence->bake) { @@ -547,74 +553,88 @@ bool digital_sequence_send(DigitalSequence* sequence) { } int32_t remainder = 0; - FURI_CRITICAL_ENTER(); - bool traded_first = false; - for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { - uint8_t signal_index = sequence->sequence[pos]; + FURI_CRITICAL_ENTER(); + + dma_buffer->dma_active = false; + dma_buffer->buffer[0] = 0xFFFFFFFF; + dma_buffer->read_pos = 0; + dma_buffer->write_pos = 0; + + for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) { + uint8_t signal_index = sequence->sequence[seq_pos]; DigitalSignal* sig = sequence->signals[signal_index]; - DigitalSignal* sig_next = NULL; + bool last_signal = ((seq_pos + 1) == sequence->sequence_used); - if(pos + 1 < sequence->sequence_used) { - sig_next = sequence->signals[sequence->sequence[pos + 1]]; + /* all signals are prepared and we can re-use the GPIO buffer from the fist signal */ + if(seq_pos == 0) { + sequence->gpio_buff = sig->internals->gpio_buff; } - if(!sig) { - FURI_LOG_D( - TAG, - "digital_sequence_send: Signal at index %u, used at pos %lu is NULL, aborting", - signal_index, - pos); - break; - } - - /* if the first edge is handled by prolonging the last pulse of the previous signal, skip it here */ - reload->entries = sig->edge_cnt - (traded_first ? 1 : 0); - - memcpy( - reload->buffers[reload->current], - &sig->reload_reg_buff[traded_first ? 1 : 0], - reload->entries * sizeof(uint32_t)); - traded_first = false; - - /* when we are too late more than half a tick, make the first edge temporarily longer */ - if(remainder >= T_TIM_DIV2) { - remainder -= T_TIM; - reload->buffers[reload->current][0] += 1; - } - - /* update the total remainder */ - remainder += sig->internals->reload_reg_remainder; - - /* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */ - if(sig_next) { - /* beware, we do not want the level after the last edge, but the last level before that edge */ - bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); - - /* take from the next, add it to the first */ - if(end_level == sig_next->start_level) { - /* add the traded prolongation to the last pulse */ - reload->buffers[reload->current][reload->entries - 1] += - sig_next->reload_reg_buff[0]; - traded_first = true; + for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) { + if(traded_first) { + traded_first = false; + continue; } - } + uint32_t pulse_length = 0; + bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries); - /* transmit */ - bool success = digital_sequence_send_signal(sequence, sig); + pulse_length = sig->reload_reg_buff[pulse_pos]; - if(!success) { - break; + /* when we are too late more than half a tick, make the first edge temporarily longer */ + if(remainder >= T_TIM_DIV2) { + remainder -= T_TIM; + pulse_length += 1; + } + remainder += sig->internals->reload_reg_remainder; + + /* last pulse in that signal and have a next signal? */ + if(last_pulse) { + if((seq_pos + 1) < sequence->sequence_used) { + DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]]; + + /* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */ + /* beware, we do not want the level after the last edge, but the last level before that edge */ + bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); + + /* take from the next, add it to the current if they have the same level */ + if(end_level == sig_next->start_level) { + pulse_length += sig_next->reload_reg_buff[0]; + traded_first = true; + } + } + } + + digital_sequence_queue_pulse(sequence, pulse_length); + + /* start transmission when buffer was filled enough */ + bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4); + + /* or it was the last pulse */ + if(last_pulse && last_signal) { + start_send = true; + } + + /* start transmission */ + if(start_send && !dma_buffer->dma_active) { + digital_sequence_setup_dma(sequence); + digital_signal_setup_timer(); + + /* if the send time is specified, wait till the core timer passed beyond that time */ + if(sequence->send_time_active) { + sequence->send_time_active = false; + while(sequence->send_time - DWT->CYCCNT < 0x80000000) { + } + } + digital_signal_start_timer(); + dma_buffer->dma_active = true; + } } } /* wait until last dma transaction was finished */ - while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { - } - - digital_signal_stop_timer(); - digital_signal_stop_dma(); + digital_sequence_finish(sequence); FURI_CRITICAL_EXIT(); return true; @@ -625,3 +645,14 @@ void digital_sequence_clear(DigitalSequence* sequence) { sequence->sequence_used = 0; } + +void digital_sequence_timebase_correction(DigitalSequence* sequence, float factor) { + for(uint32_t sig_pos = 0; sig_pos < sequence->signals_size; sig_pos++) { + DigitalSignal* signal = sequence->signals[sig_pos]; + + if(signal) { + signal->internals->factor = (uint32_t)(1024 * 1024 * factor); + digital_signal_prepare_arr(signal); + } + } +} diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 2cb107486..40afb1e41 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -61,6 +61,7 @@ void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index); bool digital_sequence_send(DigitalSequence* sequence); void digital_sequence_clear(DigitalSequence* sequence); +void digital_sequence_timebase_correction(DigitalSequence* sequence, float factor); #ifdef __cplusplus } From 32ed4d3b312b058aa812cb750cf2efdcff9e29a6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 30 Apr 2023 18:49:26 +0100 Subject: [PATCH 207/282] Fix check for normal boot (fix bootloop on flash) --- firmware/targets/f18/furi_hal/furi_hal.c | 5 ++++- firmware/targets/f7/api_symbols.csv | 1 + firmware/targets/f7/furi_hal/furi_hal.c | 5 ++++- firmware/targets/f7/src/main.c | 2 ++ firmware/targets/furi_hal_include/furi_hal.h | 5 ++++- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/firmware/targets/f18/furi_hal/furi_hal.c b/firmware/targets/f18/furi_hal/furi_hal.c index 9b234143c..1a180e698 100644 --- a/firmware/targets/f18/furi_hal/furi_hal.c +++ b/firmware/targets/f18/furi_hal/furi_hal.c @@ -8,6 +8,10 @@ bool normal_boot = false; +void furi_hal_set_is_normal_boot(bool value) { + normal_boot = value; +} + bool furi_hal_is_normal_boot() { return normal_boot; } @@ -32,7 +36,6 @@ void furi_hal_deinit_early() { } void furi_hal_init() { - normal_boot = true; furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_console_init(); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 33f3ff301..0d326943a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1415,6 +1415,7 @@ Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" Function,+,furi_hal_rtc_sync_shadow,void, Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* +Function,-,furi_hal_set_is_normal_boot,void,_Bool Function,+,furi_hal_speaker_acquire,_Bool,uint32_t Function,-,furi_hal_speaker_deinit,void, Function,-,furi_hal_speaker_init,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index 01ec37bc6..1378b6955 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -8,6 +8,10 @@ bool normal_boot = false; +void furi_hal_set_is_normal_boot(bool value) { + normal_boot = value; +} + bool furi_hal_is_normal_boot() { return normal_boot; } @@ -32,7 +36,6 @@ void furi_hal_deinit_early() { } void furi_hal_init() { - normal_boot = true; furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_console_init(); diff --git a/firmware/targets/f7/src/main.c b/firmware/targets/f7/src/main.c index 2c353f52b..2b1fef902 100644 --- a/firmware/targets/f7/src/main.c +++ b/firmware/targets/f7/src/main.c @@ -26,6 +26,7 @@ int main() { // Flipper critical FURI HAL furi_hal_init_early(); + furi_hal_set_is_normal_boot(false); FuriThread* main_thread = furi_thread_alloc_ex("Init", 4096, init_task, NULL); #ifdef FURI_RAM_EXEC @@ -58,6 +59,7 @@ int main() { furi_hal_power_reset(); } else { furi_hal_light_sequence("rgb G"); + furi_hal_set_is_normal_boot(true); furi_thread_start(main_thread); } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index 7d6dc7f24..3b1e8dc2d 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -42,7 +42,10 @@ struct STOP_EXTERNING_ME {}; extern "C" { #endif -/** True if normally with all subsystems */ +/** Set whether booting normally with all subsystems */ +void furi_hal_set_is_normal_boot(bool value); + +/** True if booting normally with all subsystems */ bool furi_hal_is_normal_boot(); /** Early FuriHal init, only essential subsystems */ From 1e5c351dd6214b443d2c4cb6a1daf229996ed045 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:08:31 +0100 Subject: [PATCH 208/282] Only load notification settings on normal boot --- applications/services/notification/notification_app.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index f91a73f32..a26d84ab3 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -538,8 +538,10 @@ int32_t notification_srv(void* p) { UNUSED(p); NotificationApp* app = notification_app_alloc(); - if(!notification_load_settings(app)) { - notification_save_settings(app); + if(furi_hal_is_normal_boot()) { + if(!notification_load_settings(app)) { + notification_save_settings(app); + } } notification_vibro_off(); From 3a07160c3880491a50ad01c84f271d60a3866ff8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 30 Apr 2023 21:46:15 +0100 Subject: [PATCH 209/282] Use flipper format file for xtreme settings --- applications/services/xtreme/application.fam | 2 +- applications/services/xtreme/settings.c | 160 ++++++++++++------- applications/services/xtreme/settings.h | 11 +- firmware/targets/f7/api_symbols.csv | 2 +- 4 files changed, 111 insertions(+), 64 deletions(-) diff --git a/applications/services/xtreme/application.fam b/applications/services/xtreme/application.fam index de90d455f..beb19e437 100644 --- a/applications/services/xtreme/application.fam +++ b/applications/services/xtreme/application.fam @@ -5,7 +5,7 @@ App( entry_point="xtreme_srv", cdefines=["SRV_XTREME"], requires=["storage"], - stack_size=1 * 1024, + stack_size=2 * 1024, order=46, sdk_headers=[ "settings.h", diff --git a/applications/services/xtreme/settings.c b/applications/services/xtreme/settings.c index ec1f89ecf..fa4ba1813 100644 --- a/applications/services/xtreme/settings.c +++ b/applications/services/xtreme/settings.c @@ -1,5 +1,7 @@ #include "settings.h" +#include + #define TAG "XtremeSettings" XtremeSettings* xtreme_settings = NULL; @@ -14,74 +16,124 @@ XtremeSettings* XTREME_SETTINGS() { void XTREME_SETTINGS_LOAD() { if(xtreme_settings == NULL) { xtreme_settings = malloc(sizeof(XtremeSettings)); - bool loaded = false; + + // Set default values + memset(xtreme_settings, 0, sizeof(XtremeSettings)); + strlcpy(xtreme_settings->asset_pack, "", MAX_PACK_NAME_LEN); // SFW + xtreme_settings->anim_speed = 100; // 100% + xtreme_settings->cycle_anims = 0; // Meta.txt + xtreme_settings->unlock_anims = false; // OFF + xtreme_settings->fallback_anim = true; // ON + xtreme_settings->wii_menu = true; // ON + xtreme_settings->lockscreen_time = true; // ON + xtreme_settings->lockscreen_seconds = false; // OFF + xtreme_settings->lockscreen_date = true; // ON + xtreme_settings->lockscreen_statusbar = true; // ON + xtreme_settings->lockscreen_prompt = true; // ON + xtreme_settings->battery_icon = BatteryIconBarPercent; // Bar % + xtreme_settings->status_icons = true; // ON + xtreme_settings->bar_borders = true; // ON + xtreme_settings->bar_background = false; // OFF + xtreme_settings->sort_dirs_first = true; // ON + xtreme_settings->dark_mode = false; // OFF + xtreme_settings->bad_bt = false; // USB + xtreme_settings->bad_bt_remember = false; // OFF + xtreme_settings->butthurt_timer = 43200; // 12 H + xtreme_settings->rgb_backlight = false; // OFF if(!furi_hal_is_normal_boot()) { FURI_LOG_W(TAG, "Load skipped. Device is in special startup mode."); - loaded = false; - } else { - loaded = saved_struct_load( - XTREME_SETTINGS_PATH, - xtreme_settings, - sizeof(XtremeSettings), - XTREME_SETTINGS_MAGIC, - XTREME_SETTINGS_VERSION); - if(!loaded) { - Storage* storage = furi_record_open(RECORD_STORAGE); - storage_common_copy(storage, XTREME_SETTINGS_OLD_PATH, XTREME_SETTINGS_PATH); - storage_common_copy(storage, XTREME_SETTINGS_OLD_INT_PATH, XTREME_SETTINGS_PATH); - storage_common_remove(storage, XTREME_SETTINGS_OLD_PATH); - storage_common_remove(storage, XTREME_SETTINGS_OLD_INT_PATH); - furi_record_close(RECORD_STORAGE); - loaded = saved_struct_load( - XTREME_SETTINGS_PATH, - xtreme_settings, - sizeof(XtremeSettings), - XTREME_SETTINGS_MAGIC, - XTREME_SETTINGS_VERSION); - } + return; } - if(!loaded) { - memset(xtreme_settings, 0, sizeof(XtremeSettings)); - strlcpy(xtreme_settings->asset_pack, "", MAX_PACK_NAME_LEN); // SFW - xtreme_settings->anim_speed = 100; // 100% - xtreme_settings->cycle_anims = 0; // Meta.txt - xtreme_settings->unlock_anims = false; // OFF - xtreme_settings->fallback_anim = true; // ON - xtreme_settings->wii_menu = true; // ON - xtreme_settings->lockscreen_time = true; // ON - xtreme_settings->lockscreen_seconds = false; // ON - xtreme_settings->lockscreen_date = true; // ON - xtreme_settings->lockscreen_statusbar = true; // ON - xtreme_settings->lockscreen_prompt = true; // ON - xtreme_settings->battery_icon = BatteryIconBarPercent; // Bar % - xtreme_settings->status_icons = true; // ON - xtreme_settings->bar_borders = true; // ON - xtreme_settings->bar_background = false; // OFF - xtreme_settings->sort_dirs_first = true; // ON - xtreme_settings->dark_mode = false; // OFF - xtreme_settings->bad_bt = false; // USB - xtreme_settings->bad_bt_remember = false; // OFF - xtreme_settings->butthurt_timer = 43200; // 12 H - xtreme_settings->rgb_backlight = false; // OFF + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_existing(file, XTREME_SETTINGS_PATH)) { + FuriString* string = furi_string_alloc(); + if(flipper_format_read_string(file, "asset_pack", string)) { + strlcpy(xtreme_settings->asset_pack, furi_string_get_cstr(string), MAX_PACK_NAME_LEN); + } + furi_string_free(string); + flipper_format_rewind(file); + flipper_format_read_uint32(file, "anim_speed", &xtreme_settings->anim_speed, 1); + flipper_format_rewind(file); + flipper_format_read_int32(file, "cycle_anims", &xtreme_settings->cycle_anims, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "unlock_anims", &xtreme_settings->unlock_anims, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "fallback_anim", &xtreme_settings->fallback_anim, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "wii_menu", &xtreme_settings->wii_menu, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_time", &xtreme_settings->lockscreen_time, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_seconds", &xtreme_settings->lockscreen_seconds, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_date", &xtreme_settings->lockscreen_date, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_statusbar", &xtreme_settings->lockscreen_statusbar, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_prompt", &xtreme_settings->lockscreen_prompt, 1); + flipper_format_rewind(file); + flipper_format_read_uint32(file, "battery_icon", (uint32_t*)&xtreme_settings->battery_icon, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "status_icons", &xtreme_settings->status_icons, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bar_borders", &xtreme_settings->bar_borders, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bar_background", &xtreme_settings->bar_background, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "sort_dirs_first", &xtreme_settings->sort_dirs_first, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "dark_mode", &xtreme_settings->dark_mode, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bad_bt", &xtreme_settings->bad_bt, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bad_bt_remember", &xtreme_settings->bad_bt_remember, 1); + flipper_format_rewind(file); + flipper_format_read_int32(file, "butthurt_timer", &xtreme_settings->butthurt_timer, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "rgb_backlight", &xtreme_settings->rgb_backlight, 1); } + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); } } -bool XTREME_SETTINGS_SAVE() { +void XTREME_SETTINGS_SAVE() { if(xtreme_settings == NULL) { XTREME_SETTINGS_LOAD(); } if(!furi_hal_is_normal_boot()) { - return true; + return; } - return saved_struct_save( - XTREME_SETTINGS_PATH, - xtreme_settings, - sizeof(XtremeSettings), - XTREME_SETTINGS_MAGIC, - XTREME_SETTINGS_VERSION); + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_always(file, XTREME_SETTINGS_PATH)) { + flipper_format_write_string_cstr(file, "asset_pack", xtreme_settings->asset_pack); + flipper_format_write_uint32(file, "anim_speed", &xtreme_settings->anim_speed, 1); + flipper_format_write_int32(file, "cycle_anims", &xtreme_settings->cycle_anims, 1); + flipper_format_write_bool(file, "unlock_anims", &xtreme_settings->unlock_anims, 1); + flipper_format_write_bool(file, "fallback_anim", &xtreme_settings->fallback_anim, 1); + flipper_format_write_bool(file, "wii_menu", &xtreme_settings->wii_menu, 1); + flipper_format_write_bool(file, "lockscreen_time", &xtreme_settings->lockscreen_time, 1); + flipper_format_write_bool(file, "lockscreen_seconds", &xtreme_settings->lockscreen_seconds, 1); + flipper_format_write_bool(file, "lockscreen_date", &xtreme_settings->lockscreen_date, 1); + flipper_format_write_bool(file, "lockscreen_statusbar", &xtreme_settings->lockscreen_statusbar, 1); + flipper_format_write_bool(file, "lockscreen_prompt", &xtreme_settings->lockscreen_prompt, 1); + flipper_format_write_uint32(file, "battery_icon", (uint32_t*)&xtreme_settings->battery_icon, 1); + flipper_format_write_bool(file, "status_icons", &xtreme_settings->status_icons, 1); + flipper_format_write_bool(file, "bar_borders", &xtreme_settings->bar_borders, 1); + flipper_format_write_bool(file, "bar_background", &xtreme_settings->bar_background, 1); + flipper_format_write_bool(file, "sort_dirs_first", &xtreme_settings->sort_dirs_first, 1); + flipper_format_write_bool(file, "dark_mode", &xtreme_settings->dark_mode, 1); + flipper_format_write_bool(file, "bad_bt", &xtreme_settings->bad_bt, 1); + flipper_format_write_bool(file, "bad_bt_remember", &xtreme_settings->bad_bt_remember, 1); + flipper_format_write_int32(file, "butthurt_timer", &xtreme_settings->butthurt_timer, 1); + flipper_format_write_bool(file, "rgb_backlight", &xtreme_settings->rgb_backlight, 1); + } + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); } diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h index 0261b68ae..a5f0b4688 100644 --- a/applications/services/xtreme/settings.h +++ b/applications/services/xtreme/settings.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -13,17 +12,13 @@ extern "C" { #define MAX_PACK_NAME_LEN 32 -#define XTREME_SETTINGS_VERSION (11) -#define XTREME_SETTINGS_MAGIC (0x69) -#define XTREME_SETTINGS_OLD_INT_PATH INT_PATH(".xtreme.settings") -#define XTREME_SETTINGS_OLD_PATH EXT_PATH(".xtreme.settings") -#define XTREME_SETTINGS_PATH CFG_PATH("xtreme.settings") +#define XTREME_SETTINGS_PATH CFG_PATH("xtreme_settings.txt") #define XTREME_APPS_PATH CFG_PATH("xtreme_apps.txt") typedef struct { char asset_pack[MAX_PACK_NAME_LEN]; - uint16_t anim_speed; + uint32_t anim_speed; int32_t cycle_anims; bool unlock_anims; bool fallback_anim; @@ -49,7 +44,7 @@ XtremeSettings* XTREME_SETTINGS(); void XTREME_SETTINGS_LOAD(); -bool XTREME_SETTINGS_SAVE(); +void XTREME_SETTINGS_SAVE(); #ifdef __cplusplus } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 0d326943a..37d2b916f 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -325,7 +325,7 @@ Function,+,XTREME_ASSETS,XtremeAssets*, Function,-,XTREME_ASSETS_LOAD,void, Function,+,XTREME_SETTINGS,XtremeSettings*, Function,-,XTREME_SETTINGS_LOAD,void, -Function,+,XTREME_SETTINGS_SAVE,_Bool, +Function,+,XTREME_SETTINGS_SAVE,void, Function,-,_Exit,void,int Function,-,__assert,void,"const char*, int, const char*" Function,+,__assert_func,void,"const char*, int, const char*, const char*" From b851102801ed2982781abed795965a0c2a57921e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 30 Apr 2023 22:10:00 +0100 Subject: [PATCH 210/282] Add cstr version of elements_scrollable_text_line --- applications/services/gui/elements.c | 18 +++++++++++++++--- applications/services/gui/elements.h | 9 +++++++++ applications/services/gui/modules/menu.c | 7 +++---- firmware/targets/f7/api_symbols.csv | 1 + 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index ffe52d354..fb64c482c 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -573,16 +573,16 @@ void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width } } -void elements_scrollable_text_line( +void elements_scrollable_text_line_str( Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, - FuriString* string, + const char* string, size_t scroll, bool ellipsis, bool centered) { - FuriString* line = furi_string_alloc_set(string); + FuriString* line = furi_string_alloc_set_str(string); size_t len_px = canvas_string_width(canvas, furi_string_get_cstr(line)); if(len_px > width) { @@ -631,6 +631,18 @@ void elements_scrollable_text_line( furi_string_free(line); } +void elements_scrollable_text_line( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + FuriString* string, + size_t scroll, + bool ellipsis, + bool centered) { + elements_scrollable_text_line_str(canvas, x, y, width, furi_string_get_cstr(string), scroll, ellipsis, centered); + } + void elements_text_box( Canvas* canvas, uint8_t x, diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index 00bffc9a6..7cab3a67f 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -220,6 +220,15 @@ void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width * @param[in] ellipsis The ellipsis flag: true to add ellipse * @param[in] centered The centered flag: true to center text on x and y */ +void elements_scrollable_text_line_str( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + const char* string, + size_t scroll, + bool ellipsis, + bool centered); void elements_scrollable_text_line( Canvas* canvas, uint8_t x, diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index 191c0e874..899d340de 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -47,9 +47,9 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { size_t items_count = MenuItemArray_size(model->items); if(items_count) { MenuItem* item; - FuriString* name = furi_string_alloc(); size_t shift_position; if(XTREME_SETTINGS()->wii_menu) { + FuriString* name = furi_string_alloc(); if(position < 2) { shift_position = 0; } else if(position >= items_count - 2 + (items_count % 2)) { @@ -99,6 +99,7 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { elements_frame(canvas, 0 + x_off, 0 + y_off, 40, 30); } } + furi_string_free(name); } else { // First line canvas_set_font(canvas, FontSecondary); @@ -129,8 +130,7 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { } else { scroll_counter -= 1; } - furi_string_set(name, item->label); - elements_scrollable_text_line(canvas, 22, 36, 98, name, scroll_counter, false, false); + elements_scrollable_text_line_str(canvas, 22, 36, 98, item->label, scroll_counter, false, false); // Third line canvas_set_font(canvas, FontSecondary); shift_position = (2 + position + items_count - 1) % items_count; @@ -147,7 +147,6 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { elements_frame(canvas, 0, 21, 128 - 5, 21); elements_scrollbar(canvas, position, items_count); } - furi_string_free(name); } else { canvas_draw_str(canvas, 2, 32, "Empty"); elements_scrollbar(canvas, 0, 0); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 37d2b916f..6e1781f75 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -853,6 +853,7 @@ Function,+,elements_multiline_text_framed,void,"Canvas*, uint8_t, uint8_t, const Function,+,elements_progress_bar,void,"Canvas*, uint8_t, uint8_t, uint8_t, float" Function,+,elements_progress_bar_with_text,void,"Canvas*, uint8_t, uint8_t, uint8_t, float, const char*" Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool, _Bool" +Function,+,elements_scrollable_text_line_str,void,"Canvas*, uint8_t, uint8_t, uint8_t, const char*, size_t, _Bool, _Bool" Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t" Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t" Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" From 35afb1787b84e5a928748131896426d1680b04e2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 30 Apr 2023 22:15:47 +0100 Subject: [PATCH 211/282] Scroll long text in variable item list --- .../services/gui/modules/variable_item_list.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index 182cd127f..dff1eaa8a 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -79,7 +79,20 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { canvas_set_color(canvas, ColorBlack); } - canvas_draw_str(canvas, 6, item_text_y, item->label); + if(item->current_value_index == 0 && furi_string_empty(item->current_value_text)) { + // Only left text, no right text + canvas_draw_str(canvas, 6, item_text_y, item->label); + } else { + elements_scrollable_text_line_str( + canvas, + 6, + item_text_y, + 66, + item->label, + scroll_counter, + false, + false); + } if(item->locked) { canvas_draw_icon(canvas, 110, item_text_y - 8, &I_Lock_7x8); From c7f0f3db77f2621dbdb180bdbfb11e3f7b6761f8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 30 Apr 2023 22:17:44 +0100 Subject: [PATCH 212/282] Fix BadBT Remember setting name (now scrolls) --- .../main/xtreme_app/scenes/xtreme_app_scene_protocols.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c index eb8285e34..d8244d30d 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -55,7 +55,7 @@ void xtreme_app_scene_protocols_on_enter(void* context) { variable_item_set_current_value_text(item, xtreme_settings->bad_bt ? "BT" : "USB"); item = variable_item_list_add( - var_item_list, "BadBT Rmembr", 2, xtreme_app_scene_protocols_badbt_remember_changed, app); + var_item_list, "BadBT Remember", 2, xtreme_app_scene_protocols_badbt_remember_changed, app); variable_item_set_current_value_index(item, xtreme_settings->bad_bt_remember); variable_item_set_current_value_text(item, xtreme_settings->bad_bt_remember ? "ON" : "OFF"); From c814da74035d6b3031db7d834f4c2164a59b6b94 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 30 Apr 2023 22:57:55 +0100 Subject: [PATCH 213/282] Setting to factory reset and format on 10 bad pins --- .../xtreme_app_scene_interface_lockscreen.c | 17 +++++++++++++++++ .../services/desktop/helpers/pin_lock.c | 11 +++++++++++ applications/services/xtreme/settings.c | 4 ++++ applications/services/xtreme/settings.h | 1 + 4 files changed, 33 insertions(+) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c index 59ac287fc..0fc1e1e9c 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c @@ -10,6 +10,14 @@ void xtreme_app_scene_interface_lockscreen_var_item_list_callback(void* context, view_dispatcher_send_custom_event(app->view_dispatcher, index); } +static void xtreme_app_scene_interface_lockscreen_bad_pins_format_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->bad_pins_format = value; + app->save_settings = true; +} + static void xtreme_app_scene_interface_lockscreen_show_time_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); @@ -56,6 +64,15 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { VariableItemList* var_item_list = app->var_item_list; VariableItem* item; + item = variable_item_list_add( + var_item_list, + "Format on 10 bad PINs", + 2, + xtreme_app_scene_interface_lockscreen_bad_pins_format_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->bad_pins_format); + variable_item_set_current_value_text(item, xtreme_settings->bad_pins_format ? "ON" : "OFF"); + item = variable_item_list_add( var_item_list, "Show Time", diff --git a/applications/services/desktop/helpers/pin_lock.c b/applications/services/desktop/helpers/pin_lock.c index decd98320..73e688bf5 100644 --- a/applications/services/desktop/helpers/pin_lock.c +++ b/applications/services/desktop/helpers/pin_lock.c @@ -10,6 +10,7 @@ #include "../desktop_i.h" #include #include +#include static const NotificationSequence sequence_pin_fail = { &message_display_backlight_on, @@ -98,6 +99,16 @@ bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered) result = true; } else { uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); + if(pin_fails >= 9 && XTREME_SETTINGS()->bad_pins_format) { + furi_hal_rtc_set_pin_fails(0); + furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_remove(storage, INT_PATH(".cnt.u2f")); + storage_simply_remove(storage, INT_PATH(".key.u2f")); + storage_sd_format(storage); + furi_record_close(RECORD_STORAGE); + power_reboot(PowerBootModeNormal); + } furi_hal_rtc_set_pin_fails(pin_fails + 1); result = false; } diff --git a/applications/services/xtreme/settings.c b/applications/services/xtreme/settings.c index fa4ba1813..c135ddc43 100644 --- a/applications/services/xtreme/settings.c +++ b/applications/services/xtreme/settings.c @@ -25,6 +25,7 @@ void XTREME_SETTINGS_LOAD() { xtreme_settings->unlock_anims = false; // OFF xtreme_settings->fallback_anim = true; // ON xtreme_settings->wii_menu = true; // ON + xtreme_settings->bad_pins_format = false; // OFF xtreme_settings->lockscreen_time = true; // ON xtreme_settings->lockscreen_seconds = false; // OFF xtreme_settings->lockscreen_date = true; // ON @@ -65,6 +66,8 @@ void XTREME_SETTINGS_LOAD() { flipper_format_rewind(file); flipper_format_read_bool(file, "wii_menu", &xtreme_settings->wii_menu, 1); flipper_format_rewind(file); + flipper_format_read_bool(file, "bad_pins_format", &xtreme_settings->bad_pins_format, 1); + flipper_format_rewind(file); flipper_format_read_bool(file, "lockscreen_time", &xtreme_settings->lockscreen_time, 1); flipper_format_rewind(file); flipper_format_read_bool(file, "lockscreen_seconds", &xtreme_settings->lockscreen_seconds, 1); @@ -118,6 +121,7 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_bool(file, "unlock_anims", &xtreme_settings->unlock_anims, 1); flipper_format_write_bool(file, "fallback_anim", &xtreme_settings->fallback_anim, 1); flipper_format_write_bool(file, "wii_menu", &xtreme_settings->wii_menu, 1); + flipper_format_write_bool(file, "bad_pins_format", &xtreme_settings->bad_pins_format, 1); flipper_format_write_bool(file, "lockscreen_time", &xtreme_settings->lockscreen_time, 1); flipper_format_write_bool(file, "lockscreen_seconds", &xtreme_settings->lockscreen_seconds, 1); flipper_format_write_bool(file, "lockscreen_date", &xtreme_settings->lockscreen_date, 1); diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h index a5f0b4688..87fb44f75 100644 --- a/applications/services/xtreme/settings.h +++ b/applications/services/xtreme/settings.h @@ -23,6 +23,7 @@ typedef struct { bool unlock_anims; bool fallback_anim; bool wii_menu; + bool bad_pins_format; bool lockscreen_time; bool lockscreen_seconds; bool lockscreen_date; From 29ffaa677c7febbb97f2ae6312011d8fec9416b6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 30 Apr 2023 23:16:33 +0100 Subject: [PATCH 214/282] Properly capitalize BadKB config --- applications/main/bad_kb/scenes/bad_kb_scene_config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c index efdbf1ce4..17b4f8674 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c @@ -52,10 +52,10 @@ void bad_kb_scene_config_on_enter(void* context) { variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF"); variable_item_set_locked(item, !bad_kb->is_bt, "Only in\nBT mode!"); - item = variable_item_list_add(var_item_list, "BT device name", 0, NULL, bad_kb); + item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_kb); variable_item_set_locked(item, !bad_kb->is_bt, "Only in\nBT mode!"); - item = variable_item_list_add(var_item_list, "BT MAC address", 0, NULL, bad_kb); + item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_kb); if(!bad_kb->is_bt) { variable_item_set_locked(item, true, "Only in\nBT mode!"); } else if(bad_kb->bt_remember) { From da5bea9b7bafb0e27f9fdadfb741463c5931f0bf Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 30 Apr 2023 23:17:01 +0100 Subject: [PATCH 215/282] Add BadKB randomize BT MAC option --- .../main/bad_kb/scenes/bad_kb_scene_config.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c index 17b4f8674..872b99d35 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c @@ -9,6 +9,7 @@ enum VarItemListIndex { VarItemListIndexBtRemember, VarItemListIndexBtDeviceName, VarItemListIndexBtMacAddress, + VarItemListIndexRandomizeBtMac, }; void bad_kb_scene_config_connection_callback(VariableItem* item) { @@ -62,6 +63,13 @@ void bad_kb_scene_config_on_enter(void* context) { variable_item_set_locked(item, true, "Remember\nmust be Off!"); } + item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_kb); + if(!bad_kb->is_bt) { + variable_item_set_locked(item, true, "Only in\nBT mode!"); + } else if(bad_kb->bt_remember) { + variable_item_set_locked(item, true, "Remember\nmust be Off!"); + } + variable_item_list_set_enter_callback( var_item_list, bad_kb_scene_config_var_item_list_callback, bad_kb); @@ -96,6 +104,10 @@ bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { case VarItemListIndexBtMacAddress: scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigMac); break; + case VarItemListIndexRandomizeBtMac: + furi_hal_random_fill_buf(bad_kb->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN); + bt_set_profile_mac_address(bad_kb->bt, bad_kb->config.bt_mac); + break; default: break; } From 91d09dc81050a79303518aab73f705f243aa24d0 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 30 Apr 2023 23:22:18 +0100 Subject: [PATCH 216/282] Format --- applications/services/gui/elements.c | 5 +- applications/services/gui/modules/menu.c | 3 +- .../services/gui/modules/variable_item_list.c | 9 +- applications/services/xtreme/settings.c | 90 ++++++++++--------- 4 files changed, 52 insertions(+), 55 deletions(-) diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index fb64c482c..fc5609a36 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -640,8 +640,9 @@ void elements_scrollable_text_line( size_t scroll, bool ellipsis, bool centered) { - elements_scrollable_text_line_str(canvas, x, y, width, furi_string_get_cstr(string), scroll, ellipsis, centered); - } + elements_scrollable_text_line_str( + canvas, x, y, width, furi_string_get_cstr(string), scroll, ellipsis, centered); +} void elements_text_box( Canvas* canvas, diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index 899d340de..5c0da9c8f 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -130,7 +130,8 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { } else { scroll_counter -= 1; } - elements_scrollable_text_line_str(canvas, 22, 36, 98, item->label, scroll_counter, false, false); + elements_scrollable_text_line_str( + canvas, 22, 36, 98, item->label, scroll_counter, false, false); // Third line canvas_set_font(canvas, FontSecondary); shift_position = (2 + position + items_count - 1) % items_count; diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index dff1eaa8a..225118586 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -84,14 +84,7 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str(canvas, 6, item_text_y, item->label); } else { elements_scrollable_text_line_str( - canvas, - 6, - item_text_y, - 66, - item->label, - scroll_counter, - false, - false); + canvas, 6, item_text_y, 66, item->label, scroll_counter, false, false); } if(item->locked) { diff --git a/applications/services/xtreme/settings.c b/applications/services/xtreme/settings.c index c135ddc43..21c8080e8 100644 --- a/applications/services/xtreme/settings.c +++ b/applications/services/xtreme/settings.c @@ -47,56 +47,57 @@ void XTREME_SETTINGS_LOAD() { return; } + XtremeSettings* x = xtreme_settings; Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); if(flipper_format_file_open_existing(file, XTREME_SETTINGS_PATH)) { FuriString* string = furi_string_alloc(); if(flipper_format_read_string(file, "asset_pack", string)) { - strlcpy(xtreme_settings->asset_pack, furi_string_get_cstr(string), MAX_PACK_NAME_LEN); + strlcpy(x->asset_pack, furi_string_get_cstr(string), MAX_PACK_NAME_LEN); } furi_string_free(string); flipper_format_rewind(file); - flipper_format_read_uint32(file, "anim_speed", &xtreme_settings->anim_speed, 1); + flipper_format_read_uint32(file, "anim_speed", &x->anim_speed, 1); flipper_format_rewind(file); - flipper_format_read_int32(file, "cycle_anims", &xtreme_settings->cycle_anims, 1); + flipper_format_read_int32(file, "cycle_anims", &x->cycle_anims, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "unlock_anims", &xtreme_settings->unlock_anims, 1); + flipper_format_read_bool(file, "unlock_anims", &x->unlock_anims, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "fallback_anim", &xtreme_settings->fallback_anim, 1); + flipper_format_read_bool(file, "fallback_anim", &x->fallback_anim, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "wii_menu", &xtreme_settings->wii_menu, 1); + flipper_format_read_bool(file, "wii_menu", &x->wii_menu, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "bad_pins_format", &xtreme_settings->bad_pins_format, 1); + flipper_format_read_bool(file, "bad_pins_format", &x->bad_pins_format, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "lockscreen_time", &xtreme_settings->lockscreen_time, 1); + flipper_format_read_bool(file, "lockscreen_time", &x->lockscreen_time, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "lockscreen_seconds", &xtreme_settings->lockscreen_seconds, 1); + flipper_format_read_bool(file, "lockscreen_seconds", &x->lockscreen_seconds, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "lockscreen_date", &xtreme_settings->lockscreen_date, 1); + flipper_format_read_bool(file, "lockscreen_date", &x->lockscreen_date, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "lockscreen_statusbar", &xtreme_settings->lockscreen_statusbar, 1); + flipper_format_read_bool(file, "lockscreen_statusbar", &x->lockscreen_statusbar, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "lockscreen_prompt", &xtreme_settings->lockscreen_prompt, 1); + flipper_format_read_bool(file, "lockscreen_prompt", &x->lockscreen_prompt, 1); flipper_format_rewind(file); - flipper_format_read_uint32(file, "battery_icon", (uint32_t*)&xtreme_settings->battery_icon, 1); + flipper_format_read_uint32(file, "battery_icon", (uint32_t*)&x->battery_icon, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "status_icons", &xtreme_settings->status_icons, 1); + flipper_format_read_bool(file, "status_icons", &x->status_icons, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "bar_borders", &xtreme_settings->bar_borders, 1); + flipper_format_read_bool(file, "bar_borders", &x->bar_borders, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "bar_background", &xtreme_settings->bar_background, 1); + flipper_format_read_bool(file, "bar_background", &x->bar_background, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "sort_dirs_first", &xtreme_settings->sort_dirs_first, 1); + flipper_format_read_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "dark_mode", &xtreme_settings->dark_mode, 1); + flipper_format_read_bool(file, "dark_mode", &x->dark_mode, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "bad_bt", &xtreme_settings->bad_bt, 1); + flipper_format_read_bool(file, "bad_bt", &x->bad_bt, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "bad_bt_remember", &xtreme_settings->bad_bt_remember, 1); + flipper_format_read_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); flipper_format_rewind(file); - flipper_format_read_int32(file, "butthurt_timer", &xtreme_settings->butthurt_timer, 1); + flipper_format_read_int32(file, "butthurt_timer", &x->butthurt_timer, 1); flipper_format_rewind(file); - flipper_format_read_bool(file, "rgb_backlight", &xtreme_settings->rgb_backlight, 1); + flipper_format_read_bool(file, "rgb_backlight", &x->rgb_backlight, 1); } flipper_format_free(file); furi_record_close(RECORD_STORAGE); @@ -112,31 +113,32 @@ void XTREME_SETTINGS_SAVE() { return; } + XtremeSettings* x = xtreme_settings; Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); if(flipper_format_file_open_always(file, XTREME_SETTINGS_PATH)) { - flipper_format_write_string_cstr(file, "asset_pack", xtreme_settings->asset_pack); - flipper_format_write_uint32(file, "anim_speed", &xtreme_settings->anim_speed, 1); - flipper_format_write_int32(file, "cycle_anims", &xtreme_settings->cycle_anims, 1); - flipper_format_write_bool(file, "unlock_anims", &xtreme_settings->unlock_anims, 1); - flipper_format_write_bool(file, "fallback_anim", &xtreme_settings->fallback_anim, 1); - flipper_format_write_bool(file, "wii_menu", &xtreme_settings->wii_menu, 1); - flipper_format_write_bool(file, "bad_pins_format", &xtreme_settings->bad_pins_format, 1); - flipper_format_write_bool(file, "lockscreen_time", &xtreme_settings->lockscreen_time, 1); - flipper_format_write_bool(file, "lockscreen_seconds", &xtreme_settings->lockscreen_seconds, 1); - flipper_format_write_bool(file, "lockscreen_date", &xtreme_settings->lockscreen_date, 1); - flipper_format_write_bool(file, "lockscreen_statusbar", &xtreme_settings->lockscreen_statusbar, 1); - flipper_format_write_bool(file, "lockscreen_prompt", &xtreme_settings->lockscreen_prompt, 1); - flipper_format_write_uint32(file, "battery_icon", (uint32_t*)&xtreme_settings->battery_icon, 1); - flipper_format_write_bool(file, "status_icons", &xtreme_settings->status_icons, 1); - flipper_format_write_bool(file, "bar_borders", &xtreme_settings->bar_borders, 1); - flipper_format_write_bool(file, "bar_background", &xtreme_settings->bar_background, 1); - flipper_format_write_bool(file, "sort_dirs_first", &xtreme_settings->sort_dirs_first, 1); - flipper_format_write_bool(file, "dark_mode", &xtreme_settings->dark_mode, 1); - flipper_format_write_bool(file, "bad_bt", &xtreme_settings->bad_bt, 1); - flipper_format_write_bool(file, "bad_bt_remember", &xtreme_settings->bad_bt_remember, 1); - flipper_format_write_int32(file, "butthurt_timer", &xtreme_settings->butthurt_timer, 1); - flipper_format_write_bool(file, "rgb_backlight", &xtreme_settings->rgb_backlight, 1); + flipper_format_write_string_cstr(file, "asset_pack", x->asset_pack); + flipper_format_write_uint32(file, "anim_speed", &x->anim_speed, 1); + flipper_format_write_int32(file, "cycle_anims", &x->cycle_anims, 1); + flipper_format_write_bool(file, "unlock_anims", &x->unlock_anims, 1); + flipper_format_write_bool(file, "fallback_anim", &x->fallback_anim, 1); + flipper_format_write_bool(file, "wii_menu", &x->wii_menu, 1); + flipper_format_write_bool(file, "bad_pins_format", &x->bad_pins_format, 1); + flipper_format_write_bool(file, "lockscreen_time", &x->lockscreen_time, 1); + flipper_format_write_bool(file, "lockscreen_seconds", &x->lockscreen_seconds, 1); + flipper_format_write_bool(file, "lockscreen_date", &x->lockscreen_date, 1); + flipper_format_write_bool(file, "lockscreen_statusbar", &x->lockscreen_statusbar, 1); + flipper_format_write_bool(file, "lockscreen_prompt", &x->lockscreen_prompt, 1); + flipper_format_write_uint32(file, "battery_icon", (uint32_t*)&x->battery_icon, 1); + flipper_format_write_bool(file, "status_icons", &x->status_icons, 1); + flipper_format_write_bool(file, "bar_borders", &x->bar_borders, 1); + flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); + flipper_format_write_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); + flipper_format_write_bool(file, "dark_mode", &x->dark_mode, 1); + flipper_format_write_bool(file, "bad_bt", &x->bad_bt, 1); + flipper_format_write_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); + flipper_format_write_int32(file, "butthurt_timer", &x->butthurt_timer, 1); + flipper_format_write_bool(file, "rgb_backlight", &x->rgb_backlight, 1); } flipper_format_free(file); furi_record_close(RECORD_STORAGE); From 999c356fedc7ac459f2b4f8193b04392e8c80f71 Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Mon, 1 May 2023 12:29:58 +1000 Subject: [PATCH 217/282] update api_symbols.csv --- firmware/targets/f7/api_symbols.csv | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 2edbdb16d..3e145d32f 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,23.0,, +Version,+,23.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -744,6 +744,7 @@ Function,-,digital_sequence_free,void,DigitalSequence* Function,-,digital_sequence_send,_Bool,DigitalSequence* Function,-,digital_sequence_set_sendtime,void,"DigitalSequence*, uint32_t" Function,-,digital_sequence_set_signal,void,"DigitalSequence*, uint8_t, DigitalSignal*" +Function,-,digital_sequence_timebase_correction,void,"DigitalSequence*, float" Function,-,digital_signal_add,void,"DigitalSignal*, uint32_t" Function,-,digital_signal_add_pulse,void,"DigitalSignal*, uint32_t, _Bool" Function,-,digital_signal_alloc,DigitalSignal*,uint32_t From 3a5d8e15ebf9df1eaa839963c51eb30ed6e06c21 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 21 Dec 2022 01:06:22 +0100 Subject: [PATCH 218/282] added unit test for pulse_reader --- applications/debug/unit_tests/nfc/nfc_test.c | 150 +++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 54bdd5909..3b3a44431 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -1,10 +1,12 @@ #include #include +#include #include #include #include #include #include +#include #include #include @@ -179,6 +181,153 @@ MU_TEST(nfc_digital_signal_test) { "NFC long digital signal test failed\r\n"); } +static bool nfc_test_pulse_reader_toggle( + uint32_t usec_low, + uint32_t usec_high, + uint32_t period_count, + uint32_t tolerance) { + furi_assert(nfc_test); + + bool success = false; + uint32_t pulses = 0; + const GpioPin* gpio_in = &gpio_ext_pa6; + const GpioPin* gpio_out = &gpio_ext_pa7; + PulseReader* reader = NULL; + + do { + reader = pulse_reader_alloc(gpio_in, 512); + + if(!reader) { + FURI_LOG_E(TAG, "failed to allocate pulse reader"); + break; + } + + /* use TIM1 to create a specific number of pulses with defined duty cycle + but first set the IO to high, so the low/high pulse can get detected */ + furi_hal_gpio_init(gpio_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(gpio_out, true); + + LL_TIM_DeInit(TIM1); + + LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetRepetitionCounter(TIM1, 0); + LL_TIM_SetClockDivision(TIM1, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM1); + + LL_TIM_OC_DisablePreload(TIM1, LL_TIM_CHANNEL_CH1); + LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM2); + LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH1); + LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1N); + + LL_TIM_EnableAllOutputs(TIM1); + + /* now calculate the TIM1 period and compare values */ + uint32_t freq_div = 64 * (usec_low + usec_high); + uint32_t prescaler = freq_div / 0x10000LU; + uint32_t period = freq_div / (prescaler + 1); + uint32_t compare = 64 * usec_low / (prescaler + 1); + + LL_TIM_SetPrescaler(TIM1, prescaler); + LL_TIM_SetAutoReload(TIM1, period - 1); + LL_TIM_SetCounter(TIM1, period - 1); + LL_TIM_OC_SetCompareCH1(TIM1, compare); + + /* timer is ready to launch, now start the pulse reader */ + pulse_reader_set_timebase(reader, PulseReaderUnitMicrosecond); + pulse_reader_start(reader); + + /* and quickly enable and switch over the GPIO to the generated signal */ + LL_TIM_EnableCounter(TIM1); + furi_hal_gpio_init_ex( + gpio_out, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedVeryHigh, GpioAltFn1TIM1); + + /* now it's time to parse the pulses received by the reader */ + uint32_t timer_pulses = period_count; + uint32_t prev_cnt = 0; + + while(timer_pulses > 0) { + /* whenever the counter gets reset, we went through a full period */ + uint32_t cur_cnt = LL_TIM_GetCounter(TIM1); + if(cur_cnt < prev_cnt) { + timer_pulses--; + } + prev_cnt = cur_cnt; + } + /* quickly halt the counter to keep a static signal */ + LL_TIM_DisableCounter(TIM1); + + do { + /* as all edges were sampled asynchronously, the timeout can be zero */ + uint32_t length = pulse_reader_receive(reader, 0); + + /* in the last pulse, we expect a "no edge" return value. if seen that, test succeeded. */ + if(pulses > period_count * 2) { + if(length != PULSE_READER_NO_EDGE) { + FURI_LOG_E( + TAG, + "last pulse expected to be PULSE_READER_NO_EDGE, but was %lu.", + length); + break; + } + success = true; + break; + } + + /* else we shall never see "no edge" or "lost edge" */ + if(length == PULSE_READER_NO_EDGE) { + FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_NO_EDGE", pulses); + break; + } + if(length == PULSE_READER_LOST_EDGE) { + FURI_LOG_E(TAG, "%lu. pulse not expected to be PULSE_READER_LOST_EDGE", pulses); + break; + } + + if(pulses > 0) { + /* throw away the first pulse, which is the 1->0 from the first start and will be irrelevant for our test */ + bool phase = ((pulses - 1) % 2) == 1; + uint32_t expected = phase ? usec_high : usec_low; + uint32_t deviation = abs((int32_t)length - (int32_t)expected); + + if(deviation > tolerance) { + FURI_LOG_E( + TAG, + "%lu. pulse expected %lu, but pulse was %lu.", + pulses, + expected, + length); + break; + } + } + pulses++; + } while(true); + } while(false); + + if(reader != NULL) { + pulse_reader_stop(reader); + pulse_reader_free(reader); + } + + LL_TIM_DeInit(TIM1); + furi_hal_gpio_init_simple(gpio_in, GpioModeAnalog); + furi_hal_gpio_init_simple(gpio_out, GpioModeAnalog); + + return success; +} + +MU_TEST(nfc_pulse_reader_test) { + mu_assert(nfc_test_pulse_reader_toggle(1500, 2500, 50, 10), "1 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(10000, 10000, 10, 10), "10 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(100000, 100000, 5, 50), "100 ms signal failed\r\n"); + mu_assert(nfc_test_pulse_reader_toggle(100, 900, 50, 10), "1 ms asymmetric signal failed\r\n"); + mu_assert( + nfc_test_pulse_reader_toggle(3333, 6666, 10, 10), "10 ms asymmetric signal failed\r\n"); + mu_assert( + nfc_test_pulse_reader_toggle(25000, 75000, 5, 10), "100 ms asymmetric signal failed\r\n"); +} + MU_TEST(mf_classic_dict_test) { MfClassicDict* instance = NULL; uint64_t key = 0; @@ -513,6 +662,7 @@ MU_TEST(mf_classic_4k_7b_file_test) { MU_TEST_SUITE(nfc) { nfc_test_alloc(); + MU_RUN_TEST(nfc_pulse_reader_test); MU_RUN_TEST(nfca_file_test); MU_RUN_TEST(mf_mini_file_test); MU_RUN_TEST(mf_classic_1k_4b_file_test); From 6a95f8010fd53ffd07ebe7899ba6ec65a87ba10b Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 28 Dec 2022 18:52:12 +0100 Subject: [PATCH 219/282] added support for ISO15693 (NfcV) emulation, added support for reading SLIX tags --- applications/main/nfc/nfc.c | 3 + .../main/nfc/scenes/nfc_scene_config.h | 5 + .../main/nfc/scenes/nfc_scene_emulate_nfcv.c | 149 +++ .../main/nfc/scenes/nfc_scene_extra_actions.c | 10 + .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 166 +++- .../nfc/scenes/nfc_scene_nfcv_key_input.c | 48 + .../main/nfc/scenes/nfc_scene_nfcv_menu.c | 63 ++ .../main/nfc/scenes/nfc_scene_nfcv_unlock.c | 155 +++ .../nfc/scenes/nfc_scene_nfcv_unlock_menu.c | 60 ++ applications/main/nfc/scenes/nfc_scene_read.c | 5 + applications/main/nfc/scenes/nfc_scene_rpc.c | 7 + .../main/nfc/scenes/nfc_scene_saved_menu.c | 2 + lib/nfc/nfc_device.c | 359 ++++++- lib/nfc/nfc_device.h | 4 + lib/nfc/nfc_worker.c | 226 ++++- lib/nfc/nfc_worker.h | 7 + lib/nfc/nfc_worker_i.h | 2 + lib/nfc/protocols/nfcv.c | 885 ++++++++++++++++++ lib/nfc/protocols/nfcv.h | 213 +++++ lib/nfc/protocols/slix.c | 407 ++++++++ lib/nfc/protocols/slix.h | 46 + 21 files changed, 2795 insertions(+), 27 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_menu.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c create mode 100644 lib/nfc/protocols/nfcv.c create mode 100644 lib/nfc/protocols/nfcv.h create mode 100644 lib/nfc/protocols/slix.c create mode 100644 lib/nfc/protocols/slix.h diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 4540f5d9f..a70a52f57 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -290,6 +290,9 @@ int32_t nfc_app(void* p) { } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); DOLPHIN_DEED(DolphinDeedNfcEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); } else { diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a9da07dfd..8f1011e75 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -14,6 +14,11 @@ ADD_SCENE(nfc, file_select, FileSelect) ADD_SCENE(nfc, emulate_uid, EmulateUid) ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) ADD_SCENE(nfc, nfca_menu, NfcaMenu) +ADD_SCENE(nfc, nfcv_menu, NfcVMenu) +ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu) +ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) +ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock) +ADD_SCENE(nfc, emulate_nfcv, EmulateNfcV) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c new file mode 100644 index 000000000..e6fc60d86 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c @@ -0,0 +1,149 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (100) + +enum { + NfcSceneEmulateNfcVStateWidget, + NfcSceneEmulateNfcVStateTextBox, +}; + +bool nfc_emulate_nfcv_worker_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); + return true; +} + +void nfc_scene_emulate_nfcv_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_emulate_nfcv_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +// Add widget with device name or inform that data received +static void nfc_scene_emulate_nfcv_widget_config(Nfc* nfc, bool data_received) { + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); + widget_add_string_element( + widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating NfcV"); + if(strcmp(nfc->dev->dev_name, "")) { + furi_string_printf(info_str, "%s", nfc->dev->dev_name); + } else { + for(uint8_t i = 0; i < data->uid_len; i++) { + furi_string_cat_printf(info_str, "%02X ", data->uid[i]); + } + } + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_emulate_nfcv_widget_callback, nfc); + } +} + +void nfc_scene_emulate_nfcv_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_emulate_nfcv_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_emulate_nfcv_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_emulate_nfcv_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateNfcV); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventWorkerExit) { + // Add data button to widget if data is received for the first time + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_emulate_nfcv_widget_config(nfc, true); + } + if(strlen(nfcv_data->last_command) > 0) { + /* use the last n bytes from the log so there's enough space for the new log entry */ + size_t maxSize = + NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); + if(furi_string_size(nfc->text_box_store) >= maxSize) { + furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); + } + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + + /* clear previously logged command */ + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneEmulateNfcVStateWidget) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateTextBox); + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneEmulateNfcVStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneEmulateNfcVStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_emulate_nfcv_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 66aaf5a26..9560768a5 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -4,6 +4,7 @@ enum SubmenuIndex { SubmenuIndexReadCardType, SubmenuIndexMfClassicKeys, SubmenuIndexMfUltralightUnlock, + SubmenuIndexNfcVUnlock, }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -34,6 +35,12 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexMfUltralightUnlock, nfc_scene_extra_actions_submenu_callback, nfc); + submenu_add_item( + submenu, + "Unlock SLIX-L", + SubmenuIndexNfcVUnlock, + nfc_scene_extra_actions_submenu_callback, + nfc); submenu_set_selected_item( submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); @@ -58,6 +65,9 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + consumed = true; } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); } diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 92ad7b56e..88df6705c 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -7,6 +7,17 @@ void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType typ } } +uint32_t nfc_scene_nfc_data_info_get_key(uint8_t* data) { + uint32_t value = 0; + + for(uint32_t pos = 0; pos < 4; pos++) { + value <<= 8; + value |= data[pos]; + } + + return value; +} + void nfc_scene_nfc_data_info_on_enter(void* context) { Nfc* nfc = context; Widget* widget = nfc->widget; @@ -15,7 +26,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) || - (protocol == NfcDeviceProtocolMifareClassic)) { + (protocol == NfcDeviceProtocolMifareClassic) || (protocol == NfcDeviceProtocolNfcV)) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); text_scroll_height = 52; @@ -41,19 +52,156 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n"); + } else if(protocol == NfcDeviceProtocolNfcV) { + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "\e#ISO15693\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n"); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n"); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n"); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } } else { furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); } // Set tag iso data - char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; - furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); - furi_string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + if(protocol == NfcDeviceProtocolNfcV) { + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + + furi_string_cat_printf(temp_str, "UID:\n"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf(temp_str, "\n"); + + furi_string_cat_printf(temp_str, "DSFID: %02X\n", nfcv_data->dsfid); + furi_string_cat_printf(temp_str, "AFI: %02X\n", nfcv_data->afi); + furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref); + furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); + furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + + furi_string_cat_printf( + temp_str, "Data (%d byte)\n", nfcv_data->block_num * nfcv_data->block_size); + + int maxBlocks = nfcv_data->block_num; + if(maxBlocks > 32) { + maxBlocks = 32; + furi_string_cat_printf(temp_str, "(truncated to %d blocks)\n", maxBlocks); + } + + for(int block = 0; block < maxBlocks; block++) { + for(int pos = 0; pos < nfcv_data->block_size; pos++) { + furi_string_cat_printf( + temp_str, " %02X", nfcv_data->data[block * nfcv_data->block_size + pos]); + } + furi_string_cat_printf(temp_str, "\n"); + } + furi_string_cat_printf(temp_str, "\n"); + + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "Type: Plain\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "Type: SLIX\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " EAS %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas)); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "Type: SLIX-S\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Read %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_read)); + furi_string_cat_printf( + temp_str, + " Write %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_write)); + furi_string_cat_printf( + temp_str, + " Privacy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy)); + furi_string_cat_printf( + temp_str, + " Destroy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy)); + furi_string_cat_printf( + temp_str, + " EAS %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas)); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "Type: SLIX-L\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Privacy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy)); + furi_string_cat_printf( + temp_str, + " Destroy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy)); + furi_string_cat_printf( + temp_str, + " EAS %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas)); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "Type: SLIX2\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Read %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_read)); + furi_string_cat_printf( + temp_str, + " Write %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_write)); + furi_string_cat_printf( + temp_str, + " Privacy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_privacy)); + furi_string_cat_printf( + temp_str, + " Destroy %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_destroy)); + furi_string_cat_printf( + temp_str, + " EAS %08lX\n", + nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas)); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } + } else { + char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; + furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf( + temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); + furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); } - furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); - furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); // Set application specific data if(protocol == NfcDeviceProtocolMifareDesfire) { @@ -139,6 +287,8 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(protocol == NfcDeviceProtocolMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); + } else if(protocol == NfcDeviceProtocolNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c new file mode 100644 index 000000000..cc53c4dcb --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c @@ -0,0 +1,48 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_nfcv_key_input_byte_input_callback(void* context) { + Nfc* nfc = context; + NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; + + memcpy(data->key_privacy, nfc->byte_input_store, 4); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_nfcv_key_input_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter The Password In Hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_nfcv_key_input_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 4); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_nfcv_key_input_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c new file mode 100644 index 000000000..b30495a05 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -0,0 +1,63 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexSave, + SubmenuIndexEmulate, +}; + +void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_nfcv_menu_submenu_callback, nfc); + + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSave) { + nfc->dev->format = NfcDeviceSaveFormatNfcV; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_nfcv_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c new file mode 100644 index 000000000..b52cc0caa --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -0,0 +1,155 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneNfcVUnlockStateIdle, + NfcSceneNfcVUnlockStateDetecting, + NfcSceneNfcVUnlockStateUnlocked, + NfcSceneNfcVUnlockStateAlreadyUnlocked, + NfcSceneNfcVUnlockStateNotSupportedCard, +} NfcSceneNfcVUnlockState; + +static bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; + + if(event == NfcWorkerEventNfcVPassKey) { + memcpy(data->key_privacy, nfc->byte_input_store, 4); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + } + return true; +} + +void nfc_scene_nfcv_unlock_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { + FuriHalNfcDevData* nfc_data = &(nfc->dev->dev_data.nfc_data); + NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data); + + uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock); + if(curr_state != state) { + Popup* popup = nfc->popup; + if(state == NfcSceneNfcVUnlockStateDetecting) { + popup_reset(popup); + popup_set_text( + popup, "Put figurine on\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50); + } else if(state == NfcSceneNfcVUnlockStateUnlocked) { + popup_reset(popup); + + if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { + nfc_text_store_set( + nfc, + "%s/SLIX_%02X%02X%02X%02X%02X%02X%02X%02X%s", + NFC_APP_FOLDER, + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7], + NFC_APP_EXTENSION); + + nfc->dev->format = NfcDeviceSaveFormatNfcV; + + if(nfc_device_save(nfc->dev, nfc->text_store)) { + popup_set_header(popup, "Successfully\nsaved", 94, 3, AlignCenter, AlignTop); + } else { + popup_set_header( + popup, "Unlocked but\nsave failed!", 94, 3, AlignCenter, AlignTop); + } + } else { + popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop); + } + + notification_message(nfc->notifications, &sequence_single_vibro); + //notification_message(nfc->notifications, &sequence_success); + + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + } else if(state == NfcSceneNfcVUnlockStateAlreadyUnlocked) { + popup_reset(popup); + + popup_set_header(popup, "Already\nUnlocked!", 94, 3, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + } else if(state == NfcSceneNfcVUnlockStateNotSupportedCard) { + popup_reset(popup); + popup_set_header(popup, "Wrong Type Of Card!", 64, 3, AlignCenter, AlignTop); + popup_set_text(popup, nfcv_data->error, 4, 22, AlignLeft, AlignTop); + popup_set_icon(popup, 73, 20, &I_DolphinCommon_56x48); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock, state); + } +} + +void nfc_scene_nfcv_unlock_on_enter(void* context) { + Nfc* nfc = context; + + nfc_device_clear(nfc->dev); + // Setup view + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVUnlockAndSave, + &nfc->dev->dev_data, + nfc_scene_nfcv_unlock_worker_callback, + nfc); + + nfc_blink_read_start(nfc); +} + +bool nfc_scene_nfcv_unlock_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventAborted) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateAlreadyUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateNotSupportedCard); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneNfcVUnlockMenu); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + nfc_blink_stop(nfc); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVUnlock, NfcSceneNfcVUnlockStateIdle); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c new file mode 100644 index 000000000..9c4c81fbd --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c @@ -0,0 +1,60 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexNfcVUnlockMenuManual, + SubmenuIndexNfcVUnlockMenuTonieBox, +}; + +void nfc_scene_nfcv_unlock_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_unlock_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + submenu_add_item( + submenu, + "Enter PWD Manually", + SubmenuIndexNfcVUnlockMenuManual, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As TonieBox", + SubmenuIndexNfcVUnlockMenuTonieBox, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNfcVUnlockMenuManual) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodManual; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu, event.event); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_menu_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 938f2da67..c3f9f1334 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -68,6 +68,11 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; + } else if(event.event == NfcWorkerEventReadNfcV) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); // Set unlock password input to 0xFFFFFFFF only on fresh read diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc/scenes/nfc_scene_rpc.c index 60d01a30d..d06ee7564 100644 --- a/applications/main/nfc/scenes/nfc_scene_rpc.c +++ b/applications/main/nfc/scenes/nfc_scene_rpc.c @@ -55,6 +55,13 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { &nfc->dev->dev_data, nfc_scene_rpc_emulate_callback, nfc); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_scene_rpc_emulate_callback, + nfc); } else { nfc_worker_start( nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index ba1f96539..b7fc66995 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -116,6 +116,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 517913070..b925d651e 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -58,6 +58,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_ furi_string_set(format_string, "Mifare Classic"); } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { furi_string_set(format_string, "Mifare DESFire"); + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + furi_string_set(format_string, "ISO15693"); } else { furi_string_set(format_string, "Unknown"); } @@ -93,6 +95,11 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_st dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire; return true; } + if(furi_string_start_with_str(format_string, "ISO15693")) { + dev->format = NfcDeviceSaveFormatNfcV; + dev->dev_data.protocol = NfcDeviceProtocolNfcV; + return true; + } return false; } @@ -650,7 +657,310 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { return parsed; } -// Leave for backward compatibility +static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVData)); + + do { + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVData)); + + do { + if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVData)); + + do { + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVData)); + + do { + if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVData* data = &dev->dev_data.nfcv_data; + + do { + uint32_t temp_uint32 = 0; + uint8_t temp_uint8 = 0; + + if(!flipper_format_write_comment_cstr(file, "Data Storage Format Identifier")) break; + if(!flipper_format_write_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_write_comment_cstr(file, "Application Family Identifier")) break; + if(!flipper_format_write_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_write_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + temp_uint32 = data->block_num; + if(!flipper_format_write_comment_cstr(file, "Number of memory blocks, usually 0 to 256")) + break; + if(!flipper_format_write_uint32(file, "Block Count", &temp_uint32, 1)) break; + if(!flipper_format_write_comment_cstr(file, "Size of a single memory block, usually 4")) + break; + if(!flipper_format_write_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_write_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) + break; + if(!flipper_format_write_comment_cstr( + file, + "Subtype of this card (0 = ISO15693, 1 = SLIX, 2 = SLIX-S, 3 = SLIX-L, 4 = SLIX2)")) + break; + temp_uint8 = (uint8_t)data->sub_type; + if(!flipper_format_write_hex(file, "Subtype", &temp_uint8, 1)) break; + + switch(data->sub_type) { + case NfcVTypePlain: + if(!flipper_format_write_comment_cstr(file, "End of ISO15693 parameters")) break; + saved = true; + break; + case NfcVTypeSlix: + saved = nfc_device_save_slix_data(file, dev); + break; + case NfcVTypeSlixS: + saved = nfc_device_save_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + saved = nfc_device_save_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + saved = nfc_device_save_slix2_data(file, dev); + break; + } + } while(false); + + return saved; +} + +bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVData* data = &dev->dev_data.nfcv_data; + + memset(data, 0, sizeof(NfcVData)); + + do { + uint32_t temp_uint32 = 0; + uint8_t temp_value = 0; + + if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break; + data->block_num = temp_uint32; + if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_read_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) + break; + if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; + data->sub_type = temp_value; + + switch(data->sub_type) { + case NfcVTypePlain: + parsed = true; + break; + case NfcVTypeSlix: + parsed = nfc_device_load_slix_data(file, dev); + break; + case NfcVTypeSlixS: + parsed = nfc_device_load_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + parsed = nfc_device_load_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + parsed = nfc_device_load_slix2_data(file, dev); + break; + } + } while(false); + + return parsed; +} + +static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + EmvData* data = &dev->dev_data.emv_data; + uint32_t data_temp = 0; + + do { + // Write Bank card specific data + if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break; + if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break; + if(!flipper_format_write_string_cstr(file, "Name", data->name)) break; + if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break; + if(data->exp_mon) { + uint8_t exp_data[2] = {data->exp_mon, data->exp_year}; + if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break; + } + if(data->country_code) { + data_temp = data->country_code; + if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break; + } + if(data->currency_code) { + data_temp = data->currency_code; + if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break; + } + saved = true; + } while(false); + + return saved; +} + bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; EmvData* data = &dev->dev_data.emv_data; @@ -1069,23 +1379,32 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; // Write nfc device type if(!flipper_format_write_comment_cstr( - file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic")) + file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693")) break; nfc_device_prepare_format_string(dev, temp_str); if(!flipper_format_write_string(file, "Device type", temp_str)) break; - // Write UID, ATQA, SAK - if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) - break; + // Write UID + if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break; if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - // Save ATQA in MSB order for correct companion apps display - uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; - if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; - if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + + if(dev->format != NfcDeviceSaveFormatNfcV) { + // Write ATQA, SAK + if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break; + // Save ATQA in MSB order for correct companion apps display + uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; + if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; + if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + } + // Save more data if necessary if(dev->format == NfcDeviceSaveFormatMifareUl) { if(!nfc_device_save_mifare_ul_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_save_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + if(!nfc_device_save_nfcv_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatBankCard) { + if(!nfc_device_save_bank_card_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { // Save data if(!nfc_device_save_mifare_classic_data(file, dev)) break; @@ -1160,18 +1479,20 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_parse_format_string(dev, temp_str)) break; // Read and parse UID, ATQA and SAK if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break; - if(!(data_cnt == 4 || data_cnt == 7)) break; + if(!(data_cnt == 4 || data_cnt == 7 || data_cnt == 8)) break; data->uid_len = data_cnt; if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; - if(version == version_with_lsb_atqa) { - if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; - } else { - uint8_t atqa[2] = {}; - if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; - data->atqa[0] = atqa[1]; - data->atqa[1] = atqa[0]; + if(dev->format != NfcDeviceSaveFormatNfcV) { + if(version == version_with_lsb_atqa) { + if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; + } else { + uint8_t atqa[2] = {}; + if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; + data->atqa[0] = atqa[1]; + data->atqa[1] = atqa[0]; + } + if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; } - if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; // Load CUID uint8_t* cuid_start = data->uid; if(data->uid_len == 7) { @@ -1186,6 +1507,8 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_load_mifare_classic_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_load_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + if(!nfc_device_load_nfcv_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatBankCard) { if(!nfc_device_load_bank_card_data(file, dev)) break; } diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index df37ec3df..20df4f891 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -31,6 +32,7 @@ typedef enum { NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, + NfcDeviceProtocolNfcV } NfcProtocol; typedef enum { @@ -39,6 +41,7 @@ typedef enum { NfcDeviceSaveFormatMifareUl, NfcDeviceSaveFormatMifareClassic, NfcDeviceSaveFormatMifareDesfire, + NfcDeviceSaveFormatNfcV, } NfcDeviceSaveFormat; typedef struct { @@ -73,6 +76,7 @@ typedef struct { MfUltralightData mf_ul_data; MfClassicData mf_classic_data; MifareDesfireData mf_df_data; + NfcVData nfcv_data; }; FuriString* parsed_data; } NfcDeviceData; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 28a1f6827..28ce057fc 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -111,6 +111,12 @@ int32_t nfc_worker_task(void* context) { nfc_worker_mf_classic_dict_attack(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { nfc_worker_analyze_reader(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + nfc_worker_emulate_nfcv(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlock) { + nfc_worker_nfcv_unlock(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + nfc_worker_nfcv_unlock(nfc_worker); } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); @@ -118,6 +124,179 @@ int32_t nfc_worker_task(void* context) { return 0; } +static bool nfc_worker_read_nfcv_content(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + NfcVReader reader = {}; + + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + do { + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; + if(!nfcv_read_card(&reader, nfc_data, nfcv_data)) break; + + read_success = true; + } while(false); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + + return read_success; +} + +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + FuriHalNfcTxRxContext tx_rx = {}; + uint8_t* key_data = nfcv_data->sub_data.slix.key_privacy; + uint32_t key = 0; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + furi_hal_nfc_sleep(); + + while((nfc_worker->state == NfcWorkerStateNfcVUnlock) || + (nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) { + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_ll_txrx_on(); + furi_hal_nfc_ll_poll(); + if(furi_hal_nfc_ll_set_mode( + FuriHalNfcModePollNfcv, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48) != + FuriHalNfcReturnOk) { + break; + } + + furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER); + furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCV_POLLER); + furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); + furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCV); + + furi_hal_console_printf("Detect presence\r\n"); + ReturnCode ret = slix_get_random(nfcv_data); + + if(ret == ERR_NONE) { + /* there is some chip, responding with a RAND */ + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; + furi_hal_console_printf(" Chip detected. In privacy?\r\n"); + ret = nfcv_inventory(NULL); + + if(ret == ERR_NONE) { + /* chip is also visible, so no action required, just save */ + if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + NfcVReader reader = {}; + + if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { + furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); + snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + } else { + furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + } else { + furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + + furi_hal_console_printf( + " => chip is already visible, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + + key_data[0] = 0; + key_data[1] = 0; + key_data[2] = 0; + key_data[3] = 0; + + } else { + /* chip is invisible, try to unlock */ + furi_hal_console_printf(" chip is invisible, unlocking\r\n"); + + if(nfcv_data->auth_method == NfcVAuthMethodManual) { + key |= key_data[0] << 24; + key |= key_data[1] << 16; + key |= key_data[2] << 8; + key |= key_data[3] << 0; + + ret = slix_unlock(nfcv_data, 4); + } else { + key = 0x7FFD6E5B; + key_data[0] = key >> 24; + key_data[1] = key >> 16; + key_data[2] = key >> 8; + key_data[3] = key >> 0; + ret = slix_unlock(nfcv_data, 4); + + if(ret != ERR_NONE) { + /* main key failed, trying second one */ + furi_hal_console_printf(" trying second key after resetting\r\n"); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + if(slix_get_random(nfcv_data) != ERR_NONE) { + furi_hal_console_printf(" reset failed\r\n"); + } + + key = 0x0F0F0F0F; + key_data[0] = key >> 24; + key_data[1] = key >> 16; + key_data[2] = key >> 8; + key_data[3] = key >> 0; + ret = slix_unlock(nfcv_data, 4); + } + } + if(ret != ERR_NONE) { + /* unlock failed */ + furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); + snprintf( + nfcv_data->error, sizeof(nfcv_data->error), "Passwords not\naccepted"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + /* wait for disappearing */ + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + } + } + } else { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + } + + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_sleep(); + furi_delay_ms(100); + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MfUltralightReader reader = {}; @@ -260,6 +439,20 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t return card_read; } +static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + furi_assert(tx_rx); + + bool card_read = false; + furi_hal_nfc_sleep(); + + /* until here the UID field is reversed from the reader IC. + we will read it here again and it will get placed in the right order. */ + card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx); + + return card_read; +} + void nfc_worker_read(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); @@ -304,7 +497,12 @@ void nfc_worker_read(NfcWorker* nfc_worker) { event = NfcWorkerEventReadUidNfcF; break; } else if(nfc_data->type == FuriHalNfcTypeV) { - event = NfcWorkerEventReadUidNfcV; + FURI_LOG_I(TAG, "NfcV detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; + if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) { + FURI_LOG_I(TAG, "nfc_worker_read_nfcv success"); + } + event = NfcWorkerEventReadNfcV; break; } } else { @@ -416,6 +614,32 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { } } +void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + nfcv_emu_init(nfc_data, nfcv_data); + while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 50)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + } + } + furi_delay_ms(0); + } + nfcv_emu_deinit(nfcv_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; FuriHalNfcDevData params = { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 8e993fc6a..5113205c4 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -18,6 +18,9 @@ typedef enum { NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, + NfcWorkerStateNfcVEmulate, + NfcWorkerStateNfcVUnlock, + NfcWorkerStateNfcVUnlockAndSave, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -39,6 +42,7 @@ typedef enum { NfcWorkerEventReadMfClassicDone, NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, + NfcWorkerEventReadNfcV, // Nfc worker common events NfcWorkerEventSuccess, @@ -69,6 +73,7 @@ typedef enum { // Mifare Ultralight events NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command + NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); @@ -87,3 +92,5 @@ void nfc_worker_start( void* context); void nfc_worker_stop(NfcWorker* nfc_worker); +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); +void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker); diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 701ecb90c..b678573ec 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include struct NfcWorker { diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c new file mode 100644 index 000000000..6c205779f --- /dev/null +++ b/lib/nfc/protocols/nfcv.c @@ -0,0 +1,885 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nfcv.h" +#include "nfc_util.h" +#include "slix.h" + +#define TAG "NfcV" + +ReturnCode nfcv_inventory(uint8_t* uid) { + uint16_t received = 0; + rfalNfcvInventoryRes res; + ReturnCode ret = ERR_NONE; + + for(int tries = 0; tries < 5; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret == ERR_NONE) { + if(uid != NULL) { + memcpy(uid, res.UID, 8); + } + } + + return ret; +} + +ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* nfcv_data) { + UNUSED(reader); + + uint16_t received = 0; + for(size_t block = 0; block < nfcv_data->block_num; block++) { + uint8_t rxBuf[32]; + FURI_LOG_D(TAG, "Reading block %d/%d", block, (nfcv_data->block_num - 1)); + + ReturnCode ret = ERR_NONE; + for(int tries = 0; tries < 5; tries++) { + ret = rfalNfcvPollerReadSingleBlock( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block, rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + break; + } + } + if(ret != ERR_NONE) { + FURI_LOG_D(TAG, "failed to read: %d", ret); + return ret; + } + memcpy( + &(nfcv_data->data[block * nfcv_data->block_size]), &rxBuf[1], nfcv_data->block_size); + FURI_LOG_D( + TAG, + " %02X %02X %02X %02X", + nfcv_data->data[block * nfcv_data->block_size + 0], + nfcv_data->data[block * nfcv_data->block_size + 1], + nfcv_data->data[block * nfcv_data->block_size + 2], + nfcv_data->data[block * nfcv_data->block_size + 3]); + } + + return ERR_NONE; +} + +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + uint8_t rxBuf[32]; + uint16_t received = 0; + ReturnCode ret = ERR_NONE; + + FURI_LOG_D(TAG, "Read SYSTEM INFORMATION..."); + + for(int tries = 0; tries < 5; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerGetSystemInformation( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret == ERR_NONE) { + nfc_data->type = FuriHalNfcTypeV; + nfc_data->uid_len = 8; + /* UID is stored reversed in this response */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + nfc_data->uid[pos] = rxBuf[2 + (7 - pos)]; + } + nfcv_data->dsfid = rxBuf[10]; + nfcv_data->afi = rxBuf[11]; + nfcv_data->block_num = rxBuf[12] + 1; + nfcv_data->block_size = rxBuf[13] + 1; + nfcv_data->ic_ref = rxBuf[14]; + FURI_LOG_D( + TAG, + " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + FURI_LOG_D( + TAG, + " DSFID %d, AFI %d, Blocks %d, Size %d, IC Ref %d", + nfcv_data->dsfid, + nfcv_data->afi, + nfcv_data->block_num, + nfcv_data->block_size, + nfcv_data->ic_ref); + return ret; + } + FURI_LOG_D(TAG, "Failed: %d", ret); + + return ret; +} + +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(reader); + furi_assert(nfc_data); + furi_assert(nfcv_data); + + if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) { + return false; + } + + if(nfcv_read_blocks(reader, nfcv_data) != ERR_NONE) { + return false; + } + + if(slix_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX detected"); + nfcv_data->sub_type = NfcVTypeSlix; + } else if(slix2_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX2 detected"); + nfcv_data->sub_type = NfcVTypeSlix2; + } else if(slix_s_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-S detected"); + nfcv_data->sub_type = NfcVTypeSlixS; + } else if(slix_l_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-L detected"); + nfcv_data->sub_type = NfcVTypeSlixL; + } else { + nfcv_data->sub_type = NfcVTypePlain; + } + + return true; +} + +void nfcv_crc(uint8_t* data, uint32_t length) { + uint32_t reg = 0xFFFF; + + for(size_t i = 0; i < length; i++) { + reg = reg ^ ((uint32_t)data[i]); + for(size_t j = 0; j < 8; j++) { + if(reg & 0x0001) { + reg = (reg >> 1) ^ 0x8408; + } else { + reg = (reg >> 1); + } + } + } + + uint16_t crc = ~(uint16_t)(reg & 0xffff); + + data[length + 0] = crc & 0xFF; + data[length + 1] = crc >> 8; +} + +void nfcv_emu_free_signals(NfcVEmuAirSignals* signals) { + if(signals->nfcv_resp_one) { + digital_signal_free(signals->nfcv_resp_one); + signals->nfcv_resp_one = NULL; + } + if(signals->nfcv_resp_zero) { + digital_signal_free(signals->nfcv_resp_zero); + signals->nfcv_resp_zero = NULL; + } + if(signals->nfcv_resp_sof) { + digital_signal_free(signals->nfcv_resp_sof); + signals->nfcv_resp_sof = NULL; + } + if(signals->nfcv_resp_eof) { + digital_signal_free(signals->nfcv_resp_eof); + signals->nfcv_resp_eof = NULL; + } +} + +void nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_t slowdown) { + if(!signals->nfcv_resp_one) { + /* logical one: unmodulated then 8 pulses */ + signals->nfcv_resp_one = digital_signal_alloc(40); + for(size_t i = 0; i < slowdown; i++) { + digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_unmod); + } + for(size_t i = 0; i < slowdown * 8; i++) { + digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_pulse); + } + } + if(!signals->nfcv_resp_zero) { + /* logical zero: 8 pulses then unmodulated */ + signals->nfcv_resp_zero = digital_signal_alloc(40); + for(size_t i = 0; i < slowdown * 8; i++) { + digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_pulse); + } + for(size_t i = 0; i < slowdown; i++) { + digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_unmod); + } + } + if(!signals->nfcv_resp_sof) { + /* SOF: unmodulated, 24 pulses, logic 1 */ + signals->nfcv_resp_sof = digital_signal_alloc(160); + for(size_t i = 0; i < slowdown * 3; i++) { + digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_unmod); + } + for(size_t i = 0; i < slowdown * 24; i++) { + digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_pulse); + } + digital_signal_append(signals->nfcv_resp_sof, signals->nfcv_resp_one); + } + if(!signals->nfcv_resp_eof) { + /* EOF: logic 0, 24 pulses, unmodulated */ + signals->nfcv_resp_eof = digital_signal_alloc(160); + digital_signal_append(signals->nfcv_resp_eof, signals->nfcv_resp_zero); + for(size_t i = 0; i < slowdown * 24; i++) { + digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_pulse); + } + for(size_t i = 0; i < slowdown * 3; i++) { + digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); + } + /* add extra silence */ + digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); + } +} + +void nfcv_emu_alloc(NfcVData* nfcv_data) { + if(!nfcv_data->emu_air.nfcv_signal) { + /* assuming max frame length is 255 bytes */ + nfcv_data->emu_air.nfcv_signal = digital_sequence_alloc(8 * 255 + 2, &gpio_spi_r_mosi); + } + if(!nfcv_data->emu_air.nfcv_resp_unmod) { + /* unmodulated 256/fc or 1024/fc signal as building block */ + nfcv_data->emu_air.nfcv_resp_unmod = digital_signal_alloc(4); + nfcv_data->emu_air.nfcv_resp_unmod->start_level = false; + nfcv_data->emu_air.nfcv_resp_unmod->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_unmod->edge_cnt = 1; + } + if(!nfcv_data->emu_air.nfcv_resp_pulse) { + /* modulated fc/32 or fc/8 pulse as building block */ + nfcv_data->emu_air.nfcv_resp_pulse = digital_signal_alloc(4); + nfcv_data->emu_air.nfcv_resp_pulse->start_level = true; + nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[1] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_pulse->edge_cnt = 2; + } + + nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_high, 1); + nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_low, 4); + + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_SOF, + nfcv_data->emu_air.signals_high.nfcv_resp_sof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_BIT0, + nfcv_data->emu_air.signals_high.nfcv_resp_zero); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_BIT1, + nfcv_data->emu_air.signals_high.nfcv_resp_one); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_EOF, + nfcv_data->emu_air.signals_high.nfcv_resp_eof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_SOF, + nfcv_data->emu_air.signals_low.nfcv_resp_sof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_BIT0, + nfcv_data->emu_air.signals_low.nfcv_resp_zero); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_BIT1, + nfcv_data->emu_air.signals_low.nfcv_resp_one); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_EOF, + nfcv_data->emu_air.signals_low.nfcv_resp_eof); +} + +void nfcv_emu_free(NfcVData* nfcv_data) { + if(nfcv_data->emu_air.nfcv_resp_unmod) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_unmod); + nfcv_data->emu_air.nfcv_resp_unmod = NULL; + } + if(nfcv_data->emu_air.nfcv_resp_pulse) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse); + nfcv_data->emu_air.nfcv_resp_pulse = NULL; + } + if(nfcv_data->emu_air.nfcv_signal) { + digital_sequence_free(nfcv_data->emu_air.nfcv_signal); + nfcv_data->emu_air.nfcv_signal = NULL; + } + if(nfcv_data->emu_air.reader_signal) { + pulse_reader_free(nfcv_data->emu_air.reader_signal); + nfcv_data->emu_air.reader_signal = NULL; + } + + nfcv_emu_free_signals(&nfcv_data->emu_air.signals_high); + nfcv_emu_free_signals(&nfcv_data->emu_air.signals_low); +} + +void nfcv_emu_send( + FuriHalNfcTxRxContext* tx_rx, + NfcVData* nfcv, + uint8_t* data, + uint8_t length, + NfcVSendFlags flags, + uint32_t send_time) { + /* picked default value (0) to match the most common format */ + if(!flags) { + flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof | + NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate; + } + + if(flags & NfcVSendFlagsCrc) { + nfcv_crc(data, length); + length += 2; + } + + /* depending on the request flags, send with high or low rate */ + uint32_t bit0 = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_BIT0 : NFCV_SIG_LOW_BIT0; + uint32_t bit1 = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_BIT1 : NFCV_SIG_LOW_BIT1; + uint32_t sof = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_SOF : NFCV_SIG_LOW_SOF; + uint32_t eof = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_EOF : NFCV_SIG_LOW_EOF; + + digital_sequence_clear(nfcv->emu_air.nfcv_signal); + + if(flags & NfcVSendFlagsSof) { + digital_sequence_add(nfcv->emu_air.nfcv_signal, sof); + } + + for(int bit_total = 0; bit_total < length * 8; bit_total++) { + uint32_t byte_pos = bit_total / 8; + uint32_t bit_pos = bit_total % 8; + uint8_t bit_val = 0x01 << bit_pos; + + digital_sequence_add(nfcv->emu_air.nfcv_signal, (data[byte_pos] & bit_val) ? bit1 : bit0); + } + + if(flags & NfcVSendFlagsEof) { + digital_sequence_add(nfcv->emu_air.nfcv_signal, eof); + } + + FURI_CRITICAL_ENTER(); + digital_sequence_set_sendtime(nfcv->emu_air.nfcv_signal, send_time); + digital_sequence_send(nfcv->emu_air.nfcv_signal); + FURI_CRITICAL_EXIT(); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + + if(tx_rx->sniff_tx) { + tx_rx->sniff_tx(data, length * 8, false, tx_rx->sniff_context); + } +} + +static void nfcv_revuidcpy(uint8_t* dst, uint8_t* src) { + for(int pos = 0; pos < 8; pos++) { + dst[pos] = src[7 - pos]; + } +} + +static int nfcv_revuidcmp(uint8_t* dst, uint8_t* src) { + for(int pos = 0; pos < 8; pos++) { + if(dst[pos] != src[7 - pos]) { + return 1; + } + } + return 0; +} + +void nfcv_emu_handle_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + + if(nfcv_data->frame_length < 2) { + return; + } + + /* parse the frame data for the upcoming part 3 handling */ + ctx->flags = nfcv_data->frame[0]; + ctx->command = nfcv_data->frame[1]; + ctx->addressed = !(ctx->flags & RFAL_NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & RFAL_NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= 0xA0); + ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? 8 : 0); + ctx->response_flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof; + ctx->send_time = nfcv_data->eof_timestamp + NFCV_FDT_FC(4130); + + if(ctx->flags & RFAL_NFCV_REQ_FLAG_DATA_RATE) { + ctx->response_flags |= NfcVSendFlagsHighRate; + } + if(ctx->flags & RFAL_NFCV_REQ_FLAG_SUB_CARRIER) { + ctx->response_flags |= NfcVSendFlagsTwoSubcarrier; + } + + /* standard behavior is implemented */ + if(ctx->addressed) { + uint8_t* address = &nfcv_data->frame[ctx->address_offset]; + if(nfcv_revuidcmp(address, nfc_data->uid)) { + FURI_LOG_D(TAG, "addressed command 0x%02X, but not for us:", ctx->command); + FURI_LOG_D( + TAG, + " dest: %02X%02X%02X%02X%02X%02X%02X%02X", + address[7], + address[6], + address[5], + address[4], + address[3], + address[2], + address[1], + address[0]); + FURI_LOG_D( + TAG, + " our UID: %02X%02X%02X%02X%02X%02X%02X%02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + return; + } + } + + /* then give control to the card subtype specific protocol filter */ + if(ctx->emu_protocol_filter != NULL) { + if(ctx->emu_protocol_filter(tx_rx, nfc_data, nfcv_data)) { + if(strlen(nfcv_data->last_command) > 0) { + FURI_LOG_D( + TAG, "Received command %s (handled by filter)", nfcv_data->last_command); + } + return; + } + } + + switch(ctx->command) { + case ISO15693_INVENTORY: { + ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[1] = nfcv_data->dsfid; + nfcv_revuidcpy(&ctx->response_buffer[2], nfc_data->uid); + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 10, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + break; + } + + case ISO15693_STAYQUIET: { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "STAYQUIET"); + break; + } + + case ISO15693_LOCKBLOCK: { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCKBLOCK"); + break; + } + + case ISO15693_SELECT: { + ctx->response_buffer[0] = ISO15693_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SELECT"); + break; + } + + case ISO15693_RESET_TO_READY: { + ctx->response_buffer[0] = ISO15693_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "RESET_TO_READY"); + break; + } + + case ISO15693_READ_MULTI_BLOCK: + case ISO15693_READBLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + + if(ctx->command == ISO15693_READ_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + } + + if(block + blocks > nfcv_data->block_num) { + ctx->response_buffer[0] = ISO15693_ERROR_CMD_NOT_REC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } else { + ctx->response_buffer[0] = ISO15693_NOERROR; + memcpy( + &ctx->response_buffer[1], + &nfcv_data->data[nfcv_data->block_size * block], + nfcv_data->block_size * blocks); + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + 1 + nfcv_data->block_size * blocks, + ctx->response_flags, + ctx->send_time); + } + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); + break; + } + + case ISO15693_WRITE_MULTI_BLOCK: + case ISO15693_WRITEBLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + uint8_t data_pos = 1; + + if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[ctx->payload_offset + data_pos]; + uint32_t data_len = nfcv_data->block_size * blocks; + + if(block + blocks > nfcv_data->block_num || + ctx->payload_offset + data_len + 2 > nfcv_data->frame_length) { + ctx->response_buffer[0] = ISO15693_ERROR_CMD_NOT_REC; + } else { + ctx->response_buffer[0] = ISO15693_NOERROR; + memcpy( + &nfcv_data->data[nfcv_data->block_size * block], + &nfcv_data->frame[ctx->payload_offset + data_pos], + data_len); + } + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + + if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE MULTI BLOCK %d, %d blocks", + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE BLOCK %d <- %02X %02X %02X %02X", + block, + data[0], + data[1], + data[2], + data[3]); + } + break; + } + + case ISO15693_GET_SYSTEM_INFO: { + ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[1] = 0x0F; + nfcv_revuidcpy(&ctx->response_buffer[2], nfc_data->uid); + ctx->response_buffer[10] = nfcv_data->dsfid; /* DSFID */ + ctx->response_buffer[11] = nfcv_data->afi; /* AFI */ + ctx->response_buffer[12] = nfcv_data->block_num - 1; /* number of blocks */ + ctx->response_buffer[13] = nfcv_data->block_size - 1; /* block size */ + ctx->response_buffer[14] = nfcv_data->ic_ref; /* IC reference */ + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 15, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO"); + break; + } + + default: + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "unsupported: %02X", + ctx->command); + break; + } + + if(strlen(nfcv_data->last_command) > 0) { + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); + } +} + +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + nfcv_emu_alloc(nfcv_data); + rfal_platform_spi_acquire(); + /* configure for transparent and passive mode */ + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); + /* set enable, rx_enable and field detector enable */ + st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3); + /* target mode: ISO14443 passive mode */ + st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88); + /* let us modulate the field using MOSI, read modulation using MISO */ + st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + + /* if not set already, initialize the default protocol handler */ + if(!nfcv_data->emu_protocol_ctx) { + nfcv_data->emu_protocol_ctx = malloc(sizeof(NfcVEmuProtocolCtx)); + nfcv_data->emu_protocol_handler = &nfcv_emu_handle_packet; + } + + FURI_LOG_D(TAG, "Starting NfcV emulation"); + FURI_LOG_D( + TAG, + " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + + switch(nfcv_data->sub_type) { + case NfcVTypeSlixL: + FURI_LOG_D(TAG, " Card type: SLIX-L"); + slix_l_prepare(nfcv_data); + break; + case NfcVTypeSlixS: + FURI_LOG_D(TAG, " Card type: SLIX-S"); + slix_s_prepare(nfcv_data); + break; + case NfcVTypeSlix2: + FURI_LOG_D(TAG, " Card type: SLIX2"); + slix2_prepare(nfcv_data); + break; + case NfcVTypeSlix: + FURI_LOG_D(TAG, " Card type: SLIX"); + slix_prepare(nfcv_data); + break; + case NfcVTypePlain: + FURI_LOG_D(TAG, " Card type: Plain"); + break; + } + + /* allocate a 512 edge buffer, more than enough */ + nfcv_data->emu_air.reader_signal = pulse_reader_alloc(&gpio_nfc_irq_rfid_pull, 512); + /* timebase shall be 1 ns */ + pulse_reader_set_timebase(nfcv_data->emu_air.reader_signal, PulseReaderUnitNanosecond); + /* and configure to already calculate the number of bits */ + pulse_reader_set_bittime(nfcv_data->emu_air.reader_signal, PULSE_DURATION_NS); + /* this IO is fed into the µC via a diode, so we need a pulldown */ + pulse_reader_set_pull(nfcv_data->emu_air.reader_signal, GpioPullDown); + + /* start sampling */ + pulse_reader_start(nfcv_data->emu_air.reader_signal); +} + +void nfcv_emu_deinit(NfcVData* nfcv_data) { + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + rfal_platform_spi_release(); + nfcv_emu_free(nfcv_data); + + if(nfcv_data->emu_protocol_ctx) { + free(nfcv_data->emu_protocol_ctx); + nfcv_data->emu_protocol_ctx = NULL; + } + + /* set registers back to how we found them */ + st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0x00); + st25r3916WriteRegister(ST25R3916_REG_MODE, 0x08); +} + +bool nfcv_emu_loop( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data, + uint32_t timeout_ms) { + bool ret = false; + uint32_t frame_state = NFCV_FRAME_STATE_SOF1; + uint32_t periods_previous = 0; + uint8_t frame_payload[128]; + uint32_t frame_pos = 0; + uint32_t byte_value = 0; + uint32_t bits_received = 0; + char reset_reason[128]; + bool wait_for_pulse = false; + + while(true) { + uint32_t periods = + pulse_reader_receive(nfcv_data->emu_air.reader_signal, timeout_ms * 1000); + uint32_t timestamp = DWT->CYCCNT; + + if(periods == PULSE_READER_NO_EDGE) { + break; + } + if(periods == PULSE_READER_LOST_EDGE) { + break; + } + + if(wait_for_pulse) { + wait_for_pulse = false; + if(periods != 1) { + snprintf( + reset_reason, + sizeof(reset_reason), + "SOF: Expected a single low pulse in state %lu, but got %lu", + frame_state, + periods); + frame_state = NFCV_FRAME_STATE_RESET; + } + continue; + } + + switch(frame_state) { + case NFCV_FRAME_STATE_SOF1: + if(periods == 1) { + frame_state = NFCV_FRAME_STATE_SOF2; + } else { + frame_state = NFCV_FRAME_STATE_SOF1; + break; + } + break; + + case NFCV_FRAME_STATE_SOF2: + /* waiting for the second low period, telling us about coding */ + if(periods == 6) { + frame_state = NFCV_FRAME_STATE_CODING_256; + periods_previous = 0; + wait_for_pulse = true; + } else if(periods == 4) { + frame_state = NFCV_FRAME_STATE_CODING_4; + periods_previous = 2; + wait_for_pulse = true; + } else { + snprintf( + reset_reason, + sizeof(reset_reason), + "SOF: Expected 4/6 periods, got %lu", + periods); + frame_state = NFCV_FRAME_STATE_SOF1; + } + break; + + case NFCV_FRAME_STATE_CODING_256: + if(periods_previous > periods) { + snprintf( + reset_reason, + sizeof(reset_reason), + "1oo256: Missing %lu periods from previous symbol, got %lu", + periods_previous, + periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + + if(periods > 512) { + snprintf( + reset_reason, sizeof(reset_reason), "1oo256: %lu periods is too much", periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } + + periods_previous = 512 - (periods + 1); + byte_value = (periods - 1) / 2; + frame_payload[frame_pos++] = (uint8_t)byte_value; + + wait_for_pulse = true; + + break; + + case NFCV_FRAME_STATE_CODING_4: + if(periods_previous > periods) { + snprintf( + reset_reason, + sizeof(reset_reason), + "1oo4: Missing %lu periods from previous symbol, got %lu at pos %lu", + periods_previous, + periods, + frame_pos); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + periods_previous = 0; + + byte_value >>= 2; + bits_received += 2; + + if(periods == 1) { + byte_value |= 0x00 << 6; + periods_previous = 6; + } else if(periods == 3) { + byte_value |= 0x01 << 6; + periods_previous = 4; + } else if(periods == 5) { + byte_value |= 0x02 << 6; + periods_previous = 2; + } else if(periods == 7) { + byte_value |= 0x03 << 6; + periods_previous = 0; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } else { + snprintf( + reset_reason, + sizeof(reset_reason), + "1oo4: Expected 1/3/5/7 low pulses, but got %lu at pos %lu", + periods, + frame_pos); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(bits_received >= 8) { + frame_payload[frame_pos++] = (uint8_t)byte_value; + bits_received = 0; + } + wait_for_pulse = true; + break; + } + + /* post-state-machine cleanup and reset */ + if(frame_state == NFCV_FRAME_STATE_RESET) { + frame_state = NFCV_FRAME_STATE_SOF1; + + FURI_LOG_D(TAG, "Resetting state machine, reason: '%s'", reset_reason); + } else if(frame_state == NFCV_FRAME_STATE_EOF) { + nfcv_data->frame = frame_payload; + nfcv_data->frame_length = frame_pos; + nfcv_data->eof_timestamp = timestamp; + break; + } + } + + if(frame_state == NFCV_FRAME_STATE_EOF) { + /* we know that this code uses TIM2, so stop pulse reader */ + pulse_reader_stop(nfcv_data->emu_air.reader_signal); + if(tx_rx->sniff_rx) { + tx_rx->sniff_rx(frame_payload, frame_pos * 8, false, tx_rx->sniff_context); + } + nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data); + pulse_reader_start(nfcv_data->emu_air.reader_signal); + ret = true; + } + + return ret; +} diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h new file mode 100644 index 000000000..9d7a56326 --- /dev/null +++ b/lib/nfc/protocols/nfcv.h @@ -0,0 +1,213 @@ +#pragma once + +#include +#include + +#include +#include +#include "nfc_util.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFCV_FC (13560000.0f) /* MHz */ +#define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC / 32) / 2.0f) /* 1.1799 µs */ +#define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */ + +#define PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) /* ns */ + +#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) +#define DIGITAL_SIGNAL_UNIT_US (100000.0f) + +#define NFCV_TOTAL_BLOCKS_MAX 256 +#define NFCV_BLOCK_SIZE 4 +#define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE * NFCV_TOTAL_BLOCKS_MAX) + +/* helpers to calculate the send time based on DWT->CYCCNT */ +#define NFCV_FDT_USEC(usec) (usec * 64) +#define NFCV_FDT_FC(ticks) (ticks * 6400 / 1356) + +#define NFCV_FRAME_STATE_SOF1 0 +#define NFCV_FRAME_STATE_SOF2 1 +#define NFCV_FRAME_STATE_CODING_4 2 +#define NFCV_FRAME_STATE_CODING_256 3 +#define NFCV_FRAME_STATE_EOF 4 +#define NFCV_FRAME_STATE_RESET 5 + +/* sequences for every section of a frame */ +#define NFCV_SIG_SOF 0 +#define NFCV_SIG_BIT0 1 +#define NFCV_SIG_BIT1 2 +#define NFCV_SIG_EOF 3 +#define NFCV_SIG_LOW_SOF 4 +#define NFCV_SIG_LOW_BIT0 5 +#define NFCV_SIG_LOW_BIT1 6 +#define NFCV_SIG_LOW_EOF 7 + +/* ISO15693 command codes */ +#define ISO15693_INVENTORY 0x01 +#define ISO15693_STAYQUIET 0x02 +#define ISO15693_READBLOCK 0x20 +#define ISO15693_WRITEBLOCK 0x21 +#define ISO15693_LOCKBLOCK 0x22 +#define ISO15693_READ_MULTI_BLOCK 0x23 +#define ISO15693_WRITE_MULTI_BLOCK 0x24 +#define ISO15693_SELECT 0x25 +#define ISO15693_RESET_TO_READY 0x26 +#define ISO15693_WRITE_AFI 0x27 +#define ISO15693_LOCK_AFI 0x28 +#define ISO15693_WRITE_DSFID 0x29 +#define ISO15693_LOCK_DSFID 0x2A +#define ISO15693_GET_SYSTEM_INFO 0x2B +#define ISO15693_READ_MULTI_SECSTATUS 0x2C + +/* ISO15693 RESPONSE ERROR CODES */ +#define ISO15693_NOERROR 0x00 +#define ISO15693_ERROR_CMD_NOT_SUP 0x01 // Command not supported +#define ISO15693_ERROR_CMD_NOT_REC 0x02 // Command not recognized (eg. parameter error) +#define ISO15693_ERROR_CMD_OPTION 0x03 // Command option not supported +#define ISO15693_ERROR_GENERIC 0x0F // No additional Info about this error +#define ISO15693_ERROR_BLOCK_UNAVAILABLE 0x10 +#define ISO15693_ERROR_BLOCK_LOCKED_ALREADY 0x11 // cannot lock again +#define ISO15693_ERROR_BLOCK_LOCKED 0x12 // cannot be changed +#define ISO15693_ERROR_BLOCK_WRITE 0x13 // Writing was unsuccessful +#define ISO15693_ERROR_BLOCL_WRITELOCK 0x14 // Locking was unsuccessful + +typedef enum { + NfcVAuthMethodManual, + NfcVAuthMethodTonieBox, +} NfcVAuthMethod; + +typedef enum { + NfcVTypePlain = 0, + NfcVTypeSlix = 1, + NfcVTypeSlixS = 2, + NfcVTypeSlixL = 3, + NfcVTypeSlix2 = 4, +} NfcVSubtype; + +typedef enum { + NfcVSendFlagsNormal = 0, + NfcVSendFlagsSof = 1 << 0, + NfcVSendFlagsCrc = 1 << 1, + NfcVSendFlagsEof = 1 << 2, + NfcVSendFlagsOneSubcarrier = 0, + NfcVSendFlagsTwoSubcarrier = 1 << 3, + NfcVSendFlagsLowRate = 0, + NfcVSendFlagsHighRate = 1 << 4 +} NfcVSendFlags; + +typedef struct { + uint8_t key_read[4]; + uint8_t key_write[4]; + uint8_t key_privacy[4]; + uint8_t key_destroy[4]; + uint8_t key_eas[4]; + uint8_t rand[2]; + bool privacy; +} NfcVSlixData; + +typedef union { + NfcVSlixData slix; +} NfcVSubtypeData; + +typedef struct { + DigitalSignal* nfcv_resp_sof; + DigitalSignal* nfcv_resp_one; + DigitalSignal* nfcv_resp_zero; + DigitalSignal* nfcv_resp_eof; +} NfcVEmuAirSignals; + +typedef struct { + PulseReader* reader_signal; + DigitalSignal* nfcv_resp_pulse; /* pulse length, fc/32 */ + DigitalSignal* nfcv_resp_unmod; /* unmodulated length 256/fc */ + NfcVEmuAirSignals signals_high; + NfcVEmuAirSignals signals_low; + DigitalSequence* nfcv_signal; +} NfcVEmuAir; + +typedef void (*NfcVEmuProtocolHandler)( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data); +typedef bool (*NfcVEmuProtocolFilter)( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data); + +typedef struct { + uint8_t flags; /* ISO15693-3 flags of the header as specified */ + uint8_t command; /* ISO15693-3 command at offset 1 as specified */ + bool addressed; /* ISO15693-3 flags: addressed frame */ + bool advanced; /* ISO15693-3 command: advanced command */ + uint8_t address_offset; /* ISO15693-3 offset of the address in frame, if addressed is set */ + uint8_t payload_offset; /* ISO15693-3 offset of the payload in frame */ + + uint8_t response_buffer[128]; /* pre-allocated response buffer */ + NfcVSendFlags response_flags; /* flags to use when sending response */ + uint32_t send_time; /* timestamp when to send the response */ + + NfcVEmuProtocolFilter emu_protocol_filter; +} NfcVEmuProtocolCtx; + +typedef struct { + /* common ISO15693 fields, being specified in ISO15693-3 */ + uint8_t dsfid; + uint8_t afi; + uint8_t ic_ref; + uint16_t block_num; + uint8_t block_size; + uint8_t data[NFCV_MAX_DUMP_SIZE]; + + /* specfic variant infos */ + NfcVSubtype sub_type; + NfcVSubtypeData sub_data; + NfcVAuthMethod auth_method; + + /* precalced air level data */ + NfcVEmuAir emu_air; + + uint8_t* frame; /* ISO15693-2 incoming raw data from air layer */ + uint8_t frame_length; /* ISO15693-2 length of incoming data */ + uint32_t eof_timestamp; /* ISO15693-2 EOF timestamp, read from DWT->CYCCNT */ + + /* handler for the protocol layer as specified in ISO15693-3 */ + NfcVEmuProtocolHandler emu_protocol_handler; + void* emu_protocol_ctx; + + /* runtime data */ + char last_command[128]; + char error[32]; +} NfcVData; + +typedef struct { + uint16_t blocks_to_read; + int16_t blocks_read; +} NfcVReader; + +ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data); +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data); +ReturnCode nfcv_inventory(uint8_t* uid); +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data); + +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); +void nfcv_emu_deinit(NfcVData* nfcv_data); +bool nfcv_emu_loop( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data, + uint32_t timeout_ms); +void nfcv_emu_send( + FuriHalNfcTxRxContext* tx_rx, + NfcVData* nfcv, + uint8_t* data, + uint8_t length, + NfcVSendFlags flags, + uint32_t send_time); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c new file mode 100644 index 000000000..e61c70919 --- /dev/null +++ b/lib/nfc/protocols/slix.c @@ -0,0 +1,407 @@ + +#include +#include "nfcv.h" +#include "slix.h" +#include "nfc_util.h" +#include +#include "furi_hal_nfc.h" +#include + +#define TAG "SLIX" + +static uint32_t slix_read_be(uint8_t* data, uint32_t length) { + uint32_t value = 0; + + for(uint32_t pos = 0; pos < length; pos++) { + value <<= 8; + value |= data[pos]; + } + + return value; +} + +uint8_t slix_get_ti(FuriHalNfcDevData* nfc_data) { + return (nfc_data->uid[3] >> 3) & 3; +} + +bool slix_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) && + slix_get_ti(nfc_data) == 2) { + return true; + } + return false; +} + +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) && + slix_get_ti(nfc_data) == 1) { + return true; + } + return false; +} + +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x02)) { + return true; + } + return false; +} + +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x03)) { + return true; + } + return false; +} + +ReturnCode slix_get_random(NfcVData* data) { + uint16_t received = 0; + uint8_t rxBuf[32]; + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + ISO15693_CMD_NXP_GET_RANDOM_NUMBER, + RFAL_NFCV_REQ_FLAG_DEFAULT, + ISO15693_MANUFACTURER_NXP, + NULL, + NULL, + 0, + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + if(received != 3) { + return ERR_PROTO; + } + if(data != NULL) { + data->sub_data.slix.rand[0] = rxBuf[2]; + data->sub_data.slix.rand[1] = rxBuf[1]; + } + } + + return ret; +} + +ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) { + furi_assert(rand); + + uint16_t received = 0; + uint8_t rxBuf[32]; + uint8_t cmd_set_pass[] = { + password_id, + data->sub_data.slix.rand[1], + data->sub_data.slix.rand[0], + data->sub_data.slix.rand[1], + data->sub_data.slix.rand[0]}; + uint8_t* password = NULL; + + switch(password_id) { + case SLIX_PASS_READ: + password = data->sub_data.slix.key_read; + break; + case SLIX_PASS_WRITE: + password = data->sub_data.slix.key_write; + break; + case SLIX_PASS_PRIVACY: + password = data->sub_data.slix.key_privacy; + break; + case SLIX_PASS_DESTROY: + password = data->sub_data.slix.key_destroy; + break; + case SLIX_PASS_EASAFI: + password = data->sub_data.slix.key_eas; + break; + default: + break; + } + + if(!password) { + return ERR_NOTSUPP; + } + + for(int pos = 0; pos < 4; pos++) { + cmd_set_pass[1 + pos] ^= password[3 - pos]; + } + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + ISO15693_CMD_NXP_SET_PASSWORD, + RFAL_NFCV_REQ_FLAG_DATA_RATE, + ISO15693_MANUFACTURER_NXP, + NULL, + cmd_set_pass, + sizeof(cmd_set_pass), + rxBuf, + sizeof(rxBuf), + &received); + + return ret; +} + +bool slix_generic_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in, + uint32_t password_supported) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + + if(slix->privacy && ctx->command != ISO15693_CMD_NXP_GET_RANDOM_NUMBER && + ctx->command != ISO15693_CMD_NXP_SET_PASSWORD) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "command 0x%02X ignored, privacy mode", + ctx->command); + FURI_LOG_D(TAG, "%s", nfcv_data->last_command); + return true; + } + + bool handled = false; + + switch(ctx->command) { + case ISO15693_CMD_NXP_GET_RANDOM_NUMBER: { + slix->rand[0] = furi_hal_random_get(); + slix->rand[1] = furi_hal_random_get(); + + ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[1] = slix->rand[1]; + ctx->response_buffer[2] = slix->rand[0]; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 3, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "GET_RANDOM_NUMBER -> 0x%02X%02X", + slix->rand[0], + slix->rand[1]); + + handled = true; + break; + } + + case ISO15693_CMD_NXP_SET_PASSWORD: { + uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; + + if(!(password_id & password_supported)) { + break; + } + + uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; + uint8_t* rand = slix->rand; + uint8_t* password = NULL; + uint8_t password_rcv[4]; + + switch(password_id) { + case SLIX_PASS_READ: + password = slix->key_read; + break; + case SLIX_PASS_WRITE: + password = slix->key_write; + break; + case SLIX_PASS_PRIVACY: + password = slix->key_privacy; + break; + case SLIX_PASS_DESTROY: + password = slix->key_destroy; + break; + case SLIX_PASS_EASAFI: + password = slix->key_eas; + break; + default: + break; + } + + for(int pos = 0; pos < 4; pos++) { + password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; + } + uint32_t pass_expect = slix_read_be(password, 4); + uint32_t pass_received = slix_read_be(password_rcv, 4); + + /* if the password is all-zeroes, just accept any password*/ + if(!pass_expect || pass_expect == pass_received) { + switch(password_id) { + case SLIX_PASS_READ: + break; + case SLIX_PASS_WRITE: + break; + case SLIX_PASS_PRIVACY: + slix->privacy = false; + break; + case SLIX_PASS_DESTROY: + FURI_LOG_D(TAG, "Pooof! Got destroyed"); + break; + case SLIX_PASS_EASAFI: + break; + default: + break; + } + ctx->response_buffer[0] = ISO15693_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "SET_PASSWORD #%02X 0x%08lX OK", + password_id, + pass_received); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "SET_PASSWORD #%02X 0x%08lX/%08lX FAIL", + password_id, + pass_received, + pass_expect); + } + handled = true; + break; + } + + case ISO15693_CMD_NXP_ENABLE_PRIVACY: { + ctx->response_buffer[0] = ISO15693_NOERROR; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "ISO15693_CMD_NXP_ENABLE_PRIVACY"); + + slix->privacy = true; + handled = true; + break; + } + } + + return handled; +} + +bool slix_l_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter( + tx_rx, + nfc_data, + nfcv_data_in, + SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI)) { + return true; + } + + return handled; +} + +void slix_l_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_l_protocol_filter; +} + +bool slix_s_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) { + return true; + } + + return handled; +} + +void slix_s_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_s_protocol_filter; +} + +bool slix_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_EASAFI)) { + return true; + } + + return handled; +} + +void slix_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_protocol_filter; +} + +bool slix2_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) { + return true; + } + + return handled; +} + +void slix2_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix2_protocol_filter; +} diff --git a/lib/nfc/protocols/slix.h b/lib/nfc/protocols/slix.h new file mode 100644 index 000000000..719fe6f43 --- /dev/null +++ b/lib/nfc/protocols/slix.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include "nfc_util.h" +#include + +#define ISO15693_MANUFACTURER_NXP 0x04 + +/* ISO15693-3 CUSTOM NXP COMMANDS */ +#define ISO15693_CMD_NXP_SET_EAS 0xA2 +#define ISO15693_CMD_NXP_RESET_EAS 0xA3 +#define ISO15693_CMD_NXP_LOCK_EAS 0xA4 +#define ISO15693_CMD_NXP_EAS_ALARM 0xA5 +#define ISO15693_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 +#define ISO15693_CMD_NXP_WRITE_EAS_ID 0xA7 +#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ 0xB0 +#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 +#define ISO15693_CMD_NXP_GET_RANDOM_NUMBER 0xB2 +#define ISO15693_CMD_NXP_SET_PASSWORD 0xB3 +#define ISO15693_CMD_NXP_WRITE_PASSWORD 0xB4 +#define ISO15693_CMD_NXP_DESTROY 0xB9 +#define ISO15693_CMD_NXP_ENABLE_PRIVACY 0xBA + +/* available passwords */ +#define SLIX_PASS_READ 0x01 +#define SLIX_PASS_WRITE 0x02 +#define SLIX_PASS_PRIVACY 0x04 +#define SLIX_PASS_DESTROY 0x08 +#define SLIX_PASS_EASAFI 0x10 + +#define SLIX_PASS_ALL \ + (SLIX_PASS_READ | SLIX_PASS_WRITE | SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI) + +bool slix_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); + +ReturnCode slix_get_random(NfcVData* data); +ReturnCode slix_unlock(NfcVData* data, uint32_t password_id); + +void slix_prepare(NfcVData* nfcv_data); +void slix_s_prepare(NfcVData* nfcv_data); +void slix_l_prepare(NfcVData* nfcv_data); +void slix2_prepare(NfcVData* nfcv_data); From 423c551cf143460590940005e9c0c894858e59f3 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Thu, 29 Dec 2022 20:53:21 +0100 Subject: [PATCH 220/282] SLIX: fixed crash situation when an invalid password was requested --- lib/nfc/protocols/slix.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c index e61c70919..6c6deacdd 100644 --- a/lib/nfc/protocols/slix.c +++ b/lib/nfc/protocols/slix.c @@ -217,6 +217,10 @@ bool slix_generic_protocol_filter( break; } + if(!password) { + break; + } + for(int pos = 0; pos < 4; pos++) { password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; } From 5d14755302e9d24c1a7d781d31945f95478804c8 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Thu, 29 Dec 2022 21:50:05 +0100 Subject: [PATCH 221/282] ISO15693: show emulate menu when opening file --- applications/main/nfc/scenes/nfc_scene_saved_menu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index b7fc66995..0f49c4e4b 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -43,6 +43,7 @@ void nfc_scene_saved_menu_on_enter(void* context) { } } else if( nfc->dev->format == NfcDeviceSaveFormatMifareUl || + nfc->dev->format == NfcDeviceSaveFormatNfcV || nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); From 2c313edcdb3621047e4fa0aed929d031e662875e Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Thu, 29 Dec 2022 22:02:02 +0100 Subject: [PATCH 222/282] rename NfcV emulate scene to match other NfcV names --- applications/main/nfc/nfc.c | 2 +- .../main/nfc/scenes/nfc_scene_config.h | 2 +- ...mulate_nfcv.c => nfc_scene_nfcv_emulate.c} | 42 +++++++++---------- .../main/nfc/scenes/nfc_scene_nfcv_menu.c | 4 +- .../main/nfc/scenes/nfc_scene_nfcv_unlock.c | 13 +++--- .../main/nfc/scenes/nfc_scene_saved_menu.c | 2 +- 6 files changed, 32 insertions(+), 33 deletions(-) rename applications/main/nfc/scenes/{nfc_scene_emulate_nfcv.c => nfc_scene_nfcv_emulate.c} (77%) diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index a70a52f57..f68b7f2f2 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -291,7 +291,7 @@ int32_t nfc_app(void* p) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); DOLPHIN_DEED(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); DOLPHIN_DEED(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 8f1011e75..b4d5a6e1d 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -18,7 +18,7 @@ ADD_SCENE(nfc, nfcv_menu, NfcVMenu) ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu) ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock) -ADD_SCENE(nfc, emulate_nfcv, EmulateNfcV) +ADD_SCENE(nfc, nfcv_emulate, NfcVEmulate) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c similarity index 77% rename from applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c rename to applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c index e6fc60d86..53da36679 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c @@ -3,11 +3,11 @@ #define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (100) enum { - NfcSceneEmulateNfcVStateWidget, - NfcSceneEmulateNfcVStateTextBox, + NfcSceneNfcVEmulateStateWidget, + NfcSceneNfcVEmulateStateTextBox, }; -bool nfc_emulate_nfcv_worker_callback(NfcWorkerEvent event, void* context) { +bool nfc_scene_nfcv_emulate_worker_callback(NfcWorkerEvent event, void* context) { UNUSED(event); furi_assert(context); Nfc* nfc = context; @@ -15,7 +15,7 @@ bool nfc_emulate_nfcv_worker_callback(NfcWorkerEvent event, void* context) { return true; } -void nfc_scene_emulate_nfcv_widget_callback(GuiButtonType result, InputType type, void* context) { +void nfc_scene_nfcv_emulate_widget_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); Nfc* nfc = context; if(type == InputTypeShort) { @@ -23,14 +23,14 @@ void nfc_scene_emulate_nfcv_widget_callback(GuiButtonType result, InputType type } } -void nfc_emulate_nfcv_textbox_callback(void* context) { +void nfc_scene_nfcv_emulate_textbox_callback(void* context) { furi_assert(context); Nfc* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); } // Add widget with device name or inform that data received -static void nfc_scene_emulate_nfcv_widget_config(Nfc* nfc, bool data_received) { +static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) { FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; Widget* widget = nfc->widget; widget_reset(widget); @@ -53,15 +53,15 @@ static void nfc_scene_emulate_nfcv_widget_config(Nfc* nfc, bool data_received) { furi_string_free(info_str); if(data_received) { widget_add_button_element( - widget, GuiButtonTypeCenter, "Log", nfc_scene_emulate_nfcv_widget_callback, nfc); + widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_emulate_widget_callback, nfc); } } -void nfc_scene_emulate_nfcv_on_enter(void* context) { +void nfc_scene_nfcv_emulate_on_enter(void* context) { Nfc* nfc = context; // Setup Widget - nfc_scene_emulate_nfcv_widget_config(nfc, false); + nfc_scene_nfcv_emulate_widget_config(nfc, false); // Setup TextBox TextBox* text_box = nfc->text_box; text_box_set_font(text_box, TextBoxFontHex); @@ -70,7 +70,7 @@ void nfc_scene_emulate_nfcv_on_enter(void* context) { // Set Widget state and view scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); // Start worker memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); @@ -78,23 +78,23 @@ void nfc_scene_emulate_nfcv_on_enter(void* context) { nfc->worker, NfcWorkerStateNfcVEmulate, &nfc->dev->dev_data, - nfc_emulate_nfcv_worker_callback, + nfc_scene_nfcv_emulate_worker_callback, nfc); nfc_blink_emulate_start(nfc); } -bool nfc_scene_emulate_nfcv_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_nfcv_emulate_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateNfcV); + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVEmulate); bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventWorkerExit) { // Add data button to widget if data is received for the first time if(!furi_string_size(nfc->text_box_store)) { - nfc_scene_emulate_nfcv_widget_config(nfc, true); + nfc_scene_nfcv_emulate_widget_config(nfc, true); } if(strlen(nfcv_data->last_command) > 0) { /* use the last n bytes from the log so there's enough space for the new log entry */ @@ -111,22 +111,22 @@ bool nfc_scene_emulate_nfcv_on_event(void* context, SceneManagerEvent event) { strcpy(nfcv_data->last_command, ""); } consumed = true; - } else if(event.event == GuiButtonTypeCenter && state == NfcSceneEmulateNfcVStateWidget) { + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVEmulateStateWidget) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateTextBox); + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateTextBox); consumed = true; - } else if(event.event == NfcCustomEventViewExit && state == NfcSceneEmulateNfcVStateTextBox) { + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVEmulateStateTextBox) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { - if(state == NfcSceneEmulateNfcVStateTextBox) { + if(state == NfcSceneNfcVEmulateStateTextBox) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); consumed = true; } } @@ -134,7 +134,7 @@ bool nfc_scene_emulate_nfcv_on_event(void* context, SceneManagerEvent event) { return consumed; } -void nfc_scene_emulate_nfcv_on_exit(void* context) { +void nfc_scene_nfcv_emulate_on_exit(void* context) { Nfc* nfc = context; // Stop worker diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c index b30495a05..44d677513 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -16,9 +16,9 @@ void nfc_scene_nfcv_menu_on_enter(void* context) { Nfc* nfc = context; Submenu* submenu = nfc->submenu; - submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_nfcv_menu_submenu_callback, nfc); + submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); submenu_set_selected_item( nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu)); @@ -38,7 +38,7 @@ bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; } else if(event.event == SubmenuIndexEmulate) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { DOLPHIN_DEED(DolphinDeedNfcAddEmulate); } else { diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c index b52cc0caa..26de304de 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -42,10 +42,10 @@ void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { popup_reset(popup); if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { - nfc_text_store_set( - nfc, - "%s/SLIX_%02X%02X%02X%02X%02X%02X%02X%02X%s", - NFC_APP_FOLDER, + snprintf( + nfc->dev->dev_name, + sizeof(nfc->dev->dev_name), + "SLIX_%02X%02X%02X%02X%02X%02X%02X%02X", nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], @@ -53,12 +53,11 @@ void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], - nfc_data->uid[7], - NFC_APP_EXTENSION); + nfc_data->uid[7]); nfc->dev->format = NfcDeviceSaveFormatNfcV; - if(nfc_device_save(nfc->dev, nfc->text_store)) { + if(nfc_save_file(nfc)) { popup_set_header(popup, "Successfully\nsaved", 94, 3, AlignCenter, AlignTop); } else { popup_set_header( diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 0f49c4e4b..2d429a59a 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -118,7 +118,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } From d52432970bb0d1a895eefb7c158dba4b3a2c4f89 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Thu, 29 Dec 2022 22:02:34 +0100 Subject: [PATCH 223/282] optimize allocation size for signals --- lib/nfc/protocols/nfcv.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 6c205779f..0f9a0b58a 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -202,7 +202,7 @@ void nfcv_emu_free_signals(NfcVEmuAirSignals* signals) { void nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_t slowdown) { if(!signals->nfcv_resp_one) { /* logical one: unmodulated then 8 pulses */ - signals->nfcv_resp_one = digital_signal_alloc(40); + signals->nfcv_resp_one = digital_signal_alloc(slowdown * 9); for(size_t i = 0; i < slowdown; i++) { digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_unmod); } @@ -212,7 +212,7 @@ void nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_ } if(!signals->nfcv_resp_zero) { /* logical zero: 8 pulses then unmodulated */ - signals->nfcv_resp_zero = digital_signal_alloc(40); + signals->nfcv_resp_zero = digital_signal_alloc(slowdown * 9); for(size_t i = 0; i < slowdown * 8; i++) { digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_pulse); } @@ -222,7 +222,8 @@ void nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_ } if(!signals->nfcv_resp_sof) { /* SOF: unmodulated, 24 pulses, logic 1 */ - signals->nfcv_resp_sof = digital_signal_alloc(160); + signals->nfcv_resp_sof = + digital_signal_alloc(slowdown * 27 + signals->nfcv_resp_one->edge_cnt); for(size_t i = 0; i < slowdown * 3; i++) { digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_unmod); } @@ -233,7 +234,8 @@ void nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_ } if(!signals->nfcv_resp_eof) { /* EOF: logic 0, 24 pulses, unmodulated */ - signals->nfcv_resp_eof = digital_signal_alloc(160); + signals->nfcv_resp_eof = + digital_signal_alloc(slowdown * 27 + signals->nfcv_resp_zero->edge_cnt); digital_signal_append(signals->nfcv_resp_eof, signals->nfcv_resp_zero); for(size_t i = 0; i < slowdown * 24; i++) { digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_pulse); From 75d7246657664f82b5d70029c9bcc7fd1ce59f79 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Thu, 29 Dec 2022 22:38:13 +0100 Subject: [PATCH 224/282] ISO15693: further optimizations of allocation and free code --- lib/nfc/protocols/nfcv.c | 103 +++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 0f9a0b58a..69f179b4b 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -181,71 +181,71 @@ void nfcv_crc(uint8_t* data, uint32_t length) { } void nfcv_emu_free_signals(NfcVEmuAirSignals* signals) { - if(signals->nfcv_resp_one) { - digital_signal_free(signals->nfcv_resp_one); - signals->nfcv_resp_one = NULL; - } - if(signals->nfcv_resp_zero) { - digital_signal_free(signals->nfcv_resp_zero); - signals->nfcv_resp_zero = NULL; - } - if(signals->nfcv_resp_sof) { - digital_signal_free(signals->nfcv_resp_sof); - signals->nfcv_resp_sof = NULL; - } - if(signals->nfcv_resp_eof) { - digital_signal_free(signals->nfcv_resp_eof); - signals->nfcv_resp_eof = NULL; - } + digital_signal_free(signals->nfcv_resp_one); + digital_signal_free(signals->nfcv_resp_zero); + digital_signal_free(signals->nfcv_resp_sof); + digital_signal_free(signals->nfcv_resp_eof); + signals->nfcv_resp_one = NULL; + signals->nfcv_resp_zero = NULL; + signals->nfcv_resp_sof = NULL; + signals->nfcv_resp_eof = NULL; } -void nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_t slowdown) { +bool nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_t slowdown) { + bool ret = true; + if(!signals->nfcv_resp_one) { /* logical one: unmodulated then 8 pulses */ - signals->nfcv_resp_one = digital_signal_alloc(slowdown * 9); + signals->nfcv_resp_one = digital_signal_alloc( + slowdown * (air->nfcv_resp_unmod->edge_cnt + 8 * air->nfcv_resp_pulse->edge_cnt)); for(size_t i = 0; i < slowdown; i++) { - digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_unmod); + ret &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_unmod); } for(size_t i = 0; i < slowdown * 8; i++) { - digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_pulse); + ret &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_pulse); } } if(!signals->nfcv_resp_zero) { /* logical zero: 8 pulses then unmodulated */ - signals->nfcv_resp_zero = digital_signal_alloc(slowdown * 9); + signals->nfcv_resp_zero = digital_signal_alloc( + slowdown * (8 * air->nfcv_resp_pulse->edge_cnt + air->nfcv_resp_unmod->edge_cnt)); for(size_t i = 0; i < slowdown * 8; i++) { - digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_pulse); + ret &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_pulse); } for(size_t i = 0; i < slowdown; i++) { - digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_unmod); + ret &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_unmod); } } if(!signals->nfcv_resp_sof) { /* SOF: unmodulated, 24 pulses, logic 1 */ - signals->nfcv_resp_sof = - digital_signal_alloc(slowdown * 27 + signals->nfcv_resp_one->edge_cnt); + signals->nfcv_resp_sof = digital_signal_alloc( + slowdown * (3 * air->nfcv_resp_unmod->edge_cnt + 24 * air->nfcv_resp_pulse->edge_cnt) + + signals->nfcv_resp_one->edge_cnt); for(size_t i = 0; i < slowdown * 3; i++) { - digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_unmod); + ret &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_unmod); } for(size_t i = 0; i < slowdown * 24; i++) { - digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_pulse); + ret &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_pulse); } - digital_signal_append(signals->nfcv_resp_sof, signals->nfcv_resp_one); + ret &= digital_signal_append(signals->nfcv_resp_sof, signals->nfcv_resp_one); } if(!signals->nfcv_resp_eof) { /* EOF: logic 0, 24 pulses, unmodulated */ - signals->nfcv_resp_eof = - digital_signal_alloc(slowdown * 27 + signals->nfcv_resp_zero->edge_cnt); - digital_signal_append(signals->nfcv_resp_eof, signals->nfcv_resp_zero); + signals->nfcv_resp_eof = digital_signal_alloc( + signals->nfcv_resp_zero->edge_cnt + + slowdown * (24 * air->nfcv_resp_pulse->edge_cnt + 3 * air->nfcv_resp_unmod->edge_cnt) + + air->nfcv_resp_unmod->edge_cnt); + ret &= digital_signal_append(signals->nfcv_resp_eof, signals->nfcv_resp_zero); for(size_t i = 0; i < slowdown * 24; i++) { - digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_pulse); + ret &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_pulse); } for(size_t i = 0; i < slowdown * 3; i++) { - digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); + ret &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); } /* add extra silence */ - digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); + ret &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); } + return ret; } void nfcv_emu_alloc(NfcVData* nfcv_data) { @@ -272,8 +272,14 @@ void nfcv_emu_alloc(NfcVData* nfcv_data) { nfcv_data->emu_air.nfcv_resp_pulse->edge_cnt = 2; } - nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_high, 1); - nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_low, 4); + bool success = true; + + success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_high, 1); + success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_low, 4); + + if(!success) { + FURI_LOG_E(TAG, "Failed to allocate signals"); + } digital_sequence_set_signal( nfcv_data->emu_air.nfcv_signal, @@ -310,22 +316,15 @@ void nfcv_emu_alloc(NfcVData* nfcv_data) { } void nfcv_emu_free(NfcVData* nfcv_data) { - if(nfcv_data->emu_air.nfcv_resp_unmod) { - digital_signal_free(nfcv_data->emu_air.nfcv_resp_unmod); - nfcv_data->emu_air.nfcv_resp_unmod = NULL; - } - if(nfcv_data->emu_air.nfcv_resp_pulse) { - digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse); - nfcv_data->emu_air.nfcv_resp_pulse = NULL; - } - if(nfcv_data->emu_air.nfcv_signal) { - digital_sequence_free(nfcv_data->emu_air.nfcv_signal); - nfcv_data->emu_air.nfcv_signal = NULL; - } - if(nfcv_data->emu_air.reader_signal) { - pulse_reader_free(nfcv_data->emu_air.reader_signal); - nfcv_data->emu_air.reader_signal = NULL; - } + digital_signal_free(nfcv_data->emu_air.nfcv_resp_unmod); + digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse); + digital_sequence_free(nfcv_data->emu_air.nfcv_signal); + pulse_reader_free(nfcv_data->emu_air.reader_signal); + + nfcv_data->emu_air.nfcv_resp_unmod = NULL; + nfcv_data->emu_air.nfcv_resp_pulse = NULL; + nfcv_data->emu_air.nfcv_signal = NULL; + nfcv_data->emu_air.reader_signal = NULL; nfcv_emu_free_signals(&nfcv_data->emu_air.signals_high); nfcv_emu_free_signals(&nfcv_data->emu_air.signals_low); From 9f5a10a2e3a114622e11b44092317d0e034dad08 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 30 Dec 2022 02:16:44 +0100 Subject: [PATCH 225/282] ISO15693: further cleanup --- .../main/nfc/helpers/nfc_custom_event.h | 2 + .../main/nfc/scenes/nfc_scene_nfcv_emulate.c | 22 +++++- lib/nfc/nfc_worker.c | 76 +++++++++---------- lib/nfc/nfc_worker.h | 4 +- lib/nfc/protocols/nfcv.c | 3 + lib/nfc/protocols/nfcv.h | 1 + lib/nfc/protocols/slix.c | 1 + 7 files changed, 62 insertions(+), 47 deletions(-) diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index 4227a5b14..aa932a3d8 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -12,4 +12,6 @@ enum NfcCustomEvent { NfcCustomEventDictAttackSkip, NfcCustomEventRpcLoad, NfcCustomEventRpcSessionClose, + NfcCustomEventUpdateLog, + NfcCustomEventSaveShadow, }; diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c index 53da36679..77d1d420d 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" -#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (100) +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200) enum { NfcSceneNfcVEmulateStateWidget, @@ -11,7 +11,17 @@ bool nfc_scene_nfcv_emulate_worker_callback(NfcWorkerEvent event, void* context) UNUSED(event); furi_assert(context); Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); + + switch(event) { + case NfcWorkerEventNfcVCommandExecuted: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); + break; + case NfcWorkerEventNfcVContentChanged: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); + break; + default: + break; + } return true; } @@ -29,7 +39,6 @@ void nfc_scene_nfcv_emulate_textbox_callback(void* context) { view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); } -// Add widget with device name or inform that data received static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) { FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; Widget* widget = nfc->widget; @@ -91,7 +100,7 @@ bool nfc_scene_nfcv_emulate_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventWorkerExit) { + if(event.event == NfcCustomEventUpdateLog) { // Add data button to widget if data is received for the first time if(!furi_string_size(nfc->text_box_store)) { nfc_scene_nfcv_emulate_widget_config(nfc, true); @@ -111,6 +120,11 @@ bool nfc_scene_nfcv_emulate_on_event(void* context, SceneManagerEvent event) { strcpy(nfcv_data->last_command, ""); } consumed = true; + } else if(event.event == NfcCustomEventSaveShadow) { + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } + consumed = true; } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVEmulateStateWidget) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); scene_manager_set_scene_state( diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 28ce057fc..b1f35c6cb 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -112,7 +112,7 @@ int32_t nfc_worker_task(void* context) { } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { nfc_worker_analyze_reader(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) { - nfc_worker_emulate_nfcv(nfc_worker); + nfc_worker_nfcv_emulate(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateNfcVUnlock) { nfc_worker_nfcv_unlock(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { @@ -124,13 +124,15 @@ int32_t nfc_worker_task(void* context) { return 0; } -static bool nfc_worker_read_nfcv_content(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { +static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; NfcVReader reader = {}; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + furi_hal_nfc_sleep(); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); @@ -150,6 +152,36 @@ static bool nfc_worker_read_nfcv_content(NfcWorker* nfc_worker, FuriHalNfcTxRxCo return read_success; } +void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + nfcv_emu_init(nfc_data, nfcv_data); + while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 50)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); + if(nfcv_data->modified) { + nfc_worker->callback(NfcWorkerEventNfcVContentChanged, nfc_worker->context); + nfcv_data->modified = false; + } + } + } + furi_delay_ms(0); + } + nfcv_emu_deinit(nfcv_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); @@ -439,20 +471,6 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t return card_read; } -static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { - furi_assert(nfc_worker); - furi_assert(tx_rx); - - bool card_read = false; - furi_hal_nfc_sleep(); - - /* until here the UID field is reversed from the reader IC. - we will read it here again and it will get placed in the right order. */ - card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx); - - return card_read; -} - void nfc_worker_read(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); @@ -614,32 +632,6 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { } } -void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker) { - FuriHalNfcTxRxContext tx_rx = {}; - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); - reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); - } - - nfcv_emu_init(nfc_data, nfcv_data); - while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { - if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 50)) { - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - } - furi_delay_ms(0); - } - nfcv_emu_deinit(nfcv_data); - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - reader_analyzer_stop(nfc_worker->reader_analyzer); - } -} - void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; FuriHalNfcDevData params = { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 5113205c4..5b50139ee 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -74,6 +74,8 @@ typedef enum { NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key + NfcWorkerEventNfcVCommandExecuted, + NfcWorkerEventNfcVContentChanged, } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); @@ -93,4 +95,4 @@ void nfc_worker_start( void nfc_worker_stop(NfcWorker* nfc_worker); void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); -void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker); +void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker); diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 69f179b4b..66b14cbd3 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -561,6 +561,7 @@ void nfcv_emu_handle_packet( &nfcv_data->data[nfcv_data->block_size * block], &nfcv_data->frame[ctx->payload_offset + data_pos], data_len); + nfcv_data->modified = true; } nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); @@ -719,7 +720,9 @@ bool nfcv_emu_loop( pulse_reader_receive(nfcv_data->emu_air.reader_signal, timeout_ms * 1000); uint32_t timestamp = DWT->CYCCNT; + /* when timed out, reset to SOF state */ if(periods == PULSE_READER_NO_EDGE) { + frame_state = NFCV_FRAME_STATE_SOF1; break; } if(periods == PULSE_READER_LOST_EDGE) { diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index 9d7a56326..c349b6e95 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -161,6 +161,7 @@ typedef struct { uint16_t block_num; uint8_t block_size; uint8_t data[NFCV_MAX_DUMP_SIZE]; + bool modified; /* specfic variant infos */ NfcVSubtype sub_type; diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c index 6c6deacdd..eaa75509f 100644 --- a/lib/nfc/protocols/slix.c +++ b/lib/nfc/protocols/slix.c @@ -236,6 +236,7 @@ bool slix_generic_protocol_filter( break; case SLIX_PASS_PRIVACY: slix->privacy = false; + nfcv_data->modified = true; break; case SLIX_PASS_DESTROY: FURI_LOG_D(TAG, "Pooof! Got destroyed"); From a34a186bbab9a2cc36c21fc2b26a1961bb8befdf Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 30 Dec 2022 16:17:41 +0100 Subject: [PATCH 226/282] ISO15693: reduce latency on state machine reset --- lib/nfc/nfc_worker.c | 4 +-- lib/nfc/protocols/nfcv.c | 53 +++++++++++++--------------------------- 2 files changed, 19 insertions(+), 38 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index b1f35c6cb..7291117a4 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -164,7 +164,7 @@ void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) { nfcv_emu_init(nfc_data, nfcv_data); while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { - if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 50)) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) { if(nfc_worker->callback) { nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); if(nfcv_data->modified) { @@ -173,7 +173,7 @@ void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) { } } } - furi_delay_ms(0); + furi_delay_ms(10); } nfcv_emu_deinit(nfcv_data); diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 66b14cbd3..136ff0db7 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -712,9 +712,11 @@ bool nfcv_emu_loop( uint32_t frame_pos = 0; uint32_t byte_value = 0; uint32_t bits_received = 0; - char reset_reason[128]; bool wait_for_pulse = false; + uint8_t period_buffer[256]; + uint32_t period_buffer_pos = 0; + while(true) { uint32_t periods = pulse_reader_receive(nfcv_data->emu_air.reader_signal, timeout_ms * 1000); @@ -729,15 +731,13 @@ bool nfcv_emu_loop( break; } + if(period_buffer_pos < sizeof(period_buffer)) { + period_buffer[period_buffer_pos++] = periods; + } + if(wait_for_pulse) { wait_for_pulse = false; if(periods != 1) { - snprintf( - reset_reason, - sizeof(reset_reason), - "SOF: Expected a single low pulse in state %lu, but got %lu", - frame_state, - periods); frame_state = NFCV_FRAME_STATE_RESET; } continue; @@ -764,23 +764,12 @@ bool nfcv_emu_loop( periods_previous = 2; wait_for_pulse = true; } else { - snprintf( - reset_reason, - sizeof(reset_reason), - "SOF: Expected 4/6 periods, got %lu", - periods); - frame_state = NFCV_FRAME_STATE_SOF1; + frame_state = NFCV_FRAME_STATE_RESET; } break; case NFCV_FRAME_STATE_CODING_256: if(periods_previous > periods) { - snprintf( - reset_reason, - sizeof(reset_reason), - "1oo256: Missing %lu periods from previous symbol, got %lu", - periods_previous, - periods); frame_state = NFCV_FRAME_STATE_RESET; break; } @@ -788,8 +777,6 @@ bool nfcv_emu_loop( periods -= periods_previous; if(periods > 512) { - snprintf( - reset_reason, sizeof(reset_reason), "1oo256: %lu periods is too much", periods); frame_state = NFCV_FRAME_STATE_RESET; break; } @@ -809,13 +796,6 @@ bool nfcv_emu_loop( case NFCV_FRAME_STATE_CODING_4: if(periods_previous > periods) { - snprintf( - reset_reason, - sizeof(reset_reason), - "1oo4: Missing %lu periods from previous symbol, got %lu at pos %lu", - periods_previous, - periods, - frame_pos); frame_state = NFCV_FRAME_STATE_RESET; break; } @@ -843,12 +823,6 @@ bool nfcv_emu_loop( frame_state = NFCV_FRAME_STATE_EOF; break; } else { - snprintf( - reset_reason, - sizeof(reset_reason), - "1oo4: Expected 1/3/5/7 low pulses, but got %lu at pos %lu", - periods, - frame_pos); frame_state = NFCV_FRAME_STATE_RESET; break; } @@ -864,8 +838,6 @@ bool nfcv_emu_loop( /* post-state-machine cleanup and reset */ if(frame_state == NFCV_FRAME_STATE_RESET) { frame_state = NFCV_FRAME_STATE_SOF1; - - FURI_LOG_D(TAG, "Resetting state machine, reason: '%s'", reset_reason); } else if(frame_state == NFCV_FRAME_STATE_EOF) { nfcv_data->frame = frame_payload; nfcv_data->frame_length = frame_pos; @@ -883,6 +855,15 @@ bool nfcv_emu_loop( nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data); pulse_reader_start(nfcv_data->emu_air.reader_signal); ret = true; + } else { + if(frame_state != NFCV_FRAME_STATE_SOF1) { + FURI_LOG_T(TAG, "leaving while in state: %lu", frame_state); + } + } + + FURI_LOG_T(TAG, "pulses:"); + for(uint32_t pos = 0; pos < period_buffer_pos; pos++) { + FURI_LOG_T(TAG, " #%lu: %u", pos, period_buffer[pos]); } return ret; From fecc686a3e18f43b252972f2066951c3a323b6b8 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Tue, 3 Jan 2023 01:27:34 +0100 Subject: [PATCH 227/282] further code cleanups --- lib/nfc/protocols/nfcv.c | 72 +++++++++++++++++++++++++++++----------- lib/nfc/protocols/nfcv.h | 18 ++++++---- 2 files changed, 64 insertions(+), 26 deletions(-) diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 136ff0db7..cc7f4ad59 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -181,6 +181,8 @@ void nfcv_crc(uint8_t* data, uint32_t length) { } void nfcv_emu_free_signals(NfcVEmuAirSignals* signals) { + furi_assert(signals); + digital_signal_free(signals->nfcv_resp_one); digital_signal_free(signals->nfcv_resp_zero); digital_signal_free(signals->nfcv_resp_sof); @@ -192,6 +194,9 @@ void nfcv_emu_free_signals(NfcVEmuAirSignals* signals) { } bool nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_t slowdown) { + furi_assert(air); + furi_assert(signals); + bool ret = true; if(!signals->nfcv_resp_one) { @@ -249,6 +254,8 @@ bool nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_ } void nfcv_emu_alloc(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + if(!nfcv_data->emu_air.nfcv_signal) { /* assuming max frame length is 255 bytes */ nfcv_data->emu_air.nfcv_signal = digital_sequence_alloc(8 * 255 + 2, &gpio_spi_r_mosi); @@ -316,6 +323,8 @@ void nfcv_emu_alloc(NfcVData* nfcv_data) { } void nfcv_emu_free(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + digital_signal_free(nfcv_data->emu_air.nfcv_resp_unmod); digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse); digital_sequence_free(nfcv_data->emu_air.nfcv_signal); @@ -337,6 +346,9 @@ void nfcv_emu_send( uint8_t length, NfcVSendFlags flags, uint32_t send_time) { + furi_assert(tx_rx); + furi_assert(nfcv); + /* picked default value (0) to match the most common format */ if(!flags) { flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof | @@ -402,6 +414,10 @@ void nfcv_emu_handle_packet( FuriHalNfcTxRxContext* tx_rx, FuriHalNfcDevData* nfc_data, void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; @@ -618,7 +634,11 @@ void nfcv_emu_handle_packet( } void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(nfc_data); + furi_assert(nfcv_data); + nfcv_emu_alloc(nfcv_data); + rfal_platform_spi_acquire(); /* configure for transparent and passive mode */ st25r3916ExecuteCommand(ST25R3916_CMD_STOP); @@ -626,7 +646,7 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3); /* target mode: ISO14443 passive mode */ st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88); - /* let us modulate the field using MOSI, read modulation using MISO */ + /* let us modulate the field using MOSI, read ASK modulation using IRQ */ st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); @@ -686,6 +706,8 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { } void nfcv_emu_deinit(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); rfal_platform_spi_release(); nfcv_emu_free(nfcv_data); @@ -705,36 +727,40 @@ bool nfcv_emu_loop( FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t timeout_ms) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data); + bool ret = false; uint32_t frame_state = NFCV_FRAME_STATE_SOF1; uint32_t periods_previous = 0; - uint8_t frame_payload[128]; uint32_t frame_pos = 0; uint32_t byte_value = 0; uint32_t bits_received = 0; + uint32_t timeout = timeout_ms * 1000; bool wait_for_pulse = false; - uint8_t period_buffer[256]; +#ifdef NFCV_DIAGNOSTIC_DUMPS + uint8_t period_buffer[NFCV_DIAGNOSTIC_DUMP_SIZE]; uint32_t period_buffer_pos = 0; +#endif while(true) { - uint32_t periods = - pulse_reader_receive(nfcv_data->emu_air.reader_signal, timeout_ms * 1000); + uint32_t periods = pulse_reader_receive(nfcv_data->emu_air.reader_signal, timeout); uint32_t timestamp = DWT->CYCCNT; /* when timed out, reset to SOF state */ - if(periods == PULSE_READER_NO_EDGE) { - frame_state = NFCV_FRAME_STATE_SOF1; - break; - } - if(periods == PULSE_READER_LOST_EDGE) { + if(periods == PULSE_READER_NO_EDGE || periods == PULSE_READER_LOST_EDGE) { break; } +#ifdef NFCV_DIAGNOSTIC_DUMPS if(period_buffer_pos < sizeof(period_buffer)) { period_buffer[period_buffer_pos++] = periods; } +#endif + /* short helper for detecting a pulse position */ if(wait_for_pulse) { wait_for_pulse = false; if(periods != 1) { @@ -773,22 +799,23 @@ bool nfcv_emu_loop( frame_state = NFCV_FRAME_STATE_RESET; break; } + /* previous symbol left us with some pulse periods */ periods -= periods_previous; if(periods > 512) { frame_state = NFCV_FRAME_STATE_RESET; break; - } - - if(periods == 2) { + } else if(periods == 2) { frame_state = NFCV_FRAME_STATE_EOF; break; } periods_previous = 512 - (periods + 1); byte_value = (periods - 1) / 2; - frame_payload[frame_pos++] = (uint8_t)byte_value; + if(frame_pos < NFCV_MAX_FRAME_SIZE) { + nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; + } wait_for_pulse = true; @@ -828,7 +855,9 @@ bool nfcv_emu_loop( } if(bits_received >= 8) { - frame_payload[frame_pos++] = (uint8_t)byte_value; + if(frame_pos < NFCV_MAX_FRAME_SIZE) { + nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; + } bits_received = 0; } wait_for_pulse = true; @@ -839,7 +868,6 @@ bool nfcv_emu_loop( if(frame_state == NFCV_FRAME_STATE_RESET) { frame_state = NFCV_FRAME_STATE_SOF1; } else if(frame_state == NFCV_FRAME_STATE_EOF) { - nfcv_data->frame = frame_payload; nfcv_data->frame_length = frame_pos; nfcv_data->eof_timestamp = timestamp; break; @@ -850,7 +878,7 @@ bool nfcv_emu_loop( /* we know that this code uses TIM2, so stop pulse reader */ pulse_reader_stop(nfcv_data->emu_air.reader_signal); if(tx_rx->sniff_rx) { - tx_rx->sniff_rx(frame_payload, frame_pos * 8, false, tx_rx->sniff_context); + tx_rx->sniff_rx(nfcv_data->frame, frame_pos * 8, false, tx_rx->sniff_context); } nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data); pulse_reader_start(nfcv_data->emu_air.reader_signal); @@ -861,10 +889,14 @@ bool nfcv_emu_loop( } } - FURI_LOG_T(TAG, "pulses:"); - for(uint32_t pos = 0; pos < period_buffer_pos; pos++) { - FURI_LOG_T(TAG, " #%lu: %u", pos, period_buffer[pos]); +#ifdef NFCV_DIAGNOSTIC_DUMPS + if(period_buffer_pos) { + FURI_LOG_T(TAG, "pulses:"); + for(uint32_t pos = 0; pos < period_buffer_pos; pos++) { + FURI_LOG_T(TAG, " #%lu: %u", pos, period_buffer[pos]); + } } +#endif return ret; } diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index c349b6e95..266b84ed8 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -16,7 +16,7 @@ extern "C" { #define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC / 32) / 2.0f) /* 1.1799 µs */ #define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */ -#define PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) /* ns */ +#define PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) #define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) #define DIGITAL_SIGNAL_UNIT_US (100000.0f) @@ -24,6 +24,11 @@ extern "C" { #define NFCV_TOTAL_BLOCKS_MAX 256 #define NFCV_BLOCK_SIZE 4 #define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE * NFCV_TOTAL_BLOCKS_MAX) +#define NFCV_MAX_FRAME_SIZE 64 +#define NFCV_LOG_STR_LEN 128 + +// #define NFCV_DIAGNOSTIC_DUMPS +// #define NFCV_DIAGNOSTIC_DUMP_SIZE 128 /* helpers to calculate the send time based on DWT->CYCCNT */ #define NFCV_FDT_USEC(usec) (usec * 64) @@ -138,6 +143,7 @@ typedef bool (*NfcVEmuProtocolFilter)( FuriHalNfcDevData* nfc_data, void* nfcv_data); +/* the default ISO15693 handler context */ typedef struct { uint8_t flags; /* ISO15693-3 flags of the header as specified */ uint8_t command; /* ISO15693-3 command at offset 1 as specified */ @@ -146,7 +152,7 @@ typedef struct { uint8_t address_offset; /* ISO15693-3 offset of the address in frame, if addressed is set */ uint8_t payload_offset; /* ISO15693-3 offset of the payload in frame */ - uint8_t response_buffer[128]; /* pre-allocated response buffer */ + uint8_t response_buffer[NFCV_MAX_FRAME_SIZE]; /* pre-allocated response buffer */ NfcVSendFlags response_flags; /* flags to use when sending response */ uint32_t send_time; /* timestamp when to send the response */ @@ -161,6 +167,7 @@ typedef struct { uint16_t block_num; uint8_t block_size; uint8_t data[NFCV_MAX_DUMP_SIZE]; + bool modified; /* specfic variant infos */ @@ -171,17 +178,16 @@ typedef struct { /* precalced air level data */ NfcVEmuAir emu_air; - uint8_t* frame; /* ISO15693-2 incoming raw data from air layer */ + uint8_t frame[NFCV_MAX_FRAME_SIZE]; /* ISO15693-2 incoming raw data from air layer */ uint8_t frame_length; /* ISO15693-2 length of incoming data */ uint32_t eof_timestamp; /* ISO15693-2 EOF timestamp, read from DWT->CYCCNT */ /* handler for the protocol layer as specified in ISO15693-3 */ NfcVEmuProtocolHandler emu_protocol_handler; void* emu_protocol_ctx; - /* runtime data */ - char last_command[128]; - char error[32]; + char last_command[NFCV_LOG_STR_LEN]; + char error[NFCV_LOG_STR_LEN]; } NfcVData; typedef struct { From aa7ead724aecde1bd4a065553a3c5d30e1c9ba93 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 20 Jan 2023 01:34:15 +0100 Subject: [PATCH 228/282] respond with block security status when option flag is set --- lib/nfc/protocols/nfcv.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index cc7f4ad59..90752fe98 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -537,20 +537,32 @@ void nfcv_emu_handle_packet( nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); } else { - ctx->response_buffer[0] = ISO15693_NOERROR; - memcpy( - &ctx->response_buffer[1], - &nfcv_data->data[nfcv_data->block_size * block], - nfcv_data->block_size * blocks); + uint8_t buffer_pos = 0; + + ctx->response_buffer[buffer_pos++] = ISO15693_NOERROR; + + for(int current_block = 0; current_block < blocks; current_block++) { + /* prepend security status */ + if(ctx->flags & RFAL_NFCV_REQ_FLAG_OPTION) { + ctx->response_buffer[buffer_pos++] = 0; + } + /* then the data block */ + memcpy( + &ctx->response_buffer[buffer_pos], + &nfcv_data->data[nfcv_data->block_size * (block + current_block)], + nfcv_data->block_size); + buffer_pos += nfcv_data->block_size; + } nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, - 1 + nfcv_data->block_size * blocks, + buffer_pos, ctx->response_flags, ctx->send_time); } snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); + break; } From f03d31b647d9de93161485ed8d239d89e1197a1d Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Fri, 20 Jan 2023 23:29:34 +0100 Subject: [PATCH 229/282] increased maximum memory size to match standard added security status handling/load/save added SELECT/QUIET handling more fine grained allocation routines and checks fix memset sizes --- .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 3 +- .../main/nfc/scenes/nfc_scene_nfcv_emulate.c | 7 +- lib/nfc/nfc_device.c | 20 +- lib/nfc/protocols/nfcv.c | 175 ++++++++++++++---- lib/nfc/protocols/nfcv.h | 24 ++- 5 files changed, 177 insertions(+), 52 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 88df6705c..87a23c8ef 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -103,11 +103,12 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { } for(int block = 0; block < maxBlocks; block++) { + const char* status = (nfcv_data->security_status[block] & 0x01) ? "(lck)" : ""; for(int pos = 0; pos < nfcv_data->block_size; pos++) { furi_string_cat_printf( temp_str, " %02X", nfcv_data->data[block * nfcv_data->block_size + pos]); } - furi_string_cat_printf(temp_str, "\n"); + furi_string_cat_printf(temp_str, " %s\n", status); } furi_string_cat_printf(temp_str, "\n"); diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c index 77d1d420d..ca10f5d6e 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c @@ -75,6 +75,7 @@ void nfc_scene_nfcv_emulate_on_enter(void* context) { TextBox* text_box = nfc->text_box; text_box_set_font(text_box, TextBoxFontHex); text_box_set_focus(text_box, TextBoxFocusEnd); + text_box_set_text(text_box, ""); furi_string_reset(nfc->text_box_store); // Set Widget state and view @@ -102,10 +103,10 @@ bool nfc_scene_nfcv_emulate_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventUpdateLog) { // Add data button to widget if data is received for the first time - if(!furi_string_size(nfc->text_box_store)) { - nfc_scene_nfcv_emulate_widget_config(nfc, true); - } if(strlen(nfcv_data->last_command) > 0) { + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_nfcv_emulate_widget_config(nfc, true); + } /* use the last n bytes from the log so there's enough space for the new log entry */ size_t maxSize = NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index b925d651e..3a3b69344 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -674,7 +674,7 @@ static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) { bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVData)); + memset(data, 0, sizeof(NfcVSlixData)); do { if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) @@ -715,7 +715,7 @@ static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) { bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVData)); + memset(data, 0, sizeof(NfcVSlixData)); do { if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) @@ -763,7 +763,7 @@ static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVData)); + memset(data, 0, sizeof(NfcVSlixData)); do { if(!flipper_format_read_hex( @@ -811,7 +811,7 @@ static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVData)); + memset(data, 0, sizeof(NfcVSlixData)); do { if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) @@ -858,6 +858,9 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { if(!flipper_format_write_hex( file, "Data Content", data->data, data->block_num * data->block_size)) break; + if(!flipper_format_write_hex( + file, "Security Status", data->security_status, data->block_num)) + break; if(!flipper_format_write_comment_cstr( file, "Subtype of this card (0 = ISO15693, 1 = SLIX, 2 = SLIX-S, 3 = SLIX-L, 4 = SLIX2)")) @@ -892,7 +895,7 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; NfcVData* data = &dev->dev_data.nfcv_data; - memset(data, 0, sizeof(NfcVData)); + memset(data, 0x00, sizeof(NfcVData)); do { uint32_t temp_uint32 = 0; @@ -907,6 +910,13 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { if(!flipper_format_read_hex( file, "Data Content", data->data, data->block_num * data->block_size)) break; + + /* optional, as added later */ + if(flipper_format_key_exist(file, "Security Status")) { + if(!flipper_format_read_hex( + file, "Security Status", data->security_status, data->block_num)) + break; + } if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; data->sub_type = temp_value; diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 90752fe98..dc37860fb 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -183,10 +183,18 @@ void nfcv_crc(uint8_t* data, uint32_t length) { void nfcv_emu_free_signals(NfcVEmuAirSignals* signals) { furi_assert(signals); - digital_signal_free(signals->nfcv_resp_one); - digital_signal_free(signals->nfcv_resp_zero); - digital_signal_free(signals->nfcv_resp_sof); - digital_signal_free(signals->nfcv_resp_eof); + if(signals->nfcv_resp_one) { + digital_signal_free(signals->nfcv_resp_one); + } + if(signals->nfcv_resp_zero) { + digital_signal_free(signals->nfcv_resp_zero); + } + if(signals->nfcv_resp_sof) { + digital_signal_free(signals->nfcv_resp_sof); + } + if(signals->nfcv_resp_eof) { + digital_signal_free(signals->nfcv_resp_eof); + } signals->nfcv_resp_one = NULL; signals->nfcv_resp_zero = NULL; signals->nfcv_resp_sof = NULL; @@ -197,28 +205,40 @@ bool nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_ furi_assert(air); furi_assert(signals); - bool ret = true; + bool success = true; if(!signals->nfcv_resp_one) { /* logical one: unmodulated then 8 pulses */ signals->nfcv_resp_one = digital_signal_alloc( slowdown * (air->nfcv_resp_unmod->edge_cnt + 8 * air->nfcv_resp_pulse->edge_cnt)); + if(!signals->nfcv_resp_one) { + return false; + } for(size_t i = 0; i < slowdown; i++) { - ret &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_unmod); + success &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_unmod); } for(size_t i = 0; i < slowdown * 8; i++) { - ret &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_pulse); + success &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_pulse); + } + if(!success) { + return false; } } if(!signals->nfcv_resp_zero) { /* logical zero: 8 pulses then unmodulated */ signals->nfcv_resp_zero = digital_signal_alloc( slowdown * (8 * air->nfcv_resp_pulse->edge_cnt + air->nfcv_resp_unmod->edge_cnt)); + if(!signals->nfcv_resp_zero) { + return false; + } for(size_t i = 0; i < slowdown * 8; i++) { - ret &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_pulse); + success &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_pulse); } for(size_t i = 0; i < slowdown; i++) { - ret &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_unmod); + success &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_unmod); + } + if(!success) { + return false; } } if(!signals->nfcv_resp_sof) { @@ -226,13 +246,19 @@ bool nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_ signals->nfcv_resp_sof = digital_signal_alloc( slowdown * (3 * air->nfcv_resp_unmod->edge_cnt + 24 * air->nfcv_resp_pulse->edge_cnt) + signals->nfcv_resp_one->edge_cnt); + if(!signals->nfcv_resp_sof) { + return false; + } for(size_t i = 0; i < slowdown * 3; i++) { - ret &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_unmod); + success &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_unmod); } for(size_t i = 0; i < slowdown * 24; i++) { - ret &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_pulse); + success &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_pulse); + } + success &= digital_signal_append(signals->nfcv_resp_sof, signals->nfcv_resp_one); + if(!success) { + return false; } - ret &= digital_signal_append(signals->nfcv_resp_sof, signals->nfcv_resp_one); } if(!signals->nfcv_resp_eof) { /* EOF: logic 0, 24 pulses, unmodulated */ @@ -240,29 +266,48 @@ bool nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_ signals->nfcv_resp_zero->edge_cnt + slowdown * (24 * air->nfcv_resp_pulse->edge_cnt + 3 * air->nfcv_resp_unmod->edge_cnt) + air->nfcv_resp_unmod->edge_cnt); - ret &= digital_signal_append(signals->nfcv_resp_eof, signals->nfcv_resp_zero); + if(!signals->nfcv_resp_eof) { + return false; + } + success &= digital_signal_append(signals->nfcv_resp_eof, signals->nfcv_resp_zero); for(size_t i = 0; i < slowdown * 24; i++) { - ret &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_pulse); + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_pulse); } for(size_t i = 0; i < slowdown * 3; i++) { - ret &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); } /* add extra silence */ - ret &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); + if(!success) { + return false; + } } - return ret; + return success; } -void nfcv_emu_alloc(NfcVData* nfcv_data) { +bool nfcv_emu_alloc(NfcVData* nfcv_data) { furi_assert(nfcv_data); + if(!nfcv_data->frame) { + nfcv_data->frame = malloc(NFCV_FRAMESIZE_MAX); + if(!nfcv_data->frame) { + return false; + } + } + if(!nfcv_data->emu_air.nfcv_signal) { /* assuming max frame length is 255 bytes */ nfcv_data->emu_air.nfcv_signal = digital_sequence_alloc(8 * 255 + 2, &gpio_spi_r_mosi); + if(!nfcv_data->emu_air.nfcv_signal) { + return false; + } } if(!nfcv_data->emu_air.nfcv_resp_unmod) { /* unmodulated 256/fc or 1024/fc signal as building block */ nfcv_data->emu_air.nfcv_resp_unmod = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_unmod) { + return false; + } nfcv_data->emu_air.nfcv_resp_unmod->start_level = false; nfcv_data->emu_air.nfcv_resp_unmod->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S); @@ -271,6 +316,9 @@ void nfcv_emu_alloc(NfcVData* nfcv_data) { if(!nfcv_data->emu_air.nfcv_resp_pulse) { /* modulated fc/32 or fc/8 pulse as building block */ nfcv_data->emu_air.nfcv_resp_pulse = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_pulse) { + return false; + } nfcv_data->emu_air.nfcv_resp_pulse->start_level = true; nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); @@ -280,12 +328,12 @@ void nfcv_emu_alloc(NfcVData* nfcv_data) { } bool success = true; - success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_high, 1); success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_low, 4); if(!success) { FURI_LOG_E(TAG, "Failed to allocate signals"); + return false; } digital_sequence_set_signal( @@ -320,16 +368,33 @@ void nfcv_emu_alloc(NfcVData* nfcv_data) { nfcv_data->emu_air.nfcv_signal, NFCV_SIG_LOW_EOF, nfcv_data->emu_air.signals_low.nfcv_resp_eof); + + return true; } void nfcv_emu_free(NfcVData* nfcv_data) { furi_assert(nfcv_data); - digital_signal_free(nfcv_data->emu_air.nfcv_resp_unmod); - digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse); - digital_sequence_free(nfcv_data->emu_air.nfcv_signal); - pulse_reader_free(nfcv_data->emu_air.reader_signal); + if(nfcv_data->frame) { + free(nfcv_data->frame); + } + if(nfcv_data->emu_protocol_ctx) { + free(nfcv_data->emu_protocol_ctx); + } + if(nfcv_data->emu_air.nfcv_resp_unmod) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_unmod); + } + if(nfcv_data->emu_air.nfcv_resp_pulse) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse); + } + if(nfcv_data->emu_air.nfcv_signal) { + digital_sequence_free(nfcv_data->emu_air.nfcv_signal); + } + if(nfcv_data->emu_air.reader_signal) { + pulse_reader_free(nfcv_data->emu_air.reader_signal); + } + nfcv_data->frame = NULL; nfcv_data->emu_air.nfcv_resp_unmod = NULL; nfcv_data->emu_air.nfcv_resp_pulse = NULL; nfcv_data->emu_air.nfcv_signal = NULL; @@ -428,6 +493,7 @@ void nfcv_emu_handle_packet( /* parse the frame data for the upcoming part 3 handling */ ctx->flags = nfcv_data->frame[0]; ctx->command = nfcv_data->frame[1]; + ctx->selected = (ctx->flags & RFAL_NFCV_REQ_FLAG_SELECT); ctx->addressed = !(ctx->flags & RFAL_NFCV_REQ_FLAG_INVENTORY) && (ctx->flags & RFAL_NFCV_REQ_FLAG_ADDRESS); ctx->advanced = (ctx->command >= 0xA0); @@ -474,6 +540,14 @@ void nfcv_emu_handle_packet( } } + if(ctx->selected && !nfcv_data->selected) { + FURI_LOG_D( + TAG, + "selected card shall execute command 0x%02X, but we were not selected", + ctx->command); + return; + } + /* then give control to the card subtype specific protocol filter */ if(ctx->emu_protocol_filter != NULL) { if(ctx->emu_protocol_filter(tx_rx, nfc_data, nfcv_data)) { @@ -487,28 +561,39 @@ void nfcv_emu_handle_packet( switch(ctx->command) { case ISO15693_INVENTORY: { - ctx->response_buffer[0] = ISO15693_NOERROR; - ctx->response_buffer[1] = nfcv_data->dsfid; - nfcv_revuidcpy(&ctx->response_buffer[2], nfc_data->uid); + if(!nfcv_data->quiet) { + ctx->response_buffer[0] = ISO15693_NOERROR; + ctx->response_buffer[1] = nfcv_data->dsfid; + nfcv_revuidcpy(&ctx->response_buffer[2], nfc_data->uid); - nfcv_emu_send( - tx_rx, nfcv_data, ctx->response_buffer, 10, ctx->response_flags, ctx->send_time); - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 10, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + } else { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY (quiet)"); + } break; } case ISO15693_STAYQUIET: { snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "STAYQUIET"); + nfcv_data->quiet = true; break; } case ISO15693_LOCKBLOCK: { - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCKBLOCK"); + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + nfcv_data->security_status[block] |= 0x01; + nfcv_data->modified = true; + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK BLOCK %d", block); break; } case ISO15693_SELECT: { ctx->response_buffer[0] = ISO15693_NOERROR; + nfcv_data->selected = true; + nfcv_data->quiet = false; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SELECT"); @@ -517,6 +602,7 @@ void nfcv_emu_handle_packet( case ISO15693_RESET_TO_READY: { ctx->response_buffer[0] = ISO15693_NOERROR; + nfcv_data->quiet = false; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "RESET_TO_READY"); @@ -541,15 +627,16 @@ void nfcv_emu_handle_packet( ctx->response_buffer[buffer_pos++] = ISO15693_NOERROR; - for(int current_block = 0; current_block < blocks; current_block++) { + for(int block_index = 0; block_index < blocks; block_index++) { + int block_current = block + block_index; /* prepend security status */ if(ctx->flags & RFAL_NFCV_REQ_FLAG_OPTION) { - ctx->response_buffer[buffer_pos++] = 0; + ctx->response_buffer[buffer_pos++] = nfcv_data->security_status[block_current]; } /* then the data block */ memcpy( &ctx->response_buffer[buffer_pos], - &nfcv_data->data[nfcv_data->block_size * (block + current_block)], + &nfcv_data->data[nfcv_data->block_size * block_current], nfcv_data->block_size); buffer_pos += nfcv_data->block_size; } @@ -649,7 +736,19 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { furi_assert(nfc_data); furi_assert(nfcv_data); - nfcv_emu_alloc(nfcv_data); + if(!nfcv_emu_alloc(nfcv_data)) { + FURI_LOG_E(TAG, "Failed to allocate structures"); + nfcv_data->ready = false; + return; + } + + strcpy(nfcv_data->last_command, ""); + nfcv_data->quiet = false; + nfcv_data->selected = false; + nfcv_data->modified = false; + + /* everything is initialized */ + nfcv_data->ready = true; rfal_platform_spi_acquire(); /* configure for transparent and passive mode */ @@ -752,6 +851,10 @@ bool nfcv_emu_loop( uint32_t timeout = timeout_ms * 1000; bool wait_for_pulse = false; + if(!nfcv_data->ready) { + return false; + } + #ifdef NFCV_DIAGNOSTIC_DUMPS uint8_t period_buffer[NFCV_DIAGNOSTIC_DUMP_SIZE]; uint32_t period_buffer_pos = 0; @@ -825,7 +928,7 @@ bool nfcv_emu_loop( periods_previous = 512 - (periods + 1); byte_value = (periods - 1) / 2; - if(frame_pos < NFCV_MAX_FRAME_SIZE) { + if(frame_pos < NFCV_FRAMESIZE_MAX) { nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; } @@ -867,7 +970,7 @@ bool nfcv_emu_loop( } if(bits_received >= 8) { - if(frame_pos < NFCV_MAX_FRAME_SIZE) { + if(frame_pos < NFCV_FRAMESIZE_MAX) { nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; } bits_received = 0; diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index 266b84ed8..3f8e78556 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -21,10 +21,15 @@ extern "C" { #define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) #define DIGITAL_SIGNAL_UNIT_US (100000.0f) -#define NFCV_TOTAL_BLOCKS_MAX 256 -#define NFCV_BLOCK_SIZE 4 -#define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE * NFCV_TOTAL_BLOCKS_MAX) -#define NFCV_MAX_FRAME_SIZE 64 +/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum number of blocks is defined as 256 */ +#define NFCV_BLOCKS_MAX 256 +/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum size of blocks is defined as 32 */ +#define NFCV_BLOCKSIZE_MAX 32 +/* the resulting memory size a card can have */ +#define NFCV_MEMSIZE_MAX (NFCV_BLOCKS_MAX * NFCV_BLOCKSIZE_MAX) +/* ISO/IEC 15693-3:2019(E) 7.1b: standard allows up to 8192, the maxium frame length that we are expected to receive/send is less */ +#define NFCV_FRAMESIZE_MAX (1 + NFCV_MEMSIZE_MAX + NFCV_BLOCKS_MAX) + #define NFCV_LOG_STR_LEN 128 // #define NFCV_DIAGNOSTIC_DUMPS @@ -147,12 +152,13 @@ typedef bool (*NfcVEmuProtocolFilter)( typedef struct { uint8_t flags; /* ISO15693-3 flags of the header as specified */ uint8_t command; /* ISO15693-3 command at offset 1 as specified */ + bool selected; /* ISO15693-3 flags: selected frame */ bool addressed; /* ISO15693-3 flags: addressed frame */ bool advanced; /* ISO15693-3 command: advanced command */ uint8_t address_offset; /* ISO15693-3 offset of the address in frame, if addressed is set */ uint8_t payload_offset; /* ISO15693-3 offset of the payload in frame */ - uint8_t response_buffer[NFCV_MAX_FRAME_SIZE]; /* pre-allocated response buffer */ + uint8_t response_buffer[NFCV_FRAMESIZE_MAX]; /* pre-allocated response buffer */ NfcVSendFlags response_flags; /* flags to use when sending response */ uint32_t send_time; /* timestamp when to send the response */ @@ -166,9 +172,13 @@ typedef struct { uint8_t ic_ref; uint16_t block_num; uint8_t block_size; - uint8_t data[NFCV_MAX_DUMP_SIZE]; + uint8_t data[NFCV_MEMSIZE_MAX]; + uint8_t security_status[NFCV_BLOCKS_MAX]; + bool selected; + bool quiet; bool modified; + bool ready; /* specfic variant infos */ NfcVSubtype sub_type; @@ -178,7 +188,7 @@ typedef struct { /* precalced air level data */ NfcVEmuAir emu_air; - uint8_t frame[NFCV_MAX_FRAME_SIZE]; /* ISO15693-2 incoming raw data from air layer */ + uint8_t* frame; /* [NFCV_FRAMESIZE_MAX] ISO15693-2 incoming raw data from air layer */ uint8_t frame_length; /* ISO15693-2 length of incoming data */ uint32_t eof_timestamp; /* ISO15693-2 EOF timestamp, read from DWT->CYCCNT */ From 4f4eca1dd7bc471b60bbd89c649c261656e7b748 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Sat, 21 Jan 2023 01:04:02 +0100 Subject: [PATCH 230/282] added "Listen NfcV Reader" to sniff traffic from reader to card --- .../main/nfc/scenes/nfc_scene_config.h | 1 + .../main/nfc/scenes/nfc_scene_extra_actions.c | 10 ++ .../main/nfc/scenes/nfc_scene_nfcv_sniff.c | 155 +++++++++++++++++ lib/nfc/nfc_device.c | 4 + lib/nfc/nfc_worker.c | 30 ++++ lib/nfc/nfc_worker.h | 2 + lib/nfc/protocols/nfcv.c | 163 +++++++++++++++++- lib/nfc/protocols/nfcv.h | 1 + 8 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index b4d5a6e1d..d6edebe73 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -19,6 +19,7 @@ ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu) ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock) ADD_SCENE(nfc, nfcv_emulate, NfcVEmulate) +ADD_SCENE(nfc, nfcv_sniff, NfcVSniff) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 9560768a5..7f5bc7e75 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -5,6 +5,7 @@ enum SubmenuIndex { SubmenuIndexMfClassicKeys, SubmenuIndexMfUltralightUnlock, SubmenuIndexNfcVUnlock, + SubmenuIndexNfcVSniff, }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -41,6 +42,12 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexNfcVUnlock, nfc_scene_extra_actions_submenu_callback, nfc); + submenu_add_item( + submenu, + "Listen NfcV Reader", + SubmenuIndexNfcVSniff, + nfc_scene_extra_actions_submenu_callback, + nfc); submenu_set_selected_item( submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); @@ -68,6 +75,9 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexNfcVUnlock) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu); consumed = true; + } else if(event.event == SubmenuIndexNfcVSniff) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVSniff); + consumed = true; } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c b/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c new file mode 100644 index 000000000..b2cb58d9f --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c @@ -0,0 +1,155 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200) + +enum { + NfcSceneNfcVSniffStateWidget, + NfcSceneNfcVSniffStateTextBox, +}; + +bool nfc_scene_nfcv_sniff_worker_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + furi_assert(context); + Nfc* nfc = context; + + switch(event) { + case NfcWorkerEventNfcVCommandExecuted: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); + break; + case NfcWorkerEventNfcVContentChanged: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); + break; + default: + break; + } + return true; +} + +void nfc_scene_nfcv_sniff_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_sniff_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +static void nfc_scene_nfcv_sniff_widget_config(Nfc* nfc, bool data_received) { + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); + widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Listen NfcV"); + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_sniff_widget_callback, nfc); + } +} + +void nfc_scene_nfcv_sniff_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_nfcv_sniff_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + text_box_set_text(text_box, ""); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVSniff, + &nfc->dev->dev_data, + nfc_scene_nfcv_sniff_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_nfcv_sniff_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVSniff); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventUpdateLog) { + // Add data button to widget if data is received for the first time + if(strlen(nfcv_data->last_command) > 0) { + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_nfcv_sniff_widget_config(nfc, true); + } + /* use the last n bytes from the log so there's enough space for the new log entry */ + size_t maxSize = + NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); + if(furi_string_size(nfc->text_box_store) >= maxSize) { + furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); + } + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + + /* clear previously logged command */ + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == NfcCustomEventSaveShadow) { + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVSniffStateWidget) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateTextBox); + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVSniffStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneNfcVSniffStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_nfcv_sniff_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 3a3b69344..0aec74f0f 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -885,6 +885,8 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { case NfcVTypeSlix2: saved = nfc_device_save_slix2_data(file, dev); break; + default: + break; } } while(false); @@ -936,6 +938,8 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { case NfcVTypeSlix2: parsed = nfc_device_load_slix2_data(file, dev); break; + default: + break; } } while(false); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 7291117a4..753bba00a 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -113,6 +113,8 @@ int32_t nfc_worker_task(void* context) { nfc_worker_analyze_reader(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) { nfc_worker_nfcv_emulate(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVSniff) { + nfc_worker_nfcv_sniff(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateNfcVUnlock) { nfc_worker_nfcv_unlock(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { @@ -182,6 +184,34 @@ void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) { } } +void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + nfcv_data->sub_type = NfcVTypeSniff; + nfcv_emu_init(nfc_data, nfcv_data); + + while(nfc_worker->state == NfcWorkerStateNfcVSniff) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); + } + } + furi_delay_ms(10); + } + nfcv_emu_deinit(nfcv_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 5b50139ee..722f14857 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -21,6 +21,7 @@ typedef enum { NfcWorkerStateNfcVEmulate, NfcWorkerStateNfcVUnlock, NfcWorkerStateNfcVUnlockAndSave, + NfcWorkerStateNfcVSniff, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -96,3 +97,4 @@ void nfc_worker_start( void nfc_worker_stop(NfcWorker* nfc_worker); void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker); +void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker); \ No newline at end of file diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index dc37860fb..c9982f156 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -732,6 +732,160 @@ void nfcv_emu_handle_packet( } } +void nfcv_emu_sniff_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + + if(nfcv_data->frame_length < 2) { + return; + } + + /* parse the frame data for the upcoming part 3 handling */ + ctx->flags = nfcv_data->frame[0]; + ctx->command = nfcv_data->frame[1]; + ctx->selected = (ctx->flags & RFAL_NFCV_REQ_FLAG_SELECT); + ctx->addressed = !(ctx->flags & RFAL_NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & RFAL_NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= 0xA0); + ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? 8 : 0); + + char flags_string[5]; + + snprintf( + flags_string, + 5, + "%c%c%c%d", + (ctx->flags & RFAL_NFCV_REQ_FLAG_INVENTORY) ? + 'I' : + (ctx->addressed ? 'A' : (ctx->selected ? 'S' : '*')), + ctx->advanced ? 'X' : ' ', + (ctx->flags & RFAL_NFCV_REQ_FLAG_DATA_RATE) ? 'h' : 'l', + (ctx->flags & RFAL_NFCV_REQ_FLAG_SUB_CARRIER) ? 2 : 1); + + switch(ctx->command) { + case ISO15693_INVENTORY: { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + break; + } + + case ISO15693_STAYQUIET: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s STAYQUIET", flags_string); + nfcv_data->quiet = true; + break; + } + + case ISO15693_LOCKBLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s LOCK %d", + flags_string, + block); + break; + } + + case ISO15693_SELECT: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s SELECT", flags_string); + break; + } + + case ISO15693_RESET_TO_READY: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s RESET", flags_string); + break; + } + + case ISO15693_READ_MULTI_BLOCK: + case ISO15693_READBLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + + if(ctx->command == ISO15693_READ_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + } + + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s READ %d cnt: %d", + flags_string, + block, + blocks); + + break; + } + + case ISO15693_WRITE_MULTI_BLOCK: + case ISO15693_WRITEBLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + uint8_t data_pos = 1; + + if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[ctx->payload_offset + data_pos]; + + if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WRITE %d, cnd %d", + flags_string, + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WRITE %d %02X %02X %02X %02X", + flags_string, + block, + data[0], + data[1], + data[2], + data[3]); + } + break; + } + + case ISO15693_GET_SYSTEM_INFO: { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s SYSTEMINFO", + flags_string); + break; + } + + default: + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s unsupported: %02X", + flags_string, + ctx->command); + break; + } + + if(strlen(nfcv_data->last_command) > 0) { + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); + } +} + void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { furi_assert(nfc_data); furi_assert(nfcv_data); @@ -765,7 +919,11 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { /* if not set already, initialize the default protocol handler */ if(!nfcv_data->emu_protocol_ctx) { nfcv_data->emu_protocol_ctx = malloc(sizeof(NfcVEmuProtocolCtx)); - nfcv_data->emu_protocol_handler = &nfcv_emu_handle_packet; + if(nfcv_data->sub_type == NfcVTypeSniff) { + nfcv_data->emu_protocol_handler = &nfcv_emu_sniff_packet; + } else { + nfcv_data->emu_protocol_handler = &nfcv_emu_handle_packet; + } } FURI_LOG_D(TAG, "Starting NfcV emulation"); @@ -801,6 +959,9 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { case NfcVTypePlain: FURI_LOG_D(TAG, " Card type: Plain"); break; + case NfcVTypeSniff: + FURI_LOG_D(TAG, " Card type: Sniffing"); + break; } /* allocate a 512 edge buffer, more than enough */ diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index 3f8e78556..fde1c933b 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -96,6 +96,7 @@ typedef enum { NfcVTypeSlixS = 2, NfcVTypeSlixL = 3, NfcVTypeSlix2 = 4, + NfcVTypeSniff = 255, } NfcVSubtype; typedef enum { From 07a44e278ce8d7d2cf036475508ede9d4287ea4c Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Sat, 21 Jan 2023 01:13:27 +0100 Subject: [PATCH 231/282] added correct description to delete menu --- applications/main/nfc/scenes/nfc_scene_delete.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/scenes/nfc_scene_delete.c b/applications/main/nfc/scenes/nfc_scene_delete.c index cbb52bfd0..0808db45a 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete.c +++ b/applications/main/nfc/scenes/nfc_scene_delete.c @@ -31,6 +31,8 @@ void nfc_scene_delete_on_enter(void* context) { nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); NfcProtocol protocol = nfc->dev->dev_data.protocol; + const char* nfc_type = "NFC-A"; + if(protocol == NfcDeviceProtocolEMV) { furi_string_set(temp_str, "EMV bank card"); } else if(protocol == NfcDeviceProtocolMifareUl) { @@ -39,12 +41,15 @@ void nfc_scene_delete_on_enter(void* context) { furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_set(temp_str, "MIFARE DESFire"); + } else if(protocol == NfcDeviceProtocolNfcV) { + furi_string_set(temp_str, "ISO15693 tag"); + nfc_type = "NFC-V"; } else { furi_string_set(temp_str, "Unknown ISO tag"); } widget_add_string_element( nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); - widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A"); + widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, nfc_type); furi_string_free(temp_str); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); From 64badf124acc011777871a2a7b7d0cd4cff6e084 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Sat, 21 Jan 2023 01:49:02 +0100 Subject: [PATCH 232/282] also added DSFID/AFI handling and locking --- .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 12 +- lib/nfc/nfc_device.c | 7 +- lib/nfc/protocols/nfcv.c | 103 +++++++++++++++++- lib/nfc/protocols/nfcv.h | 7 +- 4 files changed, 123 insertions(+), 6 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 87a23c8ef..38f3e75c7 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -87,8 +87,16 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { } furi_string_cat_printf(temp_str, "\n"); - furi_string_cat_printf(temp_str, "DSFID: %02X\n", nfcv_data->dsfid); - furi_string_cat_printf(temp_str, "AFI: %02X\n", nfcv_data->afi); + furi_string_cat_printf( + temp_str, + "DSFID: %02X %s\n", + nfcv_data->dsfid, + (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "AFI: %02X %s\n", + nfcv_data->afi, + (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref); furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 0aec74f0f..9646c262e 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -858,8 +858,11 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { if(!flipper_format_write_hex( file, "Data Content", data->data, data->block_num * data->block_size)) break; + if(!flipper_format_write_comment_cstr( + file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info")) + break; if(!flipper_format_write_hex( - file, "Security Status", data->security_status, data->block_num)) + file, "Security Status", data->security_status, 1 + data->block_num)) break; if(!flipper_format_write_comment_cstr( file, @@ -916,7 +919,7 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { /* optional, as added later */ if(flipper_format_key_exist(file, "Security Status")) { if(!flipper_format_read_hex( - file, "Security Status", data->security_status, data->block_num)) + file, "Security Status", data->security_status, 1 + data->block_num)) break; } if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index c9982f156..b2b04f878 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -586,10 +586,73 @@ void nfcv_emu_handle_packet( uint8_t block = nfcv_data->frame[ctx->payload_offset]; nfcv_data->security_status[block] |= 0x01; nfcv_data->modified = true; + + ctx->response_buffer[0] = ISO15693_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK BLOCK %d", block); break; } + case ISO15693_WRITE_DSFID: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + + if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { + nfcv_data->dsfid = id; + nfcv_data->modified = true; + ctx->response_buffer[0] = ISO15693_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE DSFID %02X", id); + break; + } + + case ISO15693_WRITE_AFI: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + + if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { + nfcv_data->afi = id; + nfcv_data->modified = true; + ctx->response_buffer[0] = ISO15693_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE AFI %02X", id); + break; + } + + case ISO15693_LOCK_DSFID: { + if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { + nfcv_data->security_status[0] |= NfcVLockBitDsfid; + nfcv_data->modified = true; + + ctx->response_buffer[0] = ISO15693_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK DSFID"); + break; + } + + case ISO15693_LOCK_AFI: { + if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { + nfcv_data->security_status[0] |= NfcVLockBitAfi; + nfcv_data->modified = true; + + ctx->response_buffer[0] = ISO15693_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK AFI"); + break; + } + case ISO15693_SELECT: { ctx->response_buffer[0] = ISO15693_NOERROR; nfcv_data->selected = true; @@ -631,7 +694,8 @@ void nfcv_emu_handle_packet( int block_current = block + block_index; /* prepend security status */ if(ctx->flags & RFAL_NFCV_REQ_FLAG_OPTION) { - ctx->response_buffer[buffer_pos++] = nfcv_data->security_status[block_current]; + ctx->response_buffer[buffer_pos++] = + nfcv_data->security_status[1 + block_current]; } /* then the data block */ memcpy( @@ -794,6 +858,43 @@ void nfcv_emu_sniff_packet( break; } + case ISO15693_WRITE_DSFID: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WR DSFID %d", + flags_string, + id); + break; + } + + case ISO15693_WRITE_AFI: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WR AFI %d", + flags_string, + id); + break; + } + + case ISO15693_LOCK_DSFID: { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s LOCK DSFID", + flags_string); + break; + } + + case ISO15693_LOCK_AFI: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s LOCK AFI", flags_string); + break; + } + case ISO15693_SELECT: { snprintf( nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s SELECT", flags_string); diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index fde1c933b..f1d6e0127 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -85,6 +85,11 @@ extern "C" { #define ISO15693_ERROR_BLOCK_WRITE 0x13 // Writing was unsuccessful #define ISO15693_ERROR_BLOCL_WRITELOCK 0x14 // Locking was unsuccessful +typedef enum { + NfcVLockBitDsfid = 1, + NfcVLockBitAfi = 2, +} NfcVLockBits; + typedef enum { NfcVAuthMethodManual, NfcVAuthMethodTonieBox, @@ -174,7 +179,7 @@ typedef struct { uint16_t block_num; uint8_t block_size; uint8_t data[NFCV_MEMSIZE_MAX]; - uint8_t security_status[NFCV_BLOCKS_MAX]; + uint8_t security_status[1 + NFCV_BLOCKS_MAX]; bool selected; bool quiet; From af7d21a02034548b0baa15de41501df80fed1c76 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Sat, 21 Jan 2023 01:51:36 +0100 Subject: [PATCH 233/282] increase sniff log size --- applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c | 2 +- lib/nfc/protocols/nfcv.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c b/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c index b2cb58d9f..2c0f17981 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" -#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200) +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (800) enum { NfcSceneNfcVSniffStateWidget, diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index b2b04f878..e18552340 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -836,7 +836,8 @@ void nfcv_emu_sniff_packet( switch(ctx->command) { case ISO15693_INVENTORY: { - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s INVENTORY", flags_string); break; } From 1d4ce4e78ac4f1339379352d9dd6cd4e318642f8 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Sat, 4 Feb 2023 01:43:04 +0100 Subject: [PATCH 234/282] scale NfcV frequency a bit, add echo mode, fix signal level at the end --- lib/nfc/protocols/nfcv.c | 118 ++++++++++++++++++++++++++++++--------- lib/nfc/protocols/nfcv.h | 9 ++- 2 files changed, 100 insertions(+), 27 deletions(-) diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index e18552340..e7b6c761a 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -270,16 +270,12 @@ bool nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_ return false; } success &= digital_signal_append(signals->nfcv_resp_eof, signals->nfcv_resp_zero); - for(size_t i = 0; i < slowdown * 24; i++) { + for(size_t i = 0; i < slowdown * 23; i++) { success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_pulse); } - for(size_t i = 0; i < slowdown * 3; i++) { - success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); - } - /* add extra silence */ - success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_unmod); - if(!success) { - return false; + /* we don't want to add the last level as we just want a transition to "unmodulated" again */ + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_half_pulse); } } return success; @@ -327,6 +323,18 @@ bool nfcv_emu_alloc(NfcVData* nfcv_data) { nfcv_data->emu_air.nfcv_resp_pulse->edge_cnt = 2; } + if(!nfcv_data->emu_air.nfcv_resp_half_pulse) { + /* modulated fc/32 or fc/8 pulse as building block */ + nfcv_data->emu_air.nfcv_resp_half_pulse = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_half_pulse) { + return false; + } + nfcv_data->emu_air.nfcv_resp_half_pulse->start_level = true; + nfcv_data->emu_air.nfcv_resp_half_pulse->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_half_pulse->edge_cnt = 1; + } + bool success = true; success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_high, 1); success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_low, 4); @@ -387,6 +395,9 @@ void nfcv_emu_free(NfcVData* nfcv_data) { if(nfcv_data->emu_air.nfcv_resp_pulse) { digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse); } + if(nfcv_data->emu_air.nfcv_resp_half_pulse) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_half_pulse); + } if(nfcv_data->emu_air.nfcv_signal) { digital_sequence_free(nfcv_data->emu_air.nfcv_signal); } @@ -397,6 +408,7 @@ void nfcv_emu_free(NfcVData* nfcv_data) { nfcv_data->frame = NULL; nfcv_data->emu_air.nfcv_resp_unmod = NULL; nfcv_data->emu_air.nfcv_resp_pulse = NULL; + nfcv_data->emu_air.nfcv_resp_half_pulse = NULL; nfcv_data->emu_air.nfcv_signal = NULL; nfcv_data->emu_air.reader_signal = NULL; @@ -490,6 +502,18 @@ void nfcv_emu_handle_packet( return; } + if(nfcv_data->echo_mode) { + nfcv_emu_send( + tx_rx, + nfcv_data, + nfcv_data->frame, + nfcv_data->frame_length, + NfcVSendFlagsSof | NfcVSendFlagsHighRate | NfcVSendFlagsEof, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO data"); + return; + } + /* parse the frame data for the upcoming part 3 handling */ ctx->flags = nfcv_data->frame[0]; ctx->command = nfcv_data->frame[1]; @@ -500,7 +524,7 @@ void nfcv_emu_handle_packet( ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); ctx->payload_offset = ctx->address_offset + (ctx->addressed ? 8 : 0); ctx->response_flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof; - ctx->send_time = nfcv_data->eof_timestamp + NFCV_FDT_FC(4130); + ctx->send_time = nfcv_data->eof_timestamp + NFCV_FDT_FC(4380); if(ctx->flags & RFAL_NFCV_REQ_FLAG_DATA_RATE) { ctx->response_flags |= NfcVSendFlagsHighRate; @@ -509,6 +533,11 @@ void nfcv_emu_handle_packet( ctx->response_flags |= NfcVSendFlagsTwoSubcarrier; } + if(ctx->payload_offset + 2 > nfcv_data->frame_length) { + FURI_LOG_D(TAG, "command 0x%02X, but packet is too short", ctx->command); + return; + } + /* standard behavior is implemented */ if(ctx->addressed) { uint8_t* address = &nfcv_data->frame[ctx->address_offset]; @@ -681,11 +710,7 @@ void nfcv_emu_handle_packet( blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; } - if(block + blocks > nfcv_data->block_num) { - ctx->response_buffer[0] = ISO15693_ERROR_CMD_NOT_REC; - nfcv_emu_send( - tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); - } else { + if(block + blocks <= nfcv_data->block_num) { uint8_t buffer_pos = 0; ctx->response_buffer[buffer_pos++] = ISO15693_NOERROR; @@ -719,31 +744,30 @@ void nfcv_emu_handle_packet( case ISO15693_WRITE_MULTI_BLOCK: case ISO15693_WRITEBLOCK: { - uint8_t block = nfcv_data->frame[ctx->payload_offset]; uint8_t blocks = 1; - uint8_t data_pos = 1; + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t data_pos = ctx->payload_offset + 1; if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) { - blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + blocks = nfcv_data->frame[data_pos] + 1; data_pos++; } - uint8_t* data = &nfcv_data->frame[ctx->payload_offset + data_pos]; + uint8_t* data = &nfcv_data->frame[data_pos]; uint32_t data_len = nfcv_data->block_size * blocks; - if(block + blocks > nfcv_data->block_num || - ctx->payload_offset + data_len + 2 > nfcv_data->frame_length) { - ctx->response_buffer[0] = ISO15693_ERROR_CMD_NOT_REC; - } else { + if((block + blocks) <= nfcv_data->block_num && + (data_pos + data_len + 2) == nfcv_data->frame_length) { ctx->response_buffer[0] = ISO15693_NOERROR; memcpy( &nfcv_data->data[nfcv_data->block_size * block], - &nfcv_data->frame[ctx->payload_offset + data_pos], + &nfcv_data->frame[data_pos], data_len); nfcv_data->modified = true; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); } - nfcv_emu_send( - tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); if(ctx->command == ISO15693_WRITE_MULTI_BLOCK) { snprintf( @@ -782,6 +806,27 @@ void nfcv_emu_handle_packet( break; } + case ISO15693_CUST_ECHO_MODE: { + ctx->response_buffer[0] = ISO15693_NOERROR; + nfcv_data->echo_mode = true; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO mode"); + break; + } + + case ISO15693_CUST_ECHO_DATA: { + nfcv_emu_send( + tx_rx, + nfcv_data, + &nfcv_data->frame[ctx->payload_offset], + nfcv_data->frame_length - ctx->payload_offset - 2, + NfcVSendFlagsSof | NfcVSendFlagsHighRate | NfcVSendFlagsEof, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO data"); + break; + } + default: snprintf( nfcv_data->last_command, @@ -1112,6 +1157,8 @@ bool nfcv_emu_loop( uint32_t byte_value = 0; uint32_t bits_received = 0; uint32_t timeout = timeout_ms * 1000; + uint32_t sof_timestamp = 0; + uint32_t eof_timestamp = 0; bool wait_for_pulse = false; if(!nfcv_data->ready) { @@ -1153,6 +1200,7 @@ bool nfcv_emu_loop( frame_state = NFCV_FRAME_STATE_SOF2; } else { frame_state = NFCV_FRAME_STATE_SOF1; + sof_timestamp = timestamp; break; } break; @@ -1186,6 +1234,7 @@ bool nfcv_emu_loop( break; } else if(periods == 2) { frame_state = NFCV_FRAME_STATE_EOF; + eof_timestamp = timestamp; break; } @@ -1226,6 +1275,7 @@ bool nfcv_emu_loop( periods_previous = 0; } else if(periods == 2) { frame_state = NFCV_FRAME_STATE_EOF; + eof_timestamp = timestamp; break; } else { frame_state = NFCV_FRAME_STATE_RESET; @@ -1259,6 +1309,24 @@ bool nfcv_emu_loop( tx_rx->sniff_rx(nfcv_data->frame, frame_pos * 8, false, tx_rx->sniff_context); } nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data); + + + /* determine readers fc by analyzing transmission duration */ + uint32_t duration = eof_timestamp - sof_timestamp; + float fc_1024 = (4.0f * duration) / (4 * (frame_pos * 4 + 1) + 1); + /* it should be 1024/fc in 64MHz ticks */ + float fact = fc_1024 / ((1000000.0f * 64.0f * 1024.0f) / NFCV_FC); + FURI_LOG_D(TAG, "1024/fc: %f -> %f %%", fc_1024, fact * 100); +#if 0 + if(fact > 0.99f && fact < 1.01f) { + static float avg_err = 0.0f; + + avg_err = (avg_err * 15.0f + (fact - 1.0f)) / 16.0f; + FURI_LOG_D(TAG, " ==> set %f %%", (1.0f + avg_err) * 100); + digital_sequence_timebase_correction(nfcv_data->emu_air.nfcv_signal, 1.0f + avg_err); + } +#endif + pulse_reader_start(nfcv_data->emu_air.reader_signal); ret = true; } else { diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index f1d6e0127..56e37f525 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -12,7 +12,7 @@ extern "C" { #endif -#define NFCV_FC (13560000.0f) /* MHz */ +#define NFCV_FC (13560000.0f / 0.9998f) /* MHz */ #define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC / 32) / 2.0f) /* 1.1799 µs */ #define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */ @@ -37,7 +37,7 @@ extern "C" { /* helpers to calculate the send time based on DWT->CYCCNT */ #define NFCV_FDT_USEC(usec) (usec * 64) -#define NFCV_FDT_FC(ticks) (ticks * 6400 / 1356) +#define NFCV_FDT_FC(ticks) ((ticks)*6400 / 1356) #define NFCV_FRAME_STATE_SOF1 0 #define NFCV_FRAME_STATE_SOF2 1 @@ -73,6 +73,9 @@ extern "C" { #define ISO15693_GET_SYSTEM_INFO 0x2B #define ISO15693_READ_MULTI_SECSTATUS 0x2C +#define ISO15693_CUST_ECHO_MODE 0xDE +#define ISO15693_CUST_ECHO_DATA 0xDF + /* ISO15693 RESPONSE ERROR CODES */ #define ISO15693_NOERROR 0x00 #define ISO15693_ERROR_CMD_NOT_SUP 0x01 // Command not supported @@ -139,6 +142,7 @@ typedef struct { typedef struct { PulseReader* reader_signal; DigitalSignal* nfcv_resp_pulse; /* pulse length, fc/32 */ + DigitalSignal* nfcv_resp_half_pulse; /* half pulse length, fc/32 */ DigitalSignal* nfcv_resp_unmod; /* unmodulated length 256/fc */ NfcVEmuAirSignals signals_high; NfcVEmuAirSignals signals_low; @@ -185,6 +189,7 @@ typedef struct { bool modified; bool ready; + bool echo_mode; /* specfic variant infos */ NfcVSubtype sub_type; From 48e7c67359b254125d4887770cc059b4162a15af Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Mon, 1 May 2023 13:07:33 +1000 Subject: [PATCH 235/282] fix debug print warnings --- lib/nfc/protocols/nfcv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index e7b6c761a..3eb295385 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -1316,13 +1316,13 @@ bool nfcv_emu_loop( float fc_1024 = (4.0f * duration) / (4 * (frame_pos * 4 + 1) + 1); /* it should be 1024/fc in 64MHz ticks */ float fact = fc_1024 / ((1000000.0f * 64.0f * 1024.0f) / NFCV_FC); - FURI_LOG_D(TAG, "1024/fc: %f -> %f %%", fc_1024, fact * 100); + FURI_LOG_D(TAG, "1024/fc: %f -> %f %%", (double)fc_1024, (double)(fact * 100)); #if 0 if(fact > 0.99f && fact < 1.01f) { static float avg_err = 0.0f; avg_err = (avg_err * 15.0f + (fact - 1.0f)) / 16.0f; - FURI_LOG_D(TAG, " ==> set %f %%", (1.0f + avg_err) * 100); + FURI_LOG_D(TAG, " ==> set %f %%", (double)((1.0f + avg_err) * 100)); digital_sequence_timebase_correction(nfcv_data->emu_air.nfcv_signal, 1.0f + avg_err); } #endif From ac9a8c5b90fc842a9a386e2fbe9c132fdd3c955c Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Mon, 1 May 2023 13:07:46 +1000 Subject: [PATCH 236/282] update api_symbols --- firmware/targets/f7/api_symbols.csv | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3e145d32f..451eb6979 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,23.1,, +Version,+,23.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2053,12 +2053,25 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* +Function,-,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" +Function,-,nfc_util_even_parity32,uint8_t,uint32_t +Function,-,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" +Function,-,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" +Function,-,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" Function,-,nfca_signal_alloc,NfcaSignal*, Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" Function,-,nfca_signal_free,void,NfcaSignal* +Function,+,nfcv_emu_deinit,void,NfcVData* +Function,+,nfcv_emu_init,void,"FuriHalNfcDevData*, NfcVData*" +Function,+,nfcv_emu_loop,_Bool,"FuriHalNfcTxRxContext*, FuriHalNfcDevData*, NfcVData*, uint32_t" +Function,+,nfcv_emu_send,void,"FuriHalNfcTxRxContext*, NfcVData*, uint8_t*, uint8_t, NfcVSendFlags, uint32_t" +Function,-,nfcv_inventory,ReturnCode,uint8_t* +Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*" +Function,-,nfcv_read_card,_Bool,"NfcVReader*, FuriHalNfcDevData*, NfcVData*" +Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*" Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" From a9e47454d5bdf3d08bf77561691e239dacd89660 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 1 May 2023 16:17:47 +0300 Subject: [PATCH 237/282] Fix SWD Probe plugin GPIO pins state Reset pins after exit --- applications/external/swd_probe/swd_probe_app.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/applications/external/swd_probe/swd_probe_app.c b/applications/external/swd_probe/swd_probe_app.c index 02cbf8a87..e8846b9e2 100644 --- a/applications/external/swd_probe/swd_probe_app.c +++ b/applications/external/swd_probe/swd_probe_app.c @@ -3166,6 +3166,11 @@ int32_t swd_probe_app_main(void* p) { furi_message_queue_free(app->event_queue); furi_mutex_free(app->gui_mutex); furi_mutex_free(app->swd_mutex); + + // Reset GPIO pins to default state + for(int io = 0; io < 8; io++) { + furi_hal_gpio_init(gpios[io], GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } free(app); furi_record_close(RECORD_GUI); From a6bc7f8abb7622cbeb99404d88d946d82785c5e6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 1 May 2023 15:28:59 +0100 Subject: [PATCH 238/282] Rework xtreme settings and assets implementation --- .pvsoptions | 2 +- .../main/archive/helpers/archive_files.h | 2 +- applications/main/bad_kb/bad_kb_app.c | 2 +- .../main/bad_kb/scenes/bad_kb_scene_config.c | 2 +- .../main/bad_kb/scenes/bad_kb_scene_error.c | 2 +- applications/main/bad_kb/views/bad_kb_view.c | 2 +- .../scenes/ibutton_scene_delete_success.c | 2 +- .../main/ibutton/scenes/ibutton_scene_read.c | 2 +- .../scenes/ibutton_scene_save_success.c | 2 +- .../scenes/ibutton_scene_write_success.c | 2 +- .../scenes/infrared_scene_edit_rename_done.c | 2 +- .../scenes/infrared_scene_learn_done.c | 2 +- .../scenes/infrared_scene_learn_success.c | 2 +- .../lfrfid/scenes/lfrfid_scene_clear_t5577.c | 2 +- .../scenes/lfrfid_scene_delete_success.c | 2 +- .../main/lfrfid/scenes/lfrfid_scene_emulate.c | 2 +- .../lfrfid/scenes/lfrfid_scene_raw_read.c | 2 +- .../main/lfrfid/scenes/lfrfid_scene_rpc.c | 2 +- .../lfrfid/scenes/lfrfid_scene_save_success.c | 2 +- .../main/lfrfid/scenes/lfrfid_scene_write.c | 2 +- .../scenes/lfrfid_scene_write_success.c | 2 +- .../nfc/scenes/nfc_scene_delete_success.c | 2 +- .../main/nfc/scenes/nfc_scene_emulate_uid.c | 2 +- .../nfc/scenes/nfc_scene_mf_classic_emulate.c | 2 +- .../nfc_scene_mf_classic_update_success.c | 2 +- .../nfc_scene_mf_classic_write_success.c | 2 +- .../scenes/nfc_scene_mf_ultralight_emulate.c | 2 +- .../nfc/scenes/nfc_scene_restore_original.c | 2 +- applications/main/nfc/scenes/nfc_scene_rpc.c | 2 +- .../main/nfc/scenes/nfc_scene_save_success.c | 2 +- .../scenes/subghz_scene_delete_success.c | 2 +- .../main/subghz/scenes/subghz_scene_rpc.c | 2 +- .../subghz/scenes/subghz_scene_save_success.c | 2 +- applications/main/subghz/views/receiver.c | 2 +- .../main/u2f/scenes/u2f_scene_error.c | 2 +- applications/main/u2f/views/u2f_view.c | 2 +- applications/main/xtreme_app/application.fam | 1 - applications/main/xtreme_app/xtreme_app.h | 3 +- applications/services/application.fam | 1 - applications/services/bt/bt_service/bt.c | 2 +- .../desktop/animations/animation_manager.c | 2 +- .../desktop/animations/animation_storage.c | 6 +- applications/services/desktop/desktop.c | 5 + .../services/desktop/helpers/pin_lock.c | 2 +- .../desktop/scenes/desktop_scene_fault.c | 2 +- .../desktop/scenes/desktop_scene_lock_menu.c | 2 +- .../desktop/views/desktop_view_lock_menu.c | 2 +- .../desktop/views/desktop_view_locked.c | 2 +- applications/services/dolphin/dolphin.c | 2 +- applications/services/gui/application.fam | 1 - applications/services/gui/canvas.c | 2 +- applications/services/gui/gui.c | 2 +- .../services/gui/modules/file_browser.c | 2 +- applications/services/gui/modules/menu.c | 2 +- applications/services/loader/loader.c | 2 +- .../services/power/power_service/power.c | 2 +- applications/services/xtreme/application.fam | 14 -- applications/services/xtreme/settings.c | 145 ------------------ applications/services/xtreme/settings.h | 52 ------- applications/services/xtreme/xtreme_srv.c | 11 -- .../bt_settings_scene_forget_dev_success.c | 2 +- .../desktop_settings_scene_pin_disable.c | 2 +- .../settings/dolphin_passport/passport.c | 2 +- .../scenes/power_settings_scene_power_off.c | 2 +- .../scenes/storage_settings_scene_unmounted.c | 2 +- firmware/targets/f7/api_symbols.csv | 5 +- firmware/targets/f7/furi_hal/furi_hal_light.c | 2 +- lib/SConscript | 1 + lib/misc.scons | 2 + .../services => lib}/xtreme/assets.c | 80 +++++----- lib/xtreme/private.h | 4 + lib/xtreme/settings.c | 126 +++++++++++++++ .../xtreme/assets.h => lib/xtreme/xtreme.h | 38 ++++- 73 files changed, 267 insertions(+), 340 deletions(-) delete mode 100644 applications/services/xtreme/application.fam delete mode 100644 applications/services/xtreme/settings.c delete mode 100644 applications/services/xtreme/settings.h delete mode 100644 applications/services/xtreme/xtreme_srv.c rename {applications/services => lib}/xtreme/assets.c (71%) create mode 100644 lib/xtreme/private.h create mode 100644 lib/xtreme/settings.c rename applications/services/xtreme/assets.h => lib/xtreme/xtreme.h (55%) diff --git a/.pvsoptions b/.pvsoptions index 6b22aed76..ecb333dec 100644 --- a/.pvsoptions +++ b/.pvsoptions @@ -1 +1 @@ ---ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap +--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/xtreme -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index e4bed3a63..7680bbd57 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -5,7 +5,7 @@ #include #include #include "toolbox/path.h" -#include "xtreme/settings.h" +#include #define FAP_MANIFEST_MAX_ICON_SIZE 32 diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index d1215d628..13a9f867c 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c index 872b99d35..dbf62e96e 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c @@ -1,7 +1,7 @@ #include "../bad_kb_app.h" #include "furi_hal_power.h" #include "furi_hal_usb.h" -#include +#include enum VarItemListIndex { VarItemListIndexKeyboardLayout, diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_error.c b/applications/main/bad_kb/scenes/bad_kb_scene_error.c index f9bdc46d4..3d29467ae 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_error.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_error.c @@ -1,5 +1,5 @@ #include "../bad_kb_app.h" -#include "xtreme/assets.h" +#include static void bad_kb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c index 4cba891f1..a05cbb282 100644 --- a/applications/main/bad_kb/views/bad_kb_view.c +++ b/applications/main/bad_kb/views/bad_kb_view.c @@ -4,7 +4,7 @@ #include #include #include -#include "xtreme/assets.h" +#include #define MAX_NAME_LEN 64 diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c index e2b7e3837..639ddbc16 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c @@ -1,5 +1,5 @@ #include "../ibutton_i.h" -#include "xtreme/assets.h" +#include static void ibutton_scene_delete_success_popup_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c index a2b3b53e4..069ce6ae1 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read.c @@ -1,6 +1,6 @@ #include "../ibutton_i.h" #include -#include "xtreme/assets.h" +#include static void ibutton_scene_read_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c index 03e88e047..81ae03059 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c @@ -1,5 +1,5 @@ #include "../ibutton_i.h" -#include "xtreme/assets.h" +#include static void ibutton_scene_save_success_popup_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/ibutton/scenes/ibutton_scene_write_success.c b/applications/main/ibutton/scenes/ibutton_scene_write_success.c index 3f565e274..cef700fe7 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_write_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_write_success.c @@ -1,5 +1,5 @@ #include "../ibutton_i.h" -#include "xtreme/assets.h" +#include static void ibutton_scene_write_success_popup_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c index 36224f418..76e555c7d 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c @@ -1,5 +1,5 @@ #include "../infrared_i.h" -#include "xtreme/assets.h" +#include void infrared_scene_edit_rename_done_on_enter(void* context) { Infrared* infrared = context; diff --git a/applications/main/infrared/scenes/infrared_scene_learn_done.c b/applications/main/infrared/scenes/infrared_scene_learn_done.c index 0d2522946..97caa20db 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_done.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_done.c @@ -1,5 +1,5 @@ #include "../infrared_i.h" -#include "xtreme/assets.h" +#include void infrared_scene_learn_done_on_enter(void* context) { Infrared* infrared = context; diff --git a/applications/main/infrared/scenes/infrared_scene_learn_success.c b/applications/main/infrared/scenes/infrared_scene_learn_success.c index bbc84ba3b..e646e7677 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_success.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_success.c @@ -1,5 +1,5 @@ #include "../infrared_i.h" -#include "xtreme/assets.h" +#include static void infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c index 8cb1d5310..6d960f331 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c @@ -1,6 +1,6 @@ #include "../lfrfid_i.h" #include "../helpers/rfid_writer.h" -#include "xtreme/assets.h" +#include static void writer_initialize(T55xxTiming* t55xxtiming) { t55xxtiming->wait_time = 400; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c index 991748515..d3952c08c 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include void lfrfid_scene_delete_success_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c index eff92dc37..b2a8be4b8 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include void lfrfid_scene_emulate_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c index 3213297d7..b9615f39c 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include #define RAW_READ_TIME 5000 diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c index 181346e9d..1555154fb 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include void lfrfid_scene_rpc_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c index 738e90bfd..612445125 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include void lfrfid_scene_save_success_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write.c b/applications/main/lfrfid/scenes/lfrfid_scene_write.c index 0f74ece45..61e92ac4f 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include static void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c index 5eeb88616..a5b90ec38 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "xtreme/assets.h" +#include void lfrfid_scene_write_success_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index d8e2652a9..07b637eb4 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include void nfc_scene_delete_success_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c index 7c016ceda..c46a2a075 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include #define NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX (200) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c index 7f5cac406..9caf0390b 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include #define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL) #define NFC_MF_CLASSIC_DATA_CHANGED (1UL) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c index 9544721b6..79256525f 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" #include -#include "xtreme/assets.h" +#include void nfc_scene_mf_classic_update_success_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c index e82dedeaf..97dc50167 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" #include -#include "xtreme/assets.h" +#include void nfc_scene_mf_classic_write_success_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c index f90d177e2..b13e169e4 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include #define NFC_MF_UL_DATA_NOT_CHANGED (0UL) #define NFC_MF_UL_DATA_CHANGED (1UL) diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original.c b/applications/main/nfc/scenes/nfc_scene_restore_original.c index 409785e26..198c54e85 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include void nfc_scene_restore_original_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc/scenes/nfc_scene_rpc.c index 6adacfda5..b57dfed37 100644 --- a/applications/main/nfc/scenes/nfc_scene_rpc.c +++ b/applications/main/nfc/scenes/nfc_scene_rpc.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include void nfc_scene_rpc_on_enter(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index d3e31fdc3..deef64524 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "xtreme/assets.h" +#include void nfc_scene_save_success_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 8e3ca5c78..252736f2d 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -1,6 +1,6 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -#include "xtreme/assets.h" +#include void subghz_scene_delete_success_popup_callback(void* context) { SubGhz* subghz = context; diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index e313ab3c2..82ab184d1 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -4,7 +4,7 @@ #include -#include "xtreme/assets.h" +#include typedef enum { SubGhzRpcStateIdle, diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c index 48804fe54..107dc7b7d 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -1,6 +1,6 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -#include "xtreme/assets.h" +#include void subghz_scene_save_success_popup_callback(void* context) { SubGhz* subghz = context; diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 4e2a1e6e6..0d38503da 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -7,7 +7,7 @@ #include #include -#include "xtreme/assets.h" +#include #define FRAME_HEIGHT 12 #define MAX_LEN_PX 111 diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index d87b13063..f77f3d731 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -1,5 +1,5 @@ #include "../u2f_app_i.h" -#include "xtreme/assets.h" +#include static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index 7bd2cf94f..4b76c2187 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -1,7 +1,7 @@ #include "u2f_view.h" #include #include -#include "xtreme/assets.h" +#include struct U2fView { View* view; diff --git a/applications/main/xtreme_app/application.fam b/applications/main/xtreme_app/application.fam index 38623386e..ce39a8739 100644 --- a/applications/main/xtreme_app/application.fam +++ b/applications/main/xtreme_app/application.fam @@ -7,7 +7,6 @@ App( requires=[ "gui", "dolphin", - "xtreme", ], stack_size=2 * 1024, icon="A_Xtreme_14", diff --git a/applications/main/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h index a352d86ee..15cd71ce9 100644 --- a/applications/main/xtreme_app/xtreme_app.h +++ b/applications/main/xtreme_app/xtreme_app.h @@ -23,8 +23,7 @@ #include #include #include -#include "xtreme/settings.h" -#include "xtreme/assets.h" +#include #define XTREME_SUBGHZ_FREQ_BUFFER_SIZE 6 diff --git a/applications/services/application.fam b/applications/services/application.fam index 62aec990d..af02ef1a1 100644 --- a/applications/services/application.fam +++ b/applications/services/application.fam @@ -6,7 +6,6 @@ App( "crypto_start", "rpc_start", "bt", - "xtreme", "desktop", "loader", "power", diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 22c62eff1..b4a6b25a4 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -5,7 +5,7 @@ #include #include #include -#include "xtreme/assets.h" +#include #define TAG "BtSrv" diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index f4bf320ff..394ffee30 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -14,7 +14,7 @@ #include "animation_storage.h" #include "animation_manager.h" -#include "xtreme/assets.h" +#include #define TAG "AnimationManager" diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 30fa0c014..674c67a91 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -11,7 +11,7 @@ #include "animation_storage_i.h" #include #include -#include "xtreme/assets.h" +#include #define ANIMATION_META_FILE "meta.txt" #define BASE_ANIMATION_DIR EXT_PATH("dolphin") #define TAG "AnimationStorage" @@ -20,7 +20,7 @@ #define ANIMATION_MANIFEST_FILE ANIMATION_DIR "/manifest.txt" */ -// 59 Max length = strlen("/ext/dolphin_custom//Anims") + MAX_PACK_NAME_LEN + 1 (Null terminator) +// 59 Max length = strlen("/ext/dolphin_custom//Anims") + XTREME_ASSETS_PACK_NAME_LEN + 1 (Null terminator) char ANIMATION_DIR[59]; // 72 Max length = ANIMATION_DIR + strlen("/manifest.txt") char ANIMATION_MANIFEST_FILE[72]; @@ -36,7 +36,7 @@ void animation_handler_select_manifest(bool force_stock) { FuriString* manifest = furi_string_alloc(); bool use_asset_pack = !force_stock && xtreme_settings->asset_pack[0] != '\0'; if(use_asset_pack) { - furi_string_printf(anim_dir, "%s/%s/Anims", PACKS_DIR, xtreme_settings->asset_pack); + furi_string_printf(anim_dir, "%s/%s/Anims", XTREME_ASSETS_PATH, xtreme_settings->asset_pack); furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index dfed41dbb..d0d61ee1c 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -15,6 +15,7 @@ #include "desktop/views/desktop_view_pin_timeout.h" #include "desktop_i.h" #include "helpers/pin_lock.h" +#include #define TAG "Desktop" @@ -347,6 +348,10 @@ int32_t desktop_srv(void* p) { furi_hal_rtc_reset_flag(FuriHalRtcFlagResetPin); } + // furi_delay_ms(1000); + XTREME_SETTINGS_LOAD(); + XTREME_ASSETS_LOAD(); + Desktop* desktop = desktop_alloc(); bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings); diff --git a/applications/services/desktop/helpers/pin_lock.c b/applications/services/desktop/helpers/pin_lock.c index 73e688bf5..2439a4ebc 100644 --- a/applications/services/desktop/helpers/pin_lock.c +++ b/applications/services/desktop/helpers/pin_lock.c @@ -10,7 +10,7 @@ #include "../desktop_i.h" #include #include -#include +#include static const NotificationSequence sequence_pin_fail = { &message_display_backlight_on, diff --git a/applications/services/desktop/scenes/desktop_scene_fault.c b/applications/services/desktop/scenes/desktop_scene_fault.c index b3801d78d..2b2dce50c 100644 --- a/applications/services/desktop/scenes/desktop_scene_fault.c +++ b/applications/services/desktop/scenes/desktop_scene_fault.c @@ -1,7 +1,7 @@ #include #include "../desktop_i.h" -#include "xtreme/assets.h" +#include #define DesktopFaultEventExit 0x00FF00FF diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index ac529b779..e68d28076 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -5,7 +5,7 @@ #include #include // #include -#include +#include #include "../desktop_i.h" #include diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 334cf9376..204dd2cfd 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include "../desktop_i.h" diff --git a/applications/services/desktop/views/desktop_view_locked.c b/applications/services/desktop/views/desktop_view_locked.c index 6d3aeb819..dc081e88d 100644 --- a/applications/services/desktop/views/desktop_view_locked.c +++ b/applications/services/desktop/views/desktop_view_locked.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include "../desktop_i.h" diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index df34abe78..5e5ff07d3 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #define DOLPHIN_LOCK_EVENT_FLAG (0x1) #define TAG "Dolphin" diff --git a/applications/services/gui/application.fam b/applications/services/gui/application.fam index 7981a4fcb..193b9dfd3 100644 --- a/applications/services/gui/application.fam +++ b/applications/services/gui/application.fam @@ -7,7 +7,6 @@ App( requires=[ "input", "notification", - "xtreme", ], stack_size=2 * 1024, order=70, diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 98fd96e8c..e4ddfb21e 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include const CanvasFontParameters canvas_font_params[FontTotalNumber] = { [FontPrimary] = {.leading_default = 12, .leading_min = 11, .height = 8, .descender = 2}, diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index dd1291890..e914e807f 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -1,4 +1,4 @@ -#include "xtreme/settings.h" +#include #include "gui_i.h" #include diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 8affe8330..4f11ffe8d 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -13,7 +13,7 @@ #include #include "m-algo.h" #include -#include "xtreme/settings.h" +#include #define LIST_ITEMS 5u #define MAX_LEN_PX 110 diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index 5c0da9c8f..cf42524af 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include struct Menu { diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index f915cda1d..7e40ae54e 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #define TAG "LoaderSrv" diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 9b9a2356f..b6f8d98a9 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -2,7 +2,7 @@ #include #include -#include "xtreme/settings.h" +#include #define POWER_OFF_TIMEOUT 90 #define TAG "Power" diff --git a/applications/services/xtreme/application.fam b/applications/services/xtreme/application.fam deleted file mode 100644 index beb19e437..000000000 --- a/applications/services/xtreme/application.fam +++ /dev/null @@ -1,14 +0,0 @@ -App( - appid="xtreme", - name="Xtreme", - apptype=FlipperAppType.SERVICE, - entry_point="xtreme_srv", - cdefines=["SRV_XTREME"], - requires=["storage"], - stack_size=2 * 1024, - order=46, - sdk_headers=[ - "settings.h", - "assets.h", - ], -) diff --git a/applications/services/xtreme/settings.c b/applications/services/xtreme/settings.c deleted file mode 100644 index 21c8080e8..000000000 --- a/applications/services/xtreme/settings.c +++ /dev/null @@ -1,145 +0,0 @@ -#include "settings.h" - -#include - -#define TAG "XtremeSettings" - -XtremeSettings* xtreme_settings = NULL; - -XtremeSettings* XTREME_SETTINGS() { - if(xtreme_settings == NULL) { - XTREME_SETTINGS_LOAD(); - } - return xtreme_settings; -} - -void XTREME_SETTINGS_LOAD() { - if(xtreme_settings == NULL) { - xtreme_settings = malloc(sizeof(XtremeSettings)); - - // Set default values - memset(xtreme_settings, 0, sizeof(XtremeSettings)); - strlcpy(xtreme_settings->asset_pack, "", MAX_PACK_NAME_LEN); // SFW - xtreme_settings->anim_speed = 100; // 100% - xtreme_settings->cycle_anims = 0; // Meta.txt - xtreme_settings->unlock_anims = false; // OFF - xtreme_settings->fallback_anim = true; // ON - xtreme_settings->wii_menu = true; // ON - xtreme_settings->bad_pins_format = false; // OFF - xtreme_settings->lockscreen_time = true; // ON - xtreme_settings->lockscreen_seconds = false; // OFF - xtreme_settings->lockscreen_date = true; // ON - xtreme_settings->lockscreen_statusbar = true; // ON - xtreme_settings->lockscreen_prompt = true; // ON - xtreme_settings->battery_icon = BatteryIconBarPercent; // Bar % - xtreme_settings->status_icons = true; // ON - xtreme_settings->bar_borders = true; // ON - xtreme_settings->bar_background = false; // OFF - xtreme_settings->sort_dirs_first = true; // ON - xtreme_settings->dark_mode = false; // OFF - xtreme_settings->bad_bt = false; // USB - xtreme_settings->bad_bt_remember = false; // OFF - xtreme_settings->butthurt_timer = 43200; // 12 H - xtreme_settings->rgb_backlight = false; // OFF - - if(!furi_hal_is_normal_boot()) { - FURI_LOG_W(TAG, "Load skipped. Device is in special startup mode."); - return; - } - - XtremeSettings* x = xtreme_settings; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_existing(file, XTREME_SETTINGS_PATH)) { - FuriString* string = furi_string_alloc(); - if(flipper_format_read_string(file, "asset_pack", string)) { - strlcpy(x->asset_pack, furi_string_get_cstr(string), MAX_PACK_NAME_LEN); - } - furi_string_free(string); - flipper_format_rewind(file); - flipper_format_read_uint32(file, "anim_speed", &x->anim_speed, 1); - flipper_format_rewind(file); - flipper_format_read_int32(file, "cycle_anims", &x->cycle_anims, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "unlock_anims", &x->unlock_anims, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "fallback_anim", &x->fallback_anim, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "wii_menu", &x->wii_menu, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "bad_pins_format", &x->bad_pins_format, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "lockscreen_time", &x->lockscreen_time, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "lockscreen_seconds", &x->lockscreen_seconds, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "lockscreen_date", &x->lockscreen_date, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "lockscreen_statusbar", &x->lockscreen_statusbar, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "lockscreen_prompt", &x->lockscreen_prompt, 1); - flipper_format_rewind(file); - flipper_format_read_uint32(file, "battery_icon", (uint32_t*)&x->battery_icon, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "status_icons", &x->status_icons, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "bar_borders", &x->bar_borders, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "bar_background", &x->bar_background, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "dark_mode", &x->dark_mode, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "bad_bt", &x->bad_bt, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); - flipper_format_rewind(file); - flipper_format_read_int32(file, "butthurt_timer", &x->butthurt_timer, 1); - flipper_format_rewind(file); - flipper_format_read_bool(file, "rgb_backlight", &x->rgb_backlight, 1); - } - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); - } -} - -void XTREME_SETTINGS_SAVE() { - if(xtreme_settings == NULL) { - XTREME_SETTINGS_LOAD(); - } - - if(!furi_hal_is_normal_boot()) { - return; - } - - XtremeSettings* x = xtreme_settings; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_always(file, XTREME_SETTINGS_PATH)) { - flipper_format_write_string_cstr(file, "asset_pack", x->asset_pack); - flipper_format_write_uint32(file, "anim_speed", &x->anim_speed, 1); - flipper_format_write_int32(file, "cycle_anims", &x->cycle_anims, 1); - flipper_format_write_bool(file, "unlock_anims", &x->unlock_anims, 1); - flipper_format_write_bool(file, "fallback_anim", &x->fallback_anim, 1); - flipper_format_write_bool(file, "wii_menu", &x->wii_menu, 1); - flipper_format_write_bool(file, "bad_pins_format", &x->bad_pins_format, 1); - flipper_format_write_bool(file, "lockscreen_time", &x->lockscreen_time, 1); - flipper_format_write_bool(file, "lockscreen_seconds", &x->lockscreen_seconds, 1); - flipper_format_write_bool(file, "lockscreen_date", &x->lockscreen_date, 1); - flipper_format_write_bool(file, "lockscreen_statusbar", &x->lockscreen_statusbar, 1); - flipper_format_write_bool(file, "lockscreen_prompt", &x->lockscreen_prompt, 1); - flipper_format_write_uint32(file, "battery_icon", (uint32_t*)&x->battery_icon, 1); - flipper_format_write_bool(file, "status_icons", &x->status_icons, 1); - flipper_format_write_bool(file, "bar_borders", &x->bar_borders, 1); - flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); - flipper_format_write_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); - flipper_format_write_bool(file, "dark_mode", &x->dark_mode, 1); - flipper_format_write_bool(file, "bad_bt", &x->bad_bt, 1); - flipper_format_write_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); - flipper_format_write_int32(file, "butthurt_timer", &x->butthurt_timer, 1); - flipper_format_write_bool(file, "rgb_backlight", &x->rgb_backlight, 1); - } - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); -} diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h deleted file mode 100644 index 87fb44f75..000000000 --- a/applications/services/xtreme/settings.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define MAX_PACK_NAME_LEN 32 - -#define XTREME_SETTINGS_PATH CFG_PATH("xtreme_settings.txt") - -#define XTREME_APPS_PATH CFG_PATH("xtreme_apps.txt") - -typedef struct { - char asset_pack[MAX_PACK_NAME_LEN]; - uint32_t anim_speed; - int32_t cycle_anims; - bool unlock_anims; - bool fallback_anim; - bool wii_menu; - bool bad_pins_format; - bool lockscreen_time; - bool lockscreen_seconds; - bool lockscreen_date; - bool lockscreen_statusbar; - bool lockscreen_prompt; - BatteryIcon battery_icon; - bool status_icons; - bool bar_borders; - bool bar_background; - bool sort_dirs_first; - bool dark_mode; - bool bad_bt; - bool bad_bt_remember; - int32_t butthurt_timer; - bool rgb_backlight; -} XtremeSettings; - -XtremeSettings* XTREME_SETTINGS(); - -void XTREME_SETTINGS_LOAD(); - -void XTREME_SETTINGS_SAVE(); - -#ifdef __cplusplus -} -#endif diff --git a/applications/services/xtreme/xtreme_srv.c b/applications/services/xtreme/xtreme_srv.c deleted file mode 100644 index fa6fb97f2..000000000 --- a/applications/services/xtreme/xtreme_srv.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "settings.h" -#include "assets.h" - -int32_t xtreme_srv(void* p) { - UNUSED(p); - - XTREME_SETTINGS_LOAD(); - XTREME_ASSETS_LOAD(); - - return 0; -} diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c index c9e4b17d4..e8aed9b45 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c @@ -1,5 +1,5 @@ #include "../bt_settings_app.h" -#include "xtreme/assets.h" +#include #include void bt_settings_app_scene_forget_dev_success_popup_callback(void* context) { diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index cad5f43f4..0ae92062a 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -6,7 +6,7 @@ #include "../desktop_settings_app.h" #include #include "desktop_settings_scene.h" -#include "xtreme/assets.h" +#include #define SCENE_EVENT_EXIT (0U) diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index d6474b4cf..dc7f754eb 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -6,7 +6,7 @@ #include #include #include "dolphin/dolphin.h" -#include "xtreme/assets.h" +#include #include "math.h" typedef struct { diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index afcc6f950..9b35e9eb6 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -1,5 +1,5 @@ #include "../power_settings_app.h" -#include "xtreme/assets.h" +#include void power_settings_scene_power_off_dialog_callback(DialogExResult result, void* context) { furi_assert(context); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c index 13f53acd1..dca53b60a 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c @@ -1,5 +1,5 @@ #include "../storage_settings.h" -#include "xtreme/assets.h" +#include static void storage_settings_scene_unmounted_dialog_callback(DialogExResult result, void* context) { diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 6e1781f75..b78883d85 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -40,8 +40,6 @@ Header,+,applications/services/power/power_service/power.h,, Header,+,applications/services/rgb_backlight/rgb_backlight.h,, Header,+,applications/services/rpc/rpc_app.h,, Header,+,applications/services/storage/storage.h,, -Header,+,applications/services/xtreme/assets.h,, -Header,+,applications/services/xtreme/settings.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_clock.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_console.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_flash.h,, @@ -218,6 +216,7 @@ Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, Header,+,lib/u8g2/u8g2.h,, +Header,+,lib/xtreme/xtreme.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* @@ -322,9 +321,7 @@ Function,-,SK6805_update,void, Function,-,SystemCoreClockUpdate,void, Function,-,SystemInit,void, Function,+,XTREME_ASSETS,XtremeAssets*, -Function,-,XTREME_ASSETS_LOAD,void, Function,+,XTREME_SETTINGS,XtremeSettings*, -Function,-,XTREME_SETTINGS_LOAD,void, Function,+,XTREME_SETTINGS_SAVE,void, Function,-,_Exit,void,int Function,-,__assert,void,"const char*, int, const char*" diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c index 7d303cfaa..5ce624d1e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #define LED_CURRENT_RED 50 diff --git a/lib/SConscript b/lib/SConscript index 9742d81a3..28a508471 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -45,6 +45,7 @@ env.Append( ) ), File("u8g2/u8g2.h"), + File("xtreme/xtreme.h"), ], CPPDEFINES=[ '"M_MEMORY_FULL(x)=abort()"', diff --git a/lib/misc.scons b/lib/misc.scons index a68f9d47d..63681f2f2 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -11,6 +11,7 @@ env.Append( "#/lib/micro-ecc", "#/lib/nanopb", "#/lib/u8g2", + "#/lib/xtreme", ], CPPDEFINES=[ "PB_ENABLE_MALLOC", @@ -32,6 +33,7 @@ libs_recurse = [ "micro-ecc", "u8g2", "update_util", + "xtreme", ] for lib in libs_recurse: diff --git a/applications/services/xtreme/assets.c b/lib/xtreme/assets.c similarity index 71% rename from applications/services/xtreme/assets.c rename to lib/xtreme/assets.c index 574e932ce..3c27f838c 100644 --- a/applications/services/xtreme/assets.c +++ b/lib/xtreme/assets.c @@ -1,12 +1,40 @@ -#include "assets.h" +#include "xtreme.h" +#include "private.h" #include +#include #include #define TAG "XtremeAssets" -#define ICONS_FMT PACKS_DIR "/%s/Icons/%s" +#define ICONS_FMT XTREME_ASSETS_PATH "/%s/Icons/%s" -XtremeAssets* xtreme_assets = NULL; +XtremeAssets xtreme_assets = { + .A_Levelup_128x64 = &A_Levelup_128x64, + .I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64, + .I_DolphinCommon_56x48 = &I_DolphinCommon_56x48, + .I_DolphinMafia_115x62 = &I_DolphinMafia_115x62, + .I_DolphinNice_96x59 = &I_DolphinNice_96x59, + .I_DolphinWait_61x59 = &I_DolphinWait_61x59, + .I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52, + .I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63, + .I_Lockscreen = &I_Lockscreen, + .I_WarningDolphin_45x42 = &I_WarningDolphin_45x42, + .I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61, + .I_passport_bad_46x49 = &I_passport_bad_46x49, + .I_passport_DB = &I_passport_DB, + .I_passport_happy_46x49 = &I_passport_happy_46x49, + .I_passport_okay_46x49 = &I_passport_okay_46x49, + .I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61, + .I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61, + .I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57, + .I_Cry_dolph_55x52 = &I_Cry_dolph_55x52, + .I_Fishing_123x52 = &I_Fishing_123x52, + .I_Scanning_123x52 = &I_Scanning_123x52, + .I_Auth_62x31 = &I_Auth_62x31, + .I_Connect_me_62x31 = &I_Connect_me_62x31, + .I_Connected_62x31 = &I_Connected_62x31, + .I_Error_62x31 = &I_Error_62x31, +}; void anim(const Icon** replace, const char* name, FuriString* path, File* file) { do { @@ -113,44 +141,9 @@ void swap(XtremeAssets* x, FuriString* p, File* f) { } void XTREME_ASSETS_LOAD() { - if(xtreme_assets != NULL) return; - - xtreme_assets = malloc(sizeof(XtremeAssets)); - - xtreme_assets->A_Levelup_128x64 = &A_Levelup_128x64; - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; - xtreme_assets->I_Lockscreen = &I_Lockscreen; - xtreme_assets->I_WarningDolphin_45x42 = &I_WarningDolphin_45x42; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; - xtreme_assets->I_passport_bad_46x49 = &I_passport_bad_46x49; - xtreme_assets->I_passport_DB = &I_passport_DB; - xtreme_assets->I_passport_happy_46x49 = &I_passport_happy_46x49; - xtreme_assets->I_passport_okay_46x49 = &I_passport_okay_46x49; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; - xtreme_assets->I_Fishing_123x52 = &I_Fishing_123x52; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; - xtreme_assets->I_Error_62x31 = &I_Error_62x31; - - if(!furi_hal_is_normal_boot()) { - FURI_LOG_W(TAG, "Load skipped. Device is in special startup mode."); - return; - } - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); if(xtreme_settings->asset_pack[0] == '\0') return; - xtreme_assets->is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; + xtreme_assets.is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; Storage* storage = furi_record_open(RECORD_STORAGE); int32_t timeout = 5000; @@ -162,11 +155,11 @@ void XTREME_ASSETS_LOAD() { FileInfo info; FuriString* path = furi_string_alloc(); - furi_string_printf(path, PACKS_DIR "/%s", xtreme_settings->asset_pack); + furi_string_printf(path, XTREME_ASSETS_PATH "/%s", xtreme_settings->asset_pack); if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && info.flags & FSF_DIRECTORY) { File* file = storage_file_alloc(storage); - swap(xtreme_assets, path, file); + swap(&xtreme_assets, path, file); storage_file_free(file); } furi_string_free(path); @@ -174,8 +167,5 @@ void XTREME_ASSETS_LOAD() { } XtremeAssets* XTREME_ASSETS() { - if(xtreme_assets == NULL) { - XTREME_ASSETS_LOAD(); - } - return xtreme_assets; + return &xtreme_assets; } diff --git a/lib/xtreme/private.h b/lib/xtreme/private.h new file mode 100644 index 000000000..8b220f586 --- /dev/null +++ b/lib/xtreme/private.h @@ -0,0 +1,4 @@ +#pragma once + +void XTREME_ASSETS_LOAD(); +void XTREME_SETTINGS_LOAD(); diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c new file mode 100644 index 000000000..1cafa6f59 --- /dev/null +++ b/lib/xtreme/settings.c @@ -0,0 +1,126 @@ +#include "xtreme.h" +#include "private.h" +#include +#include + +#define TAG "XtremeSettings" + +XtremeSettings xtreme_settings = { + .asset_pack = "", + .anim_speed = 100, // 100% + .cycle_anims = 0, // Meta.txt + .unlock_anims = false, // OFF + .fallback_anim = true, // ON + .wii_menu = true, // ON + .bad_pins_format = false, // OFF + .lockscreen_time = true, // ON + .lockscreen_seconds = false, // OFF + .lockscreen_date = true, // ON + .lockscreen_statusbar = true, // ON + .lockscreen_prompt = true, // ON + .battery_icon = BatteryIconBarPercent, // Bar % + .status_icons = true, // ON + .bar_borders = true, // ON + .bar_background = false, // OFF + .sort_dirs_first = true, // ON + .dark_mode = false, // OFF + .bad_bt = false, // USB + .bad_bt_remember = false, // OFF + .butthurt_timer = 43200, // 12 H + .rgb_backlight = false, // OFF +}; + +void XTREME_SETTINGS_LOAD() { + XtremeSettings* x = &xtreme_settings; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_existing(file, XTREME_SETTINGS_PATH)) { + FuriString* string = furi_string_alloc(); + if(flipper_format_read_string(file, "asset_pack", string)) { + strlcpy(x->asset_pack, furi_string_get_cstr(string), XTREME_ASSETS_PACK_NAME_LEN); + } + furi_string_free(string); + flipper_format_rewind(file); + flipper_format_read_uint32(file, "anim_speed", &x->anim_speed, 1); + flipper_format_rewind(file); + flipper_format_read_int32(file, "cycle_anims", &x->cycle_anims, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "unlock_anims", &x->unlock_anims, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "fallback_anim", &x->fallback_anim, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "wii_menu", &x->wii_menu, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bad_pins_format", &x->bad_pins_format, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_time", &x->lockscreen_time, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_seconds", &x->lockscreen_seconds, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_date", &x->lockscreen_date, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_statusbar", &x->lockscreen_statusbar, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "lockscreen_prompt", &x->lockscreen_prompt, 1); + flipper_format_rewind(file); + flipper_format_read_uint32(file, "battery_icon", (uint32_t*)&x->battery_icon, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "status_icons", &x->status_icons, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bar_borders", &x->bar_borders, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bar_background", &x->bar_background, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "dark_mode", &x->dark_mode, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bad_bt", &x->bad_bt, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); + flipper_format_rewind(file); + flipper_format_read_int32(file, "butthurt_timer", &x->butthurt_timer, 1); + flipper_format_rewind(file); + flipper_format_read_bool(file, "rgb_backlight", &x->rgb_backlight, 1); + } + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); +} + +void XTREME_SETTINGS_SAVE() { + if(!furi_hal_is_normal_boot()) return; + + XtremeSettings* x = &xtreme_settings; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_always(file, XTREME_SETTINGS_PATH)) { + flipper_format_write_string_cstr(file, "asset_pack", x->asset_pack); + flipper_format_write_uint32(file, "anim_speed", &x->anim_speed, 1); + flipper_format_write_int32(file, "cycle_anims", &x->cycle_anims, 1); + flipper_format_write_bool(file, "unlock_anims", &x->unlock_anims, 1); + flipper_format_write_bool(file, "fallback_anim", &x->fallback_anim, 1); + flipper_format_write_bool(file, "wii_menu", &x->wii_menu, 1); + flipper_format_write_bool(file, "bad_pins_format", &x->bad_pins_format, 1); + flipper_format_write_bool(file, "lockscreen_time", &x->lockscreen_time, 1); + flipper_format_write_bool(file, "lockscreen_seconds", &x->lockscreen_seconds, 1); + flipper_format_write_bool(file, "lockscreen_date", &x->lockscreen_date, 1); + flipper_format_write_bool(file, "lockscreen_statusbar", &x->lockscreen_statusbar, 1); + flipper_format_write_bool(file, "lockscreen_prompt", &x->lockscreen_prompt, 1); + flipper_format_write_uint32(file, "battery_icon", (uint32_t*)&x->battery_icon, 1); + flipper_format_write_bool(file, "status_icons", &x->status_icons, 1); + flipper_format_write_bool(file, "bar_borders", &x->bar_borders, 1); + flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); + flipper_format_write_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); + flipper_format_write_bool(file, "dark_mode", &x->dark_mode, 1); + flipper_format_write_bool(file, "bad_bt", &x->bad_bt, 1); + flipper_format_write_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); + flipper_format_write_int32(file, "butthurt_timer", &x->butthurt_timer, 1); + flipper_format_write_bool(file, "rgb_backlight", &x->rgb_backlight, 1); + } + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); +} + +XtremeSettings* XTREME_SETTINGS() { + return &xtreme_settings; +} diff --git a/applications/services/xtreme/assets.h b/lib/xtreme/xtreme.h similarity index 55% rename from applications/services/xtreme/assets.h rename to lib/xtreme/xtreme.h index a4995fd04..63f457c27 100644 --- a/applications/services/xtreme/assets.h +++ b/lib/xtreme/xtreme.h @@ -1,14 +1,44 @@ #pragma once -#include "settings.h" #include -#include +#include #ifdef __cplusplus extern "C" { #endif -#define PACKS_DIR EXT_PATH("dolphin_custom") +#define XTREME_SETTINGS_PATH CFG_PATH("xtreme_settings.txt") +#define XTREME_ASSETS_PATH EXT_PATH("dolphin_custom") +#define XTREME_APPS_PATH CFG_PATH("xtreme_apps.txt") +#define XTREME_ASSETS_PACK_NAME_LEN 32 + +typedef struct { + char asset_pack[XTREME_ASSETS_PACK_NAME_LEN]; + uint32_t anim_speed; + int32_t cycle_anims; + bool unlock_anims; + bool fallback_anim; + bool wii_menu; + bool bad_pins_format; + bool lockscreen_time; + bool lockscreen_seconds; + bool lockscreen_date; + bool lockscreen_statusbar; + bool lockscreen_prompt; + BatteryIcon battery_icon; + bool status_icons; + bool bar_borders; + bool bar_background; + bool sort_dirs_first; + bool dark_mode; + bool bad_bt; + bool bad_bt_remember; + int32_t butthurt_timer; + bool rgb_backlight; +} XtremeSettings; + +XtremeSettings* XTREME_SETTINGS(); +void XTREME_SETTINGS_SAVE(); typedef struct { bool is_nsfw; @@ -39,8 +69,6 @@ typedef struct { const Icon* I_Error_62x31; } XtremeAssets; -void XTREME_ASSETS_LOAD(); - XtremeAssets* XTREME_ASSETS(); #ifdef __cplusplus From 915242bf11728a59deff1fd900cb86640800f472 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 1 May 2023 15:34:16 +0100 Subject: [PATCH 239/282] Begone, debug comment --- applications/services/desktop/desktop.c | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index d0d61ee1c..45435411a 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -348,7 +348,6 @@ int32_t desktop_srv(void* p) { furi_hal_rtc_reset_flag(FuriHalRtcFlagResetPin); } - // furi_delay_ms(1000); XTREME_SETTINGS_LOAD(); XTREME_ASSETS_LOAD(); From 22910f8ca51342e587c34e3d013e9c780690814f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 1 May 2023 15:35:53 +0100 Subject: [PATCH 240/282] Format --- applications/services/desktop/animations/animation_storage.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 674c67a91..0905a6d43 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -36,7 +36,8 @@ void animation_handler_select_manifest(bool force_stock) { FuriString* manifest = furi_string_alloc(); bool use_asset_pack = !force_stock && xtreme_settings->asset_pack[0] != '\0'; if(use_asset_pack) { - furi_string_printf(anim_dir, "%s/%s/Anims", XTREME_ASSETS_PATH, xtreme_settings->asset_pack); + furi_string_printf( + anim_dir, "%s/%s/Anims", XTREME_ASSETS_PATH, xtreme_settings->asset_pack); furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { From e0b9c2ea3f96fcaeb2527a4363632c42399a15df Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 1 May 2023 15:40:13 +0100 Subject: [PATCH 241/282] =?UTF-8?q?Fix=20build=20(forgor=20to=20rename=20t?= =?UTF-8?q?his=20=F0=9F=92=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scenes/xtreme_app_scene_interface_graphics.c | 2 +- applications/main/xtreme_app/xtreme_app.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c index 2cfb1f7cc..462f7c6d3 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c @@ -20,7 +20,7 @@ static void xtreme_app_scene_interface_graphics_asset_pack_changed(VariableItem* strlcpy( XTREME_SETTINGS()->asset_pack, index == 0 ? "" : *CharList_get(app->asset_pack_names, index - 1), - MAX_PACK_NAME_LEN); + XTREME_ASSETS_PACK_NAME_LEN); app->asset_pack_index = index; app->save_settings = true; app->require_reboot = true; diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c index eabd25581..27091ac67 100644 --- a/applications/main/xtreme_app/xtreme_app.c +++ b/applications/main/xtreme_app/xtreme_app.c @@ -188,12 +188,12 @@ XtremeApp* xtreme_app_alloc() { Storage* storage = furi_record_open(RECORD_STORAGE); File* folder = storage_file_alloc(storage); FileInfo info; - char* name = malloc(MAX_PACK_NAME_LEN); + char* name = malloc(XTREME_ASSETS_PACK_NAME_LEN); if(storage_dir_open(folder, PACKS_DIR)) { - while(storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) { + while(storage_dir_read(folder, &info, name, XTREME_ASSETS_PACK_NAME_LEN)) { if(info.flags & FSF_DIRECTORY) { - char* copy = malloc(MAX_PACK_NAME_LEN); - strlcpy(copy, name, MAX_PACK_NAME_LEN); + char* copy = malloc(XTREME_ASSETS_PACK_NAME_LEN); + strlcpy(copy, name, XTREME_ASSETS_PACK_NAME_LEN); uint idx = 0; if(strcmp(copy, "NSFW") != 0) { for(; idx < CharList_size(app->asset_pack_names); idx++) { From b19573bcc57ce7db205f89baf8e0c59b0ae11031 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 1 May 2023 15:44:34 +0100 Subject: [PATCH 242/282] =?UTF-8?q?Fix=20build=20pt2=20(forgor=20this=20to?= =?UTF-8?q?o=20=F0=9F=92=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- applications/main/xtreme_app/xtreme_app.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c index 27091ac67..57706233e 100644 --- a/applications/main/xtreme_app/xtreme_app.c +++ b/applications/main/xtreme_app/xtreme_app.c @@ -189,7 +189,7 @@ XtremeApp* xtreme_app_alloc() { File* folder = storage_file_alloc(storage); FileInfo info; char* name = malloc(XTREME_ASSETS_PACK_NAME_LEN); - if(storage_dir_open(folder, PACKS_DIR)) { + if(storage_dir_open(folder, XTREME_ASSETS_PATH)) { while(storage_dir_read(folder, &info, name, XTREME_ASSETS_PACK_NAME_LEN)) { if(info.flags & FSF_DIRECTORY) { char* copy = malloc(XTREME_ASSETS_PACK_NAME_LEN); From 3b40696c9efa713c5460800b8d1403502a4a25e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D0=BD=D1=8C=20=3A=29?= <88856726+leo-need-more-coffee@users.noreply.github.com> Date: Mon, 1 May 2023 20:37:25 +0300 Subject: [PATCH 243/282] Add files via upload --- .../external/flipperzero-bomberduck/LICENSE | 22 + .../external/flipperzero-bomberduck/README.md | 2 + .../flipperzero-bomberduck/application.fam | 15 + .../flipperzero-bomberduck/assets/bomb0.png | Bin 0 -> 4536 bytes .../flipperzero-bomberduck/assets/bomb1.png | Bin 0 -> 4529 bytes .../flipperzero-bomberduck/assets/bomb2.png | Bin 0 -> 4531 bytes .../flipperzero-bomberduck/assets/box.png | Bin 0 -> 4329 bytes .../flipperzero-bomberduck/assets/end.png | Bin 0 -> 4739 bytes .../flipperzero-bomberduck/assets/enemy1.png | Bin 0 -> 4757 bytes .../assets/enemyleft.png | Bin 0 -> 4761 bytes .../assets/enemyright.png | Bin 0 -> 4536 bytes .../flipperzero-bomberduck/assets/explore.png | Bin 0 -> 4540 bytes .../assets/playerleft.png | Bin 0 -> 4311 bytes .../assets/playerright.png | Bin 0 -> 4307 bytes .../assets/unbreakbox.png | Bin 0 -> 4763 bytes .../external/flipperzero-bomberduck/bomb.png | Bin 0 -> 4534 bytes .../flipperzero-bomberduck/bomberduck.c | 645 ++++++++++++++++++ 17 files changed, 684 insertions(+) create mode 100644 applications/external/flipperzero-bomberduck/LICENSE create mode 100644 applications/external/flipperzero-bomberduck/README.md create mode 100644 applications/external/flipperzero-bomberduck/application.fam create mode 100644 applications/external/flipperzero-bomberduck/assets/bomb0.png create mode 100644 applications/external/flipperzero-bomberduck/assets/bomb1.png create mode 100644 applications/external/flipperzero-bomberduck/assets/bomb2.png create mode 100644 applications/external/flipperzero-bomberduck/assets/box.png create mode 100644 applications/external/flipperzero-bomberduck/assets/end.png create mode 100644 applications/external/flipperzero-bomberduck/assets/enemy1.png create mode 100644 applications/external/flipperzero-bomberduck/assets/enemyleft.png create mode 100644 applications/external/flipperzero-bomberduck/assets/enemyright.png create mode 100644 applications/external/flipperzero-bomberduck/assets/explore.png create mode 100644 applications/external/flipperzero-bomberduck/assets/playerleft.png create mode 100644 applications/external/flipperzero-bomberduck/assets/playerright.png create mode 100644 applications/external/flipperzero-bomberduck/assets/unbreakbox.png create mode 100644 applications/external/flipperzero-bomberduck/bomb.png create mode 100644 applications/external/flipperzero-bomberduck/bomberduck.c diff --git a/applications/external/flipperzero-bomberduck/LICENSE b/applications/external/flipperzero-bomberduck/LICENSE new file mode 100644 index 000000000..bce361a99 --- /dev/null +++ b/applications/external/flipperzero-bomberduck/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/applications/external/flipperzero-bomberduck/README.md b/applications/external/flipperzero-bomberduck/README.md new file mode 100644 index 000000000..2d133145a --- /dev/null +++ b/applications/external/flipperzero-bomberduck/README.md @@ -0,0 +1,2 @@ +# flipperzero-bomberduck +Bomberman clone on flipper zero! diff --git a/applications/external/flipperzero-bomberduck/application.fam b/applications/external/flipperzero-bomberduck/application.fam new file mode 100644 index 000000000..afcd5a6ee --- /dev/null +++ b/applications/external/flipperzero-bomberduck/application.fam @@ -0,0 +1,15 @@ +App( + appid="bomberduck", + name="Bomberduck", + apptype=FlipperAppType.EXTERNAL, + entry_point="bomberduck_app", + cdefines=["BOMBERDUCK"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + order=90, + fap_icon="bomb.png", + fap_category="Games", + fap_icon_assets="assets", +) diff --git a/applications/external/flipperzero-bomberduck/assets/bomb0.png b/applications/external/flipperzero-bomberduck/assets/bomb0.png new file mode 100644 index 0000000000000000000000000000000000000000..3fdc3a3c12c7ede770e1c99c2003c777b03f4d75 GIT binary patch literal 4536 zcmeHKYj6|S6<%y!HW&j1Oo@SwNCLLA+NY$|R^nmFU;#3+tr`c~z&`HETT5CYtu5IJ znE)k#DRI*@1C1$^5T@xs878Hv(-e#gv`h*^utTUBhjB~-37JAnNeB}jy}R;rl9`UD z<)1Yp>F&AT`R+O2Irq-qExw{f8R^r~5d_Kb6uL{`zdlYModSPTeC5~R-}PF5xmLo* z(MVL4l^{U1ng~EaLXi<9adBI5+qM(v*n7XN! z8V(A9salG3vEWq)igso#5Xb;gQUCQc;F zHl-hajl?w1R1tbak+dRm#Zgp znD5TDFF6a3&(A)&s&#QeuBm!_@hRW0pWIycY1w|?Pt*65Rh)1BxFvIs$JNr=Ij3<- z_TBOWTqFDWwT|4;>t~_|%|&(QlFkRe$lp3O^Tltvu3x%*eLDL7lBIu0A?LT|$+;7< z=(fq^*LBDX?`d9zI*S=Gf$2EV#^6q-Z8nes`Gbe z>Y8hIv#0CcT>Na2<0ma6w#7=2vkEQ_+Am@+1G;4qo@>a?!M*0|4W z?`Bcv{X74}R_@EKy!3Jt@ypij+Z&5wXWSq5 zcl@O#k6su#)qbV;g^_Jt8y+;@2><@>iN=OWb*a(T&yMDdx%FYnkN1bw zGi!IwMU-o~HL2OzDYHhr`}DsP@xU|cgI&s3w^L{Mqo4n)^w50-8Mjh_tyu0|%86>o z%uA{O%!yD0_9%il<|QJ$SP3*#00AZJG~IZo$%HDB)3n^;CA<+As89-PqoB05$S>Ab zigw8~Z*ICH!9ju$(0DWv3Wj4`!f7&iIXKqMxCu2NTBXxe?)9NAH40G1%$NzRAfZ%K zrn%{;BPz*UiF;u;1$=UvDl{#^;dneAH^*tS8V%s2-EPMT3a2OxBCuFZSmP5|I5tbC zNOHJAOpGcKO;N+B&dCdEmF6^=;5^zBUnt`B_R@!A-6}vm@B|;hNi%_mLU@0Vm{w2? zNxB32PLG%$?r6LO#MG*&2nwn}Sewcq76!AE423Z^n-#M&7B~`ENt7+TOF z%VPjoWJ(r!t0>b3l_YWt)o6%^wI4Xbg6u&PftxIys}4g@egJVR^kzA%Wom zbKzW3z-wyMuc|?(Ne>CtJ$t9Ua63u7#=Ch9KvIIDIfCJ6!cUMKVdE%!9sw==@T#Q9 zHUAB*ZywZ{51CBi0%mSl_e3i!XI*xM5{F)&33t{;m%MRnlcDti*p?;M5FByI3~hQ~eLd){|z z2IGC0pYC>hm`kAO&?1A8H;+Tm(fQ3^K4=PV`Vk4%l%WMT8i zU6+p|2Sy^F9<5lqb<_El58BHnhwrG5w6+vqZ1P6;XZ(eJBr|7J!+Vem@e~xf59Y64 F{~v;POwj-U literal 0 HcmV?d00001 diff --git a/applications/external/flipperzero-bomberduck/assets/bomb1.png b/applications/external/flipperzero-bomberduck/assets/bomb1.png new file mode 100644 index 0000000000000000000000000000000000000000..11d05b9b7c445452253792c5a51ad6448cca8aee GIT binary patch literal 4529 zcmeHKYj6|S6<*swEEzB`38t=zH-ZE|vf77MudM_zlEDIGgsmEfOoR7vSKeCE3hmmG zO&&l(8cb5>#Xtjvl7wkGJcdcr)G0%W3u&1IPlBCsjCSvy`~0E_FP>t|H6jQy#aHI7fOl4^|{MfZC%F{ugZOhB<(1vZ>mp)M`9skt&V!ivdjpyd=Jdl<4+CgUF)_3gPmz-S7 zeUE&xdgGrypCtbQUqyHSbJGVa0==Q#HNSE(ow?bsJhy~+V>;eeuoi1M`N_te6@C1w zxyygCxnz5wuSw*{zU?urHW+Izp1knw{zZ=`l)ej_uZQ+sne^e`6Y1A7bDu$ww2hL-6YzOFgNeg* zcRc#MyX@!#dGD_7TvA$OYM8d-c;J`!KizzxYH#34FDVxXq}mN zy?P(h>iDX+yC`!*K6=1h-fXVux%I-r&9kz9{$2mY&(2<)jefLr*>BUy2Re(zqUkx7 zuDi%@nvthI+)SQ0eu5J3Ei`=4U!R@7y6+X&``>-K>(z(aADnz?(=k%qc2`e*&u`kB z+ZuN{PBg!<-9gs-F@&K%TbZBF3!7eW>#e1n`H6Jx&3q3ct2L( z%c0DBcl_Q_x2LG?vlovLFLiF++FGtmGu&S@eTKK-buQ!H;IutIp7-v;=BsD#zM1wH z{Q0H#oSHPt`NfK-CUx~cajUH_y8rsI*6nvTXUxXmxeE3JOQAM=1(sjMjuDx1oKd{Z6o z6if@|8(m2T5=4N?qRB`&sxV2nN#|wYSTo}$REMZ_Zd0{CfO_OOKxs2=Ca}_^)Igc$ z8&OwW5Sa?^q5%r{n(Ik{eSn@n&X9gHs$^ZSSBqso8^P!BxG#&FV1;E@PE(nC>88z9L* zK;P@31mT9pD}W-`$9YiN0HW%gkrV9tU9%flvjmN{(7`vCkhE^3Ws* zNs*ZD1;rkPR3&j(tWmLPGkQ8B1A*>Cyra;Ax$9tv^7|RD%-3t-`MhqEwmu`syd*IC zkmfi$&su5BNzxQX+wC^YNn7EFa|pa>W$hfzj-c{I6_t(hK%;`>W(o3$qJ`pEs|}-V z0A3uNh;ck=#VDI-rwGE8h6akegtEoxa9F&8wktAuekd)Il45|Wg1$LswNfKrYtVPG;)SXS#(NTAzc zE{rD*SXGV(WjX9NX(6GS=g_ntZYP0NSud*sNJ>x^hM*Y>5hO^4aKJ$^0WBl&vLK0# z{|&8e9@Ld8d6}fZ^&53lYDZOqHL0i6V_4F+5{l}Zf?@eo3JTi*1U*j3m73yf*=PvB z(_^4r2kp}Lv;qlBE!t@d#&HDfA<+WsXAv!!jTA+aA~*_=_R;K$EUF1M4oX5$N2nES zPhBgtKrd9`Xmp|$XjOoMVFZm)&LBlHP&|qp5DdTlDbg0Z18^jPIqY!P&{mGbSc;=C zo~JlMw32o!aEuQ6e@St;C;lx(7p@(PgGF`W|55f7V8l5JrAgV~`3#SHeCWI%&`dj7 zZ{u&E-EQLwka~QPG3h%l*SK6`QeaHr@$MRzYfK7^2|V6i|2Mge!(WUb3SaaR@O!W* z{^U#WThb6Kdr(0T^BnD;mfW+T0UGa6eg4ur&QF*zcD`;u^wIl|ukC9&oAubK9~ufwSNA{`#8+DGJ+QE5!@mG~Pe@e& literal 0 HcmV?d00001 diff --git a/applications/external/flipperzero-bomberduck/assets/bomb2.png b/applications/external/flipperzero-bomberduck/assets/bomb2.png new file mode 100644 index 0000000000000000000000000000000000000000..38ce7c732bfa5e3586d1c9b9a754b85405cd4566 GIT binary patch literal 4531 zcmeHKYj6|S6<$9e*oGLGfT_peML+;Mt9|ORTZxAyg9XS4TQv?$fqmSSHA3^WiZAxzpKrA(4i6NVBeq@^iL8oQJ_lVlvzgcO=MrVOMFkKSGRIn7MR z)AG-nk#zUmbH01dcfNaP@3uhs(h1r5*$9G6@RfNh;J-1>9+wV((*xC4;NRzs!D_vN zPoS~5Dl1`t>J2e~f}|oNNb>!sS9Lyp1|5I!#1@WW-qE0gfHd5>Z{-}`g({)!%P z&Fqyw*}iyhV1DPvt7jJf<#OZ15A%B0)#T@#dW>FB2{Wmz+EVqawu+sH-rq1`&9jpy zlT|<1w5X}~iC<=pA#T>_x2q1FK6xT>z4Q)U)tK>KD3S56b|4{0TC?Ku1biM(f8ns) zeNE4}%ida4@b>zSWu--y`iZN~2A+RtYs+Ue|AFQgm(Dv8%+=D((dw2J|)~N-z zs$b?>oma1P6^+|ch#s|;w^%E>@BXM{`?TC0U-y3T(WNh@qkmey;?;C=QAe>{G%1hn zoI-xpf;{=h?c}+$=O}s3{H%9->v9X%_q^cx!`B}iIJEZs!{fi)c7~MqOzE!c{`vWq zwuS@Fb1knd+f?p)qTt>!A&kCVS`oOvsoMoOt**8PHaR{sv3be)cHmW zQ=i}$H~(#Ado6~=Gp~m;@-AZj&J1hfu{(1;cChqX#{<_Ix^6%9oB27{KTQAbk*IpU zY2N}wxl+`SQBaURYs@?A{*g?C9#!w|S8m+Qm=TO${k-zn9R#_5tpb%;?O(x(YQ)M* zssOCXNDN98L0k)yF5Y%C9ojkO{x zSr#tHb|pCo5CJ-mCL`gf#wFbr6PJTy!;D){(?zd!TdMs5)T71$%2*jIft4neddjjO z8+FAcnXB+F>4ShzZcB}>$2c5MBofvHZB^qToMc%RCn%hvFzA744N;v>Vo`0D0g=M+ z0!@r7FA_Yc5FwLTUgA9#|F;iQ$oBN2RPgr=9)Ly*3N zzA-`z!VQgA08Ooni=ea~MDLA4a?1#G0Pd62n-*<9p>F%yXg$Q{C>`>igiYMKCjzitj|fRs7RbS zWCXz>@-_xzNru80hr^Dsj17(irzFZY-XSpj5GY?%)A^_f3@8Y0RUnR?b~3cU5}4f~ z*f550Ix&G_Y#3o<8PXxM5=D_iAXdf|s7gLOG%Ev2f>2J965#Q~q&LRpDHOCZeaG7NBARw&WB zMoOp=IWX;q+ezYe-plI%f)W(X5e!EYL4xE677mIDXc^+KN{Za@ z-@J{@gSt{JFH!Uv6i8X3WCufIfj^#%7Kb2@kSi!p zlPh$d*{J!$y%ROS=mI1RBN&WggA~a@@+h*8F#O(^DB5|_LECvuq$B~Vof0u814xV( zL`oK%EW_GT`WPVae<^WL6V&h$UAS>9_BYjq|3}+X4nxjSNKMKH&u4ht;{)e?A7?P% zd->^;+r3-@MMoDI5x=8!jnXwD21evOs;*JGM#R8~oJZC5f1@jV@Wu$D@I@~H?}KlC zwr(rDOJ>E&9@Y@VI?MQ`B@ZvGhsOJKpTG3J%bC-sW}yQA_qEXUExoi__oxx$nt_;a zzd%A0ie7^nZ@;TI-=Ak(Onu(P!D$^$JAOY2A;zUQ`|e`93fbA6h0fPYDxODT?2Y63 zPrW*&>7})^$)cW1xch3(p$l^^t>3Zrh@Ae&jG38f-#JVCB8d4V+w@^|y107;Q8`s!r9<>K*$8}6g>N;sjATc4yq;7b1@BE%- zrsJ9BKTD(2-nYN+x8L{MZ+CY0`icq`7#=X_bh-teLU#%LSKEdZJ$&kYfvfOusKy^q zN|-njlSEz!14OBg0R$ujUZ+c3db;>8Pj}!cx8h58=&|)#uT^{c+77htKeX!Lk4s

Y(%&x8cXle1Amu0 zmV1XgTMc#X89QHDSYsLZ;Eip?zDuDazMnbho_o`dKC?ON)Zz6(Vr-zHP8ftfT+~0UY?>&{B)Aq|tz7sb~K6-mW>0A5f zzg)ga`Wu$MC-c$Ot2Uf1ICkwN`u6i(&STi{h11<%>*4a^4JQb*;G_-DjB@{Y~GX07XI+&jiGnm9m+u7+OqYup4iZn!)Gr} zGxskghU;}tTzrz~e7%$8SFf3S;YQ`c6^~p$>iFZ$zcwEGR@cMxKi=Cx@Q0QTRt}!% zs&A`qw0G8@+Wcsd<2&8=>|JrTx#iX$&JMizdviNaTMN>^ao@bi3%?<9e)7PLRTb`D z<;^tGbn@^^_VT9e@^|*P;y>y+c(A2NUNm>@uEqDdvyKOoSNj(=J(T%sUj66qF29rX z8n$oC*ZXEKqyAa^#O(eXd+xSfkN)abN6T|d>XS3DGq;KPv*NXzy3VvVM9x*{hZ}2i ze)kWoul9p&-Q_4M&H5~yoYseW`;$%a@88LE*+9W3Jxe~R9=QF~D{JO`c3J=JlTop2 z_u+Lq;c9kua(cRc*rdHh`>3GJFH&Z8*_m&p|C~*+iQyaY)v^! zamMxQ42}d12_isYkVGULmFa}jsPWRUt(q|-qCu2$r!nC5Audq@h{a?v;b?wBs3MK) z42VPGc)G;BX_NxKIgMqC5~DFJ9*>*iW|Js|FoL2e3@0&?L?Hr|tD_2&K%??Xm12a$ z4P;glVu~O}5tWk(ij|7fXoUU9SbUL~*E>!hl}A;8dSD4Ah7l$li$t)=9jpYw2(-2Erf$p)wqmm@;I6$LkySP$dWn zk(lNM#h!vx1b#xSDY2m)?z1ZRthC? zibX99V?%>x!iMsUg=EZDU}q_662&%2fK|zaCr71Hagd4vI0%~U0HuPA1+@TcE=mCo z@>wV|p9}CH$1)lf$I_cbDZ;?%6e3IrV6kXO>re%!H~KtIBWc1XEWR+K@X*0&+$uyX z6B8YNAp*85jH)JKvy-_LZp$Tb_#nv%P$`gPScxhpft$=0tw)^}8YTmUWz;%_1ezV@ zLc1iuD5B&S#jw+;hJ>h|8PsDNdSc+(MI-pTub^K@)fm4lR@LA}8?G z{|l`y9>g&+<%NO_$FJ5*BP(hIk)h z<*8|fWN8z%W-2;f2Gm)Af}ywtoe+!|6$~3q7*qF*v5Xzqe`(^-0FyQu=r>}6n-|;* zvGL7tR5Nwk`3gUybMY090I6pNnU=mYa?Qv!Ed{0po~f=Gxu&JSw7@gf^?#GgFmas% zQFsc9!^_gAH7PcD(V89HQsCCET)Ka|&V3KI=Ee#imUTMQO7)kNXj)$djdK)_H-FBR zSs9DX^M)S0dBQaH zlF4IiN?T%=QXmAF5>khuB+w?ABtw&-4MQMFO-egsU})OldTbuSv=C^fWD2hPog}}e znaOw>`j<5$>AU@Qf4lqJ-TUsItzKR+EqhKjf*{lUmA)GIZ?3Z^XTsmi>R=!I`}_LZ zpi#re(U_*nN*JIo zICuO{Iol7PyT13C>066Ceo^!0<*Hm=S0)!v+kFJQ5&h9ypWQiex&dv@d#s{w#upO{ z$ck&^#y|e+(8{b&as#&(wPlo)-C*Y|?GNWoeeOf)d%cV4ntPTwZx-f**Dl_8tD*hC znMVR`7m9Q;IJ3C$!JcAi!tJ#=FWt6weJdcg&x&0Nz0$lrd~)gQ2c5yb_Jh;9o_MIJ z{ijcA1x($Qw)syLE@*z|!=GRM^_>3mr+&K+zxiTlzpL~2@rg68xxRrQ89z|G-fF+s zJD503chA#XJ(b577QFxX>7@@9PjAd!*-`z%-?}ID1P@hj%-;WS-PvOoI&<3n-fcZS z3*NrB;7;(D+}rM}eci>AH+&m?)xNydUehzMZE;(E&QEUNxYpHsZ7zD|qbkNiEgf-)oybv+aH7^MBd3W9p7X ztNW#w=Ukk!X6HBm@W`IC+79M=$K&}Y>pxny_5iyn>)zKl<@tX1vS0zhO`Qj~3f92w zbvXmgy(_jZyYJk@eD>1Hmh6rj-yb-3J$mZB3mboy*J_!IAMPhJv*ImFP8~kFC30k4 z<`*xv-2bbKubpYxy`r-o!?di=qn26cut1l^9&i5l{70$pR@^+Dmp!4o|HtnZP5S(N z=J`WW_0-dQN|D)p#Z8uig3Nv8Cm;JqA|85D9oVb<^OmKfUh8=71I<8?oKgjLVlYs} ziE6~oOR50uiAW5#D1wxfC1Si-4-8ZQAtmauUO#fwiYk)Fx|#_PftVN6DV6IruxkDC zT5)~7$V%3-((IB12LU3$;L$`R9M!pm$C||D;Mx@9Ry3(%)O)PKKsD-BHGtA~+D>5Q z38j&;mS&?Rnj~{Iz9nf0c;&Iy8Ago5@pwFLk2~zD7Q#uEWpRSSDGGxcnBEjM_yiW! zZ6-tt!v}OxQ(}gqMo|-!7t{vBW3|G4bTGb1ED#uikLqa_ARl;wkKv@9z#|cSq=jyj zH$sqfKwoO1*TR9uYk;mcXd)|+gDIGK(l;vftnP*sOs$x$vV{DJBr z3zLG75{V_PAlaid4MiR%YgBCJPBNX5fk5*i+)>(txhIvORv^InRI$Mf&+qeCP5+#v zii*S~m$V?bM4q8BmZT|+cDbAwOEYjKxFu0$c$Yx)BcS|I-Qc4lFrgs0U4b}m2kT-b zH;GXqAz?HnIx)B8ggCq?5rmTl0^=9~u|iW|SMuSJQJGMZ2}QC3OR)?lxG4goS%|_* zZXN@Gkttc^ouZtAl02yC1tpg9LoOVS)cag^(R0!l{ctCAu& zeU-L3JZMR(D7kdkpitkjlR$45f| z-aXRoIw)7Zq!mb6qGT8CzyyJSJtR9|{T#9bbCR-5QiMPO(lr`gS7js4YhY0b@(8(t z?V02XU63qP(P-^>9Wbi^3Bw2)qu5%CrXQvcK92KzoDH%M1;E9G09ck&dFb|Kr_(@OHUGxB< z$DNFc-*LLe=^7IQV^SXPu5r4?#K4%8$Ghumqbqy(r5Hrvvuqr`@*XYn9fhyU_rxmK z=m=uBncs}W{<20WoMiX|<&!RF&6|=r_pgU9RYB2gqdaJM)rk2fg(M&FfrQ2tqYgD6 z@K?7?p9PhQ{JurC`KQG@0|R9ii^XQMZOq7s*{8gbYniyPa^IC761(z0+t6O`Ms^D` W?%Tg+a|JX){N>AiuP%OK!+!v0;CsXX literal 0 HcmV?d00001 diff --git a/applications/external/flipperzero-bomberduck/assets/enemy1.png b/applications/external/flipperzero-bomberduck/assets/enemy1.png new file mode 100644 index 0000000000000000000000000000000000000000..7ee7cb27f1ee52c63dbdb31e788c1011c8448d35 GIT binary patch literal 4757 zcmeHKeQ*?K8DDZ~L(Y;gZ3+PcEO&er*xQfW``9&?K<|#^AV-dH&@fWl{dhOoCU<-6 zZE_bZ*aSL45F@leGo)0-Rw9ayb$|>CLJb-!%7ii~TFfL=FfhR&18RWy?p;2$Gfu|h zU%8pvefNEz=l4F(@A)pRO1up-nWJ}%r-V}HSO=6USEIgpYbb4+b>@E zS<&G)*B_sLVe2bc#;Npmt+~hCH!7Z;_4=Kh8{Veg#)TK=ocQt6flKc#ckO-Um*FLA zmKR=I+AO`icGi~pug;ybq^02C&13Atrt_|Y`M2_ayL{H=y4d8ddv~`W$b{Erw>#i- zy9YCe`8I9ZRa@ZgTODbgrH2Dsjqk24zjXRqd*-)%?%kc8 z3yx38c~JU&_PFzAcgMnsb+gbTmX-CElFt6!&+o|2{CZz+&(*G;8R$nJ7TFBsl1q!t zZ*SZ2`#;uazv@LkxN&Ck*|}2p`^~|p4|Ls}nRoK4w`TACmi&T--HvG;&ukZp9E&@v zI{)Hdas8*oInrF;?7aCitvBj^(Xf8si<`UFYo#5HocWCd=8R`{wA%On;jKMS?TObr zzkO)>C!brl@7dpcvFVb!$9B8zrR;MRAFf#cU1nSAlpk%&^8D%$Zvds++P}ApH~ITE zWcI(-RlIA(yvt8!Gk;!HYi#S?(trAP`23k`TlZ(x8)o1~ACf7lvD#(lkDh7>o!F4_ z_jhU+{os=yepK67++KlUYU+1KOo<`Z)mGf+ul5@Y6y~S!U+{XLwQgphh3)ICr+7AS#+6Ru=xpp#0|>j!i_3ey>Vqw z*jOPjqA5Smm>XvyKnQ3Y8V^;5qioz|O5n1vt#{)lG-0AuxJ;$~0P0p$fYKJ)LSWvw zTuqtsjA*VZN^FT|*#HE5a+%6CEyCh>EEcoGtQJKr!%2o=aDu`q3WF9{v?i=^aV#7) z>kvr{4~Pn?9MNPYjOv&iuT*I+lL?NagYktTe*X}BI6A-rJP zdFT|B$)QNX3z9v`Qj?`&vPQ+Gk0jC=83=SA!X0Hjn0vw)TKWB~M-i&@@O&PZNuQq; z6+sr+M3d%uhrrorj3H?Xqa6-A#?Us{;+>)(**FJJb0eU9;i$%i1)xJgaElCa7~V=z zq}7f&MTy5~jDDTlZ+fJee*i2_LlNl79pf~NrK7>yoPBrV3N zU}+iT5po6PncxatkSNr1qpf4*K(7KM3?pcaVuBROLh>kbfH3^=r)VVzhNgG{qe&59 zw37pv(+&Ve0-mu`a4C@vVRX>{ONvkv1j*WLEJ^(TQY0KApeY9|n;p;?P16j<(T43{CTZ`b<;QWp8JRf2(3HuD|&Qb(@R-hq99fBen`y{bV29g5ZvZ4{d1!vd}k= z$M_pqQIBy32t7W@nD`y1Yn-kzF)$|MadnN;H6{keWIV2}&x|hP@Szxl;kRrI9(m91 z+!Ju^G3);c@ptp9VP~4=^Lx{-r_MCanUH>X^<>yJP4kv&ZY89jq>#i1 zJ`mBEtd*ns2mTAS8B?JV>+>uPW?vK@^!Mi*3`zp7Upp$8Is*uW+&NA zL`zbs0ctr=!Fn1&i)p1&>CqOo0Vx97=)qb7$0un_px98QP>LKC(mS(BNUZ1dY(=+GA znVDZy?BQN?p6lz*o76lTeHJg>h?n>DZ(X=4efp0-zwk*{?hH)X*X{=YTy3;Pq$6kme}b0 z`O_I^zFGO`EpL9e^`y4VcD`d}`s+1sELrhOSIhXBziCPH{_$xc6;!kw|J4@38u)z8 z^!|0dWm}fq@%Dst*I$=5SvoHKp#RYM$gx*WKlrn>jj6X0`>s+cj_o_RIo!S` z<;u>cJAZv)-olB3^Z$+P_1&{Ap@Ojn?tVtD24=(+d=+#EL)( zC#qqbmsA1ZiEtE36hZO}6H#8Q0XiyxDkb8!o^L;BMHR_yy~h?H15ppCR*D-nu&l8( zBsSKFF3DP0V98H#5FiY69!-R6BQY-Fwwkyc92;iBikdEZjoVrg2%;WU11O8LIEncZ zNLJKLLSGpX3&Dma%0W!6(?sB_2N6AcID{k)#YgM3TC*HUBtR_)L)REwl^U^Rkv|X| ziZCdsQo>O)3X(m-Q&;5cWR1wpm@&&4o(K#d!X4o~Si9*Ay#fKwtBQ3-dVa6lYOK#m zs;EeuIb;RFA@VjBb5Sgfu?~kFbFnr!5}cAK+jxh-^24C~k(ka$L|{Nca9n{nA`G&V z62K@%qA*q_X-u&53`UbK$tg>eNQ$;$5M`PIRms;5&&q(3Ae57G2`<`Y!vrTyVyw$< z$9T!fV*uD>S{8Y`C^IIMByx*XEzHC1RKk1}Afk~fbHX5;TM+cStu#(vw*+f>U4{W} zYl#x6OI)7_DPgco=M6R~yOVa>NRp;)b{FML=4|5K4YU|kqJc?~IK!IZ#l&rjwU&pzLSKO zVg9P5$PNF^+h`s%Ke^?_N(`>wV49L0wG6CDUL`MU6|;+E1trfq zd4M_X0E!<7E;|idiE@Y|lm0p-LQRkqXR~n>`Tr}?Ns9tWIc3l&uKd_7WgmLE|)NMZTAIeTT40}|--A~%!BM3gxh@nT?fGl9X zH}W&kQ8#i46dhY+RQ!(7HAdH{7#NlFn7YR38WjVhavoFH|BNon^+Pd;z<1d=Jo28> z+uPxBd2+P4GKL^{w(%X8Xe+FT#)-N=;G1}Me3m6`=3NEdw?oq`-B+P|)Ua`qLd+L@ zAfa(ZuSSg*{Ck?F-3pxw{oVzk^v;I^)C>fjl+xc{m669NElsJuryxeLkp0ky?W+$H z<$LPBbHK8C--2@XFP+ye&RBVdIlcCpy8(G;UQuW0oz(9`D8%n8^*+0BRr9|AWQvH3 literal 0 HcmV?d00001 diff --git a/applications/external/flipperzero-bomberduck/assets/enemyright.png b/applications/external/flipperzero-bomberduck/assets/enemyright.png new file mode 100644 index 0000000000000000000000000000000000000000..45e6a861ad9c914af25be2b6c1b6097fdc6f0d17 GIT binary patch literal 4536 zcmeHKeQ*@z8DB1lAqc@j2r_}Jiy-;f+mGA(T5^2kE+mIsIKnl=p;q_f-OHN0+so}H zccEZKiNFixQqR2oRlB(;cCnh8})Y=IykGX&Ci_wqS)rjt?r z%gx-~yU+VPzxR25&+mO_-_8Eg;>l^V(hvlh>@D$>!GC?6mTH2(CVxdY{JXd=P@$D^ zaWooJBsm07tu6{skdP$=NqqeLsx!|YCsS|5GuE26r?*s_<%K)0d_HgfpQT@K{rQQk zbkhfp{lEVQe(T?u?}bl4uHRlbRaGxUXH9--N~`$noc3c^rmb94h~zYGX`6RB!?AwL zM?z}DwKcCqn~$B0?|C2Dwd=<8$$K~0(k8SBG~VN8$|f$ebvb8dDA%eFfZzP~t(!Z_ za}GtVmD653;OLz%X59Q)p9wtE=SLlB{umoxE$o|^RRM=$LuIC=B$4X>T&hl;nE%+l-|;N?9iK8UFm>;DAAGm4{`&bjx5vFt zY<=kd(-USoKU?+8gfl%)_Z__y+24D-`Ipn{Q)UwfZ&CM*k8fPodhp0k!|zm^ZZvGn z`@^Ti>5UgwwpZbJZ2Z+wO6F8}f1vd3lCOW2GA9uG;yKtp*Dlp`+e(mO{mR2E&>8mo`=MctrME?E}?tJjqVgmqPd zQ^Xc8N-IdP5FiXR4o!qZ5tU82Fawu`W8F+(sNtejxv&bKA9X7+fHG#rOyWfext7Kj zrJ)5eQDVzH%laYUlMAcVv?xmu@p#-Ex0sb!kf5ASCqdE#P2n_4x+jBWgbjkPjlkMG4AG65%j0JVMoqY9UB} zLSGx92H=h+%7CiW!~{@O3nJRw;Si!Q7$2>Pg^Y4UfdC;8hOR1Hl^U^RvDfDxjL<0v z%HgOH1<4-asman1StD}OXN+=&Cj!F+Z> z#Lz@Q4ujPir?W}f9D2G=lD665FqyM~R}Nw-RHBYak!A~HgzIf#VKIxgrdf#U^yXKg38qb(Z~~GC8vZ+E)oRr z^yruCfL;C?DNvF~OLoSB^E?SPBw1kpERqGcQIbT_Bu@j%J`!D3BrVRxz|tV(5po6P zX>f%uFdCIJ(mP%W^e#ZcaFW4kXMmR^z-Z&KorCg^@m^p{vb=*QwfQwxayX?xOP*f|QRN!sA~43B$a@VxKm z4CZ?$KmBsMlS`oJ*dn9icZ{wvx<C0@_cz|4+Kr6;E#Xlh|!U&95zbJP9;$F^rDco76M zz5V!0f4N3|vZ}LQ_N0&Ui#rxRF#k)4g?NifJxzs= GJ@sFi98UKD literal 0 HcmV?d00001 diff --git a/applications/external/flipperzero-bomberduck/assets/explore.png b/applications/external/flipperzero-bomberduck/assets/explore.png new file mode 100644 index 0000000000000000000000000000000000000000..5eb50b669b3ee364bd25a37991519cf833660ae5 GIT binary patch literal 4540 zcmeHKeQ;FO6@MfFVKICS5~M+%n;>Mv+xNL2?{$}u?1p54jZ0WCDOP=-n@4u{ExT{C zn+O%Cm1dzR>NKrHD9yyy0WywM2%-c=QA^3RBvOVJZ47D=Dm22Vts&`syZIcQ>10~| z%g*fXedqqp@1FBJ=iYg@-cweRshg*RASlyS>MRHU+E_O^4g98gDz1XR>vi4=wVaK? zp|B#!ei2q{Ln183WeI}ffBD(!%HU~ia_?7pbs1(f``yaD?|<)&%Wd`UXJ0CB*4I|- zWKYUZJki#1;r@=?%9ca%ybRpZFs`AeTf1S8;ZcBr2Q~kD%A7$w#ojFVfdSKnl8996U=Iu-z{rB9!`p@z{U;5J8 z6~>1%tBPKmv;Bi74|4ndT6pWV#M4E!##_3^zF!);a*w|{%X9vLmG)g5wwr%tSeVN# zvD&cedv`3_v~cm3VL zagb@XUc1_5n7nNP++r+C7|Xl+pDo%kclxexdai$X`TBhL{gtZ@r{T*w^CiQ~Eb`(# z_}2;OsXy(&&%Sq-kQU}m`D0IX`hrJq{Mz=1Z!Ya?THjuI_do03#-(4}(;e-8xjk{J zcAxcZ;?)(K%4{1tCe|-F*3#Ph&atjnPLgj)jHx91fjLtH2Y-j>zc8;Sccb&XdM1l7 z9%wvjt!_3{fB0M*_IzhULu*-N#*`(G&79?2{97)4p?5~}+C`^}5?@}vuW!P;=oBxc}6R;Gy2PTldUPq|Zl>+{W+Dh;3coex&W`!10Y~U+>$R|K=y? zg{_}G*inrj;f!1U^sEbr`(nB=wxw^8!z`A3(K-8z+OFF>PvlL#^+DPX4+NF=&5cVT z`KqBdJv%!sXX2ShZpLH2$CduQ@|Rzw-{%cq`+UtSeGoKly$o8h!o7;&m4J~I6izh8 z10m3(5M(QehgiN^RAEl^$w9mR#_={iEDLu1Lnb%o4mrdsxwI}UuBj{Y@^#fbE$9oD z>TGca5ClY(h2sH#Fv7&``Xny{#+n(`!%2u*ZP!=0J+MOwi!f!Rj2KcJmum?9QXOmy z3ldZATs}Ynp6vQ6RShvH8jHn@G191neJD=TG>Q=@K_CEuL~4U78%KhX9E~Ey;S?i$ zSPrSO5`;BQmQ$jtU9Si8@L+s_klQ^(AB+sB0Q5lPYzW1T7#awmBRwK&aSb3D2~Bh0r)fAe6;oMrg_eMx0gPB@=7mD0T#uD;QDPATMfEfZQkp9$Msh!Ax)nX%Sh3 z!Z8A&B{Pkf%&di`DcWQ)aU&=m49lP^S^vnWG%5j5S#g@93EG5kRsusP+H6Kx!O9|{ zXp#ttXU)7sCaDCTS+0ZwEGVZOV0|JQ3i^^0n&3>K$7R6agEAqYT{-qVa3cOZl%mtD@_c8)`{T==tPYZ$BZPEoYBg{fMkHMtk$Q1AZZ7= zFpjXus!G_aD1N(M3klXdho;?NI|;1HI$2c&q!>Xm7{!p77sDAGXH4dN3|L0s6+xD2 z{~KD{Jg_ZQ@=`eh)~`*PQafsmxH0vVdi2Z5tpvl#O~J5yDuoDJBMQkl0at2@uVRBf z5u6?a?K)_ezoQj!NgyN(MIsyrx`2{MP(M;45i>4HIDv75h+9UpM-)kov0<^$2Xq8l zf%Z&l1usq(DsMD8RwZgx0D>VHg%Grtz!@MOfe#3V-uVzZq&VCY*3l{2Q0-V8EUFFtR@qa45$7n7CS?QXGdS+iq4Rz~ zGZ62c{0y|)om>Kj#}^rszTq`;WKK zgX^A|eG=Ryr-Vu?BM@ZF(Y_Pn%>^~Uc$eyO7vJ^wjQMk>GKWmDCSbZ(Ev`@N`hPz0O0VZ)6#7jA;DcPn LWzLqO$F}_kO43W9 literal 0 HcmV?d00001 diff --git a/applications/external/flipperzero-bomberduck/assets/playerleft.png b/applications/external/flipperzero-bomberduck/assets/playerleft.png new file mode 100644 index 0000000000000000000000000000000000000000..86997a985eab4de991e315858b17ed7e0a981f74 GIT binary patch literal 4311 zcmeHKeQ*@z8QF~0mx0CKOUBB$ywxh6RdktHd zO&sK&?)m!b^WCRH-G!^$_a}-oP1cspFMM#|(!zW03)g0!TE3v}Zr#$0vELZGUb-+n z`tcmk4f{`Lq#nI*_apMPhBtb)weEi5)l-XhEletV<{4f2+r2_Tt8+zP&D`Z{#O4PM zw@BN!PF|&d&b$BEbZh?J9iP53`MlWoqIZt|$djhBXS3*cf4?u&_s3n;U$vdPIJM}q zyDDYY=lAB?FP7$K9CysB>pXqwYT(LU>tF7-mmbMD`&m5adGiz;K{9?OIh|g&(>Yi; zEO+PnEsn}J7v>#X+q1Ny#LzTt#XH_#-}_|hvzoo$C$e_cG@S1GxI257+u7aQThwu9 z-p$&5Y=`ah%NI%}J~kI^H&&&L)xG^&7d@4mz4@EID<6JzWj6Yk`YQlbF=yi*SStB z$7fQ;y*ppCHMW&Bez>iZ_(e}kOGj07+N66Qntq3?=(m3TJm0joAI?9vD0Tg#Ikz(2 z#<$#`e`Z21^UoC1X2yza^(+#y2cJd9d@@;Nf+;8?75k zk9>lk*>GujcO!;HGOq>nGtXe2vwCCv(OdJK7O>>Yo}4d}7ruS^_2S9b&g<&-hUAm$ zcP>Dr%Oy#DUY@Stw&M@}I}xw{vE2W>bp0#+9AD(~FIFD71v@k$!A`98EMs{&XygRh z4~&Um7`7;a*vk@Oj&B4C>Id~w$YHp8xYK}2g2S-N>>)g1Cuoo=ng}@KRhhvd|R*t~qAOM088ilixqo!Qq_IQUPR0-;( zU|5TSVvj;9k~l2ZsN7VKR?f&oVE7R4DD+_M8W^HH9@ZuEF*QB6%VALGX9by;1Xdeb z7{bDdem`cj5`K)fnynZ|GXzF56fZCWPf~ty1eH4!Rk#ojR4Pbrlpv2;r2P~iIgB$4 zCXD773&vP^9<$I^nimA#LYajT6w4zL>`E>$GAorzfK)U=`YE0Q7)euBjJD7MW+VIn z1F&N#Mzs3J^%e`)gfie=q7H@!4L>S;OER#a9 z#IVCB1;I*%Q`ICbHp^;%r^peXEC(D0H6>Jy9CCZ$b`m&+b8!lQqy%MR37VxC9~_WombR7>&@uuq3zC@p zUubpnp!W2VS4vShe^N80chpL-F8!2#3`p8mLQ!o~upFN*A<8v@^kPA-w2N=xLiGTi z9s}(BtQ=sVhBxBNdT&{7s#-zZQz~kLDF4ve87!!EByZ&!-WevYh zfe^d}#o^0RvHVgme9@W|u3Q~O5MzP*%}BJBH9_O;irZ6h`=^<+bF#_0?9Os%x=X33 zRh)8Ay@Mm#)es11Tv8fP^=i0k!w+Uckkjod_vQA)HkaRlpc8dnm-^T4dAzEv*jiU! zw5bq5w*DNv|C4Ed>zY*bKyJ>);=gX$e&|PQ8vlgvdH7{q63*53T>oG~`_lQyYkd_@ QKsv--QRQl1^w4Af0rThwxc~qF literal 0 HcmV?d00001 diff --git a/applications/external/flipperzero-bomberduck/assets/playerright.png b/applications/external/flipperzero-bomberduck/assets/playerright.png new file mode 100644 index 0000000000000000000000000000000000000000..1a6283d9c26d9f9582fecd0cef9765ab2b288576 GIT binary patch literal 4307 zcmeHKeQ*@z8Q*+ia)5y4BWi$k(Gn8(_IB?hw>x(PxI2RA+3tBttlv!gj$hEe0MKjw9Yu0 z(f@KYclYk|{+{3aJiq68-+6bdyQpBQ<{k}#AXA-%_G0+2j5X=W@Hg3AdI|o0QtK&| zi`gg|j)=Sv0H|CO1}KOLJc7i!x3Bu^_LJz8fnUUP+kS;;H^24s%j?g4kYWBKXnHPm z|3K#3^Hz43wbp$>qpKTTP5jvx5J?i!bEdYxN3DG$1-Inikb~*j8@qNKPi<`|Xx~w1 z_^^Lqb#`)S%f7n3j+B>o4`ih^JvLjDc6zV=VC8+9SD(V}y>VCkXjfmX`}n!<{ikf7 zqwm>cnavr`HO{S^b#z0`q0Q&&XI$O5k36@{+~2={$;PS`)(%Ih0xB8o}Px{Mi(W&9gzMTk?v{A6x+)kTqIB%Hk zuKFF;!V^of-dWeV!jXejPhWM){nCS5;=OAQxS!JOSyTRQ+uzSm+vBvI@9D{InVmII z+RU_=FJHQtlfGd-dPrLo*B18-HZI$iIqjL7*RH(((UrOApC4NJS~9t$GndbqF-L!4 z7WsJ`*>r9j*?y{>;vZO)arRo(wE630DtOC>IKABcPq9!!FkdeMlNo0qQ}-*f2qAbwn^ojP;OYM>QlF3g~M+ zBoExrL@|)Wst5-h)gUA<7)#;fM*PE7k${?xk0U?;1R+#{tCF`ZS>SZJM?4e>{6a9S zdO@*oL&^d_D%NeWDKlz1V*`QiBfPhvhjUlK5an_)c9E-6!gJcKn6f_O6FI@hs6(1I z^9BI)IISmH+`xHtI7@LPo~JXIcrP%SXp$O3R9tE+uiDP-( zU^4JH%jHqH$)`7Y0dJsr-ZzHg;fMgck`0WFN}=*8RC<~RM$U*EeO?o8AUO`F4J^P} zU^a6wPS(sNqVaLeaxoHQVL62$>jy+QP9W@04wv*!HTUELRGQR z36Bs2B{Hk1Nt(=59!={_M#@Z+M&l@G4Twmv6BSNUr_~$O8Ko=?Oa=6hmJkFeldkrQUwt~-#d(CIyK~ea!bf)CR?eIk_BV4#vLJ;i&<(m|1%CCmTJ7lNJamUB0b7xLZJMy99QE0kb zc9hCCF{s?Z5%p>ad}vgV%TeWO_(;Q@b0BDm)4tS`*;)USr5Z$+eq#3E;K!e!PrGF6 zqTlSkg?7Cym&HFjs47bfvH_+H3` NI2}dyL(A50_zI?I5pDng literal 0 HcmV?d00001 diff --git a/applications/external/flipperzero-bomberduck/assets/unbreakbox.png b/applications/external/flipperzero-bomberduck/assets/unbreakbox.png new file mode 100644 index 0000000000000000000000000000000000000000..5e65912d53405d04bcd05916947a8de8fc5239c8 GIT binary patch literal 4763 zcmeHKeQ*LAa#*}l_EHe7ZnC@CWRtz7rA^W{(9lqV#IcC`@ou`N$%f6g zNqff~tuoa16pDf(PzIfh5AYZ zc0^x)uxe6uafh&a*{bL7ZU6Qbb<5kAiybrj+}Rh~@;;c=`r;Jt(bwjk*l=HGc<$nP zWy0FK-<anGNJXNLodp5GUkF@7z+}L`+vf=pS>nE*`w^*Ni z_U6;Syz8-FAHI9@QDwdPLf5ja_k!=wuX@JToHp$*%~PERpXE}4zqxxy8>jbNS)SRy z@>E6J{98W$MV9Sf3mbJ^7w_-ie<6J6t$&>py|<-bI+1eX zxv+d_#pVKJ`q{kt)a>k(r(Eyd^KCp9_`TfUE`4`7wJWG}J^GHKB1mR|1eNIblry{> zGO&Wo0Yf|#ff7ZK{K9yI<%2*)IS`P-cKwCd_vukdu#O?Y7E(6D!*{DYoE^5%O_j|mkLskGv87Kpex#ChC zsV~r>`HCPimClj@2zX`JSF37-F&bmBm?35|$V$LS*lacfbbtlOhcV7ZjD!I4>q*15=7-%g$J%W5LRh^et zA~XsDQYey$f@F{KR3&kktWmjXGl_CWCIZ8Ua7TF$)}C;NULFtQl=)gMJ-5@Y*VbnQ znU@46F{C(-=2H$0(b{ zg0X^?#Q-piq{y=tUNj}31fD68l@JTJQwp&GV2p$Vi3yExrpW8I>q!GXZ1L8xst5z@ z`f@2;8y}wVNg=REWi>Vli8CrrtXS_GCSuacKFQlgbmG|?0c%O^{SvUNa6qBAq6xDQqDYW9M*@N#jUJUnHO4BS zC;)kcTtRszxI%ADY}9R|y<^ot+XYA%hEo`6^N|Dt$s>sY!i?9xL>#AW6ie7Jkpdh> z;gl8Qa1)D}N!Cn@g21zMQU^l>{x2m$P2dD$HZwT+|0|KU5QK@xO_-VI2#f+Ef!WM9 z8g?N`5VVyfI4;=&KUawZvbT;dG2f`&`3H5IZ~T$6lMW*u6>#^HHuwmFPc-AuBW*wy zFyCwW8R)2Mxde)iFES>6$LSiUYfKD`$$4B|<8+OQfiXFctLtZ@OE-Kd24VOv8-qvQ z89nFS@VI<^r0lLJf*5kO?}T_qVI4H4t8R}g{Y+ZU^)siu^qJ!YXqv9N{Hj9^X(uTp z@q!NoG$yIlsP=-tq%q?r=v3r(7WuM{bgbR4L-2{kD|YwygIHFp{{wKclAL^e-K+_( zl_?z?d^4U32Ch85aAyv(?N?isr~6iiN)H`tCH8?-F>_AFzD>vWLNLVbTHxGOd~ef# E080^$ApigX literal 0 HcmV?d00001 diff --git a/applications/external/flipperzero-bomberduck/bomb.png b/applications/external/flipperzero-bomberduck/bomb.png new file mode 100644 index 0000000000000000000000000000000000000000..44b9bfdeae76553be52ce1c451133a4baee7bf68 GIT binary patch literal 4534 zcmeHKYj6|S6<%z|5CMh~3MO%!MQ#F^?0fat9m^P72CE<}8sWT0;SYAG!)aM&4dO*36QBnfew$RC4{;rDG78SDFiYN#Jwv&HO)-N z)AG-nk#zUm?|k>1@0@#Q??%s(g%eE=nh*q;=qhoR!+&FJ8kY`#o9I&q;or4dZ-rhi z#L$SQ%1RKRdQAkNAg;&=5+SdGG+Oq zj?BQ7b)PkKHl)qI9nkMC+j;2q{r;QBzP_>NX8O53u?cgg)_sN`W1drriaf5OqJhj| zzFXI|I7*HbWF1>`=DXj^&8VJS*5!HmAKhbnDqi(8nA(>G&K>^f^rRiGqSl_C*>6tE zx>K=-f75>TN_XzK^^c%?@gYAB9E1UUb{n6%CTb}&ErB!-G_lrW#=509{4{SPSec^+jv`uJ>H`;&p z%7Y(|U%utrCs%Ggr?pwGcdf~Muk!e!RUO=hv}yY`Om+VL6<;c-*l_w6Ek3jRtF@DQ zpSe`pvgon*(=)lhm(`oPK7Y3N@b&PCcRp%(X=-EY!^9hRsg$%>{ely39BK}|wKnC; z?e%ki`Elpj`WH)2S7Ml!b~Bth{e8@RF%^$J^YyGJ>F+H3=b5Rd`?~M`=02|_>@(0C{qj`DGbIl;@rv0*06Xab^FI?NSr4_c&Z0A+C&C$ZwV zQcaulO=zAb$$YtUK|ck2a+m|U9^nZh7K`CA23IvdL2(>MkTgNl7(`&vny@a!v2ZlU zpy=apf~cq|5nWNksKF`t)GFO!Hp6*zAihwh^?6AZ z6^Ty_S)b1)3KkaQD3-=po6U-GtObsIc1e^ig3ZSY!>C;0s4j#>U{FDFT!B1Ri4p7; zn+;<~ABnLfDPf$QBQdL;v&f`iu}Dc2rJ4e}QV0%@%Ak@Um7U^z9L-rUpPhzOoYjg6 zl3l<6u*kG53RY2O646K^zd+SO0xYKz68wOOg#C#LLvVh+$K^27I5}kT1O;7&4i58T zC0rFBn(!(iuv8ZeH7To|wp$s7qDk7qaMTcJ8PKAz6Aey^#2Gd*W0Zx5$v|NRqfa40 z!VYuci!>nUs^(SIpu=p0gc_cM({8w(BtaLPf({@lNi#gj@(k&vY&=QvEH{^gmSK2R zQskQdhBh`2n%7tI5+w@PuSuBtcGOa^w(qI$F{mWA5{f1^1uuwwDMW>8ASL32Tzykw zKnVK*JU#l`b-=ECLn~0SM9Vgo!F)av_K?iL`Y|$tSt(hDvifL1*^=3#s;tKZ4b1mL z9idjRJri1?vlE4ylZ=i9fKde~7)G)f&3S2xhvLyxzhK0@Pthte5^LjF4A^V{W342I z`6Q9W7>*_d!AAKc%90%P|B~WhPgv;W6!Qq2J4txB{deU1UW1j>4ts8dLNDYP|hEQJ*m#g7RI?`QFSkP0jO7NWr)i=2q{T>eBHuH(vc!dPhO) z7cJG}kr`>8-0$DFdUNQ)ipMUv6x9pvoL~QvcJld^0sOV5X%F#bOaBF#5LfXM=ib7n G*8dyMBTMf9 literal 0 HcmV?d00001 diff --git a/applications/external/flipperzero-bomberduck/bomberduck.c b/applications/external/flipperzero-bomberduck/bomberduck.c new file mode 100644 index 000000000..7b8b5f14a --- /dev/null +++ b/applications/external/flipperzero-bomberduck/bomberduck.c @@ -0,0 +1,645 @@ +#include +#include + +#include +#include +#include +#include +#include +#include "bomberduck_icons.h" +#include + +int max(int a, int b) { + return (a > b) ? a : b; +} + +int min(int a, int b) { + return (a < b) ? a : b; +} + +#define WorldSizeX 12 +#define WorldSizeY 6 +#define BombRange 1 + + +typedef struct { + FuriMutex* mutex; +} BomberState; + +typedef struct { + int row; + int col; +} Cell; + +typedef struct { + Cell cells[WorldSizeY * WorldSizeX]; + int front; + int rear; +} Queue; + +void enqueue(Queue* q, Cell c) { + q->cells[q->rear] = c; + q->rear++; +} + +Cell dequeue(Queue* q) { + Cell c = q->cells[q->front]; + q->front++; + + return c; +} + +bool is_empty(Queue* q) { + return q->front == q->rear; +} + +typedef struct { + int x; + int y; + int planted; +} Bomb; + +typedef struct { + int x; + int y; + bool side; +} Player; + +typedef struct { + int x; + int y; + int last; + bool side; + int level; +} Enemy; + +typedef struct { + int matrix[WorldSizeY][WorldSizeX]; + Player* player; + bool running; + int level; + + Enemy enemies[10]; + int enemies_count; + + Bomb bombs[100]; + int bombs_count; + + int endx; + int endy; +} World; + +Player player = {0, 0, 1}; +World world = {{{0}}, &player, 1, 0, {}, 0, {}, 0, 0, 0}; +bool vibration = false; + +void init() { + player.x = 1; + player.y = 1; + + world.endx = 4 + rand() % 8; + world.endy = rand() % 6; + for(int i = 0; i < WorldSizeY; i++) { + for(int j = 0; j < WorldSizeX; j++) { + world.matrix[i][j] = rand() % 3; + } + } + world.running = 1; + world.bombs_count =0; + vibration = false; + for(int j = max(0, player.y - BombRange); j < min(WorldSizeY, player.y + BombRange + 1); j++) { + world.matrix[j][player.x] = 0; + } + + for(int j = max(0, player.x - BombRange); j < min(WorldSizeX, player.x + BombRange + 1); j++) { + world.matrix[player.y][j] = 0; + } + + world.enemies_count = 0; + for(int j = 0; j < rand() % 4 + world.level / 5; j++) { + Enemy enemy; + enemy.x = 4 + rand() % 7; + enemy.y = rand() % 6; + enemy.last = 0; + enemy.side = 1; + enemy.level = 0; + + world.enemies[j] = enemy; + world.enemies_count++; + + for(int m = max(0, world.enemies[j].y - BombRange); + m < min(WorldSizeY, world.enemies[j].y + BombRange + 1); + m++) { + world.matrix[m][world.enemies[j].x] = 0; + } + + for(int m = max(0, world.enemies[j].x - BombRange); + m < min(WorldSizeX, world.enemies[j].x + BombRange + 1); + m++) { + world.matrix[world.enemies[j].y][m] = 0; + } + } + world.matrix[world.endy][world.endx] = 1; +} + +const NotificationSequence end = { + &message_vibro_on, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_vibro_off, + NULL, +}; + +static const NotificationSequence bomb2 = { + &message_vibro_on, + &message_delay_25, + &message_vibro_off, + NULL, +}; + +static const NotificationSequence bomb_explore = { + &message_vibro_on, + &message_delay_50, + &message_vibro_off, + NULL, +}; + +static const NotificationSequence vibr1 = { + &message_vibro_on, + &message_delay_10, + &message_vibro_off, + &message_delay_10, + &message_vibro_on, + &message_delay_10, + &message_vibro_off, + &message_delay_10, + + NULL, +}; + + +void intToStr(int num, char* str) { + int i = 0, sign = 0; + + if(num < 0) { + num = -num; + sign = 1; + } + + do { + str[i++] = num % 10 + '0'; + num /= 10; + } while(num > 0); + + if(sign) { + str[i++] = '-'; + } + + str[i] = '\0'; + + // Reverse the string + int j, len = i; + char temp; + for(j = 0; j < len / 2; j++) { + temp = str[j]; + str[j] = str[len - j - 1]; + str[len - j - 1] = temp; + } +} + +bool BFS() { + // Initialize visited array and queue + int visited[WorldSizeY][WorldSizeX] = {0}; + Queue q = {.front = 0, .rear = 0}; + // Mark the starting cell as visited and enqueue it + visited[world.player->y][world.player->x] = 1; + Cell startCell = {.row = world.player->y, .col = world.player->x}; + enqueue(&q, startCell); + // Traverse the field + while(!is_empty(&q)) { + // Dequeue a cell from the queue + Cell currentCell = dequeue(&q); + // Check if the current cell is the destination cell + if(currentCell.row == world.endy && currentCell.col == world.endx) { + return true; + } + // Check the neighboring cells + for(int rowOffset = -1; rowOffset <= 1; rowOffset++) { + for(int colOffset = -1; colOffset <= 1; colOffset++) { + // Skip diagonals and the current cell + if(rowOffset == 0 && colOffset == 0) { + continue; + } + if(rowOffset != 0 && colOffset != 0) { + continue; + } + // Calculate the row and column of the neighboring cell + int neighborRow = currentCell.row + rowOffset; + int neighborCol = currentCell.col + colOffset; + // Skip out-of-bounds cells and already visited cells + if(neighborRow < 0 || neighborRow >= WorldSizeY || neighborCol < 0 || + neighborCol >= WorldSizeX) { + continue; + } + if(visited[neighborRow][neighborCol]) { + continue; + } + // Mark the neighboring cell as visited and enqueue it + if(world.matrix[neighborRow][neighborCol] != 2) { + visited[neighborRow][neighborCol] = 1; + Cell neighborCell = {.row = neighborRow, .col = neighborCol}; + enqueue(&q, neighborCell); + } + } + } + } + return false; +} + +static void draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + const BomberState* bomber_state = ctx; + + furi_mutex_acquire(bomber_state->mutex, FuriWaitForever); + if(!BFS()) { + init(); + } + canvas_clear(canvas); + + canvas_draw_icon(canvas, world.endx * 10 + 4, world.endy * 10 + 2, &I_end); + + if(world.running) { + for(size_t i = 0; i < WorldSizeY; i++) { + for(size_t j = 0; j < WorldSizeX; j++) { + switch(world.matrix[i][j]) { + case 0: + break; + case 1: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_box); + break; + case 2: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_unbreakbox); + break; + case 3: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb0); + break; + case 4: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb1); + break; + case 5: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb2); + break; + case 6: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_explore); + world.matrix[i][j] = 0; + break; + } + } + } + + if(world.player->side) { + canvas_draw_icon( + canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerright); + } else { + canvas_draw_icon( + canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerleft); + } + + for(int i = 0; i < world.enemies_count; i++) { + if(world.enemies[i].level > 0) { + canvas_draw_icon( + canvas, world.enemies[i].x * 10 + 4, world.enemies[i].y * 10 + 2, &I_enemy1); + } else { + if(world.enemies[i].side) { + canvas_draw_icon( + canvas, + world.enemies[i].x * 10 + 4, + world.enemies[i].y * 10 + 2, + &I_enemyright); + } else { + canvas_draw_icon( + canvas, + world.enemies[i].x * 10 + 4, + world.enemies[i].y * 10 + 2, + &I_enemyleft); + } + } + } + } else { + canvas_set_font(canvas, FontPrimary); + if(world.player->x == world.endx && world.player->y == world.endy) { + if(world.level == 20) { + canvas_draw_str(canvas, 30, 35, "You win!"); + }else{ + canvas_draw_str(canvas, 30, 35, "Next level!"); + char str[20]; + intToStr(world.level, str); + canvas_draw_str(canvas, 90, 35, str); + } + + } else { + canvas_draw_str(canvas, 30, 35, "You died :("); + } + } + + furi_mutex_release(bomber_state->mutex); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + // Проверяем, что контекст не нулевой + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t bomberduck_app(void* p) { + UNUSED(p); + + // Текущее событие типа InputEvent + InputEvent event; + // Очередь событий на 8 элементов размера InputEvent + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + BomberState* bomber_state = malloc(sizeof(BomberState)); + + bomber_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); // Alloc Mutex + if(!bomber_state->mutex) { + FURI_LOG_E("BomberDuck", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(bomber_state); + return 255; + } + + DOLPHIN_DEED(DolphinDeedPluginGameStart); + // Создаем новый view port + ViewPort* view_port = view_port_alloc(); + // Создаем callback отрисовки, без контекста + view_port_draw_callback_set(view_port, draw_callback, bomber_state); + // Создаем callback нажатий на клавиши, в качестве контекста передаем + // нашу очередь сообщений, чтоб запихивать в неё эти события + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Создаем GUI приложения + Gui* gui = furi_record_open(RECORD_GUI); + // Подключаем view port к GUI в полноэкранном режиме + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message_block(notification, &sequence_display_backlight_enforce_on); + + init(); + + // Бесконечный цикл обработки очереди событий + while(1) { + if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { + furi_mutex_acquire(bomber_state->mutex, FuriWaitForever); + // Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения + + if(event.type == InputTypePress) { + if(event.key == InputKeyOk) { + if(world.running) { + if(world.matrix[world.player->y][world.player->x] == 0 && + world.bombs_count < 2) { + notification_message(notification, &bomb2); + world.matrix[world.player->y][world.player->x] = 3; + Bomb bomb = {world.player->x, world.player->y, furi_get_tick()}; + world.bombs[world.bombs_count] = bomb; + world.bombs_count++; + } + } else { + init(); + } + } + if(world.running) { + if(event.key == InputKeyUp) { + if(world.player->y > 0 && + world.matrix[world.player->y - 1][world.player->x] == 0) + world.player->y--; + } + if(event.key == InputKeyDown) { + if(world.player->y < WorldSizeY - 1 && + world.matrix[world.player->y + 1][world.player->x] == 0) + world.player->y++; + } + if(event.key == InputKeyLeft) { + world.player->side = 0; + if(world.player->x > 0 && + world.matrix[world.player->y][world.player->x - 1] == 0) + world.player->x--; + } + if(event.key == InputKeyRight) { + world.player->side = 1; + if(world.player->x < WorldSizeX - 1 && + world.matrix[world.player->y][world.player->x + 1] == 0) + world.player->x++; + } + } + } else if(event.type == InputTypeLong) { + if(event.key == InputKeyBack) { + break; + } + } + } + if(world.running) { + if(world.player->x == world.endx && world.player->y == world.endy) { + notification_message(notification, &end); + world.running = 0; + world.level += 1; + if(world.level%5==0){ + DOLPHIN_DEED(DolphinDeedPluginGameWin); + } + } + for(int i = 0; i < world.bombs_count; i++) { + if(furi_get_tick() - world.bombs[i].planted > + (unsigned long)max((3000 - world.level * 150), 1000)) { + vibration = false; + world.matrix[world.bombs[i].y][world.bombs[i].x] = 6; + notification_message(notification, &bomb_explore); + + for(int j = max(0, world.bombs[i].y - BombRange); + j < min(WorldSizeY, world.bombs[i].y + BombRange + 1); + j++) { + if(world.matrix[j][world.bombs[i].x] != 2) { + world.matrix[j][world.bombs[i].x] = 6; + if(j == world.player->y && world.bombs[i].x == world.player->x) { + notification_message(notification, &end); + world.running = 0; + } + for(int e = 0; e < world.enemies_count; e++) { + if(j == world.enemies[e].y && + world.bombs[i].x == world.enemies[e].x) { + if(world.enemies[e].level > 0) { + world.enemies[e].level--; + } else { + for(int l = e; l < world.enemies_count - 1; l++) { + world.enemies[l] = world.enemies[l + 1]; + } + world.enemies_count--; + } + } + } + } + } + + for(int j = max(0, world.bombs[i].x - BombRange); + j < min(WorldSizeX, world.bombs[i].x + BombRange + 1); + j++) { + if(world.matrix[world.bombs[i].y][j] != 2) { + world.matrix[world.bombs[i].y][j] = 6; + if(world.bombs[i].y == world.player->y && j == world.player->x) { + notification_message(notification, &end); + world.running = 0; + } + for(int e = 0; e < world.enemies_count; e++) { + if(world.bombs[i].y == world.enemies[e].y && + j == world.enemies[e].x) { + if(world.enemies[e].level > 0) { + world.enemies[e].level--; + } else { + for(int l = e; l < world.enemies_count - 1; l++) { + world.enemies[l] = world.enemies[l + 1]; + } + world.enemies_count--; + } + } + } + } + } + + for(int j = i; j < world.bombs_count - 1; j++) { + world.bombs[j] = world.bombs[j + 1]; + } + world.bombs_count--; + } else if(furi_get_tick() - world.bombs[i].planted > (unsigned long)max((3000 - world.level * 150)*2/3, 666)&&world.matrix[world.bombs[i].y][world.bombs[i].x]!=5) { + world.matrix[world.bombs[i].y][world.bombs[i].x] = 5; + vibration=true; + + } else if(furi_get_tick() - world.bombs[i].planted > (unsigned long)max((3000 - world.level * 150)/3, 333)&& world.matrix[world.bombs[i].y][world.bombs[i].x]!=4) { + world.matrix[world.bombs[i].y][world.bombs[i].x] = 4; + + } + } + for(int e = 0; e < world.enemies_count; e++) { + if(world.player->y == world.enemies[e].y && + world.player->x == world.enemies[e].x) { + notification_message(notification, &end); + world.running = 0; + } + } + + for(int e = 0; e < world.enemies_count; e++) { + if(world.enemies[e].level > 0) { + if(furi_get_tick() - world.enemies[e].last > + (unsigned long)max((2000 - world.level * 100), 1000)) { + world.enemies[e].last = furi_get_tick(); + int move = rand() % 4; + switch(move) { + case 0: + if(world.enemies[e].y > 0 && + world.matrix[world.enemies[e].y - 1][world.enemies[e].x] != 2) + world.enemies[e].y--; + break; + case 1: + if(world.enemies[e].y < WorldSizeY - 1 && + world.matrix[world.enemies[e].y + 1][world.enemies[e].x] != 2) + world.enemies[e].y++; + break; + case 2: + world.enemies[e].side = 0; + if(world.enemies[e].x > 0 && + world.matrix[world.enemies[e].y][world.enemies[e].x - 1] != 2) + world.enemies[e].x--; + break; + case 3: + world.enemies[e].side = 1; + if(world.enemies[e].x < WorldSizeX - 1 && + world.matrix[world.enemies[e].y][world.enemies[e].x + 1] != 2) + world.enemies[e].x++; + default: + break; + } + } + } else { + if(furi_get_tick() - world.enemies[e].last > + (unsigned long)max((1000 - world.level * 50), 500)) { + world.enemies[e].last = furi_get_tick(); + int move = rand() % 4; + switch(move) { + case 0: + if(world.enemies[e].y > 0 && + world.matrix[world.enemies[e].y - 1][world.enemies[e].x] == 0) + world.enemies[e].y--; + break; + case 1: + if(world.enemies[e].y < WorldSizeY - 1 && + world.matrix[world.enemies[e].y + 1][world.enemies[e].x] == 0) + world.enemies[e].y++; + break; + case 2: + world.enemies[e].side = 0; + if(world.enemies[e].x > 0 && + world.matrix[world.enemies[e].y][world.enemies[e].x - 1] == 0) + world.enemies[e].x--; + break; + case 3: + world.enemies[e].side = 1; + if(world.enemies[e].x < WorldSizeX - 1 && + world.matrix[world.enemies[e].y][world.enemies[e].x + 1] == 0) + world.enemies[e].x++; + default: + break; + } + } + } + } + for(int e = 0; e < world.enemies_count; e++) { + for(int h = e + 1; h < world.enemies_count; h++) { + if(world.enemies[e].y == world.enemies[h].y && + world.enemies[e].x == world.enemies[h].x) { + world.enemies[h].level++; + for(int l = e; l < world.enemies_count - 1; l++) { + world.enemies[l] = world.enemies[l + 1]; + } + world.enemies_count--; + } + } + } + if(vibration){ + notification_message(notification, &vibr1); + } + } + + view_port_update(view_port); + furi_mutex_release(bomber_state->mutex); + } + + // Return to normal backlight settings + notification_message_block(notification, &sequence_display_backlight_enforce_auto); + furi_record_close(RECORD_NOTIFICATION); + // Специальная очистка памяти, занимаемой очередью + furi_message_queue_free(event_queue); + + // Чистим созданные объекты, связанные с интерфейсом + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + + furi_mutex_free(bomber_state->mutex); + furi_record_close(RECORD_GUI); + free(bomber_state); + + return 0; +} From b65f6665785feb802852560894b28bf20f68ae93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D0=BD=D1=8C=20=3A=29?= <88856726+leo-need-more-coffee@users.noreply.github.com> Date: Mon, 1 May 2023 20:38:57 +0300 Subject: [PATCH 244/282] Delete applications/external/flipperzero-bomberduck directory --- .../external/flipperzero-bomberduck/LICENSE | 22 - .../external/flipperzero-bomberduck/README.md | 2 - .../flipperzero-bomberduck/application.fam | 15 - .../flipperzero-bomberduck/assets/bomb0.png | Bin 4536 -> 0 bytes .../flipperzero-bomberduck/assets/bomb1.png | Bin 4529 -> 0 bytes .../flipperzero-bomberduck/assets/bomb2.png | Bin 4531 -> 0 bytes .../flipperzero-bomberduck/assets/box.png | Bin 4329 -> 0 bytes .../flipperzero-bomberduck/assets/end.png | Bin 4739 -> 0 bytes .../flipperzero-bomberduck/assets/enemy1.png | Bin 4757 -> 0 bytes .../assets/enemyleft.png | Bin 4761 -> 0 bytes .../assets/enemyright.png | Bin 4536 -> 0 bytes .../flipperzero-bomberduck/assets/explore.png | Bin 4540 -> 0 bytes .../assets/playerleft.png | Bin 4311 -> 0 bytes .../assets/playerright.png | Bin 4307 -> 0 bytes .../assets/unbreakbox.png | Bin 4763 -> 0 bytes .../external/flipperzero-bomberduck/bomb.png | Bin 4534 -> 0 bytes .../flipperzero-bomberduck/bomberduck.c | 645 ------------------ 17 files changed, 684 deletions(-) delete mode 100644 applications/external/flipperzero-bomberduck/LICENSE delete mode 100644 applications/external/flipperzero-bomberduck/README.md delete mode 100644 applications/external/flipperzero-bomberduck/application.fam delete mode 100644 applications/external/flipperzero-bomberduck/assets/bomb0.png delete mode 100644 applications/external/flipperzero-bomberduck/assets/bomb1.png delete mode 100644 applications/external/flipperzero-bomberduck/assets/bomb2.png delete mode 100644 applications/external/flipperzero-bomberduck/assets/box.png delete mode 100644 applications/external/flipperzero-bomberduck/assets/end.png delete mode 100644 applications/external/flipperzero-bomberduck/assets/enemy1.png delete mode 100644 applications/external/flipperzero-bomberduck/assets/enemyleft.png delete mode 100644 applications/external/flipperzero-bomberduck/assets/enemyright.png delete mode 100644 applications/external/flipperzero-bomberduck/assets/explore.png delete mode 100644 applications/external/flipperzero-bomberduck/assets/playerleft.png delete mode 100644 applications/external/flipperzero-bomberduck/assets/playerright.png delete mode 100644 applications/external/flipperzero-bomberduck/assets/unbreakbox.png delete mode 100644 applications/external/flipperzero-bomberduck/bomb.png delete mode 100644 applications/external/flipperzero-bomberduck/bomberduck.c diff --git a/applications/external/flipperzero-bomberduck/LICENSE b/applications/external/flipperzero-bomberduck/LICENSE deleted file mode 100644 index bce361a99..000000000 --- a/applications/external/flipperzero-bomberduck/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -MIT License - -Copyright (c) [year] [fullname] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/applications/external/flipperzero-bomberduck/README.md b/applications/external/flipperzero-bomberduck/README.md deleted file mode 100644 index 2d133145a..000000000 --- a/applications/external/flipperzero-bomberduck/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# flipperzero-bomberduck -Bomberman clone on flipper zero! diff --git a/applications/external/flipperzero-bomberduck/application.fam b/applications/external/flipperzero-bomberduck/application.fam deleted file mode 100644 index afcd5a6ee..000000000 --- a/applications/external/flipperzero-bomberduck/application.fam +++ /dev/null @@ -1,15 +0,0 @@ -App( - appid="bomberduck", - name="Bomberduck", - apptype=FlipperAppType.EXTERNAL, - entry_point="bomberduck_app", - cdefines=["BOMBERDUCK"], - requires=[ - "gui", - ], - stack_size=1 * 1024, - order=90, - fap_icon="bomb.png", - fap_category="Games", - fap_icon_assets="assets", -) diff --git a/applications/external/flipperzero-bomberduck/assets/bomb0.png b/applications/external/flipperzero-bomberduck/assets/bomb0.png deleted file mode 100644 index 3fdc3a3c12c7ede770e1c99c2003c777b03f4d75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4536 zcmeHKYj6|S6<%y!HW&j1Oo@SwNCLLA+NY$|R^nmFU;#3+tr`c~z&`HETT5CYtu5IJ znE)k#DRI*@1C1$^5T@xs878Hv(-e#gv`h*^utTUBhjB~-37JAnNeB}jy}R;rl9`UD z<)1Yp>F&AT`R+O2Irq-qExw{f8R^r~5d_Kb6uL{`zdlYModSPTeC5~R-}PF5xmLo* z(MVL4l^{U1ng~EaLXi<9adBI5+qM(v*n7XN! z8V(A9salG3vEWq)igso#5Xb;gQUCQc;F zHl-hajl?w1R1tbak+dRm#Zgp znD5TDFF6a3&(A)&s&#QeuBm!_@hRW0pWIycY1w|?Pt*65Rh)1BxFvIs$JNr=Ij3<- z_TBOWTqFDWwT|4;>t~_|%|&(QlFkRe$lp3O^Tltvu3x%*eLDL7lBIu0A?LT|$+;7< z=(fq^*LBDX?`d9zI*S=Gf$2EV#^6q-Z8nes`Gbe z>Y8hIv#0CcT>Na2<0ma6w#7=2vkEQ_+Am@+1G;4qo@>a?!M*0|4W z?`Bcv{X74}R_@EKy!3Jt@ypij+Z&5wXWSq5 zcl@O#k6su#)qbV;g^_Jt8y+;@2><@>iN=OWb*a(T&yMDdx%FYnkN1bw zGi!IwMU-o~HL2OzDYHhr`}DsP@xU|cgI&s3w^L{Mqo4n)^w50-8Mjh_tyu0|%86>o z%uA{O%!yD0_9%il<|QJ$SP3*#00AZJG~IZo$%HDB)3n^;CA<+As89-PqoB05$S>Ab zigw8~Z*ICH!9ju$(0DWv3Wj4`!f7&iIXKqMxCu2NTBXxe?)9NAH40G1%$NzRAfZ%K zrn%{;BPz*UiF;u;1$=UvDl{#^;dneAH^*tS8V%s2-EPMT3a2OxBCuFZSmP5|I5tbC zNOHJAOpGcKO;N+B&dCdEmF6^=;5^zBUnt`B_R@!A-6}vm@B|;hNi%_mLU@0Vm{w2? zNxB32PLG%$?r6LO#MG*&2nwn}Sewcq76!AE423Z^n-#M&7B~`ENt7+TOF z%VPjoWJ(r!t0>b3l_YWt)o6%^wI4Xbg6u&PftxIys}4g@egJVR^kzA%Wom zbKzW3z-wyMuc|?(Ne>CtJ$t9Ua63u7#=Ch9KvIIDIfCJ6!cUMKVdE%!9sw==@T#Q9 zHUAB*ZywZ{51CBi0%mSl_e3i!XI*xM5{F)&33t{;m%MRnlcDti*p?;M5FByI3~hQ~eLd){|z z2IGC0pYC>hm`kAO&?1A8H;+Tm(fQ3^K4=PV`Vk4%l%WMT8i zU6+p|2Sy^F9<5lqb<_El58BHnhwrG5w6+vqZ1P6;XZ(eJBr|7J!+Vem@e~xf59Y64 F{~v;POwj-U diff --git a/applications/external/flipperzero-bomberduck/assets/bomb1.png b/applications/external/flipperzero-bomberduck/assets/bomb1.png deleted file mode 100644 index 11d05b9b7c445452253792c5a51ad6448cca8aee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4529 zcmeHKYj6|S6<*swEEzB`38t=zH-ZE|vf77MudM_zlEDIGgsmEfOoR7vSKeCE3hmmG zO&&l(8cb5>#Xtjvl7wkGJcdcr)G0%W3u&1IPlBCsjCSvy`~0E_FP>t|H6jQy#aHI7fOl4^|{MfZC%F{ugZOhB<(1vZ>mp)M`9skt&V!ivdjpyd=Jdl<4+CgUF)_3gPmz-S7 zeUE&xdgGrypCtbQUqyHSbJGVa0==Q#HNSE(ow?bsJhy~+V>;eeuoi1M`N_te6@C1w zxyygCxnz5wuSw*{zU?urHW+Izp1knw{zZ=`l)ej_uZQ+sne^e`6Y1A7bDu$ww2hL-6YzOFgNeg* zcRc#MyX@!#dGD_7TvA$OYM8d-c;J`!KizzxYH#34FDVxXq}mN zy?P(h>iDX+yC`!*K6=1h-fXVux%I-r&9kz9{$2mY&(2<)jefLr*>BUy2Re(zqUkx7 zuDi%@nvthI+)SQ0eu5J3Ei`=4U!R@7y6+X&``>-K>(z(aADnz?(=k%qc2`e*&u`kB z+ZuN{PBg!<-9gs-F@&K%TbZBF3!7eW>#e1n`H6Jx&3q3ct2L( z%c0DBcl_Q_x2LG?vlovLFLiF++FGtmGu&S@eTKK-buQ!H;IutIp7-v;=BsD#zM1wH z{Q0H#oSHPt`NfK-CUx~cajUH_y8rsI*6nvTXUxXmxeE3JOQAM=1(sjMjuDx1oKd{Z6o z6if@|8(m2T5=4N?qRB`&sxV2nN#|wYSTo}$REMZ_Zd0{CfO_OOKxs2=Ca}_^)Igc$ z8&OwW5Sa?^q5%r{n(Ik{eSn@n&X9gHs$^ZSSBqso8^P!BxG#&FV1;E@PE(nC>88z9L* zK;P@31mT9pD}W-`$9YiN0HW%gkrV9tU9%flvjmN{(7`vCkhE^3Ws* zNs*ZD1;rkPR3&j(tWmLPGkQ8B1A*>Cyra;Ax$9tv^7|RD%-3t-`MhqEwmu`syd*IC zkmfi$&su5BNzxQX+wC^YNn7EFa|pa>W$hfzj-c{I6_t(hK%;`>W(o3$qJ`pEs|}-V z0A3uNh;ck=#VDI-rwGE8h6akegtEoxa9F&8wktAuekd)Il45|Wg1$LswNfKrYtVPG;)SXS#(NTAzc zE{rD*SXGV(WjX9NX(6GS=g_ntZYP0NSud*sNJ>x^hM*Y>5hO^4aKJ$^0WBl&vLK0# z{|&8e9@Ld8d6}fZ^&53lYDZOqHL0i6V_4F+5{l}Zf?@eo3JTi*1U*j3m73yf*=PvB z(_^4r2kp}Lv;qlBE!t@d#&HDfA<+WsXAv!!jTA+aA~*_=_R;K$EUF1M4oX5$N2nES zPhBgtKrd9`Xmp|$XjOoMVFZm)&LBlHP&|qp5DdTlDbg0Z18^jPIqY!P&{mGbSc;=C zo~JlMw32o!aEuQ6e@St;C;lx(7p@(PgGF`W|55f7V8l5JrAgV~`3#SHeCWI%&`dj7 zZ{u&E-EQLwka~QPG3h%l*SK6`QeaHr@$MRzYfK7^2|V6i|2Mge!(WUb3SaaR@O!W* z{^U#WThb6Kdr(0T^BnD;mfW+T0UGa6eg4ur&QF*zcD`;u^wIl|ukC9&oAubK9~ufwSNA{`#8+DGJ+QE5!@mG~Pe@e& diff --git a/applications/external/flipperzero-bomberduck/assets/bomb2.png b/applications/external/flipperzero-bomberduck/assets/bomb2.png deleted file mode 100644 index 38ce7c732bfa5e3586d1c9b9a754b85405cd4566..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4531 zcmeHKYj6|S6<$9e*oGLGfT_peML+;Mt9|ORTZxAyg9XS4TQv?$fqmSSHA3^WiZAxzpKrA(4i6NVBeq@^iL8oQJ_lVlvzgcO=MrVOMFkKSGRIn7MR z)AG-nk#zUmbH01dcfNaP@3uhs(h1r5*$9G6@RfNh;J-1>9+wV((*xC4;NRzs!D_vN zPoS~5Dl1`t>J2e~f}|oNNb>!sS9Lyp1|5I!#1@WW-qE0gfHd5>Z{-}`g({)!%P z&Fqyw*}iyhV1DPvt7jJf<#OZ15A%B0)#T@#dW>FB2{Wmz+EVqawu+sH-rq1`&9jpy zlT|<1w5X}~iC<=pA#T>_x2q1FK6xT>z4Q)U)tK>KD3S56b|4{0TC?Ku1biM(f8ns) zeNE4}%ida4@b>zSWu--y`iZN~2A+RtYs+Ue|AFQgm(Dv8%+=D((dw2J|)~N-z zs$b?>oma1P6^+|ch#s|;w^%E>@BXM{`?TC0U-y3T(WNh@qkmey;?;C=QAe>{G%1hn zoI-xpf;{=h?c}+$=O}s3{H%9->v9X%_q^cx!`B}iIJEZs!{fi)c7~MqOzE!c{`vWq zwuS@Fb1knd+f?p)qTt>!A&kCVS`oOvsoMoOt**8PHaR{sv3be)cHmW zQ=i}$H~(#Ado6~=Gp~m;@-AZj&J1hfu{(1;cChqX#{<_Ix^6%9oB27{KTQAbk*IpU zY2N}wxl+`SQBaURYs@?A{*g?C9#!w|S8m+Qm=TO${k-zn9R#_5tpb%;?O(x(YQ)M* zssOCXNDN98L0k)yF5Y%C9ojkO{x zSr#tHb|pCo5CJ-mCL`gf#wFbr6PJTy!;D){(?zd!TdMs5)T71$%2*jIft4neddjjO z8+FAcnXB+F>4ShzZcB}>$2c5MBofvHZB^qToMc%RCn%hvFzA744N;v>Vo`0D0g=M+ z0!@r7FA_Yc5FwLTUgA9#|F;iQ$oBN2RPgr=9)Ly*3N zzA-`z!VQgA08Ooni=ea~MDLA4a?1#G0Pd62n-*<9p>F%yXg$Q{C>`>igiYMKCjzitj|fRs7RbS zWCXz>@-_xzNru80hr^Dsj17(irzFZY-XSpj5GY?%)A^_f3@8Y0RUnR?b~3cU5}4f~ z*f550Ix&G_Y#3o<8PXxM5=D_iAXdf|s7gLOG%Ev2f>2J965#Q~q&LRpDHOCZeaG7NBARw&WB zMoOp=IWX;q+ezYe-plI%f)W(X5e!EYL4xE677mIDXc^+KN{Za@ z-@J{@gSt{JFH!Uv6i8X3WCufIfj^#%7Kb2@kSi!p zlPh$d*{J!$y%ROS=mI1RBN&WggA~a@@+h*8F#O(^DB5|_LECvuq$B~Vof0u814xV( zL`oK%EW_GT`WPVae<^WL6V&h$UAS>9_BYjq|3}+X4nxjSNKMKH&u4ht;{)e?A7?P% zd->^;+r3-@MMoDI5x=8!jnXwD21evOs;*JGM#R8~oJZC5f1@jV@Wu$D@I@~H?}KlC zwr(rDOJ>E&9@Y@VI?MQ`B@ZvGhsOJKpTG3J%bC-sW}yQA_qEXUExoi__oxx$nt_;a zzd%A0ie7^nZ@;TI-=Ak(Onu(P!D$^$JAOY2A;zUQ`|e`93fbA6h0fPYDxODT?2Y63 zPrW*&>7})^$)cW1xch3(p$l^^t>3Zrh@Ae&jG38f-#JVCB8d4V+w@^|y107;Q8`s!r9<>K*$8}6g>N;sjATc4yq;7b1@BE%- zrsJ9BKTD(2-nYN+x8L{MZ+CY0`icq`7#=X_bh-teLU#%LSKEdZJ$&kYfvfOusKy^q zN|-njlSEz!14OBg0R$ujUZ+c3db;>8Pj}!cx8h58=&|)#uT^{c+77htKeX!Lk4s

Y(%&x8cXle1Amu0 zmV1XgTMc#X89QHDSYsLZ;Eip?zDuDazMnbho_o`dKC?ON)Zz6(Vr-zHP8ftfT+~0UY?>&{B)Aq|tz7sb~K6-mW>0A5f zzg)ga`Wu$MC-c$Ot2Uf1ICkwN`u6i(&STi{h11<%>*4a^4JQb*;G_-DjB@{Y~GX07XI+&jiGnm9m+u7+OqYup4iZn!)Gr} zGxskghU;}tTzrz~e7%$8SFf3S;YQ`c6^~p$>iFZ$zcwEGR@cMxKi=Cx@Q0QTRt}!% zs&A`qw0G8@+Wcsd<2&8=>|JrTx#iX$&JMizdviNaTMN>^ao@bi3%?<9e)7PLRTb`D z<;^tGbn@^^_VT9e@^|*P;y>y+c(A2NUNm>@uEqDdvyKOoSNj(=J(T%sUj66qF29rX z8n$oC*ZXEKqyAa^#O(eXd+xSfkN)abN6T|d>XS3DGq;KPv*NXzy3VvVM9x*{hZ}2i ze)kWoul9p&-Q_4M&H5~yoYseW`;$%a@88LE*+9W3Jxe~R9=QF~D{JO`c3J=JlTop2 z_u+Lq;c9kua(cRc*rdHh`>3GJFH&Z8*_m&p|C~*+iQyaY)v^! zamMxQ42}d12_isYkVGULmFa}jsPWRUt(q|-qCu2$r!nC5Audq@h{a?v;b?wBs3MK) z42VPGc)G;BX_NxKIgMqC5~DFJ9*>*iW|Js|FoL2e3@0&?L?Hr|tD_2&K%??Xm12a$ z4P;glVu~O}5tWk(ij|7fXoUU9SbUL~*E>!hl}A;8dSD4Ah7l$li$t)=9jpYw2(-2Erf$p)wqmm@;I6$LkySP$dWn zk(lNM#h!vx1b#xSDY2m)?z1ZRthC? zibX99V?%>x!iMsUg=EZDU}q_662&%2fK|zaCr71Hagd4vI0%~U0HuPA1+@TcE=mCo z@>wV|p9}CH$1)lf$I_cbDZ;?%6e3IrV6kXO>re%!H~KtIBWc1XEWR+K@X*0&+$uyX z6B8YNAp*85jH)JKvy-_LZp$Tb_#nv%P$`gPScxhpft$=0tw)^}8YTmUWz;%_1ezV@ zLc1iuD5B&S#jw+;hJ>h|8PsDNdSc+(MI-pTub^K@)fm4lR@LA}8?G z{|l`y9>g&+<%NO_$FJ5*BP(hIk)h z<*8|fWN8z%W-2;f2Gm)Af}ywtoe+!|6$~3q7*qF*v5Xzqe`(^-0FyQu=r>}6n-|;* zvGL7tR5Nwk`3gUybMY090I6pNnU=mYa?Qv!Ed{0po~f=Gxu&JSw7@gf^?#GgFmas% zQFsc9!^_gAH7PcD(V89HQsCCET)Ka|&V3KI=Ee#imUTMQO7)kNXj)$djdK)_H-FBR zSs9DX^M)S0dBQaH zlF4IiN?T%=QXmAF5>khuB+w?ABtw&-4MQMFO-egsU})OldTbuSv=C^fWD2hPog}}e znaOw>`j<5$>AU@Qf4lqJ-TUsItzKR+EqhKjf*{lUmA)GIZ?3Z^XTsmi>R=!I`}_LZ zpi#re(U_*nN*JIo zICuO{Iol7PyT13C>066Ceo^!0<*Hm=S0)!v+kFJQ5&h9ypWQiex&dv@d#s{w#upO{ z$ck&^#y|e+(8{b&as#&(wPlo)-C*Y|?GNWoeeOf)d%cV4ntPTwZx-f**Dl_8tD*hC znMVR`7m9Q;IJ3C$!JcAi!tJ#=FWt6weJdcg&x&0Nz0$lrd~)gQ2c5yb_Jh;9o_MIJ z{ijcA1x($Qw)syLE@*z|!=GRM^_>3mr+&K+zxiTlzpL~2@rg68xxRrQ89z|G-fF+s zJD503chA#XJ(b577QFxX>7@@9PjAd!*-`z%-?}ID1P@hj%-;WS-PvOoI&<3n-fcZS z3*NrB;7;(D+}rM}eci>AH+&m?)xNydUehzMZE;(E&QEUNxYpHsZ7zD|qbkNiEgf-)oybv+aH7^MBd3W9p7X ztNW#w=Ukk!X6HBm@W`IC+79M=$K&}Y>pxny_5iyn>)zKl<@tX1vS0zhO`Qj~3f92w zbvXmgy(_jZyYJk@eD>1Hmh6rj-yb-3J$mZB3mboy*J_!IAMPhJv*ImFP8~kFC30k4 z<`*xv-2bbKubpYxy`r-o!?di=qn26cut1l^9&i5l{70$pR@^+Dmp!4o|HtnZP5S(N z=J`WW_0-dQN|D)p#Z8uig3Nv8Cm;JqA|85D9oVb<^OmKfUh8=71I<8?oKgjLVlYs} ziE6~oOR50uiAW5#D1wxfC1Si-4-8ZQAtmauUO#fwiYk)Fx|#_PftVN6DV6IruxkDC zT5)~7$V%3-((IB12LU3$;L$`R9M!pm$C||D;Mx@9Ry3(%)O)PKKsD-BHGtA~+D>5Q z38j&;mS&?Rnj~{Iz9nf0c;&Iy8Ago5@pwFLk2~zD7Q#uEWpRSSDGGxcnBEjM_yiW! zZ6-tt!v}OxQ(}gqMo|-!7t{vBW3|G4bTGb1ED#uikLqa_ARl;wkKv@9z#|cSq=jyj zH$sqfKwoO1*TR9uYk;mcXd)|+gDIGK(l;vftnP*sOs$x$vV{DJBr z3zLG75{V_PAlaid4MiR%YgBCJPBNX5fk5*i+)>(txhIvORv^InRI$Mf&+qeCP5+#v zii*S~m$V?bM4q8BmZT|+cDbAwOEYjKxFu0$c$Yx)BcS|I-Qc4lFrgs0U4b}m2kT-b zH;GXqAz?HnIx)B8ggCq?5rmTl0^=9~u|iW|SMuSJQJGMZ2}QC3OR)?lxG4goS%|_* zZXN@Gkttc^ouZtAl02yC1tpg9LoOVS)cag^(R0!l{ctCAu& zeU-L3JZMR(D7kdkpitkjlR$45f| z-aXRoIw)7Zq!mb6qGT8CzyyJSJtR9|{T#9bbCR-5QiMPO(lr`gS7js4YhY0b@(8(t z?V02XU63qP(P-^>9Wbi^3Bw2)qu5%CrXQvcK92KzoDH%M1;E9G09ck&dFb|Kr_(@OHUGxB< z$DNFc-*LLe=^7IQV^SXPu5r4?#K4%8$Ghumqbqy(r5Hrvvuqr`@*XYn9fhyU_rxmK z=m=uBncs}W{<20WoMiX|<&!RF&6|=r_pgU9RYB2gqdaJM)rk2fg(M&FfrQ2tqYgD6 z@K?7?p9PhQ{JurC`KQG@0|R9ii^XQMZOq7s*{8gbYniyPa^IC761(z0+t6O`Ms^D` W?%Tg+a|JX){N>AiuP%OK!+!v0;CsXX diff --git a/applications/external/flipperzero-bomberduck/assets/enemy1.png b/applications/external/flipperzero-bomberduck/assets/enemy1.png deleted file mode 100644 index 7ee7cb27f1ee52c63dbdb31e788c1011c8448d35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4757 zcmeHKeQ*?K8DDZ~L(Y;gZ3+PcEO&er*xQfW``9&?K<|#^AV-dH&@fWl{dhOoCU<-6 zZE_bZ*aSL45F@leGo)0-Rw9ayb$|>CLJb-!%7ii~TFfL=FfhR&18RWy?p;2$Gfu|h zU%8pvefNEz=l4F(@A)pRO1up-nWJ}%r-V}HSO=6USEIgpYbb4+b>@E zS<&G)*B_sLVe2bc#;Npmt+~hCH!7Z;_4=Kh8{Veg#)TK=ocQt6flKc#ckO-Um*FLA zmKR=I+AO`icGi~pug;ybq^02C&13Atrt_|Y`M2_ayL{H=y4d8ddv~`W$b{Erw>#i- zy9YCe`8I9ZRa@ZgTODbgrH2Dsjqk24zjXRqd*-)%?%kc8 z3yx38c~JU&_PFzAcgMnsb+gbTmX-CElFt6!&+o|2{CZz+&(*G;8R$nJ7TFBsl1q!t zZ*SZ2`#;uazv@LkxN&Ck*|}2p`^~|p4|Ls}nRoK4w`TACmi&T--HvG;&ukZp9E&@v zI{)Hdas8*oInrF;?7aCitvBj^(Xf8si<`UFYo#5HocWCd=8R`{wA%On;jKMS?TObr zzkO)>C!brl@7dpcvFVb!$9B8zrR;MRAFf#cU1nSAlpk%&^8D%$Zvds++P}ApH~ITE zWcI(-RlIA(yvt8!Gk;!HYi#S?(trAP`23k`TlZ(x8)o1~ACf7lvD#(lkDh7>o!F4_ z_jhU+{os=yepK67++KlUYU+1KOo<`Z)mGf+ul5@Y6y~S!U+{XLwQgphh3)ICr+7AS#+6Ru=xpp#0|>j!i_3ey>Vqw z*jOPjqA5Smm>XvyKnQ3Y8V^;5qioz|O5n1vt#{)lG-0AuxJ;$~0P0p$fYKJ)LSWvw zTuqtsjA*VZN^FT|*#HE5a+%6CEyCh>EEcoGtQJKr!%2o=aDu`q3WF9{v?i=^aV#7) z>kvr{4~Pn?9MNPYjOv&iuT*I+lL?NagYktTe*X}BI6A-rJP zdFT|B$)QNX3z9v`Qj?`&vPQ+Gk0jC=83=SA!X0Hjn0vw)TKWB~M-i&@@O&PZNuQq; z6+sr+M3d%uhrrorj3H?Xqa6-A#?Us{;+>)(**FJJb0eU9;i$%i1)xJgaElCa7~V=z zq}7f&MTy5~jDDTlZ+fJee*i2_LlNl79pf~NrK7>yoPBrV3N zU}+iT5po6PncxatkSNr1qpf4*K(7KM3?pcaVuBROLh>kbfH3^=r)VVzhNgG{qe&59 zw37pv(+&Ve0-mu`a4C@vVRX>{ONvkv1j*WLEJ^(TQY0KApeY9|n;p;?P16j<(T43{CTZ`b<;QWp8JRf2(3HuD|&Qb(@R-hq99fBen`y{bV29g5ZvZ4{d1!vd}k= z$M_pqQIBy32t7W@nD`y1Yn-kzF)$|MadnN;H6{keWIV2}&x|hP@Szxl;kRrI9(m91 z+!Ju^G3);c@ptp9VP~4=^Lx{-r_MCanUH>X^<>yJP4kv&ZY89jq>#i1 zJ`mBEtd*ns2mTAS8B?JV>+>uPW?vK@^!Mi*3`zp7Upp$8Is*uW+&NA zL`zbs0ctr=!Fn1&i)p1&>CqOo0Vx97=)qb7$0un_px98QP>LKC(mS(BNUZ1dY(=+GA znVDZy?BQN?p6lz*o76lTeHJg>h?n>DZ(X=4efp0-zwk*{?hH)X*X{=YTy3;Pq$6kme}b0 z`O_I^zFGO`EpL9e^`y4VcD`d}`s+1sELrhOSIhXBziCPH{_$xc6;!kw|J4@38u)z8 z^!|0dWm}fq@%Dst*I$=5SvoHKp#RYM$gx*WKlrn>jj6X0`>s+cj_o_RIo!S` z<;u>cJAZv)-olB3^Z$+P_1&{Ap@Ojn?tVtD24=(+d=+#EL)( zC#qqbmsA1ZiEtE36hZO}6H#8Q0XiyxDkb8!o^L;BMHR_yy~h?H15ppCR*D-nu&l8( zBsSKFF3DP0V98H#5FiY69!-R6BQY-Fwwkyc92;iBikdEZjoVrg2%;WU11O8LIEncZ zNLJKLLSGpX3&Dma%0W!6(?sB_2N6AcID{k)#YgM3TC*HUBtR_)L)REwl^U^Rkv|X| ziZCdsQo>O)3X(m-Q&;5cWR1wpm@&&4o(K#d!X4o~Si9*Ay#fKwtBQ3-dVa6lYOK#m zs;EeuIb;RFA@VjBb5Sgfu?~kFbFnr!5}cAK+jxh-^24C~k(ka$L|{Nca9n{nA`G&V z62K@%qA*q_X-u&53`UbK$tg>eNQ$;$5M`PIRms;5&&q(3Ae57G2`<`Y!vrTyVyw$< z$9T!fV*uD>S{8Y`C^IIMByx*XEzHC1RKk1}Afk~fbHX5;TM+cStu#(vw*+f>U4{W} zYl#x6OI)7_DPgco=M6R~yOVa>NRp;)b{FML=4|5K4YU|kqJc?~IK!IZ#l&rjwU&pzLSKO zVg9P5$PNF^+h`s%Ke^?_N(`>wV49L0wG6CDUL`MU6|;+E1trfq zd4M_X0E!<7E;|idiE@Y|lm0p-LQRkqXR~n>`Tr}?Ns9tWIc3l&uKd_7WgmLE|)NMZTAIeTT40}|--A~%!BM3gxh@nT?fGl9X zH}W&kQ8#i46dhY+RQ!(7HAdH{7#NlFn7YR38WjVhavoFH|BNon^+Pd;z<1d=Jo28> z+uPxBd2+P4GKL^{w(%X8Xe+FT#)-N=;G1}Me3m6`=3NEdw?oq`-B+P|)Ua`qLd+L@ zAfa(ZuSSg*{Ck?F-3pxw{oVzk^v;I^)C>fjl+xc{m669NElsJuryxeLkp0ky?W+$H z<$LPBbHK8C--2@XFP+ye&RBVdIlcCpy8(G;UQuW0oz(9`D8%n8^*+0BRr9|AWQvH3 diff --git a/applications/external/flipperzero-bomberduck/assets/enemyright.png b/applications/external/flipperzero-bomberduck/assets/enemyright.png deleted file mode 100644 index 45e6a861ad9c914af25be2b6c1b6097fdc6f0d17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4536 zcmeHKeQ*@z8DB1lAqc@j2r_}Jiy-;f+mGA(T5^2kE+mIsIKnl=p;q_f-OHN0+so}H zccEZKiNFixQqR2oRlB(;cCnh8})Y=IykGX&Ci_wqS)rjt?r z%gx-~yU+VPzxR25&+mO_-_8Eg;>l^V(hvlh>@D$>!GC?6mTH2(CVxdY{JXd=P@$D^ zaWooJBsm07tu6{skdP$=NqqeLsx!|YCsS|5GuE26r?*s_<%K)0d_HgfpQT@K{rQQk zbkhfp{lEVQe(T?u?}bl4uHRlbRaGxUXH9--N~`$noc3c^rmb94h~zYGX`6RB!?AwL zM?z}DwKcCqn~$B0?|C2Dwd=<8$$K~0(k8SBG~VN8$|f$ebvb8dDA%eFfZzP~t(!Z_ za}GtVmD653;OLz%X59Q)p9wtE=SLlB{umoxE$o|^RRM=$LuIC=B$4X>T&hl;nE%+l-|;N?9iK8UFm>;DAAGm4{`&bjx5vFt zY<=kd(-USoKU?+8gfl%)_Z__y+24D-`Ipn{Q)UwfZ&CM*k8fPodhp0k!|zm^ZZvGn z`@^Ti>5UgwwpZbJZ2Z+wO6F8}f1vd3lCOW2GA9uG;yKtp*Dlp`+e(mO{mR2E&>8mo`=MctrME?E}?tJjqVgmqPd zQ^Xc8N-IdP5FiXR4o!qZ5tU82Fawu`W8F+(sNtejxv&bKA9X7+fHG#rOyWfext7Kj zrJ)5eQDVzH%laYUlMAcVv?xmu@p#-Ex0sb!kf5ASCqdE#P2n_4x+jBWgbjkPjlkMG4AG65%j0JVMoqY9UB} zLSGx92H=h+%7CiW!~{@O3nJRw;Si!Q7$2>Pg^Y4UfdC;8hOR1Hl^U^RvDfDxjL<0v z%HgOH1<4-asman1StD}OXN+=&Cj!F+Z> z#Lz@Q4ujPir?W}f9D2G=lD665FqyM~R}Nw-RHBYak!A~HgzIf#VKIxgrdf#U^yXKg38qb(Z~~GC8vZ+E)oRr z^yruCfL;C?DNvF~OLoSB^E?SPBw1kpERqGcQIbT_Bu@j%J`!D3BrVRxz|tV(5po6P zX>f%uFdCIJ(mP%W^e#ZcaFW4kXMmR^z-Z&KorCg^@m^p{vb=*QwfQwxayX?xOP*f|QRN!sA~43B$a@VxKm z4CZ?$KmBsMlS`oJ*dn9icZ{wvx<C0@_cz|4+Kr6;E#Xlh|!U&95zbJP9;$F^rDco76M zz5V!0f4N3|vZ}LQ_N0&Ui#rxRF#k)4g?NifJxzs= GJ@sFi98UKD diff --git a/applications/external/flipperzero-bomberduck/assets/explore.png b/applications/external/flipperzero-bomberduck/assets/explore.png deleted file mode 100644 index 5eb50b669b3ee364bd25a37991519cf833660ae5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4540 zcmeHKeQ;FO6@MfFVKICS5~M+%n;>Mv+xNL2?{$}u?1p54jZ0WCDOP=-n@4u{ExT{C zn+O%Cm1dzR>NKrHD9yyy0WywM2%-c=QA^3RBvOVJZ47D=Dm22Vts&`syZIcQ>10~| z%g*fXedqqp@1FBJ=iYg@-cweRshg*RASlyS>MRHU+E_O^4g98gDz1XR>vi4=wVaK? zp|B#!ei2q{Ln183WeI}ffBD(!%HU~ia_?7pbs1(f``yaD?|<)&%Wd`UXJ0CB*4I|- zWKYUZJki#1;r@=?%9ca%ybRpZFs`AeTf1S8;ZcBr2Q~kD%A7$w#ojFVfdSKnl8996U=Iu-z{rB9!`p@z{U;5J8 z6~>1%tBPKmv;Bi74|4ndT6pWV#M4E!##_3^zF!);a*w|{%X9vLmG)g5wwr%tSeVN# zvD&cedv`3_v~cm3VL zagb@XUc1_5n7nNP++r+C7|Xl+pDo%kclxexdai$X`TBhL{gtZ@r{T*w^CiQ~Eb`(# z_}2;OsXy(&&%Sq-kQU}m`D0IX`hrJq{Mz=1Z!Ya?THjuI_do03#-(4}(;e-8xjk{J zcAxcZ;?)(K%4{1tCe|-F*3#Ph&atjnPLgj)jHx91fjLtH2Y-j>zc8;Sccb&XdM1l7 z9%wvjt!_3{fB0M*_IzhULu*-N#*`(G&79?2{97)4p?5~}+C`^}5?@}vuW!P;=oBxc}6R;Gy2PTldUPq|Zl>+{W+Dh;3coex&W`!10Y~U+>$R|K=y? zg{_}G*inrj;f!1U^sEbr`(nB=wxw^8!z`A3(K-8z+OFF>PvlL#^+DPX4+NF=&5cVT z`KqBdJv%!sXX2ShZpLH2$CduQ@|Rzw-{%cq`+UtSeGoKly$o8h!o7;&m4J~I6izh8 z10m3(5M(QehgiN^RAEl^$w9mR#_={iEDLu1Lnb%o4mrdsxwI}UuBj{Y@^#fbE$9oD z>TGca5ClY(h2sH#Fv7&``Xny{#+n(`!%2u*ZP!=0J+MOwi!f!Rj2KcJmum?9QXOmy z3ldZATs}Ynp6vQ6RShvH8jHn@G191neJD=TG>Q=@K_CEuL~4U78%KhX9E~Ey;S?i$ zSPrSO5`;BQmQ$jtU9Si8@L+s_klQ^(AB+sB0Q5lPYzW1T7#awmBRwK&aSb3D2~Bh0r)fAe6;oMrg_eMx0gPB@=7mD0T#uD;QDPATMfEfZQkp9$Msh!Ax)nX%Sh3 z!Z8A&B{Pkf%&di`DcWQ)aU&=m49lP^S^vnWG%5j5S#g@93EG5kRsusP+H6Kx!O9|{ zXp#ttXU)7sCaDCTS+0ZwEGVZOV0|JQ3i^^0n&3>K$7R6agEAqYT{-qVa3cOZl%mtD@_c8)`{T==tPYZ$BZPEoYBg{fMkHMtk$Q1AZZ7= zFpjXus!G_aD1N(M3klXdho;?NI|;1HI$2c&q!>Xm7{!p77sDAGXH4dN3|L0s6+xD2 z{~KD{Jg_ZQ@=`eh)~`*PQafsmxH0vVdi2Z5tpvl#O~J5yDuoDJBMQkl0at2@uVRBf z5u6?a?K)_ezoQj!NgyN(MIsyrx`2{MP(M;45i>4HIDv75h+9UpM-)kov0<^$2Xq8l zf%Z&l1usq(DsMD8RwZgx0D>VHg%Grtz!@MOfe#3V-uVzZq&VCY*3l{2Q0-V8EUFFtR@qa45$7n7CS?QXGdS+iq4Rz~ zGZ62c{0y|)om>Kj#}^rszTq`;WKK zgX^A|eG=Ryr-Vu?BM@ZF(Y_Pn%>^~Uc$eyO7vJ^wjQMk>GKWmDCSbZ(Ev`@N`hPz0O0VZ)6#7jA;DcPn LWzLqO$F}_kO43W9 diff --git a/applications/external/flipperzero-bomberduck/assets/playerleft.png b/applications/external/flipperzero-bomberduck/assets/playerleft.png deleted file mode 100644 index 86997a985eab4de991e315858b17ed7e0a981f74..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4311 zcmeHKeQ*@z8QF~0mx0CKOUBB$ywxh6RdktHd zO&sK&?)m!b^WCRH-G!^$_a}-oP1cspFMM#|(!zW03)g0!TE3v}Zr#$0vELZGUb-+n z`tcmk4f{`Lq#nI*_apMPhBtb)weEi5)l-XhEletV<{4f2+r2_Tt8+zP&D`Z{#O4PM zw@BN!PF|&d&b$BEbZh?J9iP53`MlWoqIZt|$djhBXS3*cf4?u&_s3n;U$vdPIJM}q zyDDYY=lAB?FP7$K9CysB>pXqwYT(LU>tF7-mmbMD`&m5adGiz;K{9?OIh|g&(>Yi; zEO+PnEsn}J7v>#X+q1Ny#LzTt#XH_#-}_|hvzoo$C$e_cG@S1GxI257+u7aQThwu9 z-p$&5Y=`ah%NI%}J~kI^H&&&L)xG^&7d@4mz4@EID<6JzWj6Yk`YQlbF=yi*SStB z$7fQ;y*ppCHMW&Bez>iZ_(e}kOGj07+N66Qntq3?=(m3TJm0joAI?9vD0Tg#Ikz(2 z#<$#`e`Z21^UoC1X2yza^(+#y2cJd9d@@;Nf+;8?75k zk9>lk*>GujcO!;HGOq>nGtXe2vwCCv(OdJK7O>>Yo}4d}7ruS^_2S9b&g<&-hUAm$ zcP>Dr%Oy#DUY@Stw&M@}I}xw{vE2W>bp0#+9AD(~FIFD71v@k$!A`98EMs{&XygRh z4~&Um7`7;a*vk@Oj&B4C>Id~w$YHp8xYK}2g2S-N>>)g1Cuoo=ng}@KRhhvd|R*t~qAOM088ilixqo!Qq_IQUPR0-;( zU|5TSVvj;9k~l2ZsN7VKR?f&oVE7R4DD+_M8W^HH9@ZuEF*QB6%VALGX9by;1Xdeb z7{bDdem`cj5`K)fnynZ|GXzF56fZCWPf~ty1eH4!Rk#ojR4Pbrlpv2;r2P~iIgB$4 zCXD773&vP^9<$I^nimA#LYajT6w4zL>`E>$GAorzfK)U=`YE0Q7)euBjJD7MW+VIn z1F&N#Mzs3J^%e`)gfie=q7H@!4L>S;OER#a9 z#IVCB1;I*%Q`ICbHp^;%r^peXEC(D0H6>Jy9CCZ$b`m&+b8!lQqy%MR37VxC9~_WombR7>&@uuq3zC@p zUubpnp!W2VS4vShe^N80chpL-F8!2#3`p8mLQ!o~upFN*A<8v@^kPA-w2N=xLiGTi z9s}(BtQ=sVhBxBNdT&{7s#-zZQz~kLDF4ve87!!EByZ&!-WevYh zfe^d}#o^0RvHVgme9@W|u3Q~O5MzP*%}BJBH9_O;irZ6h`=^<+bF#_0?9Os%x=X33 zRh)8Ay@Mm#)es11Tv8fP^=i0k!w+Uckkjod_vQA)HkaRlpc8dnm-^T4dAzEv*jiU! zw5bq5w*DNv|C4Ed>zY*bKyJ>);=gX$e&|PQ8vlgvdH7{q63*53T>oG~`_lQyYkd_@ QKsv--QRQl1^w4Af0rThwxc~qF diff --git a/applications/external/flipperzero-bomberduck/assets/playerright.png b/applications/external/flipperzero-bomberduck/assets/playerright.png deleted file mode 100644 index 1a6283d9c26d9f9582fecd0cef9765ab2b288576..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4307 zcmeHKeQ*@z8Q*+ia)5y4BWi$k(Gn8(_IB?hw>x(PxI2RA+3tBttlv!gj$hEe0MKjw9Yu0 z(f@KYclYk|{+{3aJiq68-+6bdyQpBQ<{k}#AXA-%_G0+2j5X=W@Hg3AdI|o0QtK&| zi`gg|j)=Sv0H|CO1}KOLJc7i!x3Bu^_LJz8fnUUP+kS;;H^24s%j?g4kYWBKXnHPm z|3K#3^Hz43wbp$>qpKTTP5jvx5J?i!bEdYxN3DG$1-Inikb~*j8@qNKPi<`|Xx~w1 z_^^Lqb#`)S%f7n3j+B>o4`ih^JvLjDc6zV=VC8+9SD(V}y>VCkXjfmX`}n!<{ikf7 zqwm>cnavr`HO{S^b#z0`q0Q&&XI$O5k36@{+~2={$;PS`)(%Ih0xB8o}Px{Mi(W&9gzMTk?v{A6x+)kTqIB%Hk zuKFF;!V^of-dWeV!jXejPhWM){nCS5;=OAQxS!JOSyTRQ+uzSm+vBvI@9D{InVmII z+RU_=FJHQtlfGd-dPrLo*B18-HZI$iIqjL7*RH(((UrOApC4NJS~9t$GndbqF-L!4 z7WsJ`*>r9j*?y{>;vZO)arRo(wE630DtOC>IKABcPq9!!FkdeMlNo0qQ}-*f2qAbwn^ojP;OYM>QlF3g~M+ zBoExrL@|)Wst5-h)gUA<7)#;fM*PE7k${?xk0U?;1R+#{tCF`ZS>SZJM?4e>{6a9S zdO@*oL&^d_D%NeWDKlz1V*`QiBfPhvhjUlK5an_)c9E-6!gJcKn6f_O6FI@hs6(1I z^9BI)IISmH+`xHtI7@LPo~JXIcrP%SXp$O3R9tE+uiDP-( zU^4JH%jHqH$)`7Y0dJsr-ZzHg;fMgck`0WFN}=*8RC<~RM$U*EeO?o8AUO`F4J^P} zU^a6wPS(sNqVaLeaxoHQVL62$>jy+QP9W@04wv*!HTUELRGQR z36Bs2B{Hk1Nt(=59!={_M#@Z+M&l@G4Twmv6BSNUr_~$O8Ko=?Oa=6hmJkFeldkrQUwt~-#d(CIyK~ea!bf)CR?eIk_BV4#vLJ;i&<(m|1%CCmTJ7lNJamUB0b7xLZJMy99QE0kb zc9hCCF{s?Z5%p>ad}vgV%TeWO_(;Q@b0BDm)4tS`*;)USr5Z$+eq#3E;K!e!PrGF6 zqTlSkg?7Cym&HFjs47bfvH_+H3` NI2}dyL(A50_zI?I5pDng diff --git a/applications/external/flipperzero-bomberduck/assets/unbreakbox.png b/applications/external/flipperzero-bomberduck/assets/unbreakbox.png deleted file mode 100644 index 5e65912d53405d04bcd05916947a8de8fc5239c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4763 zcmeHKeQ*LAa#*}l_EHe7ZnC@CWRtz7rA^W{(9lqV#IcC`@ou`N$%f6g zNqff~tuoa16pDf(PzIfh5AYZ zc0^x)uxe6uafh&a*{bL7ZU6Qbb<5kAiybrj+}Rh~@;;c=`r;Jt(bwjk*l=HGc<$nP zWy0FK-<anGNJXNLodp5GUkF@7z+}L`+vf=pS>nE*`w^*Ni z_U6;Syz8-FAHI9@QDwdPLf5ja_k!=wuX@JToHp$*%~PERpXE}4zqxxy8>jbNS)SRy z@>E6J{98W$MV9Sf3mbJ^7w_-ie<6J6t$&>py|<-bI+1eX zxv+d_#pVKJ`q{kt)a>k(r(Eyd^KCp9_`TfUE`4`7wJWG}J^GHKB1mR|1eNIblry{> zGO&Wo0Yf|#ff7ZK{K9yI<%2*)IS`P-cKwCd_vukdu#O?Y7E(6D!*{DYoE^5%O_j|mkLskGv87Kpex#ChC zsV~r>`HCPimClj@2zX`JSF37-F&bmBm?35|$V$LS*lacfbbtlOhcV7ZjD!I4>q*15=7-%g$J%W5LRh^et zA~XsDQYey$f@F{KR3&kktWmjXGl_CWCIZ8Ua7TF$)}C;NULFtQl=)gMJ-5@Y*VbnQ znU@46F{C(-=2H$0(b{ zg0X^?#Q-piq{y=tUNj}31fD68l@JTJQwp&GV2p$Vi3yExrpW8I>q!GXZ1L8xst5z@ z`f@2;8y}wVNg=REWi>Vli8CrrtXS_GCSuacKFQlgbmG|?0c%O^{SvUNa6qBAq6xDQqDYW9M*@N#jUJUnHO4BS zC;)kcTtRszxI%ADY}9R|y<^ot+XYA%hEo`6^N|Dt$s>sY!i?9xL>#AW6ie7Jkpdh> z;gl8Qa1)D}N!Cn@g21zMQU^l>{x2m$P2dD$HZwT+|0|KU5QK@xO_-VI2#f+Ef!WM9 z8g?N`5VVyfI4;=&KUawZvbT;dG2f`&`3H5IZ~T$6lMW*u6>#^HHuwmFPc-AuBW*wy zFyCwW8R)2Mxde)iFES>6$LSiUYfKD`$$4B|<8+OQfiXFctLtZ@OE-Kd24VOv8-qvQ z89nFS@VI<^r0lLJf*5kO?}T_qVI4H4t8R}g{Y+ZU^)siu^qJ!YXqv9N{Hj9^X(uTp z@q!NoG$yIlsP=-tq%q?r=v3r(7WuM{bgbR4L-2{kD|YwygIHFp{{wKclAL^e-K+_( zl_?z?d^4U32Ch85aAyv(?N?isr~6iiN)H`tCH8?-F>_AFzD>vWLNLVbTHxGOd~ef# E080^$ApigX diff --git a/applications/external/flipperzero-bomberduck/bomb.png b/applications/external/flipperzero-bomberduck/bomb.png deleted file mode 100644 index 44b9bfdeae76553be52ce1c451133a4baee7bf68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4534 zcmeHKYj6|S6<%z|5CMh~3MO%!MQ#F^?0fat9m^P72CE<}8sWT0;SYAG!)aM&4dO*36QBnfew$RC4{;rDG78SDFiYN#Jwv&HO)-N z)AG-nk#zUm?|k>1@0@#Q??%s(g%eE=nh*q;=qhoR!+&FJ8kY`#o9I&q;or4dZ-rhi z#L$SQ%1RKRdQAkNAg;&=5+SdGG+Oq zj?BQ7b)PkKHl)qI9nkMC+j;2q{r;QBzP_>NX8O53u?cgg)_sN`W1drriaf5OqJhj| zzFXI|I7*HbWF1>`=DXj^&8VJS*5!HmAKhbnDqi(8nA(>G&K>^f^rRiGqSl_C*>6tE zx>K=-f75>TN_XzK^^c%?@gYAB9E1UUb{n6%CTb}&ErB!-G_lrW#=509{4{SPSec^+jv`uJ>H`;&p z%7Y(|U%utrCs%Ggr?pwGcdf~Muk!e!RUO=hv}yY`Om+VL6<;c-*l_w6Ek3jRtF@DQ zpSe`pvgon*(=)lhm(`oPK7Y3N@b&PCcRp%(X=-EY!^9hRsg$%>{ely39BK}|wKnC; z?e%ki`Elpj`WH)2S7Ml!b~Bth{e8@RF%^$J^YyGJ>F+H3=b5Rd`?~M`=02|_>@(0C{qj`DGbIl;@rv0*06Xab^FI?NSr4_c&Z0A+C&C$ZwV zQcaulO=zAb$$YtUK|ck2a+m|U9^nZh7K`CA23IvdL2(>MkTgNl7(`&vny@a!v2ZlU zpy=apf~cq|5nWNksKF`t)GFO!Hp6*zAihwh^?6AZ z6^Ty_S)b1)3KkaQD3-=po6U-GtObsIc1e^ig3ZSY!>C;0s4j#>U{FDFT!B1Ri4p7; zn+;<~ABnLfDPf$QBQdL;v&f`iu}Dc2rJ4e}QV0%@%Ak@Um7U^z9L-rUpPhzOoYjg6 zl3l<6u*kG53RY2O646K^zd+SO0xYKz68wOOg#C#LLvVh+$K^27I5}kT1O;7&4i58T zC0rFBn(!(iuv8ZeH7To|wp$s7qDk7qaMTcJ8PKAz6Aey^#2Gd*W0Zx5$v|NRqfa40 z!VYuci!>nUs^(SIpu=p0gc_cM({8w(BtaLPf({@lNi#gj@(k&vY&=QvEH{^gmSK2R zQskQdhBh`2n%7tI5+w@PuSuBtcGOa^w(qI$F{mWA5{f1^1uuwwDMW>8ASL32Tzykw zKnVK*JU#l`b-=ECLn~0SM9Vgo!F)av_K?iL`Y|$tSt(hDvifL1*^=3#s;tKZ4b1mL z9idjRJri1?vlE4ylZ=i9fKde~7)G)f&3S2xhvLyxzhK0@Pthte5^LjF4A^V{W342I z`6Q9W7>*_d!AAKc%90%P|B~WhPgv;W6!Qq2J4txB{deU1UW1j>4ts8dLNDYP|hEQJ*m#g7RI?`QFSkP0jO7NWr)i=2q{T>eBHuH(vc!dPhO) z7cJG}kr`>8-0$DFdUNQ)ipMUv6x9pvoL~QvcJld^0sOV5X%F#bOaBF#5LfXM=ib7n G*8dyMBTMf9 diff --git a/applications/external/flipperzero-bomberduck/bomberduck.c b/applications/external/flipperzero-bomberduck/bomberduck.c deleted file mode 100644 index 7b8b5f14a..000000000 --- a/applications/external/flipperzero-bomberduck/bomberduck.c +++ /dev/null @@ -1,645 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include -#include "bomberduck_icons.h" -#include - -int max(int a, int b) { - return (a > b) ? a : b; -} - -int min(int a, int b) { - return (a < b) ? a : b; -} - -#define WorldSizeX 12 -#define WorldSizeY 6 -#define BombRange 1 - - -typedef struct { - FuriMutex* mutex; -} BomberState; - -typedef struct { - int row; - int col; -} Cell; - -typedef struct { - Cell cells[WorldSizeY * WorldSizeX]; - int front; - int rear; -} Queue; - -void enqueue(Queue* q, Cell c) { - q->cells[q->rear] = c; - q->rear++; -} - -Cell dequeue(Queue* q) { - Cell c = q->cells[q->front]; - q->front++; - - return c; -} - -bool is_empty(Queue* q) { - return q->front == q->rear; -} - -typedef struct { - int x; - int y; - int planted; -} Bomb; - -typedef struct { - int x; - int y; - bool side; -} Player; - -typedef struct { - int x; - int y; - int last; - bool side; - int level; -} Enemy; - -typedef struct { - int matrix[WorldSizeY][WorldSizeX]; - Player* player; - bool running; - int level; - - Enemy enemies[10]; - int enemies_count; - - Bomb bombs[100]; - int bombs_count; - - int endx; - int endy; -} World; - -Player player = {0, 0, 1}; -World world = {{{0}}, &player, 1, 0, {}, 0, {}, 0, 0, 0}; -bool vibration = false; - -void init() { - player.x = 1; - player.y = 1; - - world.endx = 4 + rand() % 8; - world.endy = rand() % 6; - for(int i = 0; i < WorldSizeY; i++) { - for(int j = 0; j < WorldSizeX; j++) { - world.matrix[i][j] = rand() % 3; - } - } - world.running = 1; - world.bombs_count =0; - vibration = false; - for(int j = max(0, player.y - BombRange); j < min(WorldSizeY, player.y + BombRange + 1); j++) { - world.matrix[j][player.x] = 0; - } - - for(int j = max(0, player.x - BombRange); j < min(WorldSizeX, player.x + BombRange + 1); j++) { - world.matrix[player.y][j] = 0; - } - - world.enemies_count = 0; - for(int j = 0; j < rand() % 4 + world.level / 5; j++) { - Enemy enemy; - enemy.x = 4 + rand() % 7; - enemy.y = rand() % 6; - enemy.last = 0; - enemy.side = 1; - enemy.level = 0; - - world.enemies[j] = enemy; - world.enemies_count++; - - for(int m = max(0, world.enemies[j].y - BombRange); - m < min(WorldSizeY, world.enemies[j].y + BombRange + 1); - m++) { - world.matrix[m][world.enemies[j].x] = 0; - } - - for(int m = max(0, world.enemies[j].x - BombRange); - m < min(WorldSizeX, world.enemies[j].x + BombRange + 1); - m++) { - world.matrix[world.enemies[j].y][m] = 0; - } - } - world.matrix[world.endy][world.endx] = 1; -} - -const NotificationSequence end = { - &message_vibro_on, - - &message_note_ds4, - &message_delay_10, - &message_sound_off, - &message_delay_10, - - &message_note_ds4, - &message_delay_10, - &message_sound_off, - &message_delay_10, - - &message_note_ds4, - &message_delay_10, - &message_sound_off, - &message_delay_10, - - &message_vibro_off, - NULL, -}; - -static const NotificationSequence bomb2 = { - &message_vibro_on, - &message_delay_25, - &message_vibro_off, - NULL, -}; - -static const NotificationSequence bomb_explore = { - &message_vibro_on, - &message_delay_50, - &message_vibro_off, - NULL, -}; - -static const NotificationSequence vibr1 = { - &message_vibro_on, - &message_delay_10, - &message_vibro_off, - &message_delay_10, - &message_vibro_on, - &message_delay_10, - &message_vibro_off, - &message_delay_10, - - NULL, -}; - - -void intToStr(int num, char* str) { - int i = 0, sign = 0; - - if(num < 0) { - num = -num; - sign = 1; - } - - do { - str[i++] = num % 10 + '0'; - num /= 10; - } while(num > 0); - - if(sign) { - str[i++] = '-'; - } - - str[i] = '\0'; - - // Reverse the string - int j, len = i; - char temp; - for(j = 0; j < len / 2; j++) { - temp = str[j]; - str[j] = str[len - j - 1]; - str[len - j - 1] = temp; - } -} - -bool BFS() { - // Initialize visited array and queue - int visited[WorldSizeY][WorldSizeX] = {0}; - Queue q = {.front = 0, .rear = 0}; - // Mark the starting cell as visited and enqueue it - visited[world.player->y][world.player->x] = 1; - Cell startCell = {.row = world.player->y, .col = world.player->x}; - enqueue(&q, startCell); - // Traverse the field - while(!is_empty(&q)) { - // Dequeue a cell from the queue - Cell currentCell = dequeue(&q); - // Check if the current cell is the destination cell - if(currentCell.row == world.endy && currentCell.col == world.endx) { - return true; - } - // Check the neighboring cells - for(int rowOffset = -1; rowOffset <= 1; rowOffset++) { - for(int colOffset = -1; colOffset <= 1; colOffset++) { - // Skip diagonals and the current cell - if(rowOffset == 0 && colOffset == 0) { - continue; - } - if(rowOffset != 0 && colOffset != 0) { - continue; - } - // Calculate the row and column of the neighboring cell - int neighborRow = currentCell.row + rowOffset; - int neighborCol = currentCell.col + colOffset; - // Skip out-of-bounds cells and already visited cells - if(neighborRow < 0 || neighborRow >= WorldSizeY || neighborCol < 0 || - neighborCol >= WorldSizeX) { - continue; - } - if(visited[neighborRow][neighborCol]) { - continue; - } - // Mark the neighboring cell as visited and enqueue it - if(world.matrix[neighborRow][neighborCol] != 2) { - visited[neighborRow][neighborCol] = 1; - Cell neighborCell = {.row = neighborRow, .col = neighborCol}; - enqueue(&q, neighborCell); - } - } - } - } - return false; -} - -static void draw_callback(Canvas* canvas, void* ctx) { - furi_assert(ctx); - const BomberState* bomber_state = ctx; - - furi_mutex_acquire(bomber_state->mutex, FuriWaitForever); - if(!BFS()) { - init(); - } - canvas_clear(canvas); - - canvas_draw_icon(canvas, world.endx * 10 + 4, world.endy * 10 + 2, &I_end); - - if(world.running) { - for(size_t i = 0; i < WorldSizeY; i++) { - for(size_t j = 0; j < WorldSizeX; j++) { - switch(world.matrix[i][j]) { - case 0: - break; - case 1: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_box); - break; - case 2: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_unbreakbox); - break; - case 3: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb0); - break; - case 4: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb1); - break; - case 5: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb2); - break; - case 6: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_explore); - world.matrix[i][j] = 0; - break; - } - } - } - - if(world.player->side) { - canvas_draw_icon( - canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerright); - } else { - canvas_draw_icon( - canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerleft); - } - - for(int i = 0; i < world.enemies_count; i++) { - if(world.enemies[i].level > 0) { - canvas_draw_icon( - canvas, world.enemies[i].x * 10 + 4, world.enemies[i].y * 10 + 2, &I_enemy1); - } else { - if(world.enemies[i].side) { - canvas_draw_icon( - canvas, - world.enemies[i].x * 10 + 4, - world.enemies[i].y * 10 + 2, - &I_enemyright); - } else { - canvas_draw_icon( - canvas, - world.enemies[i].x * 10 + 4, - world.enemies[i].y * 10 + 2, - &I_enemyleft); - } - } - } - } else { - canvas_set_font(canvas, FontPrimary); - if(world.player->x == world.endx && world.player->y == world.endy) { - if(world.level == 20) { - canvas_draw_str(canvas, 30, 35, "You win!"); - }else{ - canvas_draw_str(canvas, 30, 35, "Next level!"); - char str[20]; - intToStr(world.level, str); - canvas_draw_str(canvas, 90, 35, str); - } - - } else { - canvas_draw_str(canvas, 30, 35, "You died :("); - } - } - - furi_mutex_release(bomber_state->mutex); -} - -static void input_callback(InputEvent* input_event, void* ctx) { - // Проверяем, что контекст не нулевой - furi_assert(ctx); - FuriMessageQueue* event_queue = ctx; - - furi_message_queue_put(event_queue, input_event, FuriWaitForever); -} - -int32_t bomberduck_app(void* p) { - UNUSED(p); - - // Текущее событие типа InputEvent - InputEvent event; - // Очередь событий на 8 элементов размера InputEvent - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - - BomberState* bomber_state = malloc(sizeof(BomberState)); - - bomber_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); // Alloc Mutex - if(!bomber_state->mutex) { - FURI_LOG_E("BomberDuck", "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - free(bomber_state); - return 255; - } - - DOLPHIN_DEED(DolphinDeedPluginGameStart); - // Создаем новый view port - ViewPort* view_port = view_port_alloc(); - // Создаем callback отрисовки, без контекста - view_port_draw_callback_set(view_port, draw_callback, bomber_state); - // Создаем callback нажатий на клавиши, в качестве контекста передаем - // нашу очередь сообщений, чтоб запихивать в неё эти события - view_port_input_callback_set(view_port, input_callback, event_queue); - - // Создаем GUI приложения - Gui* gui = furi_record_open(RECORD_GUI); - // Подключаем view port к GUI в полноэкранном режиме - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - notification_message_block(notification, &sequence_display_backlight_enforce_on); - - init(); - - // Бесконечный цикл обработки очереди событий - while(1) { - if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { - furi_mutex_acquire(bomber_state->mutex, FuriWaitForever); - // Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения - - if(event.type == InputTypePress) { - if(event.key == InputKeyOk) { - if(world.running) { - if(world.matrix[world.player->y][world.player->x] == 0 && - world.bombs_count < 2) { - notification_message(notification, &bomb2); - world.matrix[world.player->y][world.player->x] = 3; - Bomb bomb = {world.player->x, world.player->y, furi_get_tick()}; - world.bombs[world.bombs_count] = bomb; - world.bombs_count++; - } - } else { - init(); - } - } - if(world.running) { - if(event.key == InputKeyUp) { - if(world.player->y > 0 && - world.matrix[world.player->y - 1][world.player->x] == 0) - world.player->y--; - } - if(event.key == InputKeyDown) { - if(world.player->y < WorldSizeY - 1 && - world.matrix[world.player->y + 1][world.player->x] == 0) - world.player->y++; - } - if(event.key == InputKeyLeft) { - world.player->side = 0; - if(world.player->x > 0 && - world.matrix[world.player->y][world.player->x - 1] == 0) - world.player->x--; - } - if(event.key == InputKeyRight) { - world.player->side = 1; - if(world.player->x < WorldSizeX - 1 && - world.matrix[world.player->y][world.player->x + 1] == 0) - world.player->x++; - } - } - } else if(event.type == InputTypeLong) { - if(event.key == InputKeyBack) { - break; - } - } - } - if(world.running) { - if(world.player->x == world.endx && world.player->y == world.endy) { - notification_message(notification, &end); - world.running = 0; - world.level += 1; - if(world.level%5==0){ - DOLPHIN_DEED(DolphinDeedPluginGameWin); - } - } - for(int i = 0; i < world.bombs_count; i++) { - if(furi_get_tick() - world.bombs[i].planted > - (unsigned long)max((3000 - world.level * 150), 1000)) { - vibration = false; - world.matrix[world.bombs[i].y][world.bombs[i].x] = 6; - notification_message(notification, &bomb_explore); - - for(int j = max(0, world.bombs[i].y - BombRange); - j < min(WorldSizeY, world.bombs[i].y + BombRange + 1); - j++) { - if(world.matrix[j][world.bombs[i].x] != 2) { - world.matrix[j][world.bombs[i].x] = 6; - if(j == world.player->y && world.bombs[i].x == world.player->x) { - notification_message(notification, &end); - world.running = 0; - } - for(int e = 0; e < world.enemies_count; e++) { - if(j == world.enemies[e].y && - world.bombs[i].x == world.enemies[e].x) { - if(world.enemies[e].level > 0) { - world.enemies[e].level--; - } else { - for(int l = e; l < world.enemies_count - 1; l++) { - world.enemies[l] = world.enemies[l + 1]; - } - world.enemies_count--; - } - } - } - } - } - - for(int j = max(0, world.bombs[i].x - BombRange); - j < min(WorldSizeX, world.bombs[i].x + BombRange + 1); - j++) { - if(world.matrix[world.bombs[i].y][j] != 2) { - world.matrix[world.bombs[i].y][j] = 6; - if(world.bombs[i].y == world.player->y && j == world.player->x) { - notification_message(notification, &end); - world.running = 0; - } - for(int e = 0; e < world.enemies_count; e++) { - if(world.bombs[i].y == world.enemies[e].y && - j == world.enemies[e].x) { - if(world.enemies[e].level > 0) { - world.enemies[e].level--; - } else { - for(int l = e; l < world.enemies_count - 1; l++) { - world.enemies[l] = world.enemies[l + 1]; - } - world.enemies_count--; - } - } - } - } - } - - for(int j = i; j < world.bombs_count - 1; j++) { - world.bombs[j] = world.bombs[j + 1]; - } - world.bombs_count--; - } else if(furi_get_tick() - world.bombs[i].planted > (unsigned long)max((3000 - world.level * 150)*2/3, 666)&&world.matrix[world.bombs[i].y][world.bombs[i].x]!=5) { - world.matrix[world.bombs[i].y][world.bombs[i].x] = 5; - vibration=true; - - } else if(furi_get_tick() - world.bombs[i].planted > (unsigned long)max((3000 - world.level * 150)/3, 333)&& world.matrix[world.bombs[i].y][world.bombs[i].x]!=4) { - world.matrix[world.bombs[i].y][world.bombs[i].x] = 4; - - } - } - for(int e = 0; e < world.enemies_count; e++) { - if(world.player->y == world.enemies[e].y && - world.player->x == world.enemies[e].x) { - notification_message(notification, &end); - world.running = 0; - } - } - - for(int e = 0; e < world.enemies_count; e++) { - if(world.enemies[e].level > 0) { - if(furi_get_tick() - world.enemies[e].last > - (unsigned long)max((2000 - world.level * 100), 1000)) { - world.enemies[e].last = furi_get_tick(); - int move = rand() % 4; - switch(move) { - case 0: - if(world.enemies[e].y > 0 && - world.matrix[world.enemies[e].y - 1][world.enemies[e].x] != 2) - world.enemies[e].y--; - break; - case 1: - if(world.enemies[e].y < WorldSizeY - 1 && - world.matrix[world.enemies[e].y + 1][world.enemies[e].x] != 2) - world.enemies[e].y++; - break; - case 2: - world.enemies[e].side = 0; - if(world.enemies[e].x > 0 && - world.matrix[world.enemies[e].y][world.enemies[e].x - 1] != 2) - world.enemies[e].x--; - break; - case 3: - world.enemies[e].side = 1; - if(world.enemies[e].x < WorldSizeX - 1 && - world.matrix[world.enemies[e].y][world.enemies[e].x + 1] != 2) - world.enemies[e].x++; - default: - break; - } - } - } else { - if(furi_get_tick() - world.enemies[e].last > - (unsigned long)max((1000 - world.level * 50), 500)) { - world.enemies[e].last = furi_get_tick(); - int move = rand() % 4; - switch(move) { - case 0: - if(world.enemies[e].y > 0 && - world.matrix[world.enemies[e].y - 1][world.enemies[e].x] == 0) - world.enemies[e].y--; - break; - case 1: - if(world.enemies[e].y < WorldSizeY - 1 && - world.matrix[world.enemies[e].y + 1][world.enemies[e].x] == 0) - world.enemies[e].y++; - break; - case 2: - world.enemies[e].side = 0; - if(world.enemies[e].x > 0 && - world.matrix[world.enemies[e].y][world.enemies[e].x - 1] == 0) - world.enemies[e].x--; - break; - case 3: - world.enemies[e].side = 1; - if(world.enemies[e].x < WorldSizeX - 1 && - world.matrix[world.enemies[e].y][world.enemies[e].x + 1] == 0) - world.enemies[e].x++; - default: - break; - } - } - } - } - for(int e = 0; e < world.enemies_count; e++) { - for(int h = e + 1; h < world.enemies_count; h++) { - if(world.enemies[e].y == world.enemies[h].y && - world.enemies[e].x == world.enemies[h].x) { - world.enemies[h].level++; - for(int l = e; l < world.enemies_count - 1; l++) { - world.enemies[l] = world.enemies[l + 1]; - } - world.enemies_count--; - } - } - } - if(vibration){ - notification_message(notification, &vibr1); - } - } - - view_port_update(view_port); - furi_mutex_release(bomber_state->mutex); - } - - // Return to normal backlight settings - notification_message_block(notification, &sequence_display_backlight_enforce_auto); - furi_record_close(RECORD_NOTIFICATION); - // Специальная очистка памяти, занимаемой очередью - furi_message_queue_free(event_queue); - - // Чистим созданные объекты, связанные с интерфейсом - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - - furi_mutex_free(bomber_state->mutex); - furi_record_close(RECORD_GUI); - free(bomber_state); - - return 0; -} From 099f907972beaf3736a7348be665b64d4ffa14a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D0=BD=D1=8C=20=3A=29?= <88856726+leo-need-more-coffee@users.noreply.github.com> Date: Mon, 1 May 2023 20:39:37 +0300 Subject: [PATCH 245/282] Add files via upload --- applications/external/bomberduck/LICENSE | 22 + applications/external/bomberduck/README.md | 2 + .../external/bomberduck/application.fam | 15 + .../external/bomberduck/assets/bomb0.png | Bin 0 -> 4536 bytes .../external/bomberduck/assets/bomb1.png | Bin 0 -> 4529 bytes .../external/bomberduck/assets/bomb2.png | Bin 0 -> 4531 bytes .../external/bomberduck/assets/box.png | Bin 0 -> 4329 bytes .../external/bomberduck/assets/end.png | Bin 0 -> 4739 bytes .../external/bomberduck/assets/enemy1.png | Bin 0 -> 4757 bytes .../external/bomberduck/assets/enemyleft.png | Bin 0 -> 4761 bytes .../external/bomberduck/assets/enemyright.png | Bin 0 -> 4536 bytes .../external/bomberduck/assets/explore.png | Bin 0 -> 4540 bytes .../external/bomberduck/assets/playerleft.png | Bin 0 -> 4311 bytes .../bomberduck/assets/playerright.png | Bin 0 -> 4307 bytes .../external/bomberduck/assets/unbreakbox.png | Bin 0 -> 4763 bytes applications/external/bomberduck/bomb.png | Bin 0 -> 4534 bytes applications/external/bomberduck/bomberduck.c | 645 ++++++++++++++++++ 17 files changed, 684 insertions(+) create mode 100644 applications/external/bomberduck/LICENSE create mode 100644 applications/external/bomberduck/README.md create mode 100644 applications/external/bomberduck/application.fam create mode 100644 applications/external/bomberduck/assets/bomb0.png create mode 100644 applications/external/bomberduck/assets/bomb1.png create mode 100644 applications/external/bomberduck/assets/bomb2.png create mode 100644 applications/external/bomberduck/assets/box.png create mode 100644 applications/external/bomberduck/assets/end.png create mode 100644 applications/external/bomberduck/assets/enemy1.png create mode 100644 applications/external/bomberduck/assets/enemyleft.png create mode 100644 applications/external/bomberduck/assets/enemyright.png create mode 100644 applications/external/bomberduck/assets/explore.png create mode 100644 applications/external/bomberduck/assets/playerleft.png create mode 100644 applications/external/bomberduck/assets/playerright.png create mode 100644 applications/external/bomberduck/assets/unbreakbox.png create mode 100644 applications/external/bomberduck/bomb.png create mode 100644 applications/external/bomberduck/bomberduck.c diff --git a/applications/external/bomberduck/LICENSE b/applications/external/bomberduck/LICENSE new file mode 100644 index 000000000..bce361a99 --- /dev/null +++ b/applications/external/bomberduck/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) [year] [fullname] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/applications/external/bomberduck/README.md b/applications/external/bomberduck/README.md new file mode 100644 index 000000000..2d133145a --- /dev/null +++ b/applications/external/bomberduck/README.md @@ -0,0 +1,2 @@ +# flipperzero-bomberduck +Bomberman clone on flipper zero! diff --git a/applications/external/bomberduck/application.fam b/applications/external/bomberduck/application.fam new file mode 100644 index 000000000..afcd5a6ee --- /dev/null +++ b/applications/external/bomberduck/application.fam @@ -0,0 +1,15 @@ +App( + appid="bomberduck", + name="Bomberduck", + apptype=FlipperAppType.EXTERNAL, + entry_point="bomberduck_app", + cdefines=["BOMBERDUCK"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + order=90, + fap_icon="bomb.png", + fap_category="Games", + fap_icon_assets="assets", +) diff --git a/applications/external/bomberduck/assets/bomb0.png b/applications/external/bomberduck/assets/bomb0.png new file mode 100644 index 0000000000000000000000000000000000000000..3fdc3a3c12c7ede770e1c99c2003c777b03f4d75 GIT binary patch literal 4536 zcmeHKYj6|S6<%y!HW&j1Oo@SwNCLLA+NY$|R^nmFU;#3+tr`c~z&`HETT5CYtu5IJ znE)k#DRI*@1C1$^5T@xs878Hv(-e#gv`h*^utTUBhjB~-37JAnNeB}jy}R;rl9`UD z<)1Yp>F&AT`R+O2Irq-qExw{f8R^r~5d_Kb6uL{`zdlYModSPTeC5~R-}PF5xmLo* z(MVL4l^{U1ng~EaLXi<9adBI5+qM(v*n7XN! z8V(A9salG3vEWq)igso#5Xb;gQUCQc;F zHl-hajl?w1R1tbak+dRm#Zgp znD5TDFF6a3&(A)&s&#QeuBm!_@hRW0pWIycY1w|?Pt*65Rh)1BxFvIs$JNr=Ij3<- z_TBOWTqFDWwT|4;>t~_|%|&(QlFkRe$lp3O^Tltvu3x%*eLDL7lBIu0A?LT|$+;7< z=(fq^*LBDX?`d9zI*S=Gf$2EV#^6q-Z8nes`Gbe z>Y8hIv#0CcT>Na2<0ma6w#7=2vkEQ_+Am@+1G;4qo@>a?!M*0|4W z?`Bcv{X74}R_@EKy!3Jt@ypij+Z&5wXWSq5 zcl@O#k6su#)qbV;g^_Jt8y+;@2><@>iN=OWb*a(T&yMDdx%FYnkN1bw zGi!IwMU-o~HL2OzDYHhr`}DsP@xU|cgI&s3w^L{Mqo4n)^w50-8Mjh_tyu0|%86>o z%uA{O%!yD0_9%il<|QJ$SP3*#00AZJG~IZo$%HDB)3n^;CA<+As89-PqoB05$S>Ab zigw8~Z*ICH!9ju$(0DWv3Wj4`!f7&iIXKqMxCu2NTBXxe?)9NAH40G1%$NzRAfZ%K zrn%{;BPz*UiF;u;1$=UvDl{#^;dneAH^*tS8V%s2-EPMT3a2OxBCuFZSmP5|I5tbC zNOHJAOpGcKO;N+B&dCdEmF6^=;5^zBUnt`B_R@!A-6}vm@B|;hNi%_mLU@0Vm{w2? zNxB32PLG%$?r6LO#MG*&2nwn}Sewcq76!AE423Z^n-#M&7B~`ENt7+TOF z%VPjoWJ(r!t0>b3l_YWt)o6%^wI4Xbg6u&PftxIys}4g@egJVR^kzA%Wom zbKzW3z-wyMuc|?(Ne>CtJ$t9Ua63u7#=Ch9KvIIDIfCJ6!cUMKVdE%!9sw==@T#Q9 zHUAB*ZywZ{51CBi0%mSl_e3i!XI*xM5{F)&33t{;m%MRnlcDti*p?;M5FByI3~hQ~eLd){|z z2IGC0pYC>hm`kAO&?1A8H;+Tm(fQ3^K4=PV`Vk4%l%WMT8i zU6+p|2Sy^F9<5lqb<_El58BHnhwrG5w6+vqZ1P6;XZ(eJBr|7J!+Vem@e~xf59Y64 F{~v;POwj-U literal 0 HcmV?d00001 diff --git a/applications/external/bomberduck/assets/bomb1.png b/applications/external/bomberduck/assets/bomb1.png new file mode 100644 index 0000000000000000000000000000000000000000..11d05b9b7c445452253792c5a51ad6448cca8aee GIT binary patch literal 4529 zcmeHKYj6|S6<*swEEzB`38t=zH-ZE|vf77MudM_zlEDIGgsmEfOoR7vSKeCE3hmmG zO&&l(8cb5>#Xtjvl7wkGJcdcr)G0%W3u&1IPlBCsjCSvy`~0E_FP>t|H6jQy#aHI7fOl4^|{MfZC%F{ugZOhB<(1vZ>mp)M`9skt&V!ivdjpyd=Jdl<4+CgUF)_3gPmz-S7 zeUE&xdgGrypCtbQUqyHSbJGVa0==Q#HNSE(ow?bsJhy~+V>;eeuoi1M`N_te6@C1w zxyygCxnz5wuSw*{zU?urHW+Izp1knw{zZ=`l)ej_uZQ+sne^e`6Y1A7bDu$ww2hL-6YzOFgNeg* zcRc#MyX@!#dGD_7TvA$OYM8d-c;J`!KizzxYH#34FDVxXq}mN zy?P(h>iDX+yC`!*K6=1h-fXVux%I-r&9kz9{$2mY&(2<)jefLr*>BUy2Re(zqUkx7 zuDi%@nvthI+)SQ0eu5J3Ei`=4U!R@7y6+X&``>-K>(z(aADnz?(=k%qc2`e*&u`kB z+ZuN{PBg!<-9gs-F@&K%TbZBF3!7eW>#e1n`H6Jx&3q3ct2L( z%c0DBcl_Q_x2LG?vlovLFLiF++FGtmGu&S@eTKK-buQ!H;IutIp7-v;=BsD#zM1wH z{Q0H#oSHPt`NfK-CUx~cajUH_y8rsI*6nvTXUxXmxeE3JOQAM=1(sjMjuDx1oKd{Z6o z6if@|8(m2T5=4N?qRB`&sxV2nN#|wYSTo}$REMZ_Zd0{CfO_OOKxs2=Ca}_^)Igc$ z8&OwW5Sa?^q5%r{n(Ik{eSn@n&X9gHs$^ZSSBqso8^P!BxG#&FV1;E@PE(nC>88z9L* zK;P@31mT9pD}W-`$9YiN0HW%gkrV9tU9%flvjmN{(7`vCkhE^3Ws* zNs*ZD1;rkPR3&j(tWmLPGkQ8B1A*>Cyra;Ax$9tv^7|RD%-3t-`MhqEwmu`syd*IC zkmfi$&su5BNzxQX+wC^YNn7EFa|pa>W$hfzj-c{I6_t(hK%;`>W(o3$qJ`pEs|}-V z0A3uNh;ck=#VDI-rwGE8h6akegtEoxa9F&8wktAuekd)Il45|Wg1$LswNfKrYtVPG;)SXS#(NTAzc zE{rD*SXGV(WjX9NX(6GS=g_ntZYP0NSud*sNJ>x^hM*Y>5hO^4aKJ$^0WBl&vLK0# z{|&8e9@Ld8d6}fZ^&53lYDZOqHL0i6V_4F+5{l}Zf?@eo3JTi*1U*j3m73yf*=PvB z(_^4r2kp}Lv;qlBE!t@d#&HDfA<+WsXAv!!jTA+aA~*_=_R;K$EUF1M4oX5$N2nES zPhBgtKrd9`Xmp|$XjOoMVFZm)&LBlHP&|qp5DdTlDbg0Z18^jPIqY!P&{mGbSc;=C zo~JlMw32o!aEuQ6e@St;C;lx(7p@(PgGF`W|55f7V8l5JrAgV~`3#SHeCWI%&`dj7 zZ{u&E-EQLwka~QPG3h%l*SK6`QeaHr@$MRzYfK7^2|V6i|2Mge!(WUb3SaaR@O!W* z{^U#WThb6Kdr(0T^BnD;mfW+T0UGa6eg4ur&QF*zcD`;u^wIl|ukC9&oAubK9~ufwSNA{`#8+DGJ+QE5!@mG~Pe@e& literal 0 HcmV?d00001 diff --git a/applications/external/bomberduck/assets/bomb2.png b/applications/external/bomberduck/assets/bomb2.png new file mode 100644 index 0000000000000000000000000000000000000000..38ce7c732bfa5e3586d1c9b9a754b85405cd4566 GIT binary patch literal 4531 zcmeHKYj6|S6<$9e*oGLGfT_peML+;Mt9|ORTZxAyg9XS4TQv?$fqmSSHA3^WiZAxzpKrA(4i6NVBeq@^iL8oQJ_lVlvzgcO=MrVOMFkKSGRIn7MR z)AG-nk#zUmbH01dcfNaP@3uhs(h1r5*$9G6@RfNh;J-1>9+wV((*xC4;NRzs!D_vN zPoS~5Dl1`t>J2e~f}|oNNb>!sS9Lyp1|5I!#1@WW-qE0gfHd5>Z{-}`g({)!%P z&Fqyw*}iyhV1DPvt7jJf<#OZ15A%B0)#T@#dW>FB2{Wmz+EVqawu+sH-rq1`&9jpy zlT|<1w5X}~iC<=pA#T>_x2q1FK6xT>z4Q)U)tK>KD3S56b|4{0TC?Ku1biM(f8ns) zeNE4}%ida4@b>zSWu--y`iZN~2A+RtYs+Ue|AFQgm(Dv8%+=D((dw2J|)~N-z zs$b?>oma1P6^+|ch#s|;w^%E>@BXM{`?TC0U-y3T(WNh@qkmey;?;C=QAe>{G%1hn zoI-xpf;{=h?c}+$=O}s3{H%9->v9X%_q^cx!`B}iIJEZs!{fi)c7~MqOzE!c{`vWq zwuS@Fb1knd+f?p)qTt>!A&kCVS`oOvsoMoOt**8PHaR{sv3be)cHmW zQ=i}$H~(#Ado6~=Gp~m;@-AZj&J1hfu{(1;cChqX#{<_Ix^6%9oB27{KTQAbk*IpU zY2N}wxl+`SQBaURYs@?A{*g?C9#!w|S8m+Qm=TO${k-zn9R#_5tpb%;?O(x(YQ)M* zssOCXNDN98L0k)yF5Y%C9ojkO{x zSr#tHb|pCo5CJ-mCL`gf#wFbr6PJTy!;D){(?zd!TdMs5)T71$%2*jIft4neddjjO z8+FAcnXB+F>4ShzZcB}>$2c5MBofvHZB^qToMc%RCn%hvFzA744N;v>Vo`0D0g=M+ z0!@r7FA_Yc5FwLTUgA9#|F;iQ$oBN2RPgr=9)Ly*3N zzA-`z!VQgA08Ooni=ea~MDLA4a?1#G0Pd62n-*<9p>F%yXg$Q{C>`>igiYMKCjzitj|fRs7RbS zWCXz>@-_xzNru80hr^Dsj17(irzFZY-XSpj5GY?%)A^_f3@8Y0RUnR?b~3cU5}4f~ z*f550Ix&G_Y#3o<8PXxM5=D_iAXdf|s7gLOG%Ev2f>2J965#Q~q&LRpDHOCZeaG7NBARw&WB zMoOp=IWX;q+ezYe-plI%f)W(X5e!EYL4xE677mIDXc^+KN{Za@ z-@J{@gSt{JFH!Uv6i8X3WCufIfj^#%7Kb2@kSi!p zlPh$d*{J!$y%ROS=mI1RBN&WggA~a@@+h*8F#O(^DB5|_LECvuq$B~Vof0u814xV( zL`oK%EW_GT`WPVae<^WL6V&h$UAS>9_BYjq|3}+X4nxjSNKMKH&u4ht;{)e?A7?P% zd->^;+r3-@MMoDI5x=8!jnXwD21evOs;*JGM#R8~oJZC5f1@jV@Wu$D@I@~H?}KlC zwr(rDOJ>E&9@Y@VI?MQ`B@ZvGhsOJKpTG3J%bC-sW}yQA_qEXUExoi__oxx$nt_;a zzd%A0ie7^nZ@;TI-=Ak(Onu(P!D$^$JAOY2A;zUQ`|e`93fbA6h0fPYDxODT?2Y63 zPrW*&>7})^$)cW1xch3(p$l^^t>3Zrh@Ae&jG38f-#JVCB8d4V+w@^|y107;Q8`s!r9<>K*$8}6g>N;sjATc4yq;7b1@BE%- zrsJ9BKTD(2-nYN+x8L{MZ+CY0`icq`7#=X_bh-teLU#%LSKEdZJ$&kYfvfOusKy^q zN|-njlSEz!14OBg0R$ujUZ+c3db;>8Pj}!cx8h58=&|)#uT^{c+77htKeX!Lk4s

Y(%&x8cXle1Amu0 zmV1XgTMc#X89QHDSYsLZ;Eip?zDuDazMnbho_o`dKC?ON)Zz6(Vr-zHP8ftfT+~0UY?>&{B)Aq|tz7sb~K6-mW>0A5f zzg)ga`Wu$MC-c$Ot2Uf1ICkwN`u6i(&STi{h11<%>*4a^4JQb*;G_-DjB@{Y~GX07XI+&jiGnm9m+u7+OqYup4iZn!)Gr} zGxskghU;}tTzrz~e7%$8SFf3S;YQ`c6^~p$>iFZ$zcwEGR@cMxKi=Cx@Q0QTRt}!% zs&A`qw0G8@+Wcsd<2&8=>|JrTx#iX$&JMizdviNaTMN>^ao@bi3%?<9e)7PLRTb`D z<;^tGbn@^^_VT9e@^|*P;y>y+c(A2NUNm>@uEqDdvyKOoSNj(=J(T%sUj66qF29rX z8n$oC*ZXEKqyAa^#O(eXd+xSfkN)abN6T|d>XS3DGq;KPv*NXzy3VvVM9x*{hZ}2i ze)kWoul9p&-Q_4M&H5~yoYseW`;$%a@88LE*+9W3Jxe~R9=QF~D{JO`c3J=JlTop2 z_u+Lq;c9kua(cRc*rdHh`>3GJFH&Z8*_m&p|C~*+iQyaY)v^! zamMxQ42}d12_isYkVGULmFa}jsPWRUt(q|-qCu2$r!nC5Audq@h{a?v;b?wBs3MK) z42VPGc)G;BX_NxKIgMqC5~DFJ9*>*iW|Js|FoL2e3@0&?L?Hr|tD_2&K%??Xm12a$ z4P;glVu~O}5tWk(ij|7fXoUU9SbUL~*E>!hl}A;8dSD4Ah7l$li$t)=9jpYw2(-2Erf$p)wqmm@;I6$LkySP$dWn zk(lNM#h!vx1b#xSDY2m)?z1ZRthC? zibX99V?%>x!iMsUg=EZDU}q_662&%2fK|zaCr71Hagd4vI0%~U0HuPA1+@TcE=mCo z@>wV|p9}CH$1)lf$I_cbDZ;?%6e3IrV6kXO>re%!H~KtIBWc1XEWR+K@X*0&+$uyX z6B8YNAp*85jH)JKvy-_LZp$Tb_#nv%P$`gPScxhpft$=0tw)^}8YTmUWz;%_1ezV@ zLc1iuD5B&S#jw+;hJ>h|8PsDNdSc+(MI-pTub^K@)fm4lR@LA}8?G z{|l`y9>g&+<%NO_$FJ5*BP(hIk)h z<*8|fWN8z%W-2;f2Gm)Af}ywtoe+!|6$~3q7*qF*v5Xzqe`(^-0FyQu=r>}6n-|;* zvGL7tR5Nwk`3gUybMY090I6pNnU=mYa?Qv!Ed{0po~f=Gxu&JSw7@gf^?#GgFmas% zQFsc9!^_gAH7PcD(V89HQsCCET)Ka|&V3KI=Ee#imUTMQO7)kNXj)$djdK)_H-FBR zSs9DX^M)S0dBQaH zlF4IiN?T%=QXmAF5>khuB+w?ABtw&-4MQMFO-egsU})OldTbuSv=C^fWD2hPog}}e znaOw>`j<5$>AU@Qf4lqJ-TUsItzKR+EqhKjf*{lUmA)GIZ?3Z^XTsmi>R=!I`}_LZ zpi#re(U_*nN*JIo zICuO{Iol7PyT13C>066Ceo^!0<*Hm=S0)!v+kFJQ5&h9ypWQiex&dv@d#s{w#upO{ z$ck&^#y|e+(8{b&as#&(wPlo)-C*Y|?GNWoeeOf)d%cV4ntPTwZx-f**Dl_8tD*hC znMVR`7m9Q;IJ3C$!JcAi!tJ#=FWt6weJdcg&x&0Nz0$lrd~)gQ2c5yb_Jh;9o_MIJ z{ijcA1x($Qw)syLE@*z|!=GRM^_>3mr+&K+zxiTlzpL~2@rg68xxRrQ89z|G-fF+s zJD503chA#XJ(b577QFxX>7@@9PjAd!*-`z%-?}ID1P@hj%-;WS-PvOoI&<3n-fcZS z3*NrB;7;(D+}rM}eci>AH+&m?)xNydUehzMZE;(E&QEUNxYpHsZ7zD|qbkNiEgf-)oybv+aH7^MBd3W9p7X ztNW#w=Ukk!X6HBm@W`IC+79M=$K&}Y>pxny_5iyn>)zKl<@tX1vS0zhO`Qj~3f92w zbvXmgy(_jZyYJk@eD>1Hmh6rj-yb-3J$mZB3mboy*J_!IAMPhJv*ImFP8~kFC30k4 z<`*xv-2bbKubpYxy`r-o!?di=qn26cut1l^9&i5l{70$pR@^+Dmp!4o|HtnZP5S(N z=J`WW_0-dQN|D)p#Z8uig3Nv8Cm;JqA|85D9oVb<^OmKfUh8=71I<8?oKgjLVlYs} ziE6~oOR50uiAW5#D1wxfC1Si-4-8ZQAtmauUO#fwiYk)Fx|#_PftVN6DV6IruxkDC zT5)~7$V%3-((IB12LU3$;L$`R9M!pm$C||D;Mx@9Ry3(%)O)PKKsD-BHGtA~+D>5Q z38j&;mS&?Rnj~{Iz9nf0c;&Iy8Ago5@pwFLk2~zD7Q#uEWpRSSDGGxcnBEjM_yiW! zZ6-tt!v}OxQ(}gqMo|-!7t{vBW3|G4bTGb1ED#uikLqa_ARl;wkKv@9z#|cSq=jyj zH$sqfKwoO1*TR9uYk;mcXd)|+gDIGK(l;vftnP*sOs$x$vV{DJBr z3zLG75{V_PAlaid4MiR%YgBCJPBNX5fk5*i+)>(txhIvORv^InRI$Mf&+qeCP5+#v zii*S~m$V?bM4q8BmZT|+cDbAwOEYjKxFu0$c$Yx)BcS|I-Qc4lFrgs0U4b}m2kT-b zH;GXqAz?HnIx)B8ggCq?5rmTl0^=9~u|iW|SMuSJQJGMZ2}QC3OR)?lxG4goS%|_* zZXN@Gkttc^ouZtAl02yC1tpg9LoOVS)cag^(R0!l{ctCAu& zeU-L3JZMR(D7kdkpitkjlR$45f| z-aXRoIw)7Zq!mb6qGT8CzyyJSJtR9|{T#9bbCR-5QiMPO(lr`gS7js4YhY0b@(8(t z?V02XU63qP(P-^>9Wbi^3Bw2)qu5%CrXQvcK92KzoDH%M1;E9G09ck&dFb|Kr_(@OHUGxB< z$DNFc-*LLe=^7IQV^SXPu5r4?#K4%8$Ghumqbqy(r5Hrvvuqr`@*XYn9fhyU_rxmK z=m=uBncs}W{<20WoMiX|<&!RF&6|=r_pgU9RYB2gqdaJM)rk2fg(M&FfrQ2tqYgD6 z@K?7?p9PhQ{JurC`KQG@0|R9ii^XQMZOq7s*{8gbYniyPa^IC761(z0+t6O`Ms^D` W?%Tg+a|JX){N>AiuP%OK!+!v0;CsXX literal 0 HcmV?d00001 diff --git a/applications/external/bomberduck/assets/enemy1.png b/applications/external/bomberduck/assets/enemy1.png new file mode 100644 index 0000000000000000000000000000000000000000..7ee7cb27f1ee52c63dbdb31e788c1011c8448d35 GIT binary patch literal 4757 zcmeHKeQ*?K8DDZ~L(Y;gZ3+PcEO&er*xQfW``9&?K<|#^AV-dH&@fWl{dhOoCU<-6 zZE_bZ*aSL45F@leGo)0-Rw9ayb$|>CLJb-!%7ii~TFfL=FfhR&18RWy?p;2$Gfu|h zU%8pvefNEz=l4F(@A)pRO1up-nWJ}%r-V}HSO=6USEIgpYbb4+b>@E zS<&G)*B_sLVe2bc#;Npmt+~hCH!7Z;_4=Kh8{Veg#)TK=ocQt6flKc#ckO-Um*FLA zmKR=I+AO`icGi~pug;ybq^02C&13Atrt_|Y`M2_ayL{H=y4d8ddv~`W$b{Erw>#i- zy9YCe`8I9ZRa@ZgTODbgrH2Dsjqk24zjXRqd*-)%?%kc8 z3yx38c~JU&_PFzAcgMnsb+gbTmX-CElFt6!&+o|2{CZz+&(*G;8R$nJ7TFBsl1q!t zZ*SZ2`#;uazv@LkxN&Ck*|}2p`^~|p4|Ls}nRoK4w`TACmi&T--HvG;&ukZp9E&@v zI{)Hdas8*oInrF;?7aCitvBj^(Xf8si<`UFYo#5HocWCd=8R`{wA%On;jKMS?TObr zzkO)>C!brl@7dpcvFVb!$9B8zrR;MRAFf#cU1nSAlpk%&^8D%$Zvds++P}ApH~ITE zWcI(-RlIA(yvt8!Gk;!HYi#S?(trAP`23k`TlZ(x8)o1~ACf7lvD#(lkDh7>o!F4_ z_jhU+{os=yepK67++KlUYU+1KOo<`Z)mGf+ul5@Y6y~S!U+{XLwQgphh3)ICr+7AS#+6Ru=xpp#0|>j!i_3ey>Vqw z*jOPjqA5Smm>XvyKnQ3Y8V^;5qioz|O5n1vt#{)lG-0AuxJ;$~0P0p$fYKJ)LSWvw zTuqtsjA*VZN^FT|*#HE5a+%6CEyCh>EEcoGtQJKr!%2o=aDu`q3WF9{v?i=^aV#7) z>kvr{4~Pn?9MNPYjOv&iuT*I+lL?NagYktTe*X}BI6A-rJP zdFT|B$)QNX3z9v`Qj?`&vPQ+Gk0jC=83=SA!X0Hjn0vw)TKWB~M-i&@@O&PZNuQq; z6+sr+M3d%uhrrorj3H?Xqa6-A#?Us{;+>)(**FJJb0eU9;i$%i1)xJgaElCa7~V=z zq}7f&MTy5~jDDTlZ+fJee*i2_LlNl79pf~NrK7>yoPBrV3N zU}+iT5po6PncxatkSNr1qpf4*K(7KM3?pcaVuBROLh>kbfH3^=r)VVzhNgG{qe&59 zw37pv(+&Ve0-mu`a4C@vVRX>{ONvkv1j*WLEJ^(TQY0KApeY9|n;p;?P16j<(T43{CTZ`b<;QWp8JRf2(3HuD|&Qb(@R-hq99fBen`y{bV29g5ZvZ4{d1!vd}k= z$M_pqQIBy32t7W@nD`y1Yn-kzF)$|MadnN;H6{keWIV2}&x|hP@Szxl;kRrI9(m91 z+!Ju^G3);c@ptp9VP~4=^Lx{-r_MCanUH>X^<>yJP4kv&ZY89jq>#i1 zJ`mBEtd*ns2mTAS8B?JV>+>uPW?vK@^!Mi*3`zp7Upp$8Is*uW+&NA zL`zbs0ctr=!Fn1&i)p1&>CqOo0Vx97=)qb7$0un_px98QP>LKC(mS(BNUZ1dY(=+GA znVDZy?BQN?p6lz*o76lTeHJg>h?n>DZ(X=4efp0-zwk*{?hH)X*X{=YTy3;Pq$6kme}b0 z`O_I^zFGO`EpL9e^`y4VcD`d}`s+1sELrhOSIhXBziCPH{_$xc6;!kw|J4@38u)z8 z^!|0dWm}fq@%Dst*I$=5SvoHKp#RYM$gx*WKlrn>jj6X0`>s+cj_o_RIo!S` z<;u>cJAZv)-olB3^Z$+P_1&{Ap@Ojn?tVtD24=(+d=+#EL)( zC#qqbmsA1ZiEtE36hZO}6H#8Q0XiyxDkb8!o^L;BMHR_yy~h?H15ppCR*D-nu&l8( zBsSKFF3DP0V98H#5FiY69!-R6BQY-Fwwkyc92;iBikdEZjoVrg2%;WU11O8LIEncZ zNLJKLLSGpX3&Dma%0W!6(?sB_2N6AcID{k)#YgM3TC*HUBtR_)L)REwl^U^Rkv|X| ziZCdsQo>O)3X(m-Q&;5cWR1wpm@&&4o(K#d!X4o~Si9*Ay#fKwtBQ3-dVa6lYOK#m zs;EeuIb;RFA@VjBb5Sgfu?~kFbFnr!5}cAK+jxh-^24C~k(ka$L|{Nca9n{nA`G&V z62K@%qA*q_X-u&53`UbK$tg>eNQ$;$5M`PIRms;5&&q(3Ae57G2`<`Y!vrTyVyw$< z$9T!fV*uD>S{8Y`C^IIMByx*XEzHC1RKk1}Afk~fbHX5;TM+cStu#(vw*+f>U4{W} zYl#x6OI)7_DPgco=M6R~yOVa>NRp;)b{FML=4|5K4YU|kqJc?~IK!IZ#l&rjwU&pzLSKO zVg9P5$PNF^+h`s%Ke^?_N(`>wV49L0wG6CDUL`MU6|;+E1trfq zd4M_X0E!<7E;|idiE@Y|lm0p-LQRkqXR~n>`Tr}?Ns9tWIc3l&uKd_7WgmLE|)NMZTAIeTT40}|--A~%!BM3gxh@nT?fGl9X zH}W&kQ8#i46dhY+RQ!(7HAdH{7#NlFn7YR38WjVhavoFH|BNon^+Pd;z<1d=Jo28> z+uPxBd2+P4GKL^{w(%X8Xe+FT#)-N=;G1}Me3m6`=3NEdw?oq`-B+P|)Ua`qLd+L@ zAfa(ZuSSg*{Ck?F-3pxw{oVzk^v;I^)C>fjl+xc{m669NElsJuryxeLkp0ky?W+$H z<$LPBbHK8C--2@XFP+ye&RBVdIlcCpy8(G;UQuW0oz(9`D8%n8^*+0BRr9|AWQvH3 literal 0 HcmV?d00001 diff --git a/applications/external/bomberduck/assets/enemyright.png b/applications/external/bomberduck/assets/enemyright.png new file mode 100644 index 0000000000000000000000000000000000000000..45e6a861ad9c914af25be2b6c1b6097fdc6f0d17 GIT binary patch literal 4536 zcmeHKeQ*@z8DB1lAqc@j2r_}Jiy-;f+mGA(T5^2kE+mIsIKnl=p;q_f-OHN0+so}H zccEZKiNFixQqR2oRlB(;cCnh8})Y=IykGX&Ci_wqS)rjt?r z%gx-~yU+VPzxR25&+mO_-_8Eg;>l^V(hvlh>@D$>!GC?6mTH2(CVxdY{JXd=P@$D^ zaWooJBsm07tu6{skdP$=NqqeLsx!|YCsS|5GuE26r?*s_<%K)0d_HgfpQT@K{rQQk zbkhfp{lEVQe(T?u?}bl4uHRlbRaGxUXH9--N~`$noc3c^rmb94h~zYGX`6RB!?AwL zM?z}DwKcCqn~$B0?|C2Dwd=<8$$K~0(k8SBG~VN8$|f$ebvb8dDA%eFfZzP~t(!Z_ za}GtVmD653;OLz%X59Q)p9wtE=SLlB{umoxE$o|^RRM=$LuIC=B$4X>T&hl;nE%+l-|;N?9iK8UFm>;DAAGm4{`&bjx5vFt zY<=kd(-USoKU?+8gfl%)_Z__y+24D-`Ipn{Q)UwfZ&CM*k8fPodhp0k!|zm^ZZvGn z`@^Ti>5UgwwpZbJZ2Z+wO6F8}f1vd3lCOW2GA9uG;yKtp*Dlp`+e(mO{mR2E&>8mo`=MctrME?E}?tJjqVgmqPd zQ^Xc8N-IdP5FiXR4o!qZ5tU82Fawu`W8F+(sNtejxv&bKA9X7+fHG#rOyWfext7Kj zrJ)5eQDVzH%laYUlMAcVv?xmu@p#-Ex0sb!kf5ASCqdE#P2n_4x+jBWgbjkPjlkMG4AG65%j0JVMoqY9UB} zLSGx92H=h+%7CiW!~{@O3nJRw;Si!Q7$2>Pg^Y4UfdC;8hOR1Hl^U^RvDfDxjL<0v z%HgOH1<4-asman1StD}OXN+=&Cj!F+Z> z#Lz@Q4ujPir?W}f9D2G=lD665FqyM~R}Nw-RHBYak!A~HgzIf#VKIxgrdf#U^yXKg38qb(Z~~GC8vZ+E)oRr z^yruCfL;C?DNvF~OLoSB^E?SPBw1kpERqGcQIbT_Bu@j%J`!D3BrVRxz|tV(5po6P zX>f%uFdCIJ(mP%W^e#ZcaFW4kXMmR^z-Z&KorCg^@m^p{vb=*QwfQwxayX?xOP*f|QRN!sA~43B$a@VxKm z4CZ?$KmBsMlS`oJ*dn9icZ{wvx<C0@_cz|4+Kr6;E#Xlh|!U&95zbJP9;$F^rDco76M zz5V!0f4N3|vZ}LQ_N0&Ui#rxRF#k)4g?NifJxzs= GJ@sFi98UKD literal 0 HcmV?d00001 diff --git a/applications/external/bomberduck/assets/explore.png b/applications/external/bomberduck/assets/explore.png new file mode 100644 index 0000000000000000000000000000000000000000..5eb50b669b3ee364bd25a37991519cf833660ae5 GIT binary patch literal 4540 zcmeHKeQ;FO6@MfFVKICS5~M+%n;>Mv+xNL2?{$}u?1p54jZ0WCDOP=-n@4u{ExT{C zn+O%Cm1dzR>NKrHD9yyy0WywM2%-c=QA^3RBvOVJZ47D=Dm22Vts&`syZIcQ>10~| z%g*fXedqqp@1FBJ=iYg@-cweRshg*RASlyS>MRHU+E_O^4g98gDz1XR>vi4=wVaK? zp|B#!ei2q{Ln183WeI}ffBD(!%HU~ia_?7pbs1(f``yaD?|<)&%Wd`UXJ0CB*4I|- zWKYUZJki#1;r@=?%9ca%ybRpZFs`AeTf1S8;ZcBr2Q~kD%A7$w#ojFVfdSKnl8996U=Iu-z{rB9!`p@z{U;5J8 z6~>1%tBPKmv;Bi74|4ndT6pWV#M4E!##_3^zF!);a*w|{%X9vLmG)g5wwr%tSeVN# zvD&cedv`3_v~cm3VL zagb@XUc1_5n7nNP++r+C7|Xl+pDo%kclxexdai$X`TBhL{gtZ@r{T*w^CiQ~Eb`(# z_}2;OsXy(&&%Sq-kQU}m`D0IX`hrJq{Mz=1Z!Ya?THjuI_do03#-(4}(;e-8xjk{J zcAxcZ;?)(K%4{1tCe|-F*3#Ph&atjnPLgj)jHx91fjLtH2Y-j>zc8;Sccb&XdM1l7 z9%wvjt!_3{fB0M*_IzhULu*-N#*`(G&79?2{97)4p?5~}+C`^}5?@}vuW!P;=oBxc}6R;Gy2PTldUPq|Zl>+{W+Dh;3coex&W`!10Y~U+>$R|K=y? zg{_}G*inrj;f!1U^sEbr`(nB=wxw^8!z`A3(K-8z+OFF>PvlL#^+DPX4+NF=&5cVT z`KqBdJv%!sXX2ShZpLH2$CduQ@|Rzw-{%cq`+UtSeGoKly$o8h!o7;&m4J~I6izh8 z10m3(5M(QehgiN^RAEl^$w9mR#_={iEDLu1Lnb%o4mrdsxwI}UuBj{Y@^#fbE$9oD z>TGca5ClY(h2sH#Fv7&``Xny{#+n(`!%2u*ZP!=0J+MOwi!f!Rj2KcJmum?9QXOmy z3ldZATs}Ynp6vQ6RShvH8jHn@G191neJD=TG>Q=@K_CEuL~4U78%KhX9E~Ey;S?i$ zSPrSO5`;BQmQ$jtU9Si8@L+s_klQ^(AB+sB0Q5lPYzW1T7#awmBRwK&aSb3D2~Bh0r)fAe6;oMrg_eMx0gPB@=7mD0T#uD;QDPATMfEfZQkp9$Msh!Ax)nX%Sh3 z!Z8A&B{Pkf%&di`DcWQ)aU&=m49lP^S^vnWG%5j5S#g@93EG5kRsusP+H6Kx!O9|{ zXp#ttXU)7sCaDCTS+0ZwEGVZOV0|JQ3i^^0n&3>K$7R6agEAqYT{-qVa3cOZl%mtD@_c8)`{T==tPYZ$BZPEoYBg{fMkHMtk$Q1AZZ7= zFpjXus!G_aD1N(M3klXdho;?NI|;1HI$2c&q!>Xm7{!p77sDAGXH4dN3|L0s6+xD2 z{~KD{Jg_ZQ@=`eh)~`*PQafsmxH0vVdi2Z5tpvl#O~J5yDuoDJBMQkl0at2@uVRBf z5u6?a?K)_ezoQj!NgyN(MIsyrx`2{MP(M;45i>4HIDv75h+9UpM-)kov0<^$2Xq8l zf%Z&l1usq(DsMD8RwZgx0D>VHg%Grtz!@MOfe#3V-uVzZq&VCY*3l{2Q0-V8EUFFtR@qa45$7n7CS?QXGdS+iq4Rz~ zGZ62c{0y|)om>Kj#}^rszTq`;WKK zgX^A|eG=Ryr-Vu?BM@ZF(Y_Pn%>^~Uc$eyO7vJ^wjQMk>GKWmDCSbZ(Ev`@N`hPz0O0VZ)6#7jA;DcPn LWzLqO$F}_kO43W9 literal 0 HcmV?d00001 diff --git a/applications/external/bomberduck/assets/playerleft.png b/applications/external/bomberduck/assets/playerleft.png new file mode 100644 index 0000000000000000000000000000000000000000..86997a985eab4de991e315858b17ed7e0a981f74 GIT binary patch literal 4311 zcmeHKeQ*@z8QF~0mx0CKOUBB$ywxh6RdktHd zO&sK&?)m!b^WCRH-G!^$_a}-oP1cspFMM#|(!zW03)g0!TE3v}Zr#$0vELZGUb-+n z`tcmk4f{`Lq#nI*_apMPhBtb)weEi5)l-XhEletV<{4f2+r2_Tt8+zP&D`Z{#O4PM zw@BN!PF|&d&b$BEbZh?J9iP53`MlWoqIZt|$djhBXS3*cf4?u&_s3n;U$vdPIJM}q zyDDYY=lAB?FP7$K9CysB>pXqwYT(LU>tF7-mmbMD`&m5adGiz;K{9?OIh|g&(>Yi; zEO+PnEsn}J7v>#X+q1Ny#LzTt#XH_#-}_|hvzoo$C$e_cG@S1GxI257+u7aQThwu9 z-p$&5Y=`ah%NI%}J~kI^H&&&L)xG^&7d@4mz4@EID<6JzWj6Yk`YQlbF=yi*SStB z$7fQ;y*ppCHMW&Bez>iZ_(e}kOGj07+N66Qntq3?=(m3TJm0joAI?9vD0Tg#Ikz(2 z#<$#`e`Z21^UoC1X2yza^(+#y2cJd9d@@;Nf+;8?75k zk9>lk*>GujcO!;HGOq>nGtXe2vwCCv(OdJK7O>>Yo}4d}7ruS^_2S9b&g<&-hUAm$ zcP>Dr%Oy#DUY@Stw&M@}I}xw{vE2W>bp0#+9AD(~FIFD71v@k$!A`98EMs{&XygRh z4~&Um7`7;a*vk@Oj&B4C>Id~w$YHp8xYK}2g2S-N>>)g1Cuoo=ng}@KRhhvd|R*t~qAOM088ilixqo!Qq_IQUPR0-;( zU|5TSVvj;9k~l2ZsN7VKR?f&oVE7R4DD+_M8W^HH9@ZuEF*QB6%VALGX9by;1Xdeb z7{bDdem`cj5`K)fnynZ|GXzF56fZCWPf~ty1eH4!Rk#ojR4Pbrlpv2;r2P~iIgB$4 zCXD773&vP^9<$I^nimA#LYajT6w4zL>`E>$GAorzfK)U=`YE0Q7)euBjJD7MW+VIn z1F&N#Mzs3J^%e`)gfie=q7H@!4L>S;OER#a9 z#IVCB1;I*%Q`ICbHp^;%r^peXEC(D0H6>Jy9CCZ$b`m&+b8!lQqy%MR37VxC9~_WombR7>&@uuq3zC@p zUubpnp!W2VS4vShe^N80chpL-F8!2#3`p8mLQ!o~upFN*A<8v@^kPA-w2N=xLiGTi z9s}(BtQ=sVhBxBNdT&{7s#-zZQz~kLDF4ve87!!EByZ&!-WevYh zfe^d}#o^0RvHVgme9@W|u3Q~O5MzP*%}BJBH9_O;irZ6h`=^<+bF#_0?9Os%x=X33 zRh)8Ay@Mm#)es11Tv8fP^=i0k!w+Uckkjod_vQA)HkaRlpc8dnm-^T4dAzEv*jiU! zw5bq5w*DNv|C4Ed>zY*bKyJ>);=gX$e&|PQ8vlgvdH7{q63*53T>oG~`_lQyYkd_@ QKsv--QRQl1^w4Af0rThwxc~qF literal 0 HcmV?d00001 diff --git a/applications/external/bomberduck/assets/playerright.png b/applications/external/bomberduck/assets/playerright.png new file mode 100644 index 0000000000000000000000000000000000000000..1a6283d9c26d9f9582fecd0cef9765ab2b288576 GIT binary patch literal 4307 zcmeHKeQ*@z8Q*+ia)5y4BWi$k(Gn8(_IB?hw>x(PxI2RA+3tBttlv!gj$hEe0MKjw9Yu0 z(f@KYclYk|{+{3aJiq68-+6bdyQpBQ<{k}#AXA-%_G0+2j5X=W@Hg3AdI|o0QtK&| zi`gg|j)=Sv0H|CO1}KOLJc7i!x3Bu^_LJz8fnUUP+kS;;H^24s%j?g4kYWBKXnHPm z|3K#3^Hz43wbp$>qpKTTP5jvx5J?i!bEdYxN3DG$1-Inikb~*j8@qNKPi<`|Xx~w1 z_^^Lqb#`)S%f7n3j+B>o4`ih^JvLjDc6zV=VC8+9SD(V}y>VCkXjfmX`}n!<{ikf7 zqwm>cnavr`HO{S^b#z0`q0Q&&XI$O5k36@{+~2={$;PS`)(%Ih0xB8o}Px{Mi(W&9gzMTk?v{A6x+)kTqIB%Hk zuKFF;!V^of-dWeV!jXejPhWM){nCS5;=OAQxS!JOSyTRQ+uzSm+vBvI@9D{InVmII z+RU_=FJHQtlfGd-dPrLo*B18-HZI$iIqjL7*RH(((UrOApC4NJS~9t$GndbqF-L!4 z7WsJ`*>r9j*?y{>;vZO)arRo(wE630DtOC>IKABcPq9!!FkdeMlNo0qQ}-*f2qAbwn^ojP;OYM>QlF3g~M+ zBoExrL@|)Wst5-h)gUA<7)#;fM*PE7k${?xk0U?;1R+#{tCF`ZS>SZJM?4e>{6a9S zdO@*oL&^d_D%NeWDKlz1V*`QiBfPhvhjUlK5an_)c9E-6!gJcKn6f_O6FI@hs6(1I z^9BI)IISmH+`xHtI7@LPo~JXIcrP%SXp$O3R9tE+uiDP-( zU^4JH%jHqH$)`7Y0dJsr-ZzHg;fMgck`0WFN}=*8RC<~RM$U*EeO?o8AUO`F4J^P} zU^a6wPS(sNqVaLeaxoHQVL62$>jy+QP9W@04wv*!HTUELRGQR z36Bs2B{Hk1Nt(=59!={_M#@Z+M&l@G4Twmv6BSNUr_~$O8Ko=?Oa=6hmJkFeldkrQUwt~-#d(CIyK~ea!bf)CR?eIk_BV4#vLJ;i&<(m|1%CCmTJ7lNJamUB0b7xLZJMy99QE0kb zc9hCCF{s?Z5%p>ad}vgV%TeWO_(;Q@b0BDm)4tS`*;)USr5Z$+eq#3E;K!e!PrGF6 zqTlSkg?7Cym&HFjs47bfvH_+H3` NI2}dyL(A50_zI?I5pDng literal 0 HcmV?d00001 diff --git a/applications/external/bomberduck/assets/unbreakbox.png b/applications/external/bomberduck/assets/unbreakbox.png new file mode 100644 index 0000000000000000000000000000000000000000..5e65912d53405d04bcd05916947a8de8fc5239c8 GIT binary patch literal 4763 zcmeHKeQ*LAa#*}l_EHe7ZnC@CWRtz7rA^W{(9lqV#IcC`@ou`N$%f6g zNqff~tuoa16pDf(PzIfh5AYZ zc0^x)uxe6uafh&a*{bL7ZU6Qbb<5kAiybrj+}Rh~@;;c=`r;Jt(bwjk*l=HGc<$nP zWy0FK-<anGNJXNLodp5GUkF@7z+}L`+vf=pS>nE*`w^*Ni z_U6;Syz8-FAHI9@QDwdPLf5ja_k!=wuX@JToHp$*%~PERpXE}4zqxxy8>jbNS)SRy z@>E6J{98W$MV9Sf3mbJ^7w_-ie<6J6t$&>py|<-bI+1eX zxv+d_#pVKJ`q{kt)a>k(r(Eyd^KCp9_`TfUE`4`7wJWG}J^GHKB1mR|1eNIblry{> zGO&Wo0Yf|#ff7ZK{K9yI<%2*)IS`P-cKwCd_vukdu#O?Y7E(6D!*{DYoE^5%O_j|mkLskGv87Kpex#ChC zsV~r>`HCPimClj@2zX`JSF37-F&bmBm?35|$V$LS*lacfbbtlOhcV7ZjD!I4>q*15=7-%g$J%W5LRh^et zA~XsDQYey$f@F{KR3&kktWmjXGl_CWCIZ8Ua7TF$)}C;NULFtQl=)gMJ-5@Y*VbnQ znU@46F{C(-=2H$0(b{ zg0X^?#Q-piq{y=tUNj}31fD68l@JTJQwp&GV2p$Vi3yExrpW8I>q!GXZ1L8xst5z@ z`f@2;8y}wVNg=REWi>Vli8CrrtXS_GCSuacKFQlgbmG|?0c%O^{SvUNa6qBAq6xDQqDYW9M*@N#jUJUnHO4BS zC;)kcTtRszxI%ADY}9R|y<^ot+XYA%hEo`6^N|Dt$s>sY!i?9xL>#AW6ie7Jkpdh> z;gl8Qa1)D}N!Cn@g21zMQU^l>{x2m$P2dD$HZwT+|0|KU5QK@xO_-VI2#f+Ef!WM9 z8g?N`5VVyfI4;=&KUawZvbT;dG2f`&`3H5IZ~T$6lMW*u6>#^HHuwmFPc-AuBW*wy zFyCwW8R)2Mxde)iFES>6$LSiUYfKD`$$4B|<8+OQfiXFctLtZ@OE-Kd24VOv8-qvQ z89nFS@VI<^r0lLJf*5kO?}T_qVI4H4t8R}g{Y+ZU^)siu^qJ!YXqv9N{Hj9^X(uTp z@q!NoG$yIlsP=-tq%q?r=v3r(7WuM{bgbR4L-2{kD|YwygIHFp{{wKclAL^e-K+_( zl_?z?d^4U32Ch85aAyv(?N?isr~6iiN)H`tCH8?-F>_AFzD>vWLNLVbTHxGOd~ef# E080^$ApigX literal 0 HcmV?d00001 diff --git a/applications/external/bomberduck/bomb.png b/applications/external/bomberduck/bomb.png new file mode 100644 index 0000000000000000000000000000000000000000..44b9bfdeae76553be52ce1c451133a4baee7bf68 GIT binary patch literal 4534 zcmeHKYj6|S6<%z|5CMh~3MO%!MQ#F^?0fat9m^P72CE<}8sWT0;SYAG!)aM&4dO*36QBnfew$RC4{;rDG78SDFiYN#Jwv&HO)-N z)AG-nk#zUm?|k>1@0@#Q??%s(g%eE=nh*q;=qhoR!+&FJ8kY`#o9I&q;or4dZ-rhi z#L$SQ%1RKRdQAkNAg;&=5+SdGG+Oq zj?BQ7b)PkKHl)qI9nkMC+j;2q{r;QBzP_>NX8O53u?cgg)_sN`W1drriaf5OqJhj| zzFXI|I7*HbWF1>`=DXj^&8VJS*5!HmAKhbnDqi(8nA(>G&K>^f^rRiGqSl_C*>6tE zx>K=-f75>TN_XzK^^c%?@gYAB9E1UUb{n6%CTb}&ErB!-G_lrW#=509{4{SPSec^+jv`uJ>H`;&p z%7Y(|U%utrCs%Ggr?pwGcdf~Muk!e!RUO=hv}yY`Om+VL6<;c-*l_w6Ek3jRtF@DQ zpSe`pvgon*(=)lhm(`oPK7Y3N@b&PCcRp%(X=-EY!^9hRsg$%>{ely39BK}|wKnC; z?e%ki`Elpj`WH)2S7Ml!b~Bth{e8@RF%^$J^YyGJ>F+H3=b5Rd`?~M`=02|_>@(0C{qj`DGbIl;@rv0*06Xab^FI?NSr4_c&Z0A+C&C$ZwV zQcaulO=zAb$$YtUK|ck2a+m|U9^nZh7K`CA23IvdL2(>MkTgNl7(`&vny@a!v2ZlU zpy=apf~cq|5nWNksKF`t)GFO!Hp6*zAihwh^?6AZ z6^Ty_S)b1)3KkaQD3-=po6U-GtObsIc1e^ig3ZSY!>C;0s4j#>U{FDFT!B1Ri4p7; zn+;<~ABnLfDPf$QBQdL;v&f`iu}Dc2rJ4e}QV0%@%Ak@Um7U^z9L-rUpPhzOoYjg6 zl3l<6u*kG53RY2O646K^zd+SO0xYKz68wOOg#C#LLvVh+$K^27I5}kT1O;7&4i58T zC0rFBn(!(iuv8ZeH7To|wp$s7qDk7qaMTcJ8PKAz6Aey^#2Gd*W0Zx5$v|NRqfa40 z!VYuci!>nUs^(SIpu=p0gc_cM({8w(BtaLPf({@lNi#gj@(k&vY&=QvEH{^gmSK2R zQskQdhBh`2n%7tI5+w@PuSuBtcGOa^w(qI$F{mWA5{f1^1uuwwDMW>8ASL32Tzykw zKnVK*JU#l`b-=ECLn~0SM9Vgo!F)av_K?iL`Y|$tSt(hDvifL1*^=3#s;tKZ4b1mL z9idjRJri1?vlE4ylZ=i9fKde~7)G)f&3S2xhvLyxzhK0@Pthte5^LjF4A^V{W342I z`6Q9W7>*_d!AAKc%90%P|B~WhPgv;W6!Qq2J4txB{deU1UW1j>4ts8dLNDYP|hEQJ*m#g7RI?`QFSkP0jO7NWr)i=2q{T>eBHuH(vc!dPhO) z7cJG}kr`>8-0$DFdUNQ)ipMUv6x9pvoL~QvcJld^0sOV5X%F#bOaBF#5LfXM=ib7n G*8dyMBTMf9 literal 0 HcmV?d00001 diff --git a/applications/external/bomberduck/bomberduck.c b/applications/external/bomberduck/bomberduck.c new file mode 100644 index 000000000..7b8b5f14a --- /dev/null +++ b/applications/external/bomberduck/bomberduck.c @@ -0,0 +1,645 @@ +#include +#include + +#include +#include +#include +#include +#include +#include "bomberduck_icons.h" +#include + +int max(int a, int b) { + return (a > b) ? a : b; +} + +int min(int a, int b) { + return (a < b) ? a : b; +} + +#define WorldSizeX 12 +#define WorldSizeY 6 +#define BombRange 1 + + +typedef struct { + FuriMutex* mutex; +} BomberState; + +typedef struct { + int row; + int col; +} Cell; + +typedef struct { + Cell cells[WorldSizeY * WorldSizeX]; + int front; + int rear; +} Queue; + +void enqueue(Queue* q, Cell c) { + q->cells[q->rear] = c; + q->rear++; +} + +Cell dequeue(Queue* q) { + Cell c = q->cells[q->front]; + q->front++; + + return c; +} + +bool is_empty(Queue* q) { + return q->front == q->rear; +} + +typedef struct { + int x; + int y; + int planted; +} Bomb; + +typedef struct { + int x; + int y; + bool side; +} Player; + +typedef struct { + int x; + int y; + int last; + bool side; + int level; +} Enemy; + +typedef struct { + int matrix[WorldSizeY][WorldSizeX]; + Player* player; + bool running; + int level; + + Enemy enemies[10]; + int enemies_count; + + Bomb bombs[100]; + int bombs_count; + + int endx; + int endy; +} World; + +Player player = {0, 0, 1}; +World world = {{{0}}, &player, 1, 0, {}, 0, {}, 0, 0, 0}; +bool vibration = false; + +void init() { + player.x = 1; + player.y = 1; + + world.endx = 4 + rand() % 8; + world.endy = rand() % 6; + for(int i = 0; i < WorldSizeY; i++) { + for(int j = 0; j < WorldSizeX; j++) { + world.matrix[i][j] = rand() % 3; + } + } + world.running = 1; + world.bombs_count =0; + vibration = false; + for(int j = max(0, player.y - BombRange); j < min(WorldSizeY, player.y + BombRange + 1); j++) { + world.matrix[j][player.x] = 0; + } + + for(int j = max(0, player.x - BombRange); j < min(WorldSizeX, player.x + BombRange + 1); j++) { + world.matrix[player.y][j] = 0; + } + + world.enemies_count = 0; + for(int j = 0; j < rand() % 4 + world.level / 5; j++) { + Enemy enemy; + enemy.x = 4 + rand() % 7; + enemy.y = rand() % 6; + enemy.last = 0; + enemy.side = 1; + enemy.level = 0; + + world.enemies[j] = enemy; + world.enemies_count++; + + for(int m = max(0, world.enemies[j].y - BombRange); + m < min(WorldSizeY, world.enemies[j].y + BombRange + 1); + m++) { + world.matrix[m][world.enemies[j].x] = 0; + } + + for(int m = max(0, world.enemies[j].x - BombRange); + m < min(WorldSizeX, world.enemies[j].x + BombRange + 1); + m++) { + world.matrix[world.enemies[j].y][m] = 0; + } + } + world.matrix[world.endy][world.endx] = 1; +} + +const NotificationSequence end = { + &message_vibro_on, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_vibro_off, + NULL, +}; + +static const NotificationSequence bomb2 = { + &message_vibro_on, + &message_delay_25, + &message_vibro_off, + NULL, +}; + +static const NotificationSequence bomb_explore = { + &message_vibro_on, + &message_delay_50, + &message_vibro_off, + NULL, +}; + +static const NotificationSequence vibr1 = { + &message_vibro_on, + &message_delay_10, + &message_vibro_off, + &message_delay_10, + &message_vibro_on, + &message_delay_10, + &message_vibro_off, + &message_delay_10, + + NULL, +}; + + +void intToStr(int num, char* str) { + int i = 0, sign = 0; + + if(num < 0) { + num = -num; + sign = 1; + } + + do { + str[i++] = num % 10 + '0'; + num /= 10; + } while(num > 0); + + if(sign) { + str[i++] = '-'; + } + + str[i] = '\0'; + + // Reverse the string + int j, len = i; + char temp; + for(j = 0; j < len / 2; j++) { + temp = str[j]; + str[j] = str[len - j - 1]; + str[len - j - 1] = temp; + } +} + +bool BFS() { + // Initialize visited array and queue + int visited[WorldSizeY][WorldSizeX] = {0}; + Queue q = {.front = 0, .rear = 0}; + // Mark the starting cell as visited and enqueue it + visited[world.player->y][world.player->x] = 1; + Cell startCell = {.row = world.player->y, .col = world.player->x}; + enqueue(&q, startCell); + // Traverse the field + while(!is_empty(&q)) { + // Dequeue a cell from the queue + Cell currentCell = dequeue(&q); + // Check if the current cell is the destination cell + if(currentCell.row == world.endy && currentCell.col == world.endx) { + return true; + } + // Check the neighboring cells + for(int rowOffset = -1; rowOffset <= 1; rowOffset++) { + for(int colOffset = -1; colOffset <= 1; colOffset++) { + // Skip diagonals and the current cell + if(rowOffset == 0 && colOffset == 0) { + continue; + } + if(rowOffset != 0 && colOffset != 0) { + continue; + } + // Calculate the row and column of the neighboring cell + int neighborRow = currentCell.row + rowOffset; + int neighborCol = currentCell.col + colOffset; + // Skip out-of-bounds cells and already visited cells + if(neighborRow < 0 || neighborRow >= WorldSizeY || neighborCol < 0 || + neighborCol >= WorldSizeX) { + continue; + } + if(visited[neighborRow][neighborCol]) { + continue; + } + // Mark the neighboring cell as visited and enqueue it + if(world.matrix[neighborRow][neighborCol] != 2) { + visited[neighborRow][neighborCol] = 1; + Cell neighborCell = {.row = neighborRow, .col = neighborCol}; + enqueue(&q, neighborCell); + } + } + } + } + return false; +} + +static void draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + const BomberState* bomber_state = ctx; + + furi_mutex_acquire(bomber_state->mutex, FuriWaitForever); + if(!BFS()) { + init(); + } + canvas_clear(canvas); + + canvas_draw_icon(canvas, world.endx * 10 + 4, world.endy * 10 + 2, &I_end); + + if(world.running) { + for(size_t i = 0; i < WorldSizeY; i++) { + for(size_t j = 0; j < WorldSizeX; j++) { + switch(world.matrix[i][j]) { + case 0: + break; + case 1: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_box); + break; + case 2: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_unbreakbox); + break; + case 3: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb0); + break; + case 4: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb1); + break; + case 5: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb2); + break; + case 6: + canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_explore); + world.matrix[i][j] = 0; + break; + } + } + } + + if(world.player->side) { + canvas_draw_icon( + canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerright); + } else { + canvas_draw_icon( + canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerleft); + } + + for(int i = 0; i < world.enemies_count; i++) { + if(world.enemies[i].level > 0) { + canvas_draw_icon( + canvas, world.enemies[i].x * 10 + 4, world.enemies[i].y * 10 + 2, &I_enemy1); + } else { + if(world.enemies[i].side) { + canvas_draw_icon( + canvas, + world.enemies[i].x * 10 + 4, + world.enemies[i].y * 10 + 2, + &I_enemyright); + } else { + canvas_draw_icon( + canvas, + world.enemies[i].x * 10 + 4, + world.enemies[i].y * 10 + 2, + &I_enemyleft); + } + } + } + } else { + canvas_set_font(canvas, FontPrimary); + if(world.player->x == world.endx && world.player->y == world.endy) { + if(world.level == 20) { + canvas_draw_str(canvas, 30, 35, "You win!"); + }else{ + canvas_draw_str(canvas, 30, 35, "Next level!"); + char str[20]; + intToStr(world.level, str); + canvas_draw_str(canvas, 90, 35, str); + } + + } else { + canvas_draw_str(canvas, 30, 35, "You died :("); + } + } + + furi_mutex_release(bomber_state->mutex); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + // Проверяем, что контекст не нулевой + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t bomberduck_app(void* p) { + UNUSED(p); + + // Текущее событие типа InputEvent + InputEvent event; + // Очередь событий на 8 элементов размера InputEvent + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + BomberState* bomber_state = malloc(sizeof(BomberState)); + + bomber_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); // Alloc Mutex + if(!bomber_state->mutex) { + FURI_LOG_E("BomberDuck", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(bomber_state); + return 255; + } + + DOLPHIN_DEED(DolphinDeedPluginGameStart); + // Создаем новый view port + ViewPort* view_port = view_port_alloc(); + // Создаем callback отрисовки, без контекста + view_port_draw_callback_set(view_port, draw_callback, bomber_state); + // Создаем callback нажатий на клавиши, в качестве контекста передаем + // нашу очередь сообщений, чтоб запихивать в неё эти события + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Создаем GUI приложения + Gui* gui = furi_record_open(RECORD_GUI); + // Подключаем view port к GUI в полноэкранном режиме + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message_block(notification, &sequence_display_backlight_enforce_on); + + init(); + + // Бесконечный цикл обработки очереди событий + while(1) { + if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { + furi_mutex_acquire(bomber_state->mutex, FuriWaitForever); + // Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения + + if(event.type == InputTypePress) { + if(event.key == InputKeyOk) { + if(world.running) { + if(world.matrix[world.player->y][world.player->x] == 0 && + world.bombs_count < 2) { + notification_message(notification, &bomb2); + world.matrix[world.player->y][world.player->x] = 3; + Bomb bomb = {world.player->x, world.player->y, furi_get_tick()}; + world.bombs[world.bombs_count] = bomb; + world.bombs_count++; + } + } else { + init(); + } + } + if(world.running) { + if(event.key == InputKeyUp) { + if(world.player->y > 0 && + world.matrix[world.player->y - 1][world.player->x] == 0) + world.player->y--; + } + if(event.key == InputKeyDown) { + if(world.player->y < WorldSizeY - 1 && + world.matrix[world.player->y + 1][world.player->x] == 0) + world.player->y++; + } + if(event.key == InputKeyLeft) { + world.player->side = 0; + if(world.player->x > 0 && + world.matrix[world.player->y][world.player->x - 1] == 0) + world.player->x--; + } + if(event.key == InputKeyRight) { + world.player->side = 1; + if(world.player->x < WorldSizeX - 1 && + world.matrix[world.player->y][world.player->x + 1] == 0) + world.player->x++; + } + } + } else if(event.type == InputTypeLong) { + if(event.key == InputKeyBack) { + break; + } + } + } + if(world.running) { + if(world.player->x == world.endx && world.player->y == world.endy) { + notification_message(notification, &end); + world.running = 0; + world.level += 1; + if(world.level%5==0){ + DOLPHIN_DEED(DolphinDeedPluginGameWin); + } + } + for(int i = 0; i < world.bombs_count; i++) { + if(furi_get_tick() - world.bombs[i].planted > + (unsigned long)max((3000 - world.level * 150), 1000)) { + vibration = false; + world.matrix[world.bombs[i].y][world.bombs[i].x] = 6; + notification_message(notification, &bomb_explore); + + for(int j = max(0, world.bombs[i].y - BombRange); + j < min(WorldSizeY, world.bombs[i].y + BombRange + 1); + j++) { + if(world.matrix[j][world.bombs[i].x] != 2) { + world.matrix[j][world.bombs[i].x] = 6; + if(j == world.player->y && world.bombs[i].x == world.player->x) { + notification_message(notification, &end); + world.running = 0; + } + for(int e = 0; e < world.enemies_count; e++) { + if(j == world.enemies[e].y && + world.bombs[i].x == world.enemies[e].x) { + if(world.enemies[e].level > 0) { + world.enemies[e].level--; + } else { + for(int l = e; l < world.enemies_count - 1; l++) { + world.enemies[l] = world.enemies[l + 1]; + } + world.enemies_count--; + } + } + } + } + } + + for(int j = max(0, world.bombs[i].x - BombRange); + j < min(WorldSizeX, world.bombs[i].x + BombRange + 1); + j++) { + if(world.matrix[world.bombs[i].y][j] != 2) { + world.matrix[world.bombs[i].y][j] = 6; + if(world.bombs[i].y == world.player->y && j == world.player->x) { + notification_message(notification, &end); + world.running = 0; + } + for(int e = 0; e < world.enemies_count; e++) { + if(world.bombs[i].y == world.enemies[e].y && + j == world.enemies[e].x) { + if(world.enemies[e].level > 0) { + world.enemies[e].level--; + } else { + for(int l = e; l < world.enemies_count - 1; l++) { + world.enemies[l] = world.enemies[l + 1]; + } + world.enemies_count--; + } + } + } + } + } + + for(int j = i; j < world.bombs_count - 1; j++) { + world.bombs[j] = world.bombs[j + 1]; + } + world.bombs_count--; + } else if(furi_get_tick() - world.bombs[i].planted > (unsigned long)max((3000 - world.level * 150)*2/3, 666)&&world.matrix[world.bombs[i].y][world.bombs[i].x]!=5) { + world.matrix[world.bombs[i].y][world.bombs[i].x] = 5; + vibration=true; + + } else if(furi_get_tick() - world.bombs[i].planted > (unsigned long)max((3000 - world.level * 150)/3, 333)&& world.matrix[world.bombs[i].y][world.bombs[i].x]!=4) { + world.matrix[world.bombs[i].y][world.bombs[i].x] = 4; + + } + } + for(int e = 0; e < world.enemies_count; e++) { + if(world.player->y == world.enemies[e].y && + world.player->x == world.enemies[e].x) { + notification_message(notification, &end); + world.running = 0; + } + } + + for(int e = 0; e < world.enemies_count; e++) { + if(world.enemies[e].level > 0) { + if(furi_get_tick() - world.enemies[e].last > + (unsigned long)max((2000 - world.level * 100), 1000)) { + world.enemies[e].last = furi_get_tick(); + int move = rand() % 4; + switch(move) { + case 0: + if(world.enemies[e].y > 0 && + world.matrix[world.enemies[e].y - 1][world.enemies[e].x] != 2) + world.enemies[e].y--; + break; + case 1: + if(world.enemies[e].y < WorldSizeY - 1 && + world.matrix[world.enemies[e].y + 1][world.enemies[e].x] != 2) + world.enemies[e].y++; + break; + case 2: + world.enemies[e].side = 0; + if(world.enemies[e].x > 0 && + world.matrix[world.enemies[e].y][world.enemies[e].x - 1] != 2) + world.enemies[e].x--; + break; + case 3: + world.enemies[e].side = 1; + if(world.enemies[e].x < WorldSizeX - 1 && + world.matrix[world.enemies[e].y][world.enemies[e].x + 1] != 2) + world.enemies[e].x++; + default: + break; + } + } + } else { + if(furi_get_tick() - world.enemies[e].last > + (unsigned long)max((1000 - world.level * 50), 500)) { + world.enemies[e].last = furi_get_tick(); + int move = rand() % 4; + switch(move) { + case 0: + if(world.enemies[e].y > 0 && + world.matrix[world.enemies[e].y - 1][world.enemies[e].x] == 0) + world.enemies[e].y--; + break; + case 1: + if(world.enemies[e].y < WorldSizeY - 1 && + world.matrix[world.enemies[e].y + 1][world.enemies[e].x] == 0) + world.enemies[e].y++; + break; + case 2: + world.enemies[e].side = 0; + if(world.enemies[e].x > 0 && + world.matrix[world.enemies[e].y][world.enemies[e].x - 1] == 0) + world.enemies[e].x--; + break; + case 3: + world.enemies[e].side = 1; + if(world.enemies[e].x < WorldSizeX - 1 && + world.matrix[world.enemies[e].y][world.enemies[e].x + 1] == 0) + world.enemies[e].x++; + default: + break; + } + } + } + } + for(int e = 0; e < world.enemies_count; e++) { + for(int h = e + 1; h < world.enemies_count; h++) { + if(world.enemies[e].y == world.enemies[h].y && + world.enemies[e].x == world.enemies[h].x) { + world.enemies[h].level++; + for(int l = e; l < world.enemies_count - 1; l++) { + world.enemies[l] = world.enemies[l + 1]; + } + world.enemies_count--; + } + } + } + if(vibration){ + notification_message(notification, &vibr1); + } + } + + view_port_update(view_port); + furi_mutex_release(bomber_state->mutex); + } + + // Return to normal backlight settings + notification_message_block(notification, &sequence_display_backlight_enforce_auto); + furi_record_close(RECORD_NOTIFICATION); + // Специальная очистка памяти, занимаемой очередью + furi_message_queue_free(event_queue); + + // Чистим созданные объекты, связанные с интерфейсом + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + + furi_mutex_free(bomber_state->mutex); + furi_record_close(RECORD_GUI); + free(bomber_state); + + return 0; +} From 13a65d45a37549a2373c17c3cd590abfdde42160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D0=BD=D1=8C=20=3A=29?= <88856726+leo-need-more-coffee@users.noreply.github.com> Date: Mon, 1 May 2023 20:46:10 +0300 Subject: [PATCH 246/282] Update LICENSE --- applications/external/bomberduck/LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/bomberduck/LICENSE b/applications/external/bomberduck/LICENSE index bce361a99..4624b249c 100644 --- a/applications/external/bomberduck/LICENSE +++ b/applications/external/bomberduck/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) [year] [fullname] +Copyright (c) 2023 лень Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From d01600ea0fc01b4db8f63d7e20f4a5b838865a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D0=BD=D1=8C=20=3A=29?= <88856726+leo-need-more-coffee@users.noreply.github.com> Date: Mon, 1 May 2023 20:46:27 +0300 Subject: [PATCH 247/282] Update application.fam --- applications/external/bomberduck/application.fam | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/external/bomberduck/application.fam b/applications/external/bomberduck/application.fam index afcd5a6ee..2f8246af9 100644 --- a/applications/external/bomberduck/application.fam +++ b/applications/external/bomberduck/application.fam @@ -3,7 +3,6 @@ App( name="Bomberduck", apptype=FlipperAppType.EXTERNAL, entry_point="bomberduck_app", - cdefines=["BOMBERDUCK"], requires=[ "gui", ], From 2eac821f7f485a7b3df0b1f08e3f0610453bcb94 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 1 May 2023 20:53:09 +0300 Subject: [PATCH 248/282] Update readme --- ReadMe.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReadMe.md b/ReadMe.md index 1611bed67..91ca8235d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -176,6 +176,7 @@ Games: - Solitaire [(by teeebor)](https://github.com/teeebor/flipper_games) - BlackJack [(by teeebor)](https://github.com/teeebor/flipper_games) - 2048 game [(by eugene-kirzhanov)](https://github.com/eugene-kirzhanov/flipper-zero-2048-game) +- Bomberduck [(by leo-need-more-coffee)](https://github.com/leo-need-more-coffee/flipperzero-bomberduck) # Instructions From 0ef37df4ae9b128f025a4b99ad63084c1e20519d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 1 May 2023 20:55:09 +0300 Subject: [PATCH 249/282] Update TOTP / run fbt format --- applications/external/bomberduck/bomberduck.c | 35 ++++++++++--------- .../external/hid_app/views/hid_ytshorts.c | 1 - .../external/totp/workers/type_code_common.c | 13 ++++--- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/applications/external/bomberduck/bomberduck.c b/applications/external/bomberduck/bomberduck.c index 7b8b5f14a..2c1c2940a 100644 --- a/applications/external/bomberduck/bomberduck.c +++ b/applications/external/bomberduck/bomberduck.c @@ -21,7 +21,6 @@ int min(int a, int b) { #define WorldSizeY 6 #define BombRange 1 - typedef struct { FuriMutex* mutex; } BomberState; @@ -105,7 +104,7 @@ void init() { } } world.running = 1; - world.bombs_count =0; + world.bombs_count = 0; vibration = false; for(int j = max(0, player.y - BombRange); j < min(WorldSizeY, player.y + BombRange + 1); j++) { world.matrix[j][player.x] = 0; @@ -191,7 +190,6 @@ static const NotificationSequence vibr1 = { NULL, }; - void intToStr(int num, char* str) { int i = 0, sign = 0; @@ -344,7 +342,7 @@ static void draw_callback(Canvas* canvas, void* ctx) { if(world.player->x == world.endx && world.player->y == world.endy) { if(world.level == 20) { canvas_draw_str(canvas, 30, 35, "You win!"); - }else{ + } else { canvas_draw_str(canvas, 30, 35, "Next level!"); char str[20]; intToStr(world.level, str); @@ -427,24 +425,24 @@ int32_t bomberduck_app(void* p) { if(world.running) { if(event.key == InputKeyUp) { if(world.player->y > 0 && - world.matrix[world.player->y - 1][world.player->x] == 0) + world.matrix[world.player->y - 1][world.player->x] == 0) world.player->y--; } if(event.key == InputKeyDown) { if(world.player->y < WorldSizeY - 1 && - world.matrix[world.player->y + 1][world.player->x] == 0) + world.matrix[world.player->y + 1][world.player->x] == 0) world.player->y++; } if(event.key == InputKeyLeft) { world.player->side = 0; if(world.player->x > 0 && - world.matrix[world.player->y][world.player->x - 1] == 0) + world.matrix[world.player->y][world.player->x - 1] == 0) world.player->x--; } if(event.key == InputKeyRight) { world.player->side = 1; if(world.player->x < WorldSizeX - 1 && - world.matrix[world.player->y][world.player->x + 1] == 0) + world.matrix[world.player->y][world.player->x + 1] == 0) world.player->x++; } } @@ -459,7 +457,7 @@ int32_t bomberduck_app(void* p) { notification_message(notification, &end); world.running = 0; world.level += 1; - if(world.level%5==0){ + if(world.level % 5 == 0) { DOLPHIN_DEED(DolphinDeedPluginGameWin); } } @@ -524,13 +522,18 @@ int32_t bomberduck_app(void* p) { world.bombs[j] = world.bombs[j + 1]; } world.bombs_count--; - } else if(furi_get_tick() - world.bombs[i].planted > (unsigned long)max((3000 - world.level * 150)*2/3, 666)&&world.matrix[world.bombs[i].y][world.bombs[i].x]!=5) { - world.matrix[world.bombs[i].y][world.bombs[i].x] = 5; - vibration=true; + } else if( + furi_get_tick() - world.bombs[i].planted > + (unsigned long)max((3000 - world.level * 150) * 2 / 3, 666) && + world.matrix[world.bombs[i].y][world.bombs[i].x] != 5) { + world.matrix[world.bombs[i].y][world.bombs[i].x] = 5; + vibration = true; - } else if(furi_get_tick() - world.bombs[i].planted > (unsigned long)max((3000 - world.level * 150)/3, 333)&& world.matrix[world.bombs[i].y][world.bombs[i].x]!=4) { - world.matrix[world.bombs[i].y][world.bombs[i].x] = 4; - + } else if( + furi_get_tick() - world.bombs[i].planted > + (unsigned long)max((3000 - world.level * 150) / 3, 333) && + world.matrix[world.bombs[i].y][world.bombs[i].x] != 4) { + world.matrix[world.bombs[i].y][world.bombs[i].x] = 4; } } for(int e = 0; e < world.enemies_count; e++) { @@ -618,7 +621,7 @@ int32_t bomberduck_app(void* p) { } } } - if(vibration){ + if(vibration) { notification_message(notification, &vibr1); } } diff --git a/applications/external/hid_app/views/hid_ytshorts.c b/applications/external/hid_app/views/hid_ytshorts.c index 9be2f853c..359091640 100644 --- a/applications/external/hid_app/views/hid_ytshorts.c +++ b/applications/external/hid_app/views/hid_ytshorts.c @@ -109,7 +109,6 @@ static void hid_ytshorts_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); } - static void hid_ytshorts_reset_cursor(HidYTShorts* hid_ytshorts) { // Set cursor to the phone's left up corner // Delays to guarantee one packet per connection interval diff --git a/applications/external/totp/workers/type_code_common.c b/applications/external/totp/workers/type_code_common.c index 696df3b1f..bf5818ab2 100644 --- a/applications/external/totp/workers/type_code_common.c +++ b/applications/external/totp/workers/type_code_common.c @@ -30,7 +30,7 @@ static uint32_t get_keypress_delay(TokenAutomationFeature features) { } static void totp_type_code_worker_press_key( - uint8_t key, + uint16_t key, TOTP_AUTOMATION_KEY_HANDLER key_press_fn, TOTP_AUTOMATION_KEY_HANDLER key_release_fn, TokenAutomationFeature features) { @@ -47,8 +47,6 @@ void totp_type_code_worker_execute_automation( TokenAutomationFeature features) { furi_delay_ms(500); uint8_t i = 0; - totp_type_code_worker_press_key( - HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features); while(i < code_buffer_size && code_buffer[i] != 0) { uint8_t char_index = CONVERT_CHAR_TO_DIGIT(code_buffer[i]); @@ -58,7 +56,11 @@ void totp_type_code_worker_execute_automation( if(char_index > 35) break; - uint8_t hid_kb_key = hid_number_keys[char_index]; + uint16_t hid_kb_key = hid_number_keys[char_index]; + if(char_index > 9) { + hid_kb_key |= KEY_MOD_LEFT_SHIFT; + } + totp_type_code_worker_press_key(hid_kb_key, key_press_fn, key_release_fn, features); furi_delay_ms(get_keystroke_delay(features)); i++; @@ -74,7 +76,4 @@ void totp_type_code_worker_execute_automation( furi_delay_ms(get_keystroke_delay(features)); totp_type_code_worker_press_key(HID_KEYBOARD_TAB, key_press_fn, key_release_fn, features); } - - totp_type_code_worker_press_key( - HID_KEYBOARD_CAPS_LOCK, key_press_fn, key_release_fn, features); } \ No newline at end of file From 1fb9cfcb981eacd353db63f2cc10b6aea53fcc9b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 1 May 2023 20:02:21 +0100 Subject: [PATCH 250/282] BadKB add BT_ID + switch mode with ID / BT_ID --- applications/main/bad_kb/bad_kb_app.c | 20 +++---- applications/main/bad_kb/bad_kb_app.h | 48 --------------- .../main/bad_kb/helpers/ducky_script.c | 56 +++++++++++++++--- .../main/bad_kb/helpers/ducky_script.h | 58 ++++++++++++++++++- .../main/bad_kb/scenes/bad_kb_scene_config.c | 3 +- .../bad_kb/scenes/bad_kb_scene_file_select.c | 2 +- applications/main/bad_kb/views/bad_kb_view.c | 6 -- applications/main/bad_kb/views/bad_kb_view.h | 10 +++- 8 files changed, 123 insertions(+), 80 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 13a9f867c..7fcf02b7c 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -100,19 +100,19 @@ static void bad_kb_save_settings(BadKbApp* app) { void bad_kb_reload_worker(BadKbApp* app) { bad_kb_script_close(app->bad_kb_script); - app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL); + app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); } -void bad_kb_config_switch_mode(BadKbApp* app) { - scene_manager_previous_scene(app->scene_manager); - if(app->is_bt) { - furi_hal_bt_start_advertising(); - } else { - furi_hal_bt_stop_advertising(); - } - scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); +int32_t bad_kb_config_switch_mode(BadKbApp* app) { + if(!app->is_bt) furi_hal_bt_stop_advertising(); + XTREME_SETTINGS()->bad_bt = app->is_bt; + XTREME_SETTINGS_SAVE(); bad_kb_reload_worker(app); + if(app->is_bt) furi_hal_bt_start_advertising(); + scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); + scene_manager_previous_scene(app->scene_manager); + return 0; } void bad_kb_config_switch_remember_mode(BadKbApp* app) { @@ -269,7 +269,7 @@ BadKbApp* bad_kb_app_alloc(char* arg) { "BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_connection_init, app); furi_thread_start(app->conn_init_thread); if(!furi_string_empty(app->file_path)) { - app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL); + app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); scene_manager_next_scene(app->scene_manager, BadKbSceneWork); } else { diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h index 2da40a21f..923ba7780 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_kb/bad_kb_app.h @@ -6,67 +6,21 @@ #include #include -#include #include #include #include -#include -#include -#include -#include -#include "views/bad_kb_view.h" #define BAD_KB_APP_BASE_FOLDER EXT_PATH("badkb") #define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_KB_APP_BASE_FOLDER "/assets/layouts" #define BAD_KB_APP_SCRIPT_EXTENSION ".txt" #define BAD_KB_APP_LAYOUT_EXTENSION ".kl" -#define BAD_KB_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH -#define BAD_KB_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE - -typedef enum { - BadKbAppErrorNoFiles, - BadKbAppErrorCloseRpc, -} BadKbAppError; - typedef enum BadKbCustomEvent { BadKbAppCustomEventTextEditResult, BadKbAppCustomEventByteInputDone, BadKbCustomEventErrorBack } BadKbCustomEvent; -typedef struct { - char bt_name[BAD_KB_ADV_NAME_MAX_LEN + 1]; - uint8_t bt_mac[BAD_KB_MAC_ADDRESS_LEN]; - FuriHalUsbInterface* usb_mode; - GapPairing bt_mode; -} BadKbConfig; - -typedef struct { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - NotificationApp* notifications; - DialogsApp* dialogs; - Widget* widget; - VariableItemList* var_item_list; - TextInput* text_input; - ByteInput* byte_input; - - BadKbAppError error; - FuriString* file_path; - FuriString* keyboard_layout; - BadKb* bad_kb_view; - BadKbScript* bad_kb_script; - - Bt* bt; - bool is_bt; - bool bt_remember; - BadKbConfig config; - BadKbConfig prev_config; - FuriThread* conn_init_thread; -} BadKbApp; - typedef enum { BadKbAppViewError, BadKbAppViewWork, @@ -75,8 +29,6 @@ typedef enum { BadKbAppViewConfigName } BadKbAppView; -void bad_kb_config_switch_mode(BadKbApp* app); - void bad_kb_config_switch_remember_mode(BadKbApp* app); int32_t bad_kb_connection_init(BadKbApp* app); diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 6b212a68d..216afb7b5 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -10,6 +10,7 @@ #include "ducky_script.h" #include "ducky_script_i.h" #include +#include #define TAG "BadKB" #define WORKER_TAG TAG "Worker" @@ -67,6 +68,7 @@ typedef enum { } WorkerEvtFlags; static const char ducky_cmd_id[] = {"ID"}; +static const char ducky_cmd_bt_id[] = {"BT_ID"}; static const uint8_t numpad_keys[10] = { HID_KEYPAD_0, @@ -327,6 +329,28 @@ static bool ducky_set_usb_id(BadKbScript* bad_kb, const char* line) { return false; } +static bool ducky_set_bt_id(BadKbScript* bad_kb, const char* line) { + size_t line_len = strlen(line); + size_t mac_len = BAD_KB_MAC_ADDRESS_LEN * 3; + if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name + + uint8_t mac[BAD_KB_MAC_ADDRESS_LEN]; + for(size_t i = 0; i < BAD_KB_MAC_ADDRESS_LEN; i++) { + char a = line[i * 3]; + char b = line[i * 3 + 1]; + if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') || (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &mac[i])) { + return false; + } + } + + strncpy(bad_kb->app->config.bt_name, line + mac_len, BAD_KB_ADV_NAME_MAX_LEN); + memcpy(bad_kb->app->config.bt_mac, mac, BAD_KB_MAC_ADDRESS_LEN); + + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bad_kb->app->config.bt_name); + bt_set_profile_mac_address(bad_kb->bt, bad_kb->app->config.bt_mac); + return true; +} + static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { uint8_t ret = 0; uint32_t line_len = 0; @@ -354,20 +378,35 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { } } while(ret > 0); - if(!bad_kb->bt) { - const char* line_tmp = furi_string_get_cstr(bad_kb->line); - bool id_set = false; // Looking for ID command at first line - if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { - id_set = ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1]); + const char* line_tmp = furi_string_get_cstr(bad_kb->line); + // Looking for ID or BT_ID command at first line + if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { + if(bad_kb->bt) { + bad_kb->app->is_bt = false; + FuriThread* thread = furi_thread_alloc_ex( + "BadKbSwitchMode", 1024, (FuriThreadCallback)bad_kb_config_switch_mode, bad_kb->app); + furi_thread_start(thread); + return false; } - - if(id_set) { + if(ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1])) { furi_check(furi_hal_usb_set_config(&usb_hid, &bad_kb->hid_cfg)); } else { furi_check(furi_hal_usb_set_config(&usb_hid, NULL)); } + } else if(strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0) { + if(!bad_kb->bt) { + bad_kb->app->is_bt = true; + FuriThread* thread = furi_thread_alloc_ex( + "BadKbSwitchMode", 1024, (FuriThreadCallback)bad_kb_config_switch_mode, bad_kb->app); + furi_thread_start(thread); + return false; + } + if(!bad_kb->app->bt_remember) { + ducky_set_bt_id(bad_kb, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); + } } + storage_file_seek(script_file, 0, true); furi_string_reset(bad_kb->line); @@ -766,10 +805,11 @@ static void bad_kb_script_set_default_keyboard_layout(BadKbScript* bad_kb) { memcpy(bad_kb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_kb->layout))); } -BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt) { +BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt, BadKbApp* app) { furi_assert(file_path); BadKbScript* bad_kb = malloc(sizeof(BadKbScript)); + bad_kb->app = app; bad_kb->file_path = furi_string_alloc(); furi_string_set(bad_kb->file_path, file_path); bad_kb->keyboard_layout = furi_string_alloc(); diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index 6f5e03e0b..56ef3286e 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -8,6 +8,13 @@ extern "C" { #include #include +#include +#include +#include +#include +#include +#include "../views/bad_kb_view.h" + #define FILE_BUFFER_LEN 16 typedef enum { @@ -38,7 +45,7 @@ typedef enum { BadKbStateFileError, } BadKbWorkerState; -typedef struct { +struct BadKbState { BadKbWorkerState state; bool is_bt; uint32_t pin; @@ -47,7 +54,9 @@ typedef struct { uint32_t delay_remain; uint16_t error_line; char error[64]; -} BadKbState; +}; + +typedef struct BadKbApp BadKbApp; typedef struct { FuriHalUsbHidConfig hid_cfg; @@ -74,9 +83,10 @@ typedef struct { size_t string_print_pos; Bt* bt; + BadKbApp* app; } BadKbScript; -BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt); +BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt, BadKbApp* app); void bad_kb_script_close(BadKbScript* bad_kb); @@ -90,6 +100,48 @@ void bad_kb_script_toggle(BadKbScript* bad_kb); BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb); +#define BAD_KB_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH +#define BAD_KB_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE + +typedef enum { + BadKbAppErrorNoFiles, + BadKbAppErrorCloseRpc, +} BadKbAppError; + +typedef struct { + char bt_name[BAD_KB_ADV_NAME_MAX_LEN + 1]; + uint8_t bt_mac[BAD_KB_MAC_ADDRESS_LEN]; + FuriHalUsbInterface* usb_mode; + GapPairing bt_mode; +} BadKbConfig; + +struct BadKbApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + DialogsApp* dialogs; + Widget* widget; + VariableItemList* var_item_list; + TextInput* text_input; + ByteInput* byte_input; + + BadKbAppError error; + FuriString* file_path; + FuriString* keyboard_layout; + BadKb* bad_kb_view; + BadKbScript* bad_kb_script; + + Bt* bt; + bool is_bt; + bool bt_remember; + BadKbConfig config; + BadKbConfig prev_config; + FuriThread* conn_init_thread; +}; + +int32_t bad_kb_config_switch_mode(BadKbApp* app); + #ifdef __cplusplus } #endif diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c index dbf62e96e..3f16a4c48 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c @@ -1,4 +1,5 @@ #include "../bad_kb_app.h" +#include "../helpers/ducky_script.h" #include "furi_hal_power.h" #include "furi_hal_usb.h" #include @@ -15,8 +16,6 @@ enum VarItemListIndex { void bad_kb_scene_config_connection_callback(VariableItem* item) { BadKbApp* bad_kb = variable_item_get_context(item); bad_kb->is_bt = variable_item_get_current_value_index(item); - XTREME_SETTINGS()->bad_bt = bad_kb->is_bt; - XTREME_SETTINGS_SAVE(); variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection); } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c index d14624447..3a8c0748d 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c @@ -29,7 +29,7 @@ void bad_kb_scene_file_select_on_enter(void* context) { if(bad_kb_file_select(bad_kb)) { bad_kb->bad_kb_script = - bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); + bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL, bad_kb); bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneWork); diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c index a05cbb282..95ffe3e4a 100644 --- a/applications/main/bad_kb/views/bad_kb_view.c +++ b/applications/main/bad_kb/views/bad_kb_view.c @@ -8,12 +8,6 @@ #define MAX_NAME_LEN 64 -struct BadKb { - View* view; - BadKbButtonCallback callback; - void* context; -}; - typedef struct { char file_name[MAX_NAME_LEN]; char layout[MAX_NAME_LEN]; diff --git a/applications/main/bad_kb/views/bad_kb_view.h b/applications/main/bad_kb/views/bad_kb_view.h index d8f2559bb..797fafb69 100644 --- a/applications/main/bad_kb/views/bad_kb_view.h +++ b/applications/main/bad_kb/views/bad_kb_view.h @@ -1,11 +1,17 @@ #pragma once #include -#include "../helpers/ducky_script.h" -typedef struct BadKb BadKb; typedef void (*BadKbButtonCallback)(InputKey key, void* context); +typedef struct { + View* view; + BadKbButtonCallback callback; + void* context; +} BadKb; + +typedef struct BadKbState BadKbState; + BadKb* bad_kb_alloc(); void bad_kb_free(BadKb* bad_kb); From c1bf552882db7106c491f31553478de9a4cbc924 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 1 May 2023 20:18:53 +0100 Subject: [PATCH 251/282] badKB dont override user config with BT_ID --- applications/main/bad_kb/helpers/ducky_script.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 216afb7b5..f5199af85 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -343,11 +343,8 @@ static bool ducky_set_bt_id(BadKbScript* bad_kb, const char* line) { } } - strncpy(bad_kb->app->config.bt_name, line + mac_len, BAD_KB_ADV_NAME_MAX_LEN); - memcpy(bad_kb->app->config.bt_mac, mac, BAD_KB_MAC_ADDRESS_LEN); - - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bad_kb->app->config.bt_name); - bt_set_profile_mac_address(bad_kb->bt, bad_kb->app->config.bt_mac); + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, line + mac_len); + bt_set_profile_mac_address(bad_kb->bt, mac); return true; } @@ -380,6 +377,7 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { const char* line_tmp = furi_string_get_cstr(bad_kb->line); // Looking for ID or BT_ID command at first line + bool reset_bt_id = !!bad_kb->bt; if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { if(bad_kb->bt) { bad_kb->app->is_bt = false; @@ -402,9 +400,13 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { return false; } if(!bad_kb->app->bt_remember) { - ducky_set_bt_id(bad_kb, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); + reset_bt_id = !ducky_set_bt_id(bad_kb, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); } } + if(reset_bt_id) { + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bad_kb->app->config.bt_name); + bt_set_profile_mac_address(bad_kb->bt, bad_kb->app->config.bt_mac); + } storage_file_seek(script_file, 0, true); From 044cbd1d7d35cfe6a4658929a36e663efafe76d9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 1 May 2023 20:25:46 +0100 Subject: [PATCH 252/282] Format --- applications/main/bad_kb/bad_kb_app.c | 3 ++- applications/main/bad_kb/helpers/ducky_script.c | 14 ++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 7fcf02b7c..94a74aa92 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -269,7 +269,8 @@ BadKbApp* bad_kb_app_alloc(char* arg) { "BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_connection_init, app); furi_thread_start(app->conn_init_thread); if(!furi_string_empty(app->file_path)) { - app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); + app->bad_kb_script = + bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); scene_manager_next_scene(app->scene_manager, BadKbSceneWork); } else { diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index f5199af85..a9072dba5 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -338,7 +338,8 @@ static bool ducky_set_bt_id(BadKbScript* bad_kb, const char* line) { for(size_t i = 0; i < BAD_KB_MAC_ADDRESS_LEN; i++) { char a = line[i * 3]; char b = line[i * 3 + 1]; - if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') || (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &mac[i])) { + if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') || + (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &mac[i])) { return false; } } @@ -382,7 +383,10 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { if(bad_kb->bt) { bad_kb->app->is_bt = false; FuriThread* thread = furi_thread_alloc_ex( - "BadKbSwitchMode", 1024, (FuriThreadCallback)bad_kb_config_switch_mode, bad_kb->app); + "BadKbSwitchMode", + 1024, + (FuriThreadCallback)bad_kb_config_switch_mode, + bad_kb->app); furi_thread_start(thread); return false; } @@ -395,7 +399,10 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { if(!bad_kb->bt) { bad_kb->app->is_bt = true; FuriThread* thread = furi_thread_alloc_ex( - "BadKbSwitchMode", 1024, (FuriThreadCallback)bad_kb_config_switch_mode, bad_kb->app); + "BadKbSwitchMode", + 1024, + (FuriThreadCallback)bad_kb_config_switch_mode, + bad_kb->app); furi_thread_start(thread); return false; } @@ -408,7 +415,6 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { bt_set_profile_mac_address(bad_kb->bt, bad_kb->app->config.bt_mac); } - storage_file_seek(script_file, 0, true); furi_string_reset(bad_kb->line); From 677d30dd7343ae8832a69500a7aa646c471481b4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 1 May 2023 21:12:48 +0100 Subject: [PATCH 253/282] BadKB properly discard switch mode thread --- applications/main/bad_kb/helpers/ducky_script.c | 13 +++++++++---- applications/main/bad_kb/helpers/ducky_script.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index a9072dba5..71c307cdf 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -377,17 +377,22 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { } while(ret > 0); const char* line_tmp = furi_string_get_cstr(bad_kb->line); + if(bad_kb->app->switch_mode_thread) { + furi_thread_join(bad_kb->app->switch_mode_thread); + furi_thread_free(bad_kb->app->switch_mode_thread); + bad_kb->app->switch_mode_thread = NULL; + } // Looking for ID or BT_ID command at first line bool reset_bt_id = !!bad_kb->bt; if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { if(bad_kb->bt) { bad_kb->app->is_bt = false; - FuriThread* thread = furi_thread_alloc_ex( + bad_kb->app->switch_mode_thread = furi_thread_alloc_ex( "BadKbSwitchMode", 1024, (FuriThreadCallback)bad_kb_config_switch_mode, bad_kb->app); - furi_thread_start(thread); + furi_thread_start(bad_kb->app->switch_mode_thread); return false; } if(ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1])) { @@ -398,12 +403,12 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { } else if(strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0) { if(!bad_kb->bt) { bad_kb->app->is_bt = true; - FuriThread* thread = furi_thread_alloc_ex( + bad_kb->app->switch_mode_thread = furi_thread_alloc_ex( "BadKbSwitchMode", 1024, (FuriThreadCallback)bad_kb_config_switch_mode, bad_kb->app); - furi_thread_start(thread); + furi_thread_start(bad_kb->app->switch_mode_thread); return false; } if(!bad_kb->app->bt_remember) { diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index 56ef3286e..6a0386cf1 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -138,6 +138,7 @@ struct BadKbApp { BadKbConfig config; BadKbConfig prev_config; FuriThread* conn_init_thread; + FuriThread* switch_mode_thread; }; int32_t bad_kb_config_switch_mode(BadKbApp* app); From fe6bf3c7d6fe6c10a3f915f4de8d8ea160b3272f Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 2 May 2023 02:25:52 +0300 Subject: [PATCH 254/282] Revert some changes --- lib/nfc/protocols/mifare_classic.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 204e3a5eb..d2d7467dc 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -892,25 +892,11 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ MfClassicSectorTrailer* sector_trailer = (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD) { - if(mf_classic_is_key_found( - &emulator->data, mf_classic_get_sector_by_block(block), MfClassicKeyA)) { - key = nfc_util_bytes2num(sector_trailer->key_a, 6); - access_key = MfClassicKeyA; - } else { - FURI_LOG_D(TAG, "Key not known"); - command_processed = true; - break; - } + key = nfc_util_bytes2num(sector_trailer->key_a, 6); + access_key = MfClassicKeyA; } else { - if(mf_classic_is_key_found( - &emulator->data, mf_classic_get_sector_by_block(block), MfClassicKeyB)) { - key = nfc_util_bytes2num(sector_trailer->key_b, 6); - access_key = MfClassicKeyB; - } else { - FURI_LOG_D(TAG, "Key not known"); - command_processed = true; - break; - } + key = nfc_util_bytes2num(sector_trailer->key_b, 6); + access_key = MfClassicKeyB; } uint32_t nonce = prng_successor(DWT->CYCCNT, 32) ^ 0xAA; From e24cb944ff795387599abfa6d4895ffb1f522957 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 2 May 2023 02:38:42 +0300 Subject: [PATCH 255/282] Temp fix desktop lock bug and update changelog --- CHANGELOG.md | 29 +++++-------------- .../desktop/views/desktop_view_locked.c | 3 +- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dfbf46cf..70948b2f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,12 @@ ### New changes -* Power + BLE: DeepSleep + required ble stack upgrade added back, all known issues was fixed in OFW, no issues was found during our tests -* Desktop: Allow locking without pin using Up menu on desktop (Short click on `Lock` = Without PIN / Long = With PIN) -* RFID: Add confirmation message before running `Clear T5577 Password` -* RFID: Add more user friendly RAW emulation via UI [(by Dan Caprita)](https://forum.flipperzero.one/t/electra-intercom/6368/43) -* SubGHz: Fixed `Frequency Analyzer` issues, fixed `Read` mode issues -* SubGHz: Fix NFC crash when using external CC1101 radio module -* SubGHz: Fix multiple external CC1101 radio module issues, (int callbacks, SPI handlers init/reinit) -* SubGHz: Using scene manager function in add manually (by @gid9798 | PR #437) -* Plugins: ESP32: WiFi Marauder - add icon for log files in logs browser -* Plugins: Update **ESP32: WiFi Marauder companion** plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) merged [PR by @tcpassos](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/11) +* NFC: Temp fix for Detect reader not collecting nonces +* Desktop: Temp fix for old backlight bug when locking by holding up arrow +* BLE Info: Show version instead of branch +* Plugins: Add new game - Bomberduck (by @leo-need-more-coffee | PR #450) +* Plugins: Fix `SWD Probe` plugin GPIO pins state reset on exit +* Plugins: Bluetooth Remote - new UI (by @krolchonok | PR #447) * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) -* Plugins: Fix RFID Fuzzer and iButton Fuzzer crashes -* Plugins: iButton Fuzzer default keys update (by @team-orangeBlue) -* Infrared: Updated infrared assets (by @amec0e | PR #441) -* Docs: Update **How To Install** images (by @krolchonok | PR #436) -* OFW PR 2620: NFC: Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys (by Astrrra) -* OFW PR 2616: Picopass: remove spaces in CSN (by bettse) -* OFW PR 2604: WS: add protocol "Wendox W6726" (by Skorpionm) -* OFW PR 2607: BadUSB: command parser fix (by nminaylov) -* OFW: Keep HSI16 working in stop mode. -* OFW: FuriHal: use proper divider for core2 when transition to sleep, remove extra stop mode transition checks, cleanup code. Furi: proper assert and check messages. -* OFW: Don't reboot on crash in debug builds -* OFW: cubewb: downgraded to v1.15.0 +* Docs: Update HowToInstall (by @krolchonok | PR #443) #### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) diff --git a/applications/services/desktop/views/desktop_view_locked.c b/applications/services/desktop/views/desktop_view_locked.c index 0bf757036..8a0ddb3af 100644 --- a/applications/services/desktop/views/desktop_view_locked.c +++ b/applications/services/desktop/views/desktop_view_locked.c @@ -242,5 +242,6 @@ bool desktop_view_locked_is_locked_hint_visible(DesktopViewLocked* locked_view) DesktopViewLockedModel* model = view_get_model(locked_view->view); const DesktopViewLockedState view_state = model->view_state; view_commit_model(locked_view->view, false); - return view_state == DesktopViewLockedStateLockedHintShown; + return view_state == DesktopViewLockedStateLockedHintShown || + view_state == DesktopViewLockedStateLocked; } From b801f70f3a39139d443fba05655e3ad5e38f6769 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 2 May 2023 03:01:50 +0300 Subject: [PATCH 256/282] OFW PR 2627: Add HID mouse auto-clicker by rwl4 --- applications/external/hid_app/hid.c | 21 ++ applications/external/hid_app/hid.h | 2 + applications/external/hid_app/views.h | 1 + .../hid_app/views/hid_mouse_clicker.c | 214 ++++++++++++++++++ .../hid_app/views/hid_mouse_clicker.h | 14 ++ 5 files changed, 252 insertions(+) create mode 100644 applications/external/hid_app/views/hid_mouse_clicker.c create mode 100644 applications/external/hid_app/views/hid_mouse_clicker.h diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index f29a3b22a..f6b853f9c 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -13,6 +13,7 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexTikTok, HidSubmenuIndexYTShorts, HidSubmenuIndexMouse, + HidSubmenuIndexMouseClicker, HidSubmenuIndexMouseJiggler, }; @@ -40,6 +41,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexYTShorts) { app->view_id = BtHidViewYTShorts; view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewYTShorts); + } else if(index == HidSubmenuIndexMouseClicker) { + app->view_id = HidViewMouseClicker; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker); } else if(index == HidSubmenuIndexMouseJiggler) { app->view_id = HidViewMouseJiggler; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); @@ -62,6 +66,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con hid_keyboard_set_connected_status(hid->hid_keyboard, connected); hid_media_set_connected_status(hid->hid_media, connected); hid_mouse_set_connected_status(hid->hid_mouse, connected); + hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected); hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); hid_tiktok_set_connected_status(hid->hid_tiktok, connected); hid_ytshorts_set_connected_status(hid->hid_ytshorts, connected); @@ -136,6 +141,12 @@ Hid* hid_alloc(HidTransport transport) { hid_submenu_callback, app); } + submenu_add_item( + app->device_type_submenu, + "Mouse Clicker", + HidSubmenuIndexMouseClicker, + hid_submenu_callback, + app); submenu_add_item( app->device_type_submenu, "Mouse Jiggler", @@ -209,6 +220,14 @@ Hid* hid_app_alloc_view(void* context) { view_dispatcher_add_view( app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); + // Mouse clicker view + app->hid_mouse_clicker = hid_mouse_clicker_alloc(app); + view_set_previous_callback( + hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, + HidViewMouseClicker, + hid_mouse_clicker_get_view(app->hid_mouse_clicker)); // Mouse jiggler view app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); view_set_previous_callback( @@ -244,6 +263,8 @@ void hid_free(Hid* app) { hid_media_free(app->hid_media); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); hid_mouse_free(app->hid_mouse); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker); + hid_mouse_clicker_free(app->hid_mouse_clicker); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); hid_mouse_jiggler_free(app->hid_mouse_jiggler); view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); diff --git a/applications/external/hid_app/hid.h b/applications/external/hid_app/hid.h index be9176a28..6fe7d381c 100644 --- a/applications/external/hid_app/hid.h +++ b/applications/external/hid_app/hid.h @@ -24,6 +24,7 @@ #include "views/hid_mouse_jiggler.h" #include "views/hid_tiktok.h" #include "views/hid_ytshorts.h" +#include "views/hid_mouse_clicker.h" #define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" @@ -46,6 +47,7 @@ struct Hid { HidKeyboard* hid_keyboard; HidMedia* hid_media; HidMouse* hid_mouse; + HidMouseClicker* hid_mouse_clicker; HidMouseJiggler* hid_mouse_jiggler; HidTikTok* hid_tiktok; HidYTShorts* hid_ytshorts; diff --git a/applications/external/hid_app/views.h b/applications/external/hid_app/views.h index 81e8d6dbe..297fc7bc2 100644 --- a/applications/external/hid_app/views.h +++ b/applications/external/hid_app/views.h @@ -5,6 +5,7 @@ typedef enum { HidViewKeyboard, HidViewMedia, HidViewMouse, + HidViewMouseClicker, HidViewMouseJiggler, BtHidViewTikTok, BtHidViewYTShorts, diff --git a/applications/external/hid_app/views/hid_mouse_clicker.c b/applications/external/hid_app/views/hid_mouse_clicker.c new file mode 100644 index 000000000..efaca190a --- /dev/null +++ b/applications/external/hid_app/views/hid_mouse_clicker.c @@ -0,0 +1,214 @@ +#include "hid_mouse_clicker.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMouseClicker" +#define DEFAULT_CLICK_RATE 1 +#define MAXIMUM_CLICK_RATE 60 + +struct HidMouseClicker { + View* view; + Hid* hid; + FuriTimer* timer; +}; + +typedef struct { + bool connected; + bool running; + int rate; + HidTransport transport; +} HidMouseClickerModel; + +static void hid_mouse_clicker_start_or_restart_timer(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + + if(furi_timer_is_running(hid_mouse_clicker->timer)) { + furi_timer_stop(hid_mouse_clicker->timer); + } + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + furi_timer_start( + hid_mouse_clicker->timer, furi_kernel_get_tick_frequency() / model->rate); + }, + true); +} + +static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMouseClickerModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Clicker"); + + // Ok + canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); + if(model->running) { + canvas_set_font(canvas, FontPrimary); + + FuriString* rate_label = furi_string_alloc(); + furi_string_printf(rate_label, "%d clicks/s\n\nUp / Down", model->rate); + elements_multiline_text(canvas, AlignLeft, 35, furi_string_get_cstr(rate_label)); + canvas_set_font(canvas, FontSecondary); + furi_string_free(rate_label); + + elements_slightly_rounded_box(canvas, 66, 27, 60, 13); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto start\nclicking"); + canvas_set_font(canvas, FontSecondary); + } + canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); + if(model->running) { + elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop"); + } else { + elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start"); + } + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit"); +} + +static void hid_mouse_clicker_timer_callback(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + if(model->running) { + hid_hal_mouse_press(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); + } + }, + false); +} + +static void hid_mouse_clicker_enter_callback(void* context) { + hid_mouse_clicker_start_or_restart_timer(context); +} + +static void hid_mouse_clicker_exit_callback(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + furi_timer_stop(hid_mouse_clicker->timer); +} + +static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + + bool consumed = false; + bool rate_changed = false; + + if(event->type != InputTypeRelease) { + return false; + } + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + switch(event->key) { + case InputKeyOk: + model->running = !model->running; + consumed = true; + break; + case InputKeyUp: + if(model->rate < MAXIMUM_CLICK_RATE) { + model->rate++; + } + rate_changed = true; + consumed = true; + break; + case InputKeyDown: + if(model->rate > 1) { + model->rate--; + } + rate_changed = true; + consumed = true; + break; + default: + consumed = true; + break; + } + }, + true); + + if(rate_changed) { + hid_mouse_clicker_start_or_restart_timer(context); + } + + return consumed; +} + +HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) { + HidMouseClicker* hid_mouse_clicker = malloc(sizeof(HidMouseClicker)); + + hid_mouse_clicker->view = view_alloc(); + view_set_context(hid_mouse_clicker->view, hid_mouse_clicker); + view_allocate_model( + hid_mouse_clicker->view, ViewModelTypeLocking, sizeof(HidMouseClickerModel)); + view_set_draw_callback(hid_mouse_clicker->view, hid_mouse_clicker_draw_callback); + view_set_input_callback(hid_mouse_clicker->view, hid_mouse_clicker_input_callback); + view_set_enter_callback(hid_mouse_clicker->view, hid_mouse_clicker_enter_callback); + view_set_exit_callback(hid_mouse_clicker->view, hid_mouse_clicker_exit_callback); + + hid_mouse_clicker->hid = hid; + + hid_mouse_clicker->timer = furi_timer_alloc( + hid_mouse_clicker_timer_callback, FuriTimerTypePeriodic, hid_mouse_clicker); + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + model->transport = hid->transport; + model->rate = DEFAULT_CLICK_RATE; + }, + true); + + return hid_mouse_clicker; +} + +void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker) { + furi_assert(hid_mouse_clicker); + + furi_timer_stop(hid_mouse_clicker->timer); + furi_timer_free(hid_mouse_clicker->timer); + + view_free(hid_mouse_clicker->view); + + free(hid_mouse_clicker); +} + +View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker) { + furi_assert(hid_mouse_clicker); + return hid_mouse_clicker->view; +} + +void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected) { + furi_assert(hid_mouse_clicker); + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { model->connected = connected; }, + true); +} diff --git a/applications/external/hid_app/views/hid_mouse_clicker.h b/applications/external/hid_app/views/hid_mouse_clicker.h new file mode 100644 index 000000000..d72847baa --- /dev/null +++ b/applications/external/hid_app/views/hid_mouse_clicker.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidMouseClicker HidMouseClicker; + +HidMouseClicker* hid_mouse_clicker_alloc(Hid* bt_hid); + +void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker); + +View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker); + +void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected); From 28529905662005884e6cbf29416684b41d9d98ac Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 2 May 2023 03:36:28 +0300 Subject: [PATCH 257/282] Add sharp and vizio to ir database --- assets/resources/infrared/assets/tv.ir | 40 ++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index c55ad267d..bd246f3d6 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 07th Mar, 2023 -# Last Checked 25th Apr, 2023 +# Last Updated 2, May, 2023 +# Last Checked 2, May, 2023 # name: POWER type: parsed @@ -1899,3 +1899,39 @@ type: parsed protocol: SIRC20 address: 10 01 00 00 command: 33 00 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 195 1833 300 766 280 760 275 790 276 737 309 731 304 1801 301 1804 309 731 304 1801 270 795 282 758 277 762 273 1832 270 769 246 45851 326 1780 302 739 307 785 282 732 303 736 310 1795 307 732 303 763 303 1775 307 733 334 1798 273 1832 270 1810 251 814 273 1780 281 43762 302 1804 309 758 277 737 330 762 284 730 305 734 301 1803 310 1796 306 733 302 1829 273 767 279 734 301 791 275 1804 278 762 253 45870 307 1798 304 763 272 767 279 787 279 760 275 1829 284 730 305 734 301 1804 309 757 278 1827 275 1804 278 1828 274 765 270 1835 278 43740 303 1776 306 787 279 760 275 765 281 759 307 758 277 1775 307 1799 303 736 299 1832 281 759 276 763 304 736 299 1832 281 733 302 45820 306 1800 302 764 282 758 277 788 278 762 284 1821 281 732 303 736 310 1796 307 733 302 1829 273 1806 276 1830 272 767 268 1837 245 43772 302 1778 304 789 277 762 284 756 279 786 249 765 301 1777 336 1770 301 764 282 1824 278 761 274 765 301 738 308 1824 278 761 274 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 254 1721 360 681 354 738 308 706 329 711 355 1774 307 1772 361 1744 327 687 359 1772 299 742 335 705 330 736 279 1825 298 742 283 44773 384 1721 360 707 308 707 359 733 302 711 335 705 361 704 331 708 338 1766 336 704 331 1773 329 1776 306 1773 360 681 323 1782 331 44726 411 1722 328 686 360 733 302 711 335 705 361 1742 329 1803 330 1749 332 708 327 1777 335 705 330 710 325 741 274 1830 303 737 278 44778 359 1747 355 712 303 711 355 711 335 705 330 709 337 703 363 703 332 1770 332 709 337 1767 335 1771 300 1752 360 733 302 1776 326 44731 355 1751 330 711 355 737 309 705 330 710 336 1793 309 1771 331 1774 307 706 360 1771 300 740 326 714 332 735 280 1798 325 741 274 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9219 4484 662 469 661 469 661 1627 660 471 658 474 656 499 631 499 631 499 631 1657 630 1657 631 500 630 1657 631 1657 631 1657 630 1657 631 1657 631 500 630 500 630 500 630 1657 630 500 631 500 630 500 631 500 630 1657 630 1658 630 1657 631 500 630 1657 631 1658 630 1658 630 1658 630 40107 9106 2202 631 +# +name: VOL+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9218 4484 636 495 660 469 661 1627 660 471 658 472 658 474 656 475 655 474 656 1632 655 1632 656 474 657 1632 656 1631 657 1632 656 1631 656 1632 656 474 656 1632 655 475 656 474 657 474 656 474 656 474 656 474 656 1632 655 474 656 1632 656 1632 656 1632 656 1632 656 1632 656 1632 656 40103 9107 2177 655 +# +name: VOL- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9245 4429 689 467 662 468 661 1626 660 471 658 473 657 474 656 474 656 474 656 1631 657 1631 657 474 656 1631 656 1632 656 1631 657 1631 657 1631 656 1632 656 1631 657 474 656 474 656 474 657 474 656 474 656 474 657 474 656 474 656 1631 656 1632 656 1632 656 1632 656 1632 656 1631 656 40082 9109 2175 656 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9219 4485 636 495 660 469 661 1626 661 471 658 473 657 474 656 499 631 500 630 1657 630 1657 631 500 630 1657 630 1657 631 1657 631 1657 630 1657 631 1657 631 500 630 500 630 1657 631 500 630 500 630 500 630 500 631 500 630 1657 631 1657 631 500 630 1657 631 1658 630 1657 631 1658 630 39868 9106 2178 655 From 77d63731c93c2d2f7ca34661940c77f0d6e38b3c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 01:38:19 +0100 Subject: [PATCH 258/282] Loader log arguments passed to app --- applications/services/loader/loader.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 7e40ae54e..808c8e18e 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -43,10 +43,11 @@ static bool furi_assert(loader_instance->application_arguments == NULL); if(arguments && strlen(arguments) > 0) { loader_instance->application_arguments = strdup(arguments); + FURI_LOG_I(TAG, "Starting: %s, args: %s", loader_instance->application->name, arguments); + } else { + FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name); } - FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name); - FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); if(mode > FuriHalRtcHeapTrackModeNone) { furi_thread_enable_heap_trace(loader_instance->application_thread); From 311cbf709c4cdfd7f8ca9a558d7f3ec92f777181 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 2 May 2023 03:39:17 +0300 Subject: [PATCH 259/282] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70948b2f5..3a861b0a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,14 @@ ### New changes * NFC: Temp fix for Detect reader not collecting nonces * Desktop: Temp fix for old backlight bug when locking by holding up arrow +* IR: Add Sharp and Vizio to Universal TV remote * BLE Info: Show version instead of branch * Plugins: Add new game - Bomberduck (by @leo-need-more-coffee | PR #450) * Plugins: Fix `SWD Probe` plugin GPIO pins state reset on exit * Plugins: Bluetooth Remote - new UI (by @krolchonok | PR #447) * Plugins: Update **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) * Docs: Update HowToInstall (by @krolchonok | PR #443) +* OFW PR 2627: Add HID mouse auto-clicker (by @rwl4) #### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) From 2e162c11311a7e6937e26901fe692fde56a6479c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 2 May 2023 04:20:22 +0300 Subject: [PATCH 260/282] Replace QR code with good old link --- assets/slideshow/update_default/frame_01.png | Bin 933 -> 3213 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/slideshow/update_default/frame_01.png b/assets/slideshow/update_default/frame_01.png index 8a1b6585738886eee91d24c415ea17210706e004..ea37077ccd150cf1ac6149567f0cdb3a9ac6eb4b 100644 GIT binary patch literal 3213 zcmcIn32+nV6_$-LhJp#9q@0aLP!7|T{=E-uV_6chTMHW_8oOYcJ^qzdOIo3oPaMa% zKn*l7NiYfE1PUYsN((p{I^hbYfoTl}Cm}YZ7@8p&a|B4HE;mho*^=Wn4)J8Bnpy4c zzwiIv_ul*7(`rS|?5Bne89T&aFbvDgu;-$4H9FD~V$pZO+n1-J)8IfxzHTs>Cx(xh zve##pptU%UD=(Di%o1eHXBHJrg61+`09hLh$ur9WqFe++MhUt-{uI;A1MixQ9wo)} zH0=b=fDIOUGAe>_ZpCbuTu~(RifLx5F}X}Y06rKJjb%QsUl+8g^*RbLJRrS`Zo;Gr(j32qVjzafYHe?x#iq5WoUh3l1=X5C9|4q%ra_A#XuN z6>{yfBEHaFim5OZ3J4ZUX=$mslr(EWw*}{U-U0{(?V`LJ6~!i2)042H}vRhEC_BO?|!|Ydz#BL0%$! z`vlfq9Ezk>n$V^AX@E%uzLM^1}h1$2o z(et<`nt)&nLNTNTU7F^N7Ahx-Vk8JNFiw5i<5#p&eOj1NWCOH|A(&!9RU0pM0HE8{iFBZdA+eV+sxJ;)I3rL%AejF$))bFg-XBw^Q^@q|A<-|xOnZt6 zkurNciolSJDzcJ@K^1x76pFw^iBm9{APHU~c!J=hE?#KAU6YH$=?m}gmaU@62%{%M z0!5GtK?5FRNu0naoKi8Km5@4dk!4k!5mkwaqGbm?sB=VbZ`3d=1tE$wqQ$Wy24tj3 z2q_lh30%c!o}pEdBuR$kO-4l)R4wQekxo25(G4vDzuRQ|nvG5KYC)%_pq!Jv*E4Ol zoS>$9yy$_R`&7Cy(_v$9o?$S;jE7~9XrOR2JUXgzd8Co%!bOoQugBP>PC=BzO_5@f z!)kzvDYES8BhCE_s2jGl5F*e=sHEFW*VIs{7=%_gD#>@M(DH5Mb+P2p)OnTWc^WVn zOA$DxDwKk8G7m6bCKwi1SU^EF8v6f*dL*rKq3Cx*G+`~K?;lM6XDkJi|(H4ruN;KBqIVG@;t_n6lzC6k>OsKFXaS3q(X3$N$9uo#IZWr~5Qwb8VI3IgOHr(!D35(-oZ0?4qxSdz-p1j+Ci$lfC?QRjj-GmPhNP%d*!`IVy735_&%f zU-rD1pqrjo8|X*f7DO*DsfS{%2EzbLrrqi)tG~XX@x{Lw6Vl|37w&yE^W*v3$s3L5 zh7Pm?d{_R39UCP_jJ07}$HBO&^9M)Wxw$l^e((TpWPD)q(0^o&KC&KElcK?W)Nrcz zgOgYPSpEFk4{nXQwW#RySE(anPHoG7;J9>&JZ`mDlP`T%9oyM?Zbd>str?&8@X+O~ z><1e@{ncbs6wRDFuzCKE9B~|J9p$Rs7ywJ>jL4~9^aLAHym2Qtj?0#7WA;6}CZSLw z)6V6e+g-PC_wdA`(fOmQR|zwf#iq1d*Xs2VuN-|i_g+&?$F*IDYOQT&*DhbbPA!?-s>8_ ze%|t=>fm?IS5sT#E0|K^@| z_wdDI5{JW*=e+GXol`2Sell)o#qXPMWM4`keAj3%xN&jt*|>A7tA9CsLKP@JGT^T( zy<0p}bA^MmNbPpsCyryUmDlZmzm$uee!tasw>9rW{1@>{9_(9_+IGS_`^eR_i_KR$ z+HH$%)a*l^yA_9CJMDIk{@YtW|LFYvEcwcrqboPGzP|TV#iUmg+n2N)9*oavj&a<0 zSas3==hGc8=~c-;zJ0%7oqPNC*w}k}cdbfTR=r^2o_}uHP25h(BbR;p#D-_bwEf`f zbpEnu?)0@+(uxvVD=T*_ozWDN^WwbC1q11~R?^p7mi+eRY&tu6TaJI>in#Koro>t7 zu4Ut2D6F~u_tot;t6Se|U+kN=HUF`Ooj)5gbTaPoM?gwB>7 zUpnv9FI2zWI&Ac@=8Bj9?b1#6>Qj^5pZsp}Z?f74{p;|njZ<87L!xtU;+;TpvtjGF yQ4<#X?(KN^g}K2!*L?l~e?I=ejhIHR%5a!=JyZ0-)}7%$44IDE_CKXRQ}Z9Je{J0W delta 922 zcmV;L17-Y;8Kno18Gi-<007d~e}4b~00DDSM?wIu&K&6g00Uo1L_t(|ob8iBe`WTIE%GqFHinSeN1D{8gnErJ{z^)%wO&bBzMd8!d6raw;G_ZsClw$#sQ|%A z1qexbc^7;ek2sM5Y`9a z;FQavbV!(YUGfFAo`*100IxlkTglq0-|2fkr5EOTXMa6s32OnYj;MA#RRXILuTp+f zKfr9Egd5MzYwasQ~ffSQ&bHhIHumsl_D^fSAGFdoHP>m1ouJh(P86 zf@A6j(0cFMW(#Q?nwEW2zThd+0tBawo)P4|w`${it;I-Fkw~;yjb%gc-Jb1fxaPa}? z_}OY77Y#^qadIR)p?!c^W>)|1JCHa`nLuk_0Dm2au2i%ZVDV8b-oD3$-)Wsj|6^zi zpt+D9EB2|ai3@eFYwY;fCg!^~C-I1FiWE9|e!Jl?CXWQzQJ|hUdvqxJi zm^uDQ7oS7p*MzeG9v7`vzuVbMDZhWc Date: Tue, 2 May 2023 03:39:50 +0100 Subject: [PATCH 261/282] Add favorite timeout setting --- .../main/archive/helpers/favorite_timeout.c | 34 +++++++++++++++++++ .../main/archive/helpers/favorite_timeout.h | 16 +++++++++ .../archive/scenes/archive_scene_browser.c | 14 +++++--- applications/main/bad_kb/bad_kb_app.c | 6 ++-- applications/main/fap_loader/fap_loader_app.c | 4 ++- applications/main/ibutton/ibutton.c | 10 ++++-- applications/main/infrared/infrared.c | 4 ++- applications/main/lfrfid/lfrfid.c | 11 ++++-- applications/main/nfc/nfc.c | 13 ++++--- applications/main/subghz/subghz.c | 4 ++- .../xtreme_app_scene_interface_common.c | 17 ++++++++++ applications/system/updater/application.fam | 2 ++ applications/system/updater/updater.c | 8 ++--- lib/xtreme/settings.c | 4 +++ lib/xtreme/xtreme.h | 1 + 15 files changed, 126 insertions(+), 22 deletions(-) create mode 100644 applications/main/archive/helpers/favorite_timeout.c create mode 100644 applications/main/archive/helpers/favorite_timeout.h diff --git a/applications/main/archive/helpers/favorite_timeout.c b/applications/main/archive/helpers/favorite_timeout.c new file mode 100644 index 000000000..703f6c796 --- /dev/null +++ b/applications/main/archive/helpers/favorite_timeout.c @@ -0,0 +1,34 @@ +#include "favorite_timeout.h" +#include + +bool process_favorite_launch(char** args) { + if(*args && strlen(*args) > 4 && strncmp(*args, "fav/", 4) == 0) { + *args += 3; + return true; + } + return false; +} + +void favorite_timeout_callback(void* _ctx) { + FavoriteTImeoutCtx* ctx = _ctx; + while(scene_manager_handle_back_event(ctx->scene_manager)); + view_dispatcher_stop(ctx->view_dispatcher); +} + +void favorite_timeout_run(ViewDispatcher* view_dispatcher, SceneManager* scene_manager) { + int32_t timeout = XTREME_SETTINGS()->favorite_timeout; + if(timeout == 0) { + view_dispatcher_run(view_dispatcher); + return; + } + + FavoriteTImeoutCtx ctx = { + .view_dispatcher = view_dispatcher, + .scene_manager = scene_manager + }; + FuriTimer* timer = furi_timer_alloc(favorite_timeout_callback, FuriTimerTypeOnce, &ctx); + furi_timer_start(timer, timeout * furi_kernel_get_tick_frequency()); + view_dispatcher_run(view_dispatcher); + furi_timer_stop(timer); + furi_timer_free(timer); +} diff --git a/applications/main/archive/helpers/favorite_timeout.h b/applications/main/archive/helpers/favorite_timeout.h new file mode 100644 index 000000000..aded05708 --- /dev/null +++ b/applications/main/archive/helpers/favorite_timeout.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +bool process_favorite_launch(char** p); + +typedef struct { + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; +} FavoriteTImeoutCtx; + +void favorite_timeout_callback(void* _ctx); + +void favorite_timeout_run(ViewDispatcher* view_dispatcher, SceneManager* scene_manager); diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index 3154fbec6..0f4d48db1 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -36,7 +36,7 @@ static void archive_loader_callback(const void* message, void* context) { } } -static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected) { +static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected, bool favorites) { UNUSED(browser); Loader* loader = furi_record_open(RECORD_LOADER); @@ -48,8 +48,14 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec } status = loader_start(loader, flipper_app_name[selected->type], param); } else { - status = loader_start( - loader, flipper_app_name[selected->type], furi_string_get_cstr(selected->path)); + if(favorites) { + const char* str = furi_string_get_cstr(selected->path); + char arg[strlen(str) + 4]; + snprintf(arg, sizeof(arg), "fav%s", str); + status = loader_start(loader, flipper_app_name[selected->type], arg); + } else { + status = loader_start(loader, flipper_app_name[selected->type], furi_string_get_cstr(selected->path)); + } } if(status != LoaderStatusOk) { @@ -113,7 +119,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { archive_show_file_menu(browser, false); archive_enter_dir(browser, selected->path); } else if(archive_is_known_app(selected->type)) { - archive_run_in_app(browser, selected); + archive_run_in_app(browser, selected, favorites); archive_show_file_menu(browser, false); } consumed = true; diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 94a74aa92..e8b29b837 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -204,6 +205,7 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->file_path = furi_string_alloc(); app->keyboard_layout = furi_string_alloc(); + process_favorite_launch(&arg); if(arg && strlen(arg)) { furi_string_set(app->file_path, arg); } @@ -336,8 +338,8 @@ void bad_kb_app_free(BadKbApp* app) { free(app); } -int32_t bad_kb_app(void* p) { - BadKbApp* bad_kb_app = bad_kb_app_alloc((char*)p); +int32_t bad_kb_app(char* p) { + BadKbApp* bad_kb_app = bad_kb_app_alloc(p); view_dispatcher_run(bad_kb_app->view_dispatcher); diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index b313d67ad..87eafaecd 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -12,6 +12,7 @@ #include #include #include +#include #define TAG "FapLoader" @@ -241,8 +242,9 @@ static void fap_loader_free(FapLoader* loader) { free(loader); } -int32_t fap_loader_app(void* p) { +int32_t fap_loader_app(char* p) { FapLoader* loader; + process_favorite_launch(&p); if(p) { loader = fap_loader_alloc((const char*)p); view_dispatcher_switch_to_view(loader->view_dispatcher, 0); diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 79999adb2..3fea2d2f6 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -2,6 +2,7 @@ #include #include +#include #define TAG "iButtonApp" @@ -258,13 +259,14 @@ void ibutton_widget_callback(GuiButtonType result, InputType type, void* context } } -int32_t ibutton_app(void* arg) { +int32_t ibutton_app(char* arg) { iButton* ibutton = ibutton_alloc(); ibutton_make_app_folder(ibutton); bool key_loaded = false; + bool is_favorite = process_favorite_launch(&arg); if((arg != NULL) && (strlen(arg) != 0)) { if(sscanf(arg, "RPC %lX", (uint32_t*)&ibutton->rpc) == 1) { FURI_LOG_D(TAG, "Running in RPC mode"); @@ -295,7 +297,11 @@ int32_t ibutton_app(void* arg) { } } - view_dispatcher_run(ibutton->view_dispatcher); + if(is_favorite) { + favorite_timeout_run(ibutton->view_dispatcher, ibutton->scene_manager); + } else { + view_dispatcher_run(ibutton->view_dispatcher); + } if(ibutton->rpc) { rpc_system_app_set_callback(ibutton->rpc, NULL, NULL); diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index b8195941b..0abd6c709 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -2,6 +2,7 @@ #include #include +#include #define INFRARED_TX_MIN_INTERVAL_MS 50U @@ -435,7 +436,7 @@ void infrared_popup_closed_callback(void* context) { infrared->view_dispatcher, InfraredCustomEventTypePopupClosed); } -int32_t infrared_app(void* p) { +int32_t infrared_app(char* p) { Infrared* infrared = infrared_alloc(); infrared_make_app_folder(infrared); @@ -443,6 +444,7 @@ int32_t infrared_app(void* p) { bool is_remote_loaded = false; bool is_rpc_mode = false; + process_favorite_launch(&p); if(p && strlen(p)) { uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index a2bcdcf52..eacb05614 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -1,5 +1,6 @@ #include "lfrfid_i.h" #include +#include static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -168,12 +169,12 @@ static void lfrfid_free(LfRfid* lfrfid) { free(lfrfid); } -int32_t lfrfid_app(void* p) { +int32_t lfrfid_app(char* args) { LfRfid* app = lfrfid_alloc(); - char* args = p; lfrfid_make_app_folder(app); + bool is_favorite = process_favorite_launch(&args); if(args && strlen(args)) { uint32_t rpc_ctx_ptr = 0; if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) { @@ -199,7 +200,11 @@ int32_t lfrfid_app(void* p) { scene_manager_next_scene(app->scene_manager, LfRfidSceneStart); } - view_dispatcher_run(app->view_dispatcher); + if(is_favorite) { + favorite_timeout_run(app->view_dispatcher, app->scene_manager); + } else { + view_dispatcher_run(app->view_dispatcher); + } lfrfid_free(app); diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index f68b7f2f2..9ff82eaa4 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -1,6 +1,7 @@ #include "nfc_i.h" #include #include +#include bool nfc_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -263,14 +264,14 @@ static bool nfc_is_hal_ready() { } } -int32_t nfc_app(void* p) { +int32_t nfc_app(char* p) { if(!nfc_is_hal_ready()) return 0; Nfc* nfc = nfc_alloc(); - char* args = p; // Check argument and run corresponding scene - if(args && strlen(args)) { + bool is_favorite = process_favorite_launch(&p); + if(p && strlen(p)) { nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc); uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { @@ -311,7 +312,11 @@ int32_t nfc_app(void* p) { scene_manager_next_scene(nfc->scene_manager, NfcSceneStart); } - view_dispatcher_run(nfc->view_dispatcher); + if(is_favorite) { + favorite_timeout_run(nfc->view_dispatcher, nfc->scene_manager); + } else { + view_dispatcher_run(nfc->view_dispatcher); + } nfc_free(nfc); diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 47da7ae40..a092fa6f6 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -4,6 +4,7 @@ #include #include "subghz_i.h" #include +#include #define TAG "SubGhzApp" @@ -423,7 +424,7 @@ void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) { free(subghz); } -int32_t subghz_app(void* p) { +int32_t subghz_app(char* p) { bool alloc_for_tx; if(p && strlen(p)) { alloc_for_tx = true; @@ -455,6 +456,7 @@ int32_t subghz_app(void* p) { furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } // Check argument and run corresponding scene + process_favorite_launch(&p); if(p && strlen(p)) { uint32_t rpc_ctx = 0; diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c index 259d8a260..08b7425f6 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c @@ -37,6 +37,16 @@ static void xtreme_app_scene_interface_common_left_handed_changed(VariableItem* } } +static void xtreme_app_scene_interface_common_favorite_timeout_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint32_t value = variable_item_get_current_value_index(item); + char text[6]; + snprintf(text, sizeof(text), "%lu S", value); + variable_item_set_current_value_text(item, value ? text : "OFF"); + XTREME_SETTINGS()->favorite_timeout = value; + app->save_settings = true; +} + void xtreme_app_scene_interface_common_on_enter(void* context) { XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); @@ -67,6 +77,13 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { variable_item_set_current_value_index(item, value); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + item = variable_item_list_add( + var_item_list, "Favorite Timeout", 61, xtreme_app_scene_interface_common_favorite_timeout_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->favorite_timeout); + char text[4]; + snprintf(text, sizeof(text), "%lu S", xtreme_settings->favorite_timeout); + variable_item_set_current_value_text(item, xtreme_settings->favorite_timeout ? text : "OFF"); + variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_interface_common_var_item_list_callback, app); diff --git a/applications/system/updater/application.fam b/applications/system/updater/application.fam index ae6914eb7..4d62c1d48 100644 --- a/applications/system/updater/application.fam +++ b/applications/system/updater/application.fam @@ -7,6 +7,7 @@ App( "gui", "storage", "rgb_backlight", + "archive", ], conflicts=["desktop"], entry_point="updater_srv", @@ -23,6 +24,7 @@ App( "gui", "storage", "bt", + "archive", ], conflicts=["updater"], provides=["updater_start"], diff --git a/applications/system/updater/updater.c b/applications/system/updater/updater.c index e749f3ce6..e0b723270 100644 --- a/applications/system/updater/updater.c +++ b/applications/system/updater/updater.c @@ -7,6 +7,7 @@ #include #include #include +#include static bool updater_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -34,6 +35,7 @@ static void Updater* updater_alloc(const char* arg) { Updater* updater = malloc(sizeof(Updater)); + process_favorite_launch(&arg); if(arg && strlen(arg)) { updater->startup_arg = furi_string_alloc_set(arg); furi_string_replace(updater->startup_arg, ANY_PATH(""), EXT_PATH("")); @@ -118,10 +120,8 @@ void updater_free(Updater* updater) { free(updater); } -int32_t updater_srv(void* p) { - const char* cfgpath = p; - - Updater* updater = updater_alloc(cfgpath); +int32_t updater_srv(char* p) { + Updater* updater = updater_alloc(p); view_dispatcher_run(updater->view_dispatcher); updater_free(updater); diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index 1cafa6f59..fe6520808 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -24,6 +24,7 @@ XtremeSettings xtreme_settings = { .bar_background = false, // OFF .sort_dirs_first = true, // ON .dark_mode = false, // OFF + .favorite_timeout = 0, // OFF .bad_bt = false, // USB .bad_bt_remember = false, // OFF .butthurt_timer = 43200, // 12 H @@ -75,6 +76,8 @@ void XTREME_SETTINGS_LOAD() { flipper_format_rewind(file); flipper_format_read_bool(file, "dark_mode", &x->dark_mode, 1); flipper_format_rewind(file); + flipper_format_read_uint32(file, "favorite_timeout", &x->favorite_timeout, 1); + flipper_format_rewind(file); flipper_format_read_bool(file, "bad_bt", &x->bad_bt, 1); flipper_format_rewind(file); flipper_format_read_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); @@ -112,6 +115,7 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); flipper_format_write_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); flipper_format_write_bool(file, "dark_mode", &x->dark_mode, 1); + flipper_format_write_uint32(file, "favorite_timeout", &x->favorite_timeout, 1); flipper_format_write_bool(file, "bad_bt", &x->bad_bt, 1); flipper_format_write_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); flipper_format_write_int32(file, "butthurt_timer", &x->butthurt_timer, 1); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 63f457c27..54c208ae7 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -31,6 +31,7 @@ typedef struct { bool bar_background; bool sort_dirs_first; bool dark_mode; + uint32_t favorite_timeout; bool bad_bt; bool bad_bt_remember; int32_t butthurt_timer; From 8a5782ab9eb4cacde003886148bf153145bef448 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 03:44:29 +0100 Subject: [PATCH 262/282] Format --- applications/main/archive/helpers/favorite_timeout.c | 8 +++----- applications/main/archive/scenes/archive_scene_browser.c | 7 ++++--- .../xtreme_app/scenes/xtreme_app_scene_interface_common.c | 6 +++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/applications/main/archive/helpers/favorite_timeout.c b/applications/main/archive/helpers/favorite_timeout.c index 703f6c796..b46057d82 100644 --- a/applications/main/archive/helpers/favorite_timeout.c +++ b/applications/main/archive/helpers/favorite_timeout.c @@ -11,7 +11,8 @@ bool process_favorite_launch(char** args) { void favorite_timeout_callback(void* _ctx) { FavoriteTImeoutCtx* ctx = _ctx; - while(scene_manager_handle_back_event(ctx->scene_manager)); + while(scene_manager_handle_back_event(ctx->scene_manager)) + ; view_dispatcher_stop(ctx->view_dispatcher); } @@ -22,10 +23,7 @@ void favorite_timeout_run(ViewDispatcher* view_dispatcher, SceneManager* scene_m return; } - FavoriteTImeoutCtx ctx = { - .view_dispatcher = view_dispatcher, - .scene_manager = scene_manager - }; + FavoriteTImeoutCtx ctx = {.view_dispatcher = view_dispatcher, .scene_manager = scene_manager}; FuriTimer* timer = furi_timer_alloc(favorite_timeout_callback, FuriTimerTypeOnce, &ctx); furi_timer_start(timer, timeout * furi_kernel_get_tick_frequency()); view_dispatcher_run(view_dispatcher); diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index 0f4d48db1..f0884ab73 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -36,7 +36,8 @@ static void archive_loader_callback(const void* message, void* context) { } } -static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected, bool favorites) { +static void + archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected, bool favorites) { UNUSED(browser); Loader* loader = furi_record_open(RECORD_LOADER); @@ -48,13 +49,13 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec } status = loader_start(loader, flipper_app_name[selected->type], param); } else { + const char* str = furi_string_get_cstr(selected->path); if(favorites) { - const char* str = furi_string_get_cstr(selected->path); char arg[strlen(str) + 4]; snprintf(arg, sizeof(arg), "fav%s", str); status = loader_start(loader, flipper_app_name[selected->type], arg); } else { - status = loader_start(loader, flipper_app_name[selected->type], furi_string_get_cstr(selected->path)); + status = loader_start(loader, flipper_app_name[selected->type], str); } } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c index 08b7425f6..54b0241d9 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c @@ -78,7 +78,11 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { variable_item_set_current_value_text(item, value ? "ON" : "OFF"); item = variable_item_list_add( - var_item_list, "Favorite Timeout", 61, xtreme_app_scene_interface_common_favorite_timeout_changed, app); + var_item_list, + "Favorite Timeout", + 61, + xtreme_app_scene_interface_common_favorite_timeout_changed, + app); variable_item_set_current_value_index(item, xtreme_settings->favorite_timeout); char text[4]; snprintf(text, sizeof(text), "%lu S", xtreme_settings->favorite_timeout); From 602459869571e1aeca25eb9dfc8481eb8d07271b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 03:44:38 +0100 Subject: [PATCH 263/282] Fix build --- applications/system/updater/updater.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/system/updater/updater.c b/applications/system/updater/updater.c index e0b723270..59af8a3b0 100644 --- a/applications/system/updater/updater.c +++ b/applications/system/updater/updater.c @@ -33,7 +33,7 @@ static void updater_main_model_set_state(main_view, message, progress, failed); } -Updater* updater_alloc(const char* arg) { +Updater* updater_alloc(char* arg) { Updater* updater = malloc(sizeof(Updater)); process_favorite_launch(&arg); if(arg && strlen(arg)) { From 706ce56a48b18dac3f924cf8d50a87c4bea9ecb7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 03:48:00 +0100 Subject: [PATCH 264/282] Actually fix build --- applications/system/updater/updater_i.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/system/updater/updater_i.h b/applications/system/updater/updater_i.h index 4e3c704d2..4940eb60e 100644 --- a/applications/system/updater/updater_i.h +++ b/applications/system/updater/updater_i.h @@ -52,7 +52,7 @@ typedef struct { int32_t idle_ticks; } Updater; -Updater* updater_alloc(const char* arg); +Updater* updater_alloc(char* arg); void updater_free(Updater* updater); From 84c1cac63c813a0f41ebf439ad99513d73e72b3e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 05:45:19 +0100 Subject: [PATCH 265/282] Format --- applications/external/bomberduck/application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/bomberduck/application.fam b/applications/external/bomberduck/application.fam index 2f8246af9..d2ee5564b 100644 --- a/applications/external/bomberduck/application.fam +++ b/applications/external/bomberduck/application.fam @@ -8,7 +8,7 @@ App( ], stack_size=1 * 1024, order=90, - fap_icon="bomb.png", + fap_icon="bomb.png", fap_category="Games", fap_icon_assets="assets", ) From c0cf699edeac7411649bce2c5afae95e185ba912 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 12:32:45 +0100 Subject: [PATCH 266/282] Support subghz raw in favorite timeout setting --- .../subghz/scenes/subghz_scene_read_raw.c | 20 ++++++++++++++++ applications/main/subghz/subghz.c | 5 +++- .../main/subghz/views/subghz_read_raw.c | 22 ----------------- .../main/subghz/views/subghz_read_raw.h | 24 +++++++++++++++++-- 4 files changed, 46 insertions(+), 25 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 0c692e000..2638d2089 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -122,6 +122,19 @@ void subghz_scene_read_raw_on_enter(void* context) { //set filter RAW feed subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_RAW); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); + + // Start sending immediately with favorites + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW)) { + with_view_model( + subghz->subghz_read_raw->view, + SubGhzReadRAWModel * model, + { + scene_manager_handle_custom_event( + subghz->scene_manager, SubGhzCustomEventViewReadRAWSendStart); + model->status = SubGhzReadRAWStatusTXRepeat; + }, + true); + } } bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { @@ -278,6 +291,13 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz_sleep(subghz); } subghz_read_raw_stop_send(subghz->subghz_read_raw); + + // Exit / stop with favorites + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW)) { + while(scene_manager_handle_back_event(subghz->scene_manager)) + ; + view_dispatcher_stop(subghz->view_dispatcher); + } consumed = true; break; diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index a092fa6f6..906b8e074 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -5,6 +5,7 @@ #include "subghz_i.h" #include #include +#include #define TAG "SubGhzApp" @@ -456,7 +457,7 @@ int32_t subghz_app(char* p) { furi_hal_subghz_init_radio_type(SubGhzRadioInternal); } // Check argument and run corresponding scene - process_favorite_launch(&p); + bool is_favorite = process_favorite_launch(&p) && XTREME_SETTINGS()->favorite_timeout; if(p && strlen(p)) { uint32_t rpc_ctx = 0; @@ -476,6 +477,8 @@ int32_t subghz_app(char* p) { if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { //Load Raw TX subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, is_favorite); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); } else { //Load transmitter TX diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index fcc077efa..7676c527d 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -11,28 +11,6 @@ #define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100 #define TAG "SubGhzReadRAW" -struct SubGhzReadRAW { - View* view; - SubGhzReadRAWCallback callback; - void* context; -}; - -typedef struct { - FuriString* frequency_str; - FuriString* preset_str; - FuriString* sample_write; - FuriString* file_name; - uint8_t* rssi_history; - uint8_t rssi_current; - bool rssi_history_end; - uint8_t ind_write; - uint8_t ind_sin; - SubGhzReadRAWStatus status; - bool raw_send_only; - float raw_threshold_rssi; - bool not_showing_samples; -} SubGhzReadRAWModel; - void subghz_read_raw_set_callback( SubGhzReadRAW* subghz_read_raw, SubGhzReadRAWCallback callback, diff --git a/applications/main/subghz/views/subghz_read_raw.h b/applications/main/subghz/views/subghz_read_raw.h index 9d63870d5..f9a56ca04 100644 --- a/applications/main/subghz/views/subghz_read_raw.h +++ b/applications/main/subghz/views/subghz_read_raw.h @@ -5,10 +5,14 @@ #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f -typedef struct SubGhzReadRAW SubGhzReadRAW; - typedef void (*SubGhzReadRAWCallback)(SubGhzCustomEvent event, void* context); +typedef struct { + View* view; + SubGhzReadRAWCallback callback; + void* context; +} SubGhzReadRAW; + typedef enum { SubGhzReadRAWStatusStart, SubGhzReadRAWStatusIDLE, @@ -22,6 +26,22 @@ typedef enum { SubGhzReadRAWStatusSaveKey, } SubGhzReadRAWStatus; +typedef struct { + FuriString* frequency_str; + FuriString* preset_str; + FuriString* sample_write; + FuriString* file_name; + uint8_t* rssi_history; + uint8_t rssi_current; + bool rssi_history_end; + uint8_t ind_write; + uint8_t ind_sin; + SubGhzReadRAWStatus status; + bool raw_send_only; + float raw_threshold_rssi; + bool not_showing_samples; +} SubGhzReadRAWModel; + void subghz_read_raw_set_callback( SubGhzReadRAW* subghz_read_raw, SubGhzReadRAWCallback callback, From 45406cf579e7f31bb062b07f32341c1eff9b2b07 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 12:33:08 +0100 Subject: [PATCH 267/282] Remove USB HID Autofire (now in USB / BT Remotes) --- .../external/usb_hid_autofire/CHANGELOG.md | 13 - .../external/usb_hid_autofire/LICENSE | 674 ------------------ .../external/usb_hid_autofire/application.fam | 13 - .../external/usb_hid_autofire/tools.c | 50 -- .../external/usb_hid_autofire/tools.h | 7 - .../usb_hid_autofire/usb_hid_autofire.c | 127 ---- .../usb_hid_autofire/usb_hid_autofire.kra | Bin 50949 -> 0 bytes .../usb_hid_autofire/usb_hid_autofire.png | Bin 641 -> 0 bytes .../usb_hid_autofire/usb_hid_autofire.svg | 75 -- .../external/usb_hid_autofire/version.h | 1 - 10 files changed, 960 deletions(-) delete mode 100644 applications/external/usb_hid_autofire/CHANGELOG.md delete mode 100644 applications/external/usb_hid_autofire/LICENSE delete mode 100644 applications/external/usb_hid_autofire/application.fam delete mode 100644 applications/external/usb_hid_autofire/tools.c delete mode 100644 applications/external/usb_hid_autofire/tools.h delete mode 100644 applications/external/usb_hid_autofire/usb_hid_autofire.c delete mode 100644 applications/external/usb_hid_autofire/usb_hid_autofire.kra delete mode 100644 applications/external/usb_hid_autofire/usb_hid_autofire.png delete mode 100644 applications/external/usb_hid_autofire/usb_hid_autofire.svg delete mode 100644 applications/external/usb_hid_autofire/version.h diff --git a/applications/external/usb_hid_autofire/CHANGELOG.md b/applications/external/usb_hid_autofire/CHANGELOG.md deleted file mode 100644 index d0924edd3..000000000 --- a/applications/external/usb_hid_autofire/CHANGELOG.md +++ /dev/null @@ -1,13 +0,0 @@ -# Changelog - -## 0.4 -- Show active/inactive state in primary font (bold) - -## 0.3 -- Add a delay between key-presses (with left/right buttons) - -## 0.2 -- Update icon - -## 0.1 -- Initial release of the USB HID Autofire application diff --git a/applications/external/usb_hid_autofire/LICENSE b/applications/external/usb_hid_autofire/LICENSE deleted file mode 100644 index f288702d2..000000000 --- a/applications/external/usb_hid_autofire/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/applications/external/usb_hid_autofire/application.fam b/applications/external/usb_hid_autofire/application.fam deleted file mode 100644 index 9e7b9378c..000000000 --- a/applications/external/usb_hid_autofire/application.fam +++ /dev/null @@ -1,13 +0,0 @@ -App( - appid="usb_hid_autofire", - name="USB HID Autofire", - apptype=FlipperAppType.EXTERNAL, - entry_point="usb_hid_autofire_app", - cdefines=["APP_USB_HID_AUTOFIRE"], - requires=[ - "gui", - ], - stack_size=1 * 1024, - fap_icon="usb_hid_autofire.png", - fap_category="Misc", -) diff --git a/applications/external/usb_hid_autofire/tools.c b/applications/external/usb_hid_autofire/tools.c deleted file mode 100644 index 566d00564..000000000 --- a/applications/external/usb_hid_autofire/tools.c +++ /dev/null @@ -1,50 +0,0 @@ -// -// Tools for USB HID Autofire -// - -void strrev(char* arr, int start, int end) { - char temp; - - if(start >= end) return; - - temp = *(arr + start); - *(arr + start) = *(arr + end); - *(arr + end) = temp; - - start++; - end--; - strrev(arr, start, end); -} - -char* itoa(int number, char* arr, int base) { - int i = 0, r, negative = 0; - - if(number == 0) { - arr[i] = '0'; - arr[i + 1] = '\0'; - return arr; - } - - if(number < 0 && base == 10) { - number *= -1; - negative = 1; - } - - while(number != 0) { - r = number % base; - arr[i] = (r > 9) ? (r - 10) + 'a' : r + '0'; - i++; - number /= base; - } - - if(negative) { - arr[i] = '-'; - i++; - } - - strrev(arr, 0, i - 1); - - arr[i] = '\0'; - - return arr; -} diff --git a/applications/external/usb_hid_autofire/tools.h b/applications/external/usb_hid_autofire/tools.h deleted file mode 100644 index 7e5226514..000000000 --- a/applications/external/usb_hid_autofire/tools.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef FLIPPERZERO_FIRMWARE_TOOLS_H -#define FLIPPERZERO_FIRMWARE_TOOLS_H - -void strrev(char* arr, int start, int end); -char* itoa(int number, char* arr, int base); - -#endif //FLIPPERZERO_FIRMWARE_TOOLS_H diff --git a/applications/external/usb_hid_autofire/usb_hid_autofire.c b/applications/external/usb_hid_autofire/usb_hid_autofire.c deleted file mode 100644 index cf8077ae7..000000000 --- a/applications/external/usb_hid_autofire/usb_hid_autofire.c +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include -#include -#include -#include -#include "version.h" -#include "tools.h" - -// Uncomment to be able to make a screenshot -//#define USB_HID_AUTOFIRE_SCREENSHOT - -typedef enum { - EventTypeInput, -} EventType; - -typedef struct { - union { - InputEvent input; - }; - EventType type; -} UsbMouseEvent; - -bool btn_left_autofire = false; -uint32_t autofire_delay = 10; - -static void usb_hid_autofire_render_callback(Canvas* canvas, void* ctx) { - UNUSED(ctx); - char autofire_delay_str[12]; - //std::string pi = "pi is " + std::to_string(3.1415926); - itoa(autofire_delay, autofire_delay_str, 10); - //sprintf(autofire_delay_str, "%lu", autofire_delay); - - canvas_clear(canvas); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 0, 10, "USB HID Autofire"); - canvas_draw_str(canvas, 0, 34, btn_left_autofire ? "" : ""); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 90, 10, "v"); - canvas_draw_str(canvas, 96, 10, VERSION); - canvas_draw_str(canvas, 0, 22, "Press [ok] for auto left clicking"); - canvas_draw_str(canvas, 0, 46, "delay [ms]:"); - canvas_draw_str(canvas, 50, 46, autofire_delay_str); - canvas_draw_str(canvas, 0, 63, "Press [back] to exit"); -} - -static void usb_hid_autofire_input_callback(InputEvent* input_event, void* ctx) { - FuriMessageQueue* event_queue = ctx; - - UsbMouseEvent event; - event.type = EventTypeInput; - event.input = *input_event; - furi_message_queue_put(event_queue, &event, FuriWaitForever); -} - -int32_t usb_hid_autofire_app(void* p) { - UNUSED(p); - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent)); - furi_check(event_queue); - ViewPort* view_port = view_port_alloc(); - - FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); -#ifndef USB_HID_AUTOFIRE_SCREENSHOT - furi_hal_usb_unlock(); - furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); -#endif - - view_port_draw_callback_set(view_port, usb_hid_autofire_render_callback, NULL); - view_port_input_callback_set(view_port, usb_hid_autofire_input_callback, event_queue); - - // Open GUI and register view_port - Gui* gui = furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - UsbMouseEvent event; - while(1) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 50); - - if(event_status == FuriStatusOk) { - if(event.type == EventTypeInput) { - if(event.input.key == InputKeyBack) { - break; - } - - if(event.input.type != InputTypeRelease) { - continue; - } - - switch(event.input.key) { - case InputKeyOk: - btn_left_autofire = !btn_left_autofire; - break; - case InputKeyLeft: - if(autofire_delay > 0) { - autofire_delay -= 10; - } - break; - case InputKeyRight: - autofire_delay += 10; - break; - default: - break; - } - } - } - - if(btn_left_autofire) { - furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); - // TODO: Don't wait, but use the timer directly to just don't send the release event (see furi_hal_cortex_delay_us) - furi_delay_us(autofire_delay * 500); - furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); - furi_delay_us(autofire_delay * 500); - } - - view_port_update(view_port); - } - - furi_hal_usb_set_config(usb_mode_prev, NULL); - - // remove & free all stuff created by app - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - furi_message_queue_free(event_queue); - - return 0; -} diff --git a/applications/external/usb_hid_autofire/usb_hid_autofire.kra b/applications/external/usb_hid_autofire/usb_hid_autofire.kra deleted file mode 100644 index 21d41654829152f856c81b842ab51642a698c97b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50949 zcmV)WK(4<~O9KQ7000000E>ikRiW?$rxO4G022TJ00;m80Bvb)WpsIPWnpk|Y-wX* zbZKvHFL*6$a%ps7P)h>@6aWAK2mp(Ob5#@CAd^@F003_X000XB003=aX>Me1V=j1Y zY^_yYZ`(E$eb2Ap_>vA7EmN{AtFTfyaT*kDH)Bfz^pT=yn>8g0B%Q=4`rmg+$+4G@ zb`KR`i&wl~=iDP|`tG4t=uQ|bbyHwE7-A$Et}EHB3Vd_@o=ot&vy^S>^xzbq~h z+|lCZ{L_aEbV{P=3|LrjO?y)?BVfg9Dr2PX;a{jA@1x1O+S2| zU0tFbF#<}_A^6m?76oQ)t0ZR*VxtGLF;Jy7dMOp`*}t#OiBL+A+n89hDxKj2$vH-q zu-wSjg^C!hgUhG1_irRp4)vBw5Bo#>prx6EaHpnB-iH3D3qf%ju-#<@h_)U9+YUYN^=D z`fj{P0bWWchbtzgQlTT#ab(%hDV_*;dU)-wM;fH^Z;!ac+^8utQ0D^ z?!tdxPiemCYf`u{f?%Lr$)>BK8(`0FWD|lk@FE=~h|?4!AGv-*lI$lzqmuX2D z6BQXPD_B(tBYB)wrI_%^lCtm5Ucx7ndZPNkY|mvNcDgY7EA$G)oin~!8NF?)Zc^cY z&KxUn1lMd<6n*@a(j`+fYo&FpaklgSN7k(pM^Q8z|FQphhPvPdQ^Gl6I}46Vy)}Gq zvPRb7=Z2@2mn(yWq9_&vD!2Ns;}&g0&lZ;b%~ zUW4a8@$iMb)9ARJX%xOV@QI7y8+Uf{FHlPZ1QY-O00;n!gmYCCOO%3}0RRAL0{{RJ z0001FZ)0_BWo~q7Zf0*Tcx`Nrl0j4BAQ*=4{S`5no(Lt5?%JU^+SHjHcXmCbhaJZQ zB{ZT0IzTq|-wQEGo9e++-aOy?@$mxq`mK`De{QVMY7wJk7Drq$T?kb!;&pxpe#X~i zg3q$Q%lzNNDk?OqE3O<6YOAA%_3iIp%V-K9yw6q;W_fnvKna3y^=BGQpPk#yA#9o^ zVN^;{8k%TRh8{TT0lp_GTru@?$BX&BD!Yiky;4jj5vChmJ1-*agp-_L`0)*-;FcM& zb3VN1$A6(}>&**i&R2RBDBW0RXuxNW%j_n{V5IO{BV^M!gw1BF`yr(u6HTz2Q# zhN+=S%pc(IbS;#hjg!7$X=%CWE|*$bT1sOYrXpW#9&RYl#VFB3k?(OR zVCU0ijPkL(oyoi%>atQdRFzS#4xct5)~V-kA5QsRTJ9}c+sXbrW4vEqd>H5xv#Yr~ zQ6CJ~S*z$3uk7xU1hIi99B79w=gFq$0Rg*jcCAfU^tX?OHuD7=UK8B{5@)JcUXH4` z4RLo}zvOHp2Ss4We@pM`H@^|XiIvncvl6oTLm{w`z4Ykscj+{B<8R>_c))W8L!*2~>pfMC|i`?7u4?yHi1 zHNoh_!sV1Ub#YUp<|HzB-ZDV<9h3JVE&xv;q>y6&$u;e7AyREXBT z4w#!QmIn?yN;0o%q$LLxKFcvVtPZJ6(M?JQXLb#Yy~s1tmSq4_`u#Y!nn|x>ZST;P zI_9jnR5lGJH_+}!e8huK_fc;#u7(0}-IVgnt_Gs@y5p)8(2Ts=>BrVaZ%Iy^l4Q+k zZlLa3#+tzmI)xqA90yjcr~rjXuIcVuM$1MW7uE*x?9pRfOV98ZD)RWqqed zLulc&K>uhR3H^DUkuDkyKBSt$aW}2&il~8rJu3tUgTMj#sO0Yyd-v=}+tp0Evf&DI zC=pV>V%j7Qjai*w%eYDr8{hI{!)IEH{GRf9q3G0pFRA`H%2PUeS?W&AS(_X*59HP0aF8)R{QUF&-py1|E?cEBJWQIGXwYW*KY3ZzWI%vyG5D$TV-t9nyv-IKh2%(=wBLA z_WnTzd=|Y-ILy0+-81hgOi>&8=;%+t;^p7mbf_d1D8Es;+~ecQg$kvBH=!R1C#+GTy^agE%8PMd)rl6Y&^Xr z$h`A`T?0?o6OYR#=BK8ADJyJ8U|&5yNk_wAjxr}ghdqaa6YENiP+VyC8DS^Jl-@=4 z++ZnNYW)#gu3KXY_!b9E%M@OPbKV>Uf@{^>bo64I|r{Gg<6#*i0fIgg+2oylp+TJ~1-F)~V;*ySzK1 zp>i*<$ye9Zmhqyr4yAXq#I8OZFpGExo~w#ffc87rD%`Nb%czJ12qXmSV9;(x_b4T^nCf5b|-A zABKzV_nP+Zk*kTyX3Bb+i7V{V%Mhg?)c@FSU>xpir z>_i{=>MoyPrI}gj*cp92sxQY%tj7{|e85rgAuq2NJ)d;&HQ~$B!N)fJJa9r*FEsMU zoy7Q@)7o>+a7s9{JuJgRZp2|AEgX_wqfU+Q63}In;t|(8uY5+QSA_p;kwvZ5lPShjQO z1HZi~*8G|*a)=uNff5fAqhV(3sjUEi%-pdjCJ!`MoL%E&=P7cXTkd;-WW#nhnLtVK z?WE9;*z()=7WNt~LF0apvx{ZVz+|w$tjc9{=t=LRGDwIAr5*7)_!q4&RCW=>7K9~G zZfxV5xE1dq#1^@Mx0Ym1Uj6&E{PVb4`r;au2P`w?gxEX;0VQY2F4a0U8kh-Oyoea4(N#`>p`=;$M z9la@O1*g!bo{Lw>I~~%-$XLyJa)a81b&U){h-l0PBpSPDN_EBS#3WLJ|JdMR~^Hgd=C9e05$}R+b zVyykgdF90&il1wu6z=eKi|+{dVq8j;%7z(x7JKT+SzvGx-YrIYl7K*!HH6*BggsVFjq>T(`JgKZz6d7S^j%w!9X#p%e1rOT8?Fa=^nn_*RvSsEu(p zIJ@;8JT?`cH#;j$zk|5O1_&Gjkt@jscBM>b>e~XvK_Ro>Q5V`kGcSpEubH?)&Pe?t zdVO`_ZBGHF)?!MW>#sY{l^#lfh>mC#H3&Cf<-K^w^@FsK*t6p17ec{UbaR3RMJ?I0 z{e+ntu6{H?qpE3O?%5|LoN$Zv%bv;F3*j5AMQoflExj-~;NBmRkTkjE+wgLG|7g{1 zCn~PB>h_MxSZQa+01j$NS}G=&qgv30H!`FGH#k=)XK5SazRC{{94s+O!HGst%Mna+mk#}eZQYh4DUMZ-Dw>wFlDo2`o!l%0fc2e$@e}{G=&(x;3v#q7om)yKbmqwhBBNLiH*=3 zj~q2v!54+EoDT<=X_$8lHx;@l+(|)RZ*BzOK4Ptgxv)%RzlB{!kjh=_((H@h#ui)- zwRr=4l;lHx&=3WoFKpFT;YJ1k-o?A=VJK(fi8pkMjpgMTrUi8`8G?nRJkyW+B+&wG>$F&ru-_&zTv9ZM*hJF+PGf zibp+xp2W=x{dcAFpwd4zCN7ht8f4$~Il+8#W#4$V+_vWzk~(htPGBNO z6B^Fo<&LoftK+EDQlhhb&VWSyrvFixy6Vakl~yTozf16xTZx1bG0#hGXYlYwzqYZ# z$ZTO05G_7afg^gbS1)ujfW#S?+^66fAfiJ0mn)yD6ssASCB;rC=G|LwUHjWVzvUcK z&_@2Qr*5qCE}j{o&WE=HW`l{lhsOyt8;!uii;;G%eE{12(*~iG!+AYE9s>nbW5GNt zk=hm&%60PuDfFKpkU|=04BO#uSuR$VJvlr+so-WPcAQ$Lj~n8q?a(+S4dm3^$iOmC zn>qYEDX<)Ee!Toq_SaWpL(m9I7fQ4SRXtSgA{5vJ`#GWfBi4I|(&OkX)JI6Iyn(5u z#;MeriR)gP-?=#+0TZLNSS4MM+vP{b%p{!;N`#aNdK_n!m#}55zOXtVyxuPz3d`ym z=br{Xh9=!}y>{f{bA-#zL89N?Iu4?mwGDe{5}X4E^^+h*RP$9W@d zJ4@99nd$U^yLZllBKgEfI@js4f^C8)pan9BSOv+rLvuw>QxzU2y3i{9WNHwI#4>=? zLH7Kw0_cr%sAlBQPR-FZ<7K(QI2g1GoLM(u_2w`(hB~jTXAxd&mcQY$+=s@W zXZ}9tYk!>au)_4V#y6Pb+oGmhSSr9FEgu*o|0Q%U}njrOtK6Vraj=LG|t7vgy%e2M5(N}==ayM7?FWp>t{{E=Uw zI;Qz2(--^wi^OKFU2{|Dm?Q=Sz72qy_4*`wk3hb&p!XX93SpbGHk{<=47!Icw94dM+B`<8cd{4s&12=f_o>!YQhD`M7Ry(E+DDMlf>zqo0@c6OLjiq$Es< z$E}kdF&HF*-E_;+)(iSd|2b>2K9!d*4k;YupXwpUm0gJ_AC-kl4_){W-THrt9y`RCkl5d$A{&~pGET^okp}iq0J7b4y&x5}yyS&{Z3=N@bLfw63GUWs?eBsM z=$Dqda@iFKCcWzT2Pjnr%-<}+xRP(oTYLfDf8?vrqAi`{^kYnzM%(1Ukw4>S)<_z~ zt9ByD>|aVziH~&{pn0>D1X+w(DtHnuO(re+HYgK6^|{=uy%uEj*Pq-!v|~;QlX;v4 zkGByrP*rcou^r?DoD_($0yDNCM&QoR#$w*P>uG*bZ=m|LYK&1hc~c{dU{!ldRL)f)SP=U7Ta8*bw8BH zu_4?62(Kt0;oPb5otYgQv>=c>QB$4*p7na(IjHK(@h5slAg59U!P3DTDFKo{JqM){ z89f|E`H`L)>HaxU=~r}oz5Pjfu>#Ek5n*sh=Sm1E^r&Bg_$Q!W-SWsXIp0*c1Tr7~ zc>Z8$gSQR(1Wd%{H`?FKxKq(q_HgS9&$U!iTexG3^q$MNEIR9pB-z4n$i!V`Lkf_n z{FXZi1vtYKi$e#GJcJEJ;fAg)^4Ga?vozA z;;qmm1w@r@%WLiZiwL|FIZHXWrjwFIdWOZFm(XHBk>4sMi&DgP?wAV^o;HcL^NJ-x z3eZ*64^qi~Ec!3r(%Um&^9n}_`+)S z6+>YRE&givP#8C{v>cGg%!isYeI4D2l3&eLQ2#XAAwu_FzGUi+%4XeyjzpjWdN>adod z#fiE@QIIIrM+rnF-`{;E6TB-rB<$`YNOK&0(s?o1n2C*z9vS$e|%1q9pdtXdm zeduDMD|a5*tbOo^x8Lq4zi)4MPVCtv9mqWLN=OOEMX07y+ym}B%{RFCY>5tQMy8p& zIZ%*0n;h#~BWm36W1^?3wEE(x&`M}a3pt+y({-TSxsK(7;wINl1tBuWZeJnsGADO0 z1#%|mzfTpz5omioKFB^hf8&siuefa4V5umflX?f6lQnsH`+y#5qA4yi>}+?t==cHLCSr*e>~h_23Dr}dKVvp9yk~EMzj14R9b)fTM;V z2J%TAE&6HFCj~tEX*H}AOaHJ}`Vl7a@=o7xrbI^r{Z7+fXR?ViN=2Iq$RU;?(4v5M zke2CyCM&`&&y^QRP;4N}I7F51Tl@uDn1Cdqx$29LOLbPjRKd60pz9`}N7%x!W};FZ zz1vN<1rh7CkUnr+JHW-8YGv8z;!`&~&bwi>9a{ZXGi8Od8(e!0nTU0H+81~isxW;oet zJbxSD99;A6vr2lyr32;9F3K2M%Bg(=rLL*rT&9%AwIRiP?BNv_Z`OzlHg>}otbF^F z{g=L0Ao$L)H~jP){q1~n%@{p6vf>=icctb-NyYR2bt&RPdJzrs_GE;YGGL zuS_+<5MS0-iuY2f*?mk#1GFQtzb;1m@0c_s=@%5N^3z*g@kLjSJkM#;nDufI(tbT` zVgD`I?wQH9?NP&xA=7u4TcxXt);6p8ie1a@ncje}d*|KIMX#51P5b=aopP0W#E8$Y zkQvYy`B0;ZR}^oyqBx`T^JbBl4V}CrXK4x*fZv5-4*B>tZdPYmK>L#bbHzArs;uJ zk1sGNbcurK2pd$q`}nuv3)w*Hs##H&Ps61<4$k{2RTFmA)&!kQswEkN_a7gUWq!Y( z@#+hyti2zmx%-t+Haa>~K6BzhXu0T)6co#&{d**1s}GQe$vLa#S)Z>2Q=~nfP12LnySScka`hCxU8jJgt@?u zSBPs~rh%XvUfs2a;5fEQnV~MK(f*l5S?KlklHbMJ@K-X`DMQmbV~%Ob^4Hsiuj4G( zWbUz9198;30KaQ2!{>P)vkC>i{PaWHVW;uK5gS|&7V(-mm22DQ?wBPwsxZRiuUysl z8df0e!XQjOn&nG*;A-659C}ih);q*xWi>gSUiyRm!_=|UF%mU0aV+mJ!*6ptcq&Vg zmAa~@28wV+;7{=((Hzsn?X=YUOAt|E zQ$qqZMJJL!Z0y&j|LG<3V)3e=nqaZ6@Z>ae=a}N|v=!N?qiq1joisH%bGrltDLUfg z^&!?ejC?_E&AT=n<6fm=rX`ajTh0KVy7rAy52aQ0qdTBMW_OqtD9`p6Mo~HUrma+` z9$f)r^{b$)cesKB*e>K4T+*oi6svFUwDX!hn55z)@$I@U1Pt*y+J+VW*vfSNnt71& z?NNrpZ{>Sz5L`KvlDCk1T=si5CdKv#h(XLPpRk8L8ncJS?vk_**kw1Cb6%R9T18II zzc?toWaATjOYc<(4`GC9`0flF>(^Teql)WH3mBdx3D;KQABWMrr&uSP*&X3ffn*ri zUGF(yU!kQowFbq*ce_54LumM3xn9}GWX(Z~8r?-l7NV3-nyOQ`9!|Lw?Wt+Gwd1VR@LM@G2-kqHn^8>hCq3K^+u8J&CB{*=A@52x!S`_3Fx)5%wgbgBua z`kdpJ-V}gi*CuB)0R_GX!vQ<(=Xf?p&NXyPuqYT&8c)mb*cKdpAStx?^4u2Y@^9Wd zf9m!Uwquq67buull+~kyRSTFF4rFeXgN={5J&Rb7ZDx#%o1D(M{fD>6elstwRT@~q z5>K3_!?A2oN5$2O%(G_UM;?ax2LlkF)6~KtWZC}q`qf|Q0imhm&+)dP8;eh48e<4E z-!}rWEcV}Sv1T|fDWgNFfC*zZ_l(N3!%C?zIF$TlNC{Xdf;pK+Or)Bt3c(O-Rj6<^ z=4|EkDPrC(>OnFTR_gT-VGKRa>&#@z!MtW_s6ehe0d<%>gp;uqIVex<`=>uB zujuJA?sCCd$n4#oB;yG4jHR+{eDlB4t?^?Y^InLf5FX%ZnuRt=o;1#!A;PXNTummt z88a#qLb+i^27@X3;<$zNoBThFSbEvUbJnsO18msQh|p z9QsgG^~2x3w5{ryw?#)<XIissO$Z{BIUl1|&`t$P0 z$U!rOqRA784jG}m=)y{2ReNV&hF4E3Kt4T8%^!*AGJSuzrA$6yhPfZ2HIj>`D|(Ye zfAGXb0UL#&f{0*RYAarP3Jr##x}zQ*|54sja(3g|UvsT~;Lg=`)fySDy5@IpK&(3% zVI7|+!4O@z2m~bX43gDSCeI%_Q*&mZq1=>Kl7p)%x@$h9<>RtxT41CIAFQxlI5Ej7 z?!Kq6;2q$+N_ub+5`0W*2F;xrA8>&1b+4HiW4&Ot5YM}@n6)7GkSbQKc=5nFWF?fdtmXMC~p>^f75p$%y} zD8G}->1)TTe_Fbcv&NFRA~>o8bk#ZL6PXV zG{ zQe7>jKB>hL(Ztv{OQ|=v5Yn(&`rjfhr1%Yt?(MKwYYR)@2j3{p1#UFEgyLXeW>hz_ z_3TF8nz{Po$!H2UU`yqY@w38P@Y01*>8WBNs;%!*cU$Nd4zOrAB4j7Bjv)60>=6fDml{Mx z6oKdknNr>;;IcN4i69W&F-v9fvLweHS=xeX)n2f;d7pYf#@PD!l66{ zQH-MJveBz&9J+wNbky9^lC$fv*&G5x|EIMQ$y6mTPdMlz^J* zmvSn9cUCwIi9Q!5d9=R1er}o~hf3wLU+&kCLg*lD<*+g-b zC3u~l93M7DLkJ8KpM+pmfZ(2E(QAxP zGr|gQQYip1J3FfjVlN$RjLh{e-t9kY&)C~D15j~Bd#&L&#}2dueE3w$geeGEH$V`U zac4mM_aD%4m-M`u?8#Y^lrXgN-s5(tv-YTdZ49G;b3kqR@SO>+aHwD6C5l$S*F%9N zq3}C0*0LvOQ{4IRozCddQHw6zMAH`--88Cm=EtT{+*@!+Dm`QYCx`2pphP-K^gO?k z=#e?d;(DUjy2$a5i%|c+J8_1lAPRy4wdy&6!AKPtsGgRz787KG6MROm!N&Jk9djx0 z)kFVBcj`#2#%T8D4oQ`B%5<|KrRe!vNa?2Z7S+D2Wm=VM0;k79^M^D`^ z)JSS4Z3!^7UTO)^C`$6Vx-f`3RiQ1*KEu82GcvT--O7GX4U4A&R%J)VlGiYA*|s(5 zt*9av&zi}45e8$2s$=y`10ncJ^gyZUT45!uW_p>Jenv#ZwTp5#P;nL{ltO&e?>x@r z$qFtL8&@+xA4V0qXcqqsl4ILl>7{HuRyqP&3^#F!lU3vaSTOK-D+HC@B7Nk^B1HV{Y0{ z$?>3qJ||8%rw9DE%o|Oit{PUd^snsZoyYge$w~CQlS3*GveOz;H~i=}4zG2WQh>G9 z{0q6QVgZ0OZ)$UFA{+XQG`z-!D2Re-$9|8TSP#2p$Vpj%ip;)dIEoHz!o*33euIr* z@t506Y3hY1s+Z<5^6&jlV+OBO=_lTtO;77U62_=_CT^M@fB!l~2#2`{uu`C6YL${4_HEht2J14h5!B>-K`C8NMP;U*(t6doBOU-^_G!M?-{#Q;)I)^h3vL(+5EY3X4 ziaz56PUfBc-eVR%ZEB5GK02u>^u5mhgE%D4bV*j0$SAgZ#E!LM1FwRf-`3ujS+r3&IHP~W2sQ|S}o>~-G;I40g_rP zfcM+n8a>iC;AIB{%gnVRDvb6K7+hNy!=Qa@65~VSYb-@Fo|3K~OSd~Lxb4@{oObb@ zc)aq7*al+l3Ch^P$!LAgq_CAbF`D&!yMF<|NHgPjLQVI^+WV`|UvIAX{%XafoNRju zBh&}d!+}KCLb;iE`k+-Zl_R%mgplbRsXMg66Rp;zqTF(+q|fjWDN+f=+Os?!@~Vqp z_A4AgYPcEO{0`MvaW3C_y~8`q@w)aJTONDH#)E|+ioE3}*k!R0E>zav6>EHo#R4+t zd{$`}DRulkbfsP+(rM@GG0*i$Zo9t>kU#}AI*~^M?`_)jmm`<#L>aldz0`fwuudZc ztT;p1!y_VOb}LsDl}AOySXziYpJxnuTzDxLs}wEb-jJ2VIETMW4%Hb*0*6eQ^&H4` zu|$iwlWpmmveXF-(p)^<^US84#XczZ7d>EfG?igQp6GFZEjUKZ2ijXGf8ValAPCSC z){+p3356QH-eI>ZqIuguZG7LJpcCf8IO%UyTJ1qqG?W?E?Fo{)7JeV2ghm!tR8-RB zQ$Hq%J*@5*N1J*ObuPvwLgQV>gmeMwxl#u0TAc;6nD6>}gKb9OF}>s{i^HMLFqLJ}mgXBZ%%6MBH>ax_L zGZl>i#ph6KuWTQtQMizgnAaY(`|TPTpz&TdCyA7jFgY&9M~wf4w=++oC9KN^tZ`Os zg!)v}4C27jhSKIbF5lJ@6!1IsRy{r)gZH$>@blq`HGwDDn_=ICpypKCuO3=@(CP2fYg9%zOUX@pqlmu{Mx~ zR8@8=xodYZQijJL5SQJ>T0hu*#;%<+*Q7sI-LTz3GR7luZ#W4p zZZ)RzT#N%g!We+B|DqPl$QRfi-p6dm|Ja`M?}4y#bB`k^=#W#^DD7R?^w);|RyHS+oN%~#BpECT)-*>i{&ll^BzBCt z^=mFU{@1n{rurz$5m^Pt=x*%7@yXYEOZLHP2iupFBn3_hj1j=6W~HniS{fx@Uxb)TOL`g!m|+RgEu znzQ%?hY963UdS*}3m5IzR1R31kcC0-|H;q4VQq{ae8S&8N2XJC+2J~DAKMF@I`uu= zw}=>}A!FsvNp-}&tdf%(<)!-Bq`@0b&_hvze)o#bSS4cykQ;sX(Wa!9wDSN7;4eO2 zeY&6b@4M`RI!eO19E>Db{?cb6w$lp;-m{ zARTor?`%+f-*bvzYu!QelJ0;&@>p1I5`5jCRW$DJsx&2$Ym_485S@)`N*h=yNMND3 z^TD8Qdghebk!F7LknQ4q5_Yf8-AIp?KcbkwYr!2dPs9V zd?IxwC|-vRD#sGUy=$3*~Q`;UYb9^oLm7vE=@B_;8d=Ud(K7xNpTprx)r!udwXvUkd1;()4h=8Pi3 zM|Py`Q=na1dbra)3^e@i4218BNZw1CKS_*T`$nIq`kd{0#++hNYR_0G;l3(4mY^VY z>)d+3aKY?ZuKbw95&CYKxv4l@l};MK-0kL!z5wq>Ys8zHFZDH!Cpb8C>~=(7JOM+) zKgp&5%X#JQ>+Zvq%LN`x2|r{sMuLPu>fP%SLx1*7>2ng|bEwFg8QL%qbb6LoDukp5 z2Rq0YYZ$b@)C#~sR=B%p;>nMVK`8)-5_9x10=(h%HIg`bL?EbqaKsmdH6Z5&@l z4YNz=C2i_Ip(;5!_vmkU_eHfxSXZq1{!Lo)K@nWcmrXEPfilBU!(xC zB?3oBRbd7iF~mp+wl&H1Z92`yaZJxUe%$uKov&5=gM^K~scuH##7Ce9odcyAXkhH(Vmh6%cniD zkbd&7>d_S^dORGOvMF-xr^8V66=IdD_Q>vrn(Y<>CxVoMgg9Ommj)Vs`8R5*)NMi& z$(z}A*#nh*pUVSAQH|VP9!HHu^eLOLF+7(Q4@X47b9pa_3a8x0bL()aM z$FK~0tjMgPO^W=_h=+o#usgeN>{zG7#FOh4=d^OQI+eI3XmD`rxJrr))g2xnk+=20Ee2agTl z5kmCt{J5EPpDrmuZjB%BtkjP`bi?AcpbF1kbewXK{vhatiWoiKGH0Q&NYLCIYJpl@ zsHIZKZ2CC*LpgGL$?4Z?p%EKB#5bQ>iX12|(BPkmqi*whYOXw>>=x}>FbdBuwfm@Y z!$Ly4*|W(f^`;u&vABO}4vFiPac`1mu^!z!+X#tW3)-Ha?WkoQUbo30{Nl&{I^Nuj zNkd>Wu)>Sq#xgJK0jr@6tT5)<&RHKOne^sTVEtDsFJEt%lizrgWbEOz z`wtF!Lb;M%Fe3}sw?)!us9}@?lc|(}S2ptezC;epfU!8tM*M z;7G`_O=oVDkM$}&SC8Rm2D$zRsY?gaoO%mG{}gftbUA3(*MEkV%%1x>X0gLH)ZD%C zQc9j9C%{)`$&q&@9jfMA{3jfJW?!s;0yN0w%Uwyp)HsMOG{gQ4hA^> z88<5D5`B5n!Zvj#8eq-OB8myqYf_?D77H+Y(>jKtt`Pp@ECnbl3KCfP?q!U}rES5| z`hbsjLF(>Q;`RhmAYnq3mLYSfa5MeqX=!p!++@THoblGcMa^sJFq~~PliGEd1H6Nl zGVlHuJ4vhge(T7Q`qrD~N1x`OsRfsm!c=y3#+CxZLXLwco}GVj^OYz4pXw|J6AmD7 zX%4hj1-7uHF}S4=Q{R1k%0j}mM&v)rP%AVGWQH`BKa){`guu07zy!A4*;wQRtPIS zY|stbBG*b5u)O`KK|ylvpKoA$+gf7v51R?hl!UH&t(dH69%dqE=BAGwy*VwZw*V+E4<5~wgedgV9s)P=F zV!cyP?P0$#^PVD~``nv=5ZUYgp)$VB8tovfBRxu&Q72QnRhpP>&aF)cYKL$p72fCh zanhpfaKf8Mf`!(?VB?;Jx8xY)iw{qbS{L=_=GX7ko3H1*T=}pXu8J%5(7~zVTIuY{ z9%|5tY8ntJaCc`u+$yZta=SbcV-=j)-JznAlVItvT_@b6P&51coyuhlFVT{$%D`LC z3c=aHWDxGwU3wX-Z*Ykx9x%r)jf8}XxqYdt-Q1QvB4P`(e^@hE)pc;f{w%th*0?{)4z=f`dBevK*yHD&a7 z=p1u4c&c~kfTtTT8sYfqb^`RyhES#`SB1UhkKhAC-Mh|`0oaE0H+lxbkf7BVOcqIm zmp!oX7S{zrb2wEWzWc5UCW5LJE3fYL4v3tDNWLPEe$PG)LEI#w9S=lmF~Cg-^Es%f}*Y7gzR z&YIa}ZAprr^o>)Hl3S{i$~gv%;@q;DZv>r8Tfi3qeoVEV`PGmgHo4nezBiaba`M6` z&navK(1Qaf;=m>OQY`hfnmR&1h|?!# z4Wb~Zzzf^`%ozsokU8)GpfbJU$=$KsdRNWn`XC5rEO=Y$!SW0=%5-Rs`~xB`b6JY; zZ-sM63YZkU@{Z#)*|9CBpo8u&5f2IX*`Fgw!?uE#AhWn#-Qry=x@A@qa5ez~#;ly+5sh}UyuPbNYvd7)w z`fOmSE59paWryCLQu2Da`buwxT1j6#D? z*4mTkjW(=2gtSZijO)c7FgI2J$lEqh6S8DjHRgUlqP)X=<43%{#_VO4g&4CP=?{+Y zJ&=m&u9jbje#jX7ZZv{-9j50Qu`^J1e&|WI2(fmLuF+#OqD zRVUDR_Fz5;+_1jPms^<0hDkjXHu_Ay@_^nJZdwIq;|U;pw(~Vg>$1v>*I2L=Pq5tH z|Gs+NqQz`?;E9j#1?S#R$$A3MgdPPRS8UiNIqi72B=vUN@LP;1rw-E{*chKSC3BnA z(U_A?o}-%N-HK)KNMJuNo)X|Om&?-!@P>$L#Jy^lycfow&-YvUrU7<+cvooekQ$y4 zCI%7su&O8B`RawvD+Gl1!_DqOa|rheqelnYWx3LboY!W}QPumz53h3xF}uL^$G^8oNx*+)XXxXrmtGj37dSJ*D9{(Dw( z)$Nvu2(%d;i=s}2(Qh{}qi5

ZOTCY z`)NwT15kO7gL}2+kN0fVraN+vt*py4Z)&6zIi5fAF+Z=@IZ^`V(h~--42Hm7LiZE| zxS4$b^x#t)h(GG?7U<;;0X#K?V-Sr+1NbZEa5)zRjo^77iUC~+B=>1%moCI#Sh(#i z4FQY0$8K@5obX)(aXwl+*hqUkr~vLr;(*9}DmTW#^bvx(&knlV8$7a8r~W6U*-P&( z>&M+fJ23#K7cFZE8$SdGx1Em!_m94V0}YVgXy9GB(E+G;+%DH*06><`0}+Prxfw@t z?;(x1)Ga3v@=U9bxq$m0As{=dtTX@ss@OES)5o`NF~rSC1`KUgChryMDr5!#tZ{iZ ztRZy(|Klna|CC8ad{TQJfGzU)20u|%EmLzG7@(~VT=;0|2?3B0rY;b3*}dPbk?xFx za=+}+_9e0+M~}EVS?ipqlAhfbh@ahUZgwi*0(csT3=t^E_-62 z3OP=G97E@%BDAs&iTEO%KMb#q4u6mxraOE11YjJRiRCO85Sr)W8(++ zU4t~ppOt$T$JsO&|9pB3^he+Gdj2zs>_E>awXNK*_o=LguzB}nU^rbyDd)U_fL?W| zIBKEXf+(4WrqYjP!bs(}&D@adM;UIFN2C5^biMU=%Yd#MbEtq6AMfaFAS4c6PxASJ zJz1WL-60^)1tafTiV51^TGt8bC%y8}#|r?-;;HH!LQgspkB6#F{RZ+8P78I=$KisQ_MxIes7?4xwO4X!s3*!oaAj7FfWo8@rSwKIYct@>yEO zrw@SlaZLkqV&7>-N3e9>(SiqKZ4u-{w%OSN!p%L54=W}JJUXeJjzcZ7=i*GacmVdH zU)GL@-`^3xyG%f8dcvVC=zv+@yrzC~+ocwpIVvaBQcPw$M*`gDhE*%zFYN$oHmJGS zcY0t`qB%gUSR|M4M>H{&7FDTyAn&0);ObA%vnD!j{IbD-s|keJaj;XM`+)jO=|J zQ@(QNV#4uY0Q`0cWKMUk{Xvg=(xtaT(FqHt;Nl~Pm9tjP+M`3q!!Z?~Nw0Yf)@z94 z1`}zFcbl-)mZ^irIVRJs5(cf$pcBfw0hb+`u>Kr&bWvOCr3H6S2I07{#=YJ@@$Ul7 zMdBa=s5`rECKhY@hj#Ox3uSNLph%kDGOVxjvb@8zO!G#}9qozW@1VkW30^(cfdQnB zQW6-3JAzkMF514$#Z1C}v$X0i+VE|5bcR1+;0F?oLAaEBGz^&(&*EKNW(9W1)9er?& z=7|KEj~m7ZsDl7dP2%L~5(96_R;Xk+8}M3JQ{=ooqNF8l@l@2)^M&1{C&X55d*#xyNKQ zKQ>WSX%ajhf(0+izPWj@StF2HN?8H+&fKW+xUC)v_&pIrCzx_Cre~f2A9Gj6_=t&QtT>IV^p71Be z9S(rHpX(fh5XU1@Z8%ylMgSZ<30-7r%wbEGMFfZ*Ykvy5rm2irtgQc1d*95_;N=o_ z(|Ye6=#2mno?OqMh#a@tZ&CwIF^@tbl=X|Ecm@c-q9#~76viMkScludwU)~u*iiAX z*+*Kp1(Q@wV;Fy3*Sr362U7onFR{=KlNX;^++V5A)W725&WbqRYDi7uImV})zV&Ut zY=7dzdn@Cw2ex_XqcQ$sKm%$O+4@5VIBOH?7d(vD@QG;T`xhL`F$t_J#a(8OXI1KuSy=SSex<_-DsO!V(kq0-&?ppo9rZ zsBuO~g|F$D3CM6-M z@@r2~N<&OmSxQTcgdHCEM&%6&GyI>+BUl5H!hy*5+q2$og4tb{YxPWBcqB8brnpx+8Zy^U4+amu5`I^p4}`;7`V(V zw)csF)U-U2)KZX8kifUX7-QjifsuLn;Ly?j`CWrfIkn%s7{{CMEx$QG7Z-hMDf(7* z0u9(m3ils!PlNLxDhcM6U=z*AY=uFrg@pwmhx^0NJc4f;Q51+McRgF+D}Y^2@RMIC zR6cvHJX;0}i+h8-e7P#P!SwY}9`1LyXJ%xf)D~~td-;CzN9pVmc(hcI89c&-1q9y4 ztv&0@-Alkce@cFyau|;l_gW+X;IZ8LnLj^&fDqXv14D6L)n+=kLwq)AJM)r@Hv|Hi zjdd_gHn##F(Ko@3MIpeSB))i)5OIUE8NeC?Ab^Lu(E>Z*7&im%C@_HmLXz{D5dQSA zEBMc5htJqD1|KON*zm;$KMsUIhfz#o>|m;Vw0P}hGM$B%Ux33NVI1n`Z4NG)i1j`m zrGSPC8xyY|));b-1?TK3I(*M~!GBMZhXIfLD-*j&;`Ic~;4^y3b7w`t=+`V!ku3`> zaq+seroFt$cAIM=auEoew`bA9TJKay$YZ}tutgf(QUZ@DtMKJ)f zxPYZWH@9c@qhs6L)h$CJ|EZGaK8aj#VIQ9fzXu_~1aScM9ROkZA#S1*-d`pRu?6}R8%3c)N4jv)+z&~=ne-m@x21~IZKW7-9-0fuh?ljZ-)9)hUr)olPE59|#I z0??lt1p>_y)&wjLM4|xNmjI}0 z$ZuWH^Wc2z)e^<%_R#nKboLO(T@dbIcIyz{0c%9*U?eDzM=W?XgQmActqCOXz@T{n zy{(%PIq+c;sE>i{@kp$~uZN&{NU*m9!0k~8z(ew2F@ng~sj0yRyWte!M}(nDAXW45 zW&(K?k@1AdxuG(;EG7}Ogz3y7j`AR~AhPpb=>S&pU@ij%Cy}AMDD81|z(m(+aiGZ! zFmzyy)?eemps!;!K^zBqtqZ0>B6nlBLz;FOokETU@|+^@!(6OWY5=ObtWKfZpia8H zs=?K|VNT(^q4m4ePZ1vgLfz2wkeyvvw-628Nb?~iu%YoN@IAsBu*j$+Xuwcv6gFUp z1}SGa#Aa|4DN#5wn!kBG3I#%h$h&w*VAw|zTuH3=VHhNolDJACGo*-;ufB#llllw+ zSOaBAl!pjvg3C$phmdN*2uRJ$iMQdKMTz3Ew;_(g9`XR;U{Io@iZ~cxVsEAt=`oNg zMWhtrodMEetRLv9VMc=B^F$R z)i|xhIJ%+d%0D76x2D-S@0Eq_hvLTv=u4(AsL+=d0G*GZY z#SGyz&{9H8452jO)Bdzx11qZDw zaudV`8?AG5qsoUWZ#Z+Kj|W_=2icSAAP|Sd^%mMg=|Ii(T-lQ^Lvr<2oNC?@coQ9m zGW9&3lJG<72T5$OxD#|Bu7wbHA#YgR!tuWn3e)e2I>oi zI%NbG&qL9GBzhylOsaum4J!|c97y~|6qdXX#W|Q=1nYypqId}@J1Q@%Krm?!q+x8e z$UGSV$~Y``FkjEBji^%zW)#wJdmsiGR)W8zIDxP+NeyaqI5?0rALnf#cL2473i$yF z6{=jgDG)y&Vk%@$TnC6i!bo08CXQkh?$Afr=hG+J$G!>o4(Sa1bGUnudzi)>Z(uoT z2l+SDP82^BK1m$Vj7fl`fRYlcA>~PElLY&SqJ$wOAx*v$FD%;)gOnkG zQxfJQ+gHr}h|4!W$#I8Jj3HU^aN}e}ipbdF7lypINsU=*AT=Y)g};&fNGOgsi?&vnni4azaLx5Eriy?=%iv6oAYWxSNN;FvC4Cq3N=@*qg=!~ z3U(Fq%J-Q9+e2}N)k_hrfGrK4YLJu6)a6a-P+iqXEndZz=Ss@7e&ST6F&HA@?=o zHR(0WHLfe_JyvU^ZtqjqQ;+Qy3O9XhaKR?Gy*D>$7PmZJvA=S!#TJ5n5w}$q#5mM$ z#6>UEMvueiCJG%qMi@5~^=Q6;Pkpl+8TJ)TPEAD1fMeQYUR!WC;u~15sLa9kZiX$v zt&T0=-zMv!OS8Eq!ID*LzEb3(y%+hT0Jx(bRQBYWpF4E&~4F< z(TZRd;4Xr>f>nF4dw4gHQOVwgQ;@w%h>|3aL@kVn1iIuiP6eBbIg_-YwMPW>F~37# ziAZ}hMvjFVhe{W5)i>Sex(W48$RV6tyiG2PLXfnBq~moZ%3S!5J|T+mV9Eq3YYL+G zBvOGCGD@Tt2?$c(Dfqq$zQ-QU#gLACO_}gj%Ht#1R|h4^(tsJ+1CpzF#)MF*t&d(G zExxjSCEFL+N8g9Bh(btUF=kDV856}Kn;v#FR;@u=NVAr5C239Y8Gf@3>&Slx;0e=_ zWF%FO|1oSfM7T|KPU4ZqFW;e%Ln=ho`MN5;GoEW`34jyBW!-1SWz%HiVew$TV+&#JXD(zmVIef8H7ql*G_W+~9du6Eho8oo zCM=>U;wcg%q_h*J(s1^RL|sTaM1wYD1&1_0b z+<}~lB6(@V41%TL6>NQ$mV!-@tI~amdMQ`wO=(;yQ;Ez>+zj!I5Irmw_^Vg1kfWHR zFrv_+u%cL^IHK61uw_VOAZ5U1cx41fQBxdKpi{6?7*l*xtmwDsku`E^=&RezG>sMv z7EJo9l{9!XEXvW!&&vg8O{%!pDWA1?PF$dFut81@8rs`FHb53y$+hwaK4nKXcT& z)Lhjf*2+~27-t%J^*4-MXWK*(cFP-^nd};28>Jel8_k(D8n_wX7$6(O87LS}7z+*g zMz?3QN3;h$lHH@+%U#19DlA|xsLywPwsRoi4ksul@FsXi;7D*l08NlWAWxu2fDlU^ zYaDAGOBSmX>lup`dqI%OEzWanFYDB{?ml{!Zj^{_zsX&i#h*o%Rg-0z^)m}AD>h3n z3t^mk+<2T|yf@1v>nf`v3!58;XOR2ELGZiIy3Y59k>>u8bu!1#PH^^9%^1yfO|nha z&C5*>O+C$MO{~pG4%zEvJC@%MHe32QMqE;@LVr-sc~w^FXfMOsqH*zY#c03Rj?fNU zN?OWU3gYwUd(W577k1C=#&aWe%y~?7OnU5aEOpFt%(a}`G}r9rVb4#`@6Er-f6kx5 zKfvF>Z_EG0kIEmxugNdQZ^DnrZ^JLZFUr5gkL;0iMt|o0ePKJq9j?u~4ZO`+k5Z3N zk4+CoPeBjb4&elB)q53f)pB*ZExQe(ZO-H5OmIVYTX*MixV1NIr{MhTJmP}p)Z#4t zbm79|6zW{;qVRm@EcsORBK!M~&DK7*>=VH?<&HUT;=6BmU_v>$G5Y@c@AcF5Q`Z95 zBG$r$f`xK~f;!_nqdNmSvpZ9t@tzr;ES@}`OrB02{Ggb@RlxMX9KgiDY{1ySB*2&g z3<9_VoC8F`3&9b=ZNS^X=OArg$-ZjC&_KNop){x6BiW619gB8V);NNF?gVS_)Jt?)MZ3;_%eDrrY4#u`U09*l#%q? z&%3{;|9$^?e{9rjRDIM{L`>ugb|$(P-L6#7bvel@lrvVgO|v=Fu6vtZg++^5**-{1O5RX8Dq zEFCAKAj2f>m$Lf4d#HK@c4TPSd{kqoX$W$pZ^U>6V{~SyZbT=knURg=Lc~ln4rwjESyfH91uq^O%U_p;>2z7{aNH6>>dYI@@{3|D*DPO%0K3PEH*CKF2pEG%6C>Q z71@tgiu+pf_2bvJuM&l8BRD3mCa5NqCJ-ikCd50iJM24TJC-|YJH9*kBhw>QNuAVo zpHNhrRQG5tX^UvbX~Ae6XcuS)Rq#}JRYO&dOU6p+Kb3zXQ`J;~Qr%I#Qn68OSLszn zF2O2QD{Uyd{cv3O!+|Z>x!a6(+}T^u*y?Ds^zIwx{6n*AGItdE#@y~ zEH?1I&6dzr(iL6NSy2^`6nHD3-!A)D=Nb3Y<%;G??26;c1~DHxE;K2$C^RHAQ@mQd zX5dW>e2i&~U<^LqG+q@)hXuG?WRZVSc~NXpOi`+7lWE3x!!KH2YBPc|8ad23QaQNl zCbg`!^0h{^2DIe0z_c8+nzg9vGU{6D#p{deu}Qi&BS7HbvD?YcVx!>`CCc>1pX{>RIX8?MdSq;yLSi__oF_K&$AZU_3aetFMI&$UmnU<>|h!sEgdUE>Cw zZ=&DCcEbftL$^b7A($ep;ythfacgiRam(;Ba9i*~af)!TaKGWX;vnI!vSo2SSU1@K zInkKkvwh`qWIkt$WE)|l<+9{F;L2dm;LzgyX%fx$oil~?8*2mSHD@gAIIE!TvhlJN zzbU`b!#DR8NShu*GQ)hscNUu#Xol|%mEL_d|7zp7V!UWPyj@H)lGLlxqtZL`b!M8q zhrRbQZJ%bEXqsgjce=3%-xP;Uo~;)4U@0~4=T zi@J(NzL$Sd&6LVi=UlT%Xdq~C)@IPg*VbR+Uh-a&UaD;nZ9ulhw&`3VJ(@q-bJcQf zaJ@g;JcK_iJLEmAT$EdEZ@4#%YuK$zt(p7W*m%>ZP$y)pZ?$G3^iIg;`J3-F*lyPk zl3j!K^+S}y_yhaC4^tr6yr{Rlx2#uRBC;U9pr#A#$S!E z-F&!qI8IzFZk;eBHqsyI*z}>gxjMvO<7zzF>LA1}izKx4+<(wQ>8-8bIU}i*SrgpG)gLb-h<`QP3 zR3n$|k7I5(K{pRKBR8%a`y2Wj)*H?ny5q!SyJaqZdm?Wlc%s)t$3&<^3q(6aM?~rZ zMf|It59bu_@b0u886KXV;$Gi9)I4%72+p$3bgmz^#Lm#Jl`d+|7f+JcRrgd+W{+H^ zt;T;Oa&3S4E_d_o%=OgNo%odTOz=ox-DkpU>OKtlTiBe$>frp~3e{Jtx#F+Iy=Hx8 z17-`mRZNLhB~_tS6;+*fHFlp*Xx0SR_&S(;I!`1{b{Ci$*W5doo`xQq@9A!GmxOAb z&z@!|utWaA2BXfKb~|DlpDa(NJU1-tfWT3lN3iR}eQ)p-`q!YLKpwv5-L0hMr0+>RN$*8^L~2B7MaM&QkUj9uX)us>V3XlE;9XwD zz<)umfNMwLCHhHrX@$cZ%_Yk+#y`eKFIY}p&QdOPXgmL@I>!E8lVX!+la0HOOutjwZnZ1`iGo z?j5HiT_le*zcked4J|z_Um0r|-GTT_ZAsPCp82YIp?TEaTRrP7sbNv?~p z%aN7ElKLj?DvKmDE>$UiEZ>pb7T=a2B8@6HCGQ{)r!Xv6Dz`7yoOn*VqfSey%CE{b zOUXf7Pf|}>&vfN`pmdN6K@UTLj6-zFjpCKJCp;a{4WSI9_Ei{9B10!bGs8|xNlQx0 zYSDgix~|??g;&2tx-$(G#44OrhLX@wUhn$RDs6eoQY3t2EYxI$9 zgKWBNqU_07-&jF9Lwawz0^3~8q~?RBrKYQ9x2C41s^*GjW38UaZi$<{6z~odTVS<=|!VW`^e8W(B9t zZNqbO4?GVf4?_K=7R!ZIFNY>qq#NgPq({eV2Y6{i2{+&2$Tekov3d?-xrKlR?I=ur>R7NZyYDpn-cAx0xc6*-Q%La#-=$Uo++ab7)T zc+x8s7Dp)P<$iejuyOTH`ZMse;b$|4cMehxUS}F-QJYuf+vI2x2yyO2Qv8mGlJ^Op z$uA_n#VN&pjl(0l;>~cryq&6*up{vCaIOt*2A1_T^%YYTeysagYn;EWI{b|##fWuq zEcT92hEL7mu;(-1U`nhF@h*3z{qh0#mixD}#Vwyfs<^5we}&T=MFB%UvIk&y!~lA# z!dR9DU4IIEvSn&Z%0LQMYC}qFiXhXYrZ`PODsJjsQejeZvP<#`qnGk^V^)FAhvgRj z68_@0O7A@HvIirmvOu*Ds-LUKiZ<=VWctnfFCwp`r3;Ma5Q^=$=LEveW5SW;xlKcBZx|77ga6?7Qr_ z)*W9T>h8Q=HH+2sms93Zek!aqsorrIol|>9*FO7kwu+;+zQHBWrRYiq|ASoqmnIuO zZEK5)p>dY%L>@B_##^1$X64<=Y%gEi+QfS0j_D${@(V>Xr`@aZkZ~d1 zEL~dNSluaIiI#C!IcJ7!nBBW^s~k6hT>Fm8hmeb#i7Gu|eUy$ozwTX;#gKznkJzS+ z-bb%pWLz`d*jk)g@ms50giclu4S&2n{CpB|$g@1X%C=fN^UCwitKM_PE75b*%gdAa z+P5XVliGJ`JO8==Fp7!^N!Q!ep&+ha;qyc#Q%zMS-u4~F18N5^-CQ3cp))}Y$jlzkb;5R?>7D$Yk?W!&8 zjZb&hb4PVjXPH`c?QZpEs64rW{BE~N{M2M zVuukS`m{@T&^pZ?SkBM(e5z0%tYXv+s+y|Y*S+XS@vS&s+_7?+;|yI0O%5dtGYX4C z%q8|^esF*k5+P>6hr^@6u^__5_Yi;=hZ1*@XcwOrw~}Cr8;`NoGi$B%UfK6tzCxdC ztrBYM;1tJ8B&x-Gg-?fPMcm49{RDjwI)qq&|HS2Jo7KYNX>>L)F4vK{$R%J`v{K&g zarb@s%NK`OhuJfqt%$9iE$qSK!A=Ecg&~D`1)u^!Cb!U;!R7_SCd0nAT4RqLVaKe) zkMWAxrTHba#xJ%oYl7~rSJ$^kZf=Thz+;>>xVA*M-LY(Zx7Fp46ZjR5rXOxxySCFS zQ>|6U0bX@pKX1HGOjfMgxE$@yR*!uG?mcgOj?GquJp68-dLFZ39*~`hZ~2G3?a#YU zgF;~V62TKa6FU+$6Jrv|6QdGaku8v~h>!i!9#3a_e<15CyipER4pn|qZdNWXwv}ZN z5aCDjFn$O>D_MU#S`Z;|Qp_^FIc+*kIvp}?R(vAsm)1QY`9)&_I7X1YpOK!W=_Tty z|2+TXaJcn#uszO<=-IpHCvYcXG;y0sRRKDe->c>->Av)va#b;|f`TBSM+^5WQz$b; zGYHcQGi_5U)70H+r%RsE=F?_!CtasRC;c220cH2^xAae%cePJu3b~J-<5xNt&AS(u zyHvSx{C-#9o}`yOw@_RC-PlJ=$8ERF3Tdh7joEQILM&^1&+fjDl=m`MsmDyydfz$; ze4Cp946qD1Ow3qF40TM)y0>+bT1nb<`qA3-`iJ^ix(|<$_sQ3Ex$2r#8G0-3SH~Pb z>-(%m=TNKSIyz2&PW>RZn_n$yckpvsmwU|I;HpYlr%a%nE{reimC2S#l+j;PTBGfB z@yI-HxIUU~e07rN)^j4e3e`#H>2|yl+iUrxMi&)zR6ZS))(wpW@WHgzSALpVlu(cSSG4B9K23 z!x5VZ@p?x;Yuufk58lP`3jMxdB5>ous*mzeBrRXjKmohv6~_oSmq zRX$q5S4mYdSFW$^S0Av9vR1tqzDm7EM>*?#Gf{!x~;^yWj4E?VZo=O)*0e!wy4Z#bt%EHdmwC6Uy_@v%^#3dGXW4UT&k` zXTQSd#b>z(?TeemwYjAdo1YC|T$f#Uu9J^K2;C*T2Q8^Qi@nY4&3v&BBagKX#g9{t zkq$fNR~~OV-wTL7uw|t~b@31ae!u``X240X5CHu@p27Z%jyxK|o2h^zt`UNQCDQ)| zI>N^2WNv70@;3~I$;i&u85EU`(aF_pM^!Fvi4&#$q$;f@^_nE|V~1p~{ii8@U7fxnfVEjE>Wf$T9Hp|MZ@6i>>>e#;3xhFq^wTjblRxcOt5)-&K53E_p9tT zr!8y^Spnon30K`Vgfx&~o7wbjlGD2r!Tro*Q|L|L1Q52a<#HFN1Q~RNJsU|nU19D- z#|2?>&9je{I{T|)+=MzmuWU?QZ{6pHh3{S0DlYM)*lp?rb`1Ct^ z4~J`}zWW;GrV+&mwpqvV7%~9Cgbp$QtNV!XOpvMx(#x3Ks)2*3 zBe75u?t#!;Ceu1P1ksY*UV}2wH3oUqGMZU>)yyNT={XBH$sz;&z#bksR$LES#;!;D zDP@fPp@EgmH+Z{3o3#P2&%M8Kd`*aeW{}I` zu)I%)mQhO4LkRvJNIQt);2kkE1QiE)Oc|Lllr}=WRYDTWGb$_sgkmjficCdQ4x1R_ zG@KpUZ=SEF_NM0qbbneeiM=S)#zYqo8q7K^cs;>ok+bPg1*t9*o4%!Y6nvG1KIc9G zis5kKdtBJk2bYv;aK20~2y(BNa$IgfXVZDi-jF}*hm6MfU5)riG0isN48zK4kF?gO z%I=G$Q_D|=cGO_Ajhmvy3Q7YIfb-B$5x&d zYW5{b!;;Wpp*_V>yie?By08Z@*blHjlDEU{^4}v}+zTmrOE1kXKc+KOK0*Ka>O=*Tovhdy|3-#6nOK_`{fch-zv0tZ{ym=tD)1Q^^a~V}5y1SPmHua1 z4Gi$~{QTz@p)vcghJmyiK%%67rDaI-Jm>aI-Jm>) zFW|@vIPwCHynrJw;K&O&@&b;$fFm#9$O|~~0*<_ZBQM~{3pnxuj=X>)FW|@vIPwCH zynrJw;K&O&@&b;$fFm#9$O|~~0*?G&2#(M)Ke-}+Jifqy5;6!Ju`zKpGcmTXF*Gw_ zw6`?_g8{pJicpl7K!nGG2c1Tgk_0M&K7Vb)!~XKd6vH~v4*Gzzm(+9uNf`c0U?oDu z7NCR8QlLPCIO;a>Ebzh1C}LqIQ8S!@L~rQpFHv%a@VC*_;Lm8U8!Rb_x)&be5_DH! zAbOvL5{-chSx#CQA*PxGBFGFR<}H=eWo6DeX4yp%Aw*hhH)E`6yUmQFBHdb};IO?A zOlODpj8gCO9!cetJ=cA>ezag2jcuV&`A)OP^f^4hp2s}FcWiMZUBP{8u$c)QjChMN z+)~LJyTwszqkTsFQR4UqoY#FTQCcsvxn{~-&y?sZZvdIykPH?EVgaZTe-WM0P}qEv z800($50vmhx&9z-K%MssxM8xeH8yc)bhohv!vo)WMk+^~)H-gRykg+f_laZL% z8rd0J*qRBDsj7%GaFYoM!t;{?6+~4u6~#ylZT}&}U}NHJXl&?gNTR4JA}b|ILdC$u zBm)#d6}5p+}s$A46Us#%p46F?HtX1J?rvl3N zPtjjle%CZ{x3_b2W^gibb_R8z6Q~#xekT(L7f_d)h+9~jC>lDO|I_|o&HujMN{+{t}@G2!a9t>>W*9Elk{gtt7t}`+IcLf0PU|G)88e2cf9n6 zMEPGy{K*Z|?|T0gqQm{C*x%Z6^_O}esP1=}-+fsAQu_NyFgyJFmi?7S%iooM_rLeg zI#L9TJO7LFzw^fTyC6v5PcHTT5R`TKj|BhQ{`}p!-Y@mPcSPP_ssC43d%suqcQ1E; zRMyn>Un={ziNo=`#9z;tZI8c@_-|YI*E5E}^RM*&&5Zf4GQa4}f4(RJ%(MQG`HkiL zy=}kW$^Q`f(D5(#_1}$uznlGGyx93)Yw$1kHbohzUrzx5RM5K}6gil+=hxo<0qY71 A761SM diff --git a/applications/external/usb_hid_autofire/usb_hid_autofire.png b/applications/external/usb_hid_autofire/usb_hid_autofire.png deleted file mode 100644 index 369bff02253d7473f33d23cb592132f35982d180..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 641 zcmV-{0)G98P)e zlG%+*k||Ai4%8=T%ML zhSPQEyt6Y-=3Am1!eYE_=-5_Wc>PxeO_zW@erXbqKn z*v9|>010qNS#tmY4`BcR4`BhQKc{H`006m3L_t&-S8b3n3WP8aMTcdJ)HZ@1K`gcO z5T3{z!CK1#2*U#}@$CC9Ns^%bAx%?y;C(mix)x*1;}9o)(=?65 zar_AKjs6P0<2VlW3D&l4)Pl!}Q54C#t}>3}HK}b|@gJl|?&}GA`@0^P!!T%Bme<13 bLkoNWB5%k`78DJm00000NkvXXu0mjfs(>Tu diff --git a/applications/external/usb_hid_autofire/usb_hid_autofire.svg b/applications/external/usb_hid_autofire/usb_hid_autofire.svg deleted file mode 100644 index ed66f3cfd..000000000 --- a/applications/external/usb_hid_autofire/usb_hid_autofire.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - A - - diff --git a/applications/external/usb_hid_autofire/version.h b/applications/external/usb_hid_autofire/version.h deleted file mode 100644 index ac1f5d0fa..000000000 --- a/applications/external/usb_hid_autofire/version.h +++ /dev/null @@ -1 +0,0 @@ -#define VERSION "0.4" From ca71eaf328448c7116d4484ac3453a571e0f9c39 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 12:44:02 +0100 Subject: [PATCH 268/282] Fix favorite timeout with nfc --- applications/main/archive/helpers/favorite_timeout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/archive/helpers/favorite_timeout.c b/applications/main/archive/helpers/favorite_timeout.c index b46057d82..7643ebfc8 100644 --- a/applications/main/archive/helpers/favorite_timeout.c +++ b/applications/main/archive/helpers/favorite_timeout.c @@ -11,7 +11,7 @@ bool process_favorite_launch(char** args) { void favorite_timeout_callback(void* _ctx) { FavoriteTImeoutCtx* ctx = _ctx; - while(scene_manager_handle_back_event(ctx->scene_manager)) + while(scene_manager_previous_scene(ctx->scene_manager)) ; view_dispatcher_stop(ctx->view_dispatcher); } From d2c1a91a3ab6b9bc4fcfd5d40cfe9a37061a0bcd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 13:55:31 +0100 Subject: [PATCH 269/282] Support subghz parsed recs for favorite timeout --- .../subghz/scenes/subghz_scene_transmitter.c | 35 +++++++++++++++++++ applications/main/subghz/subghz.c | 2 ++ applications/main/subghz/views/transmitter.c | 15 -------- applications/main/subghz/views/transmitter.h | 15 +++++++- 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index ab9155fb7..77ae97cba 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -1,6 +1,7 @@ #include "../subghz_i.h" #include "../views/transmitter.h" #include +#include #include #include @@ -50,6 +51,14 @@ bool subghz_scene_transmitter_update_data_show(void* context) { return ret; } +FuriTimer* fav_timer = NULL; + +void fav_timer_callback(void* context) { + SubGhz* subghz = context; + scene_manager_handle_custom_event( + subghz->scene_manager, SubGhzCustomEventViewTransmitterSendStop); +} + void subghz_scene_transmitter_on_enter(void* context) { SubGhz* subghz = context; @@ -66,6 +75,23 @@ void subghz_scene_transmitter_on_enter(void* context) { subghz->state_notifications = SubGhzNotificationStateIDLE; view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTransmitter); + + // Auto send and exit with favorites + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneTransmitter)) { + subghz_custom_btn_set(0); + scene_manager_handle_custom_event( + subghz->scene_manager, SubGhzCustomEventViewTransmitterSendStart); + with_view_model( + subghz->subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + model->show_button = false; + }, + true); + fav_timer = furi_timer_alloc(fav_timer_callback, FuriTimerTypeOnce, subghz); + furi_timer_start(fav_timer, XTREME_SETTINGS()->favorite_timeout * furi_kernel_get_tick_frequency()); + subghz->state_notifications = SubGhzNotificationStateTx; + } } bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { @@ -109,6 +135,15 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { subghz_sleep(subghz); furi_hal_subghz_set_rolling_counter_mult(tmp_counter); } + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneTransmitter)) { + if(fav_timer) { + furi_timer_stop(fav_timer); + furi_timer_free(fav_timer); + } + while(scene_manager_handle_back_event(subghz->scene_manager)) + ; + view_dispatcher_stop(subghz->view_dispatcher); + } return true; } else if(event.event == SubGhzCustomEventViewTransmitterBack) { subghz->state_notifications = SubGhzNotificationStateIDLE; diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 906b8e074..6752b4e55 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -482,6 +482,8 @@ int32_t subghz_app(char* p) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); } else { //Load transmitter TX + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneTransmitter, is_favorite); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); } } else { diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index 2bec77690..5667cd59f 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -6,21 +6,6 @@ #include -struct SubGhzViewTransmitter { - View* view; - SubGhzViewTransmitterCallback callback; - void* context; -}; - -typedef struct { - FuriString* frequency_str; - FuriString* preset_str; - FuriString* key_str; - uint8_t show_button; - FuriString* temp_button_id; - bool draw_temp_button; -} SubGhzViewTransmitterModel; - void subghz_view_transmitter_set_callback( SubGhzViewTransmitter* subghz_transmitter, SubGhzViewTransmitterCallback callback, diff --git a/applications/main/subghz/views/transmitter.h b/applications/main/subghz/views/transmitter.h index 64bcbd1af..1ae51afbd 100644 --- a/applications/main/subghz/views/transmitter.h +++ b/applications/main/subghz/views/transmitter.h @@ -3,10 +3,23 @@ #include #include "../helpers/subghz_custom_event.h" -typedef struct SubGhzViewTransmitter SubGhzViewTransmitter; +typedef struct { + FuriString* frequency_str; + FuriString* preset_str; + FuriString* key_str; + uint8_t show_button; + FuriString* temp_button_id; + bool draw_temp_button; +} SubGhzViewTransmitterModel; typedef void (*SubGhzViewTransmitterCallback)(SubGhzCustomEvent event, void* context); +typedef struct { + View* view; + SubGhzViewTransmitterCallback callback; + void* context; +} SubGhzViewTransmitter; + void subghz_view_transmitter_set_callback( SubGhzViewTransmitter* subghz_transmitter, SubGhzViewTransmitterCallback callback, From 2fcc3d1ae2f9dba3f07c8a1131e5e17cd4b1a445 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 14:29:30 +0100 Subject: [PATCH 270/282] Format --- applications/main/nfc/scenes/nfc_scene_nfc_data_info.c | 2 +- applications/main/subghz/scenes/subghz_scene_transmitter.c | 7 +++---- lib/nfc/protocols/nfcv.c | 2 -- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index f3e94d2be..1a879d8a0 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -204,7 +204,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); break; } - // Set tag general data + // Set tag general data } else if(type == FuriHalNfcTypeF) { // Set NFC-F data furi_string_cat_printf(temp_str, "ISO 18092 (NFC-F)\n"); diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 77ae97cba..7877e3c14 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -84,12 +84,11 @@ void subghz_scene_transmitter_on_enter(void* context) { with_view_model( subghz->subghz_transmitter->view, SubGhzViewTransmitterModel * model, - { - model->show_button = false; - }, + { model->show_button = false; }, true); fav_timer = furi_timer_alloc(fav_timer_callback, FuriTimerTypeOnce, subghz); - furi_timer_start(fav_timer, XTREME_SETTINGS()->favorite_timeout * furi_kernel_get_tick_frequency()); + furi_timer_start( + fav_timer, XTREME_SETTINGS()->favorite_timeout * furi_kernel_get_tick_frequency()); subghz->state_notifications = SubGhzNotificationStateTx; } } diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index c9025ee90..2e817a538 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -1310,7 +1310,6 @@ bool nfcv_emu_loop( } nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data); - /* determine readers fc by analyzing transmission duration */ uint32_t duration = eof_timestamp - sof_timestamp; float fc_1024 = (4.0f * duration) / (4 * (frame_pos * 4 + 1) + 1); @@ -1346,4 +1345,3 @@ bool nfcv_emu_loop( return ret; } - From 38ba334a1ab2358a9fed715185fbe5bb41cd2436 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 14:41:24 +0100 Subject: [PATCH 271/282] Fix build --- lib/nfc/nfc_device.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 7f8518c7d..af0ef42ae 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -1401,17 +1401,18 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { break; nfc_device_prepare_format_string(dev, temp_str); if(!flipper_format_write_string(file, "Device type", temp_str)) break; - // Write UID - if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break; + // Write UID, ATQA, SAK + if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) + break; if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; if(dev->format != NfcDeviceSaveFormatNfcV) { // Write ATQA, SAK if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break; // Save ATQA in MSB order for correct companion apps display - uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; + uint8_t atqa[2] = {data->a_data.atqa[1], data->a_data.atqa[0]}; if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; - if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + if(!flipper_format_write_hex(file, "SAK", &data->a_data.sak, 1)) break; } // Save more data if necessary @@ -1502,14 +1503,14 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; if(dev->format != NfcDeviceSaveFormatNfcV) { if(version == version_with_lsb_atqa) { - if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; + if(!flipper_format_read_hex(file, "ATQA", data->a_data.atqa, 2)) break; } else { uint8_t atqa[2] = {}; if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; - data->atqa[0] = atqa[1]; - data->atqa[1] = atqa[0]; + data->a_data.atqa[0] = atqa[1]; + data->a_data.atqa[1] = atqa[0]; } - if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; + if(!flipper_format_read_hex(file, "SAK", &data->a_data.sak, 1)) break; } // Load CUID uint8_t* cuid_start = data->uid; From 4fb63eb36b503530f3896a8541c6d74f10ef62a3 Mon Sep 17 00:00:00 2001 From: jl1990 Date: Tue, 2 May 2023 18:36:06 +0200 Subject: [PATCH 272/282] Code cleanup --- applications/debug/accessor/accessor_app.cpp | 2 +- .../battery_test_app/views/battery_info.c | 4 ++-- .../airmouse/tracking/util/matrix_4x4.h | 2 +- .../avr_isp_programmer/helpers/avr_isp.c | 18 ++++++++--------- .../lib/driver/avr_isp_prog.c | 4 ++-- .../external/barcode_gen/barcode_utils.c | 4 ++-- applications/external/brainfuck/worker.c | 2 +- applications/external/calculator/calculator.c | 2 +- .../external/doom/doom_music_player_worker.c | 2 +- .../scene/flipfrid_scene_run_attack.c | 8 ++++---- applications/external/hex_viewer/hex_viewer.c | 2 +- .../scene/ibtnfuzzer_scene_run_attack.c | 12 +++++------ .../external/ifttt/ifttt_virtual_button.c | 2 +- applications/external/ifttt/views/send_view.c | 2 +- applications/external/metronome/metronome.c | 2 +- .../music_beeper/music_beeper_worker.c | 2 +- .../music_player/music_player_worker.c | 2 +- .../external/picopass/rfal_picopass.c | 2 +- .../pocsag_pager/pocsag_pager_app_i.c | 2 +- .../scenes/pocsag_pager_receiver.c | 4 ++-- .../external/pomodoro/flipp_pomodoro_app.c | 12 +++++------ applications/external/pomodoro/helpers/time.c | 4 ++-- .../pomodoro/modules/flipp_pomodoro.c | 18 ++++++++--------- .../scenes/flipp_pomodoro_scene_timer.c | 14 ++++++------- .../views/flipp_pomodoro_timer_view.c | 20 +++++++++---------- .../rubiks_cube_scrambler.c | 2 +- applications/external/sam/stm32_sam.cpp | 2 +- .../external/text_viewer/text_viewer.c | 2 +- .../external/totp/lib/roll_value/roll_value.c | 2 +- applications/external/wav_player/wav_parser.c | 2 +- .../scenes/weather_station_receiver.c | 4 ++-- .../weather_station/weather_station_app_i.c | 2 +- applications/main/fap_loader/fap_loader_app.c | 2 +- applications/main/ibutton/ibutton_cli.c | 8 ++++---- applications/main/lfrfid/lfrfid_cli.c | 2 +- applications/main/onewire/onewire_cli.c | 2 +- .../subghz/scenes/subghz_scene_read_raw.c | 8 ++++---- applications/main/subghz/subghz_i.c | 2 +- applications/services/cli/cli_commands.c | 2 +- applications/services/crypto/crypto_cli.c | 2 +- .../services/dolphin/helpers/dolphin_state.c | 2 +- applications/services/gui/modules/popup.c | 2 +- applications/services/gui/modules/text_box.c | 2 +- .../services/notification/notification_app.c | 10 +++++----- .../notification/notification_app_api.c | 8 ++++---- .../services/rgb_backlight/rgb_backlight.c | 4 ++-- applications/services/storage/storage_cli.c | 6 +++--- .../services/storage/storages/storage_int.c | 2 +- applications/settings/about/about.c | 4 ++-- .../power_settings_app/views/battery_info.c | 4 ++-- 50 files changed, 118 insertions(+), 118 deletions(-) diff --git a/applications/debug/accessor/accessor_app.cpp b/applications/debug/accessor/accessor_app.cpp index 2e40b3c35..05a248efa 100644 --- a/applications/debug/accessor/accessor_app.cpp +++ b/applications/debug/accessor/accessor_app.cpp @@ -23,7 +23,7 @@ void AccessorApp::run(void) { exit = switch_to_previous_scene(); } } - }; + } scenes[current_scene]->on_exit(this); diff --git a/applications/debug/battery_test_app/views/battery_info.c b/applications/debug/battery_test_app/views/battery_info.c index 5353a2e2a..e32d1b461 100644 --- a/applications/debug/battery_test_app/views/battery_info.c +++ b/applications/debug/battery_test_app/views/battery_info.c @@ -17,7 +17,7 @@ static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) canvas_draw_box(canvas, x - 4, y + 16, 24, 6); canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); -}; +} static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { char emote[20] = {}; @@ -85,7 +85,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); -}; +} static void battery_info_draw_callback(Canvas* canvas, void* context) { furi_assert(context); diff --git a/applications/external/airmouse/tracking/util/matrix_4x4.h b/applications/external/airmouse/tracking/util/matrix_4x4.h index 9934f6be0..83f151fc4 100644 --- a/applications/external/airmouse/tracking/util/matrix_4x4.h +++ b/applications/external/airmouse/tracking/util/matrix_4x4.h @@ -34,4 +34,4 @@ private: } // namespace cardboard -#endif // CARDBOARD_SDK_UTIL_MATRIX4X4_H_ +#endif // CARDBOARD_SDK_UTIL_MATRIX_4X4_H_ diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c index 283c17bfd..1b9e2fd1f 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.c @@ -174,7 +174,7 @@ static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) { while((furi_get_tick() - starttime) < 30) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { break; - }; + } } } } @@ -357,7 +357,7 @@ uint8_t avr_isp_read_lock_byte(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) { break; - }; + } data = 0x00; } return data; @@ -377,7 +377,7 @@ bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) { ret = true; break; - }; + } } } return ret; @@ -392,7 +392,7 @@ uint8_t avr_isp_read_fuse_low(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) { break; - }; + } data = 0x00; } return data; @@ -412,7 +412,7 @@ bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) { ret = true; break; - }; + } } } return ret; @@ -427,7 +427,7 @@ uint8_t avr_isp_read_fuse_high(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) { break; - }; + } data = 0x00; } return data; @@ -447,7 +447,7 @@ bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) { ret = true; break; - }; + } } } return ret; @@ -462,7 +462,7 @@ uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) { break; - }; + } data = 0x00; } return data; @@ -482,7 +482,7 @@ bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) { ret = true; break; - }; + } } } return ret; diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c index 0f46872dd..051d97e9c 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -111,7 +111,7 @@ static uint8_t avr_isp_prog_getch(AvrIspProg* instance) { uint8_t data[1] = {0}; while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) { if(instance->exit) break; - }; + } return data[0]; } @@ -348,7 +348,7 @@ static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t dat while((furi_get_tick() - starttime) < 30) { if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { break; - }; + } } } } diff --git a/applications/external/barcode_gen/barcode_utils.c b/applications/external/barcode_gen/barcode_utils.c index 0a4770045..502014d85 100644 --- a/applications/external/barcode_gen/barcode_utils.c +++ b/applications/external/barcode_gen/barcode_utils.c @@ -98,7 +98,7 @@ const char* get_error_code_name(ErrorCode error_code) { return "OK"; default: return "Unknown Code"; - }; + } } const char* get_error_code_message(ErrorCode error_code) { @@ -121,5 +121,5 @@ const char* get_error_code_message(ErrorCode error_code) { return "OK"; default: return "Could not read barcode data"; - }; + } } \ No newline at end of file diff --git a/applications/external/brainfuck/worker.c b/applications/external/brainfuck/worker.c index 584bb9fb8..8fed94b40 100644 --- a/applications/external/brainfuck/worker.c +++ b/applications/external/brainfuck/worker.c @@ -111,7 +111,7 @@ void rShift() { memset((tmp + stackSize) - BF_STACK_STEP_SIZE, 0x00, BF_STACK_STEP_SIZE); bfStack = (uint8_t*)tmp; - }; + } if(stackPtr > stackSizeReal) { stackSizeReal = stackPtr; } diff --git a/applications/external/calculator/calculator.c b/applications/external/calculator/calculator.c index 1ca1d3a86..cc12cb7d1 100644 --- a/applications/external/calculator/calculator.c +++ b/applications/external/calculator/calculator.c @@ -199,7 +199,7 @@ void generate_calculator_layout(Canvas* canvas) { canvas_draw_str(canvas, 19, 118, " 0"); canvas_draw_str(canvas, 35, 118, " ."); canvas_draw_str(canvas, 51, 118, " ="); -}; +} void calculator_draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); diff --git a/applications/external/doom/doom_music_player_worker.c b/applications/external/doom/doom_music_player_worker.c index e81549625..c8b9bb9db 100644 --- a/applications/external/doom/doom_music_player_worker.c +++ b/applications/external/doom/doom_music_player_worker.c @@ -393,7 +393,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E(TAG, "Unable to open file"); break; - }; + } uint16_t ret = 0; do { diff --git a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c index 6c726832a..225752559 100644 --- a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c +++ b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c @@ -356,7 +356,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { stream_rewind(context->uids_stream); end_of_list = true; break; - }; + } if(furi_string_get_char(context->data_str, 0) == '#') continue; if(furi_string_size(context->data_str) != 11) break; break; @@ -370,7 +370,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { notification_message(context->notify, &sequence_blink_stop); notification_message(context->notify, &sequence_error); break; - }; + } // string is valid, parse it in context->payload for(uint8_t i = 0; i < 5; i++) { @@ -394,7 +394,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { stream_rewind(context->uids_stream); end_of_list = true; break; - }; + } if(furi_string_get_char(context->data_str, 0) == '#') continue; if(furi_string_size(context->data_str) != 9) break; break; @@ -408,7 +408,7 @@ void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { notification_message(context->notify, &sequence_blink_stop); notification_message(context->notify, &sequence_error); break; - }; + } // string is valid, parse it in context->payload for(uint8_t i = 0; i < 4; i++) { diff --git a/applications/external/hex_viewer/hex_viewer.c b/applications/external/hex_viewer/hex_viewer.c index 50c34d634..bd4afa1a8 100644 --- a/applications/external/hex_viewer/hex_viewer.c +++ b/applications/external/hex_viewer/hex_viewer.c @@ -163,7 +163,7 @@ static bool hex_viewer_open_file(HexViewer* hex_viewer, const char* file_path) { FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); isOk = false; break; - }; + } hex_viewer->model->file_size = stream_size(hex_viewer->model->stream); } while(false); diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c index 13ec6e6be..53f2f694f 100644 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c +++ b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c @@ -242,7 +242,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { stream_rewind(context->uids_stream); end_of_list = true; break; - }; + } if(furi_string_get_char(context->data_str, 0) == '#') continue; if(furi_string_size(context->data_str) != 17) break; break; @@ -256,7 +256,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { notification_message(context->notify, &sequence_blink_stop); notification_message(context->notify, &sequence_error); break; - }; + } // string is valid, parse it in context->payload for(uint8_t i = 0; i < 8; i++) { @@ -280,7 +280,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { stream_rewind(context->uids_stream); end_of_list = true; break; - }; + } if(furi_string_get_char(context->data_str, 0) == '#') continue; if(furi_string_size(context->data_str) != 5) break; break; @@ -294,7 +294,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { notification_message(context->notify, &sequence_blink_stop); notification_message(context->notify, &sequence_error); break; - }; + } // string is valid, parse it in context->payload for(uint8_t i = 0; i < 2; i++) { @@ -318,7 +318,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { stream_rewind(context->uids_stream); end_of_list = true; break; - }; + } if(furi_string_get_char(context->data_str, 0) == '#') continue; if(furi_string_size(context->data_str) != 9) break; break; @@ -332,7 +332,7 @@ void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { notification_message(context->notify, &sequence_blink_stop); notification_message(context->notify, &sequence_error); break; - }; + } // string is valid, parse it in context->payload for(uint8_t i = 0; i < 4; i++) { diff --git a/applications/external/ifttt/ifttt_virtual_button.c b/applications/external/ifttt/ifttt_virtual_button.c index e23b8715d..9a0be7ef7 100644 --- a/applications/external/ifttt/ifttt_virtual_button.c +++ b/applications/external/ifttt/ifttt_virtual_button.c @@ -139,7 +139,7 @@ void send_serial_command_config(ESerialCommand command, Settings* settings) { break; default: return; - }; + } furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); } diff --git a/applications/external/ifttt/views/send_view.c b/applications/external/ifttt/views/send_view.c index 6046c39e3..e1638e7a7 100644 --- a/applications/external/ifttt/views/send_view.c +++ b/applications/external/ifttt/views/send_view.c @@ -38,7 +38,7 @@ void send_serial_command_send(ESerialCommand command) { break; default: return; - }; + } furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); } diff --git a/applications/external/metronome/metronome.c b/applications/external/metronome/metronome.c index a01f4418d..46231d66d 100644 --- a/applications/external/metronome/metronome.c +++ b/applications/external/metronome/metronome.c @@ -178,7 +178,7 @@ static void timer_callback(void* ctx) { case Silent: break; } - }; + } // this is a bit of a kludge... if we are on vibro and unpronounced, stop vibro after half the usual duration switch(metronome_state->output_mode) { diff --git a/applications/external/music_beeper/music_beeper_worker.c b/applications/external/music_beeper/music_beeper_worker.c index e06e77447..c95fe8d3a 100644 --- a/applications/external/music_beeper/music_beeper_worker.c +++ b/applications/external/music_beeper/music_beeper_worker.c @@ -399,7 +399,7 @@ bool music_beeper_worker_load_rtttl_from_file(MusicBeeperWorker* instance, const if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E(TAG, "Unable to open file"); break; - }; + } uint16_t ret = 0; do { diff --git a/applications/external/music_player/music_player_worker.c b/applications/external/music_player/music_player_worker.c index ee350ee80..6a712d3e3 100644 --- a/applications/external/music_player/music_player_worker.c +++ b/applications/external/music_player/music_player_worker.c @@ -397,7 +397,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E(TAG, "Unable to open file"); break; - }; + } uint16_t ret = 0; do { diff --git a/applications/external/picopass/rfal_picopass.c b/applications/external/picopass/rfal_picopass.c index ac66cb92d..e8ca64403 100644 --- a/applications/external/picopass/rfal_picopass.c +++ b/applications/external/picopass/rfal_picopass.c @@ -48,7 +48,7 @@ FuriHalNfcReturn rfalPicoPassPollerInitialize(void) { FuriHalNfcModePollPicopass, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48); if(ret != FuriHalNfcReturnOk) { return ret; - }; + } furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_PICOPASS); diff --git a/applications/external/pocsag_pager/pocsag_pager_app_i.c b/applications/external/pocsag_pager/pocsag_pager_app_i.c index ff73ab50e..85e2a4bcf 100644 --- a/applications/external/pocsag_pager/pocsag_pager_app_i.c +++ b/applications/external/pocsag_pager/pocsag_pager_app_i.c @@ -131,7 +131,7 @@ void pcsg_hopper_update(POCSAGPagerApp* app) { if(app->txrx->txrx_state == PCSGTxRxStateRx) { pcsg_rx_end(app); - }; + } if(app->txrx->txrx_state == PCSGTxRxStateIDLE) { subghz_receiver_reset(app->txrx->receiver); app->txrx->preset->frequency = diff --git a/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c b/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c index 658b70fea..60dca01c9 100644 --- a/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c +++ b/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c @@ -133,7 +133,7 @@ void pocsag_pager_scene_receiver_on_enter(void* context) { if(app->txrx->txrx_state == PCSGTxRxStateRx) { pcsg_rx_end(app); - }; + } if((app->txrx->txrx_state == PCSGTxRxStateIDLE) || (app->txrx->txrx_state == PCSGTxRxStateSleep)) { pcsg_begin( @@ -158,7 +158,7 @@ bool pocsag_pager_scene_receiver_on_event(void* context, SceneManagerEvent event if(app->txrx->txrx_state == PCSGTxRxStateRx) { pcsg_rx_end(app); pcsg_sleep(app); - }; + } app->txrx->hopper_state = PCSGHopperStateOFF; app->txrx->idx_menu_chosen = 0; subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app); diff --git a/applications/external/pomodoro/flipp_pomodoro_app.c b/applications/external/pomodoro/flipp_pomodoro_app.c index 5adca1edb..718d18b00 100644 --- a/applications/external/pomodoro/flipp_pomodoro_app.c +++ b/applications/external/pomodoro/flipp_pomodoro_app.c @@ -9,14 +9,14 @@ static bool flipp_pomodoro_app_back_event_callback(void* ctx) { furi_assert(ctx); FlippPomodoroApp* app = ctx; return scene_manager_handle_back_event(app->scene_manager); -}; +} static void flipp_pomodoro_app_tick_event_callback(void* ctx) { furi_assert(ctx); FlippPomodoroApp* app = ctx; scene_manager_handle_custom_event(app->scene_manager, FlippPomodoroAppCustomEventTimerTick); -}; +} static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) { furi_assert(ctx); @@ -40,7 +40,7 @@ static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) break; } return scene_manager_handle_custom_event(app->scene_manager, event); -}; +} FlippPomodoroApp* flipp_pomodoro_app_alloc() { FlippPomodoroApp* app = malloc(sizeof(FlippPomodoroApp)); @@ -71,7 +71,7 @@ FlippPomodoroApp* flipp_pomodoro_app_alloc() { scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneTimer); return app; -}; +} void flipp_pomodoro_app_free(FlippPomodoroApp* app) { view_dispatcher_remove_view(app->view_dispatcher, FlippPomodoroAppViewTimer); @@ -81,7 +81,7 @@ void flipp_pomodoro_app_free(FlippPomodoroApp* app) { free(app); furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); -}; +} int32_t flipp_pomodoro_app(void* p) { UNUSED(p); @@ -92,4 +92,4 @@ int32_t flipp_pomodoro_app(void* p) { flipp_pomodoro_app_free(app); return 0; -}; +} diff --git a/applications/external/pomodoro/helpers/time.c b/applications/external/pomodoro/helpers/time.c index 7fb0d13c2..02540a939 100644 --- a/applications/external/pomodoro/helpers/time.c +++ b/applications/external/pomodoro/helpers/time.c @@ -7,7 +7,7 @@ const int TIME_MINUTES_IN_HOUR = 60; uint32_t time_now() { return furi_hal_rtc_get_timestamp(); -}; +} TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) { const uint32_t duration_seconds = end - begin; @@ -17,4 +17,4 @@ TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) { return ( TimeDifference){.total_seconds = duration_seconds, .minutes = minutes, .seconds = seconds}; -}; +} diff --git a/applications/external/pomodoro/modules/flipp_pomodoro.c b/applications/external/pomodoro/modules/flipp_pomodoro.c index 161e862f8..133db1eba 100644 --- a/applications/external/pomodoro/modules/flipp_pomodoro.c +++ b/applications/external/pomodoro/modules/flipp_pomodoro.c @@ -38,22 +38,22 @@ void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) { furi_assert(state); state->current_stage_index = state->current_stage_index + 1; state->started_at_timestamp = time_now(); -}; +} PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state) { furi_assert(state); return flipp_pomodoro__stage_by_index(state->current_stage_index); -}; +} char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state) { furi_assert(state); return current_stage_label[flipp_pomodoro__get_stage(state)]; -}; +} char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) { furi_assert(state); return next_stage_label[flipp_pomodoro__stage_by_index(state->current_stage_index + 1)]; -}; +} uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) { const int32_t stage_duration_seconds_map[] = { @@ -63,22 +63,22 @@ uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) }; return stage_duration_seconds_map[flipp_pomodoro__get_stage(state)]; -}; +} uint32_t flipp_pomodoro__stage_expires_timestamp(FlippPomodoroState* state) { return state->started_at_timestamp + flipp_pomodoro__current_stage_total_duration(state); -}; +} TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state) { const uint32_t stage_ends_at = flipp_pomodoro__stage_expires_timestamp(state); return time_difference_seconds(time_now(), stage_ends_at); -}; +} bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state) { const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state); const uint8_t seamless_change_span_seconds = 1; return (time_now() - seamless_change_span_seconds) >= expired_by; -}; +} FlippPomodoroState* flipp_pomodoro__new() { FlippPomodoroState* state = malloc(sizeof(FlippPomodoroState)); @@ -86,4 +86,4 @@ FlippPomodoroState* flipp_pomodoro__new() { state->started_at_timestamp = now; state->current_stage_index = 0; return state; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/applications/external/pomodoro/scenes/flipp_pomodoro_scene_timer.c b/applications/external/pomodoro/scenes/flipp_pomodoro_scene_timer.c index 2190dbdb7..8ed5dd5e7 100644 --- a/applications/external/pomodoro/scenes/flipp_pomodoro_scene_timer.c +++ b/applications/external/pomodoro/scenes/flipp_pomodoro_scene_timer.c @@ -15,7 +15,7 @@ void flipp_pomodoro_scene_timer_sync_view_state(void* ctx) { flipp_pomodoro_view_timer_set_state( flipp_pomodoro_view_timer_get_view(app->timer_view), app->state); -}; +} void flipp_pomodoro_scene_timer_on_next_stage(void* ctx) { furi_assert(ctx); @@ -23,7 +23,7 @@ void flipp_pomodoro_scene_timer_on_next_stage(void* ctx) { FlippPomodoroApp* app = ctx; view_dispatcher_send_custom_event(app->view_dispatcher, FlippPomodoroAppCustomEventStageSkip); -}; +} void flipp_pomodoro_scene_timer_on_enter(void* ctx) { furi_assert(ctx); @@ -34,7 +34,7 @@ void flipp_pomodoro_scene_timer_on_enter(void* ctx) { flipp_pomodoro_scene_timer_sync_view_state(app); flipp_pomodoro_view_timer_set_on_right_cb( app->timer_view, flipp_pomodoro_scene_timer_on_next_stage, app); -}; +} void flipp_pomodoro_scene_timer_handle_custom_event( FlippPomodoroApp* app, @@ -48,7 +48,7 @@ void flipp_pomodoro_scene_timer_handle_custom_event( if(custom_event == FlippPomodoroAppCustomEventStateUpdated) { flipp_pomodoro_scene_timer_sync_view_state(app); } -}; +} bool flipp_pomodoro_scene_timer_on_event(void* ctx, SceneManagerEvent event) { furi_assert(ctx); @@ -62,10 +62,10 @@ bool flipp_pomodoro_scene_timer_on_event(void* ctx, SceneManagerEvent event) { return ExitSignal; default: break; - }; + } return SceneEventNotConusmed; -}; +} void flipp_pomodoro_scene_timer_on_exit(void* ctx) { UNUSED(ctx); -}; \ No newline at end of file +} \ No newline at end of file diff --git a/applications/external/pomodoro/views/flipp_pomodoro_timer_view.c b/applications/external/pomodoro/views/flipp_pomodoro_timer_view.c index e8e0383b7..302380ddd 100644 --- a/applications/external/pomodoro/views/flipp_pomodoro_timer_view.c +++ b/applications/external/pomodoro/views/flipp_pomodoro_timer_view.c @@ -58,7 +58,7 @@ static void remaining_stage_time_string); furi_string_free(timer_string); -}; +} static void draw_str_with_drop_shadow( Canvas* canvas, @@ -92,7 +92,7 @@ static void static void flipp_pomodoro_view_timer_draw_callback(Canvas* canvas, void* _model) { if(!_model) { return; - }; + } FlippPomodoroTimerViewModel* model = _model; @@ -109,7 +109,7 @@ static void flipp_pomodoro_view_timer_draw_callback(Canvas* canvas, void* _model canvas_set_font(canvas, FontSecondary); elements_button_right(canvas, flipp_pomodoro__next_stage_label(model->state)); -}; +} bool flipp_pomodoro_view_timer_input_callback(InputEvent* event, void* ctx) { furi_assert(ctx); @@ -125,15 +125,15 @@ bool flipp_pomodoro_view_timer_input_callback(InputEvent* event, void* ctx) { furi_assert(timer->right_cb_ctx); timer->right_cb(timer->right_cb_ctx); return ViewInputConsumed; - }; + } return ViewInputNotConusmed; -}; +} View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer) { furi_assert(timer); return timer->view; -}; +} void flipp_pomodoro_view_timer_assign_animation(View* view) { with_view_model( @@ -162,7 +162,7 @@ FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc() { view_set_input_callback(timer->view, flipp_pomodoro_view_timer_input_callback); return timer; -}; +} void flipp_pomodoro_view_timer_set_on_right_cb( FlippPomodoroTimerView* timer, @@ -172,7 +172,7 @@ void flipp_pomodoro_view_timer_set_on_right_cb( furi_assert(right_cb_ctx); timer->right_cb = right_cb; timer->right_cb_ctx = right_cb_ctx; -}; +} void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state) { furi_assert(view); @@ -180,7 +180,7 @@ void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state) with_view_model( view, FlippPomodoroTimerViewModel * model, { model->state = state; }, false); flipp_pomodoro_view_timer_assign_animation(view); -}; +} void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer) { furi_assert(timer); @@ -192,4 +192,4 @@ void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer) { view_free(timer->view); free(timer); -}; \ No newline at end of file +} \ No newline at end of file diff --git a/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c b/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c index 4c845b883..b73f2c448 100644 --- a/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c +++ b/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c @@ -63,7 +63,7 @@ static void draw_callback(Canvas* canvas, void* ctx) { elements_button_center(canvas, "New"); elements_button_left(canvas, notifications_enabled ? "On" : "Off"); -}; +} static void input_callback(InputEvent* input_event, void* ctx) { furi_assert(ctx); diff --git a/applications/external/sam/stm32_sam.cpp b/applications/external/sam/stm32_sam.cpp index 16f6fcaab..1ab73a66d 100644 --- a/applications/external/sam/stm32_sam.cpp +++ b/applications/external/sam/stm32_sam.cpp @@ -3945,7 +3945,7 @@ void STM32SAM::Code41240() { Insert(pos + 1, index + 1, phonemeLengthTable[index + 1], stress[pos]); Insert(pos + 2, index + 2, phonemeLengthTable[index + 2], stress[pos]); pos += 3; - }; + } } // Rewrites the phonemes using the following rules: diff --git a/applications/external/text_viewer/text_viewer.c b/applications/external/text_viewer/text_viewer.c index 59923adb9..b5ccb6ef3 100644 --- a/applications/external/text_viewer/text_viewer.c +++ b/applications/external/text_viewer/text_viewer.c @@ -157,7 +157,7 @@ static bool text_viewer_open_file(TextViewer* text_viewer, const char* file_path FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); isOk = false; break; - }; + } text_viewer->model->file_size = stream_size(text_viewer->model->stream); } while(false); diff --git a/applications/external/totp/lib/roll_value/roll_value.c b/applications/external/totp/lib/roll_value/roll_value.c index 326c7846a..563429d0d 100644 --- a/applications/external/totp/lib/roll_value/roll_value.c +++ b/applications/external/totp/lib/roll_value/roll_value.c @@ -25,4 +25,4 @@ TOTP_ROLL_VALUE_FN(int8_t, int8_t) TOTP_ROLL_VALUE_FN(uint8_t, int8_t) -TOTP_ROLL_VALUE_FN(size_t, int16_t); \ No newline at end of file +TOTP_ROLL_VALUE_FN(size_t, int16_t) \ No newline at end of file diff --git a/applications/external/wav_player/wav_parser.c b/applications/external/wav_player/wav_parser.c index 1f534bacb..8c1f22b19 100644 --- a/applications/external/wav_player/wav_parser.c +++ b/applications/external/wav_player/wav_parser.c @@ -11,7 +11,7 @@ const char* format_text(FormatTag tag) { default: return "Unknown"; } -}; +} struct WavParser { WavHeaderChunk header; diff --git a/applications/external/weather_station/scenes/weather_station_receiver.c b/applications/external/weather_station/scenes/weather_station_receiver.c index e76810430..79d01f13d 100644 --- a/applications/external/weather_station/scenes/weather_station_receiver.c +++ b/applications/external/weather_station/scenes/weather_station_receiver.c @@ -133,7 +133,7 @@ void weather_station_scene_receiver_on_enter(void* context) { if(app->txrx->txrx_state == WSTxRxStateRx) { ws_rx_end(app); - }; + } if((app->txrx->txrx_state == WSTxRxStateIDLE) || (app->txrx->txrx_state == WSTxRxStateSleep)) { ws_begin( app, @@ -157,7 +157,7 @@ bool weather_station_scene_receiver_on_event(void* context, SceneManagerEvent ev if(app->txrx->txrx_state == WSTxRxStateRx) { ws_rx_end(app); ws_sleep(app); - }; + } app->txrx->hopper_state = WSHopperStateOFF; app->txrx->idx_menu_chosen = 0; subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app); diff --git a/applications/external/weather_station/weather_station_app_i.c b/applications/external/weather_station/weather_station_app_i.c index 7236b6625..2e83a6e30 100644 --- a/applications/external/weather_station/weather_station_app_i.c +++ b/applications/external/weather_station/weather_station_app_i.c @@ -146,7 +146,7 @@ void ws_hopper_update(WeatherStationApp* app) { if(app->txrx->txrx_state == WSTxRxStateRx) { ws_rx_end(app); - }; + } if(app->txrx->txrx_state == WSTxRxStateIDLE) { subghz_receiver_reset(app->txrx->receiver); app->txrx->preset->frequency = diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 87eafaecd..2a06e348c 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -258,7 +258,7 @@ int32_t fap_loader_app(char* p) { if(fap_loader_run_selected_app(loader, false)) { fap_loader_run_selected_app(loader, true); } - }; + } } fap_loader_free(loader); diff --git a/applications/main/ibutton/ibutton_cli.c b/applications/main/ibutton/ibutton_cli.c index 54bc808b5..12da64fcc 100644 --- a/applications/main/ibutton/ibutton_cli.c +++ b/applications/main/ibutton/ibutton_cli.c @@ -31,7 +31,7 @@ static void ibutton_cli_print_usage() { printf("\tCyfral (2 bytes key_data)\r\n"); printf("\tMetakom (4 bytes key_data), must contain correct parity\r\n"); printf("\t are hex-formatted\r\n"); -}; +} static bool ibutton_cli_parse_key(iButtonProtocols* protocols, iButtonKey* key, FuriString* args) { bool result = false; @@ -124,7 +124,7 @@ static void ibutton_cli_read(Cli* cli) { ibutton_protocols_free(protocols); furi_event_flag_free(event); -}; +} typedef struct { FuriEventFlag* event; @@ -216,7 +216,7 @@ void ibutton_cli_emulate(Cli* cli, FuriString* args) { while(!cli_cmd_interrupt_received(cli)) { furi_delay_ms(100); - }; + } } while(false); @@ -226,7 +226,7 @@ void ibutton_cli_emulate(Cli* cli, FuriString* args) { ibutton_key_free(key); ibutton_worker_free(worker); ibutton_protocols_free(protocols); -}; +} void ibutton_cli(Cli* cli, FuriString* args, void* context) { UNUSED(cli); diff --git a/applications/main/lfrfid/lfrfid_cli.c b/applications/main/lfrfid/lfrfid_cli.c index a57e40de9..cc1fa2bd7 100644 --- a/applications/main/lfrfid/lfrfid_cli.c +++ b/applications/main/lfrfid/lfrfid_cli.c @@ -30,7 +30,7 @@ static void lfrfid_cli_print_usage() { printf("rfid raw_read \r\n"); printf("rfid raw_emulate \r\n"); printf("rfid raw_analyze \r\n"); -}; +} typedef struct { ProtocolId protocol; diff --git a/applications/main/onewire/onewire_cli.c b/applications/main/onewire/onewire_cli.c index 5f6cdc670..9a7ffe55c 100644 --- a/applications/main/onewire/onewire_cli.c +++ b/applications/main/onewire/onewire_cli.c @@ -21,7 +21,7 @@ void onewire_on_system_start() { static void onewire_cli_print_usage() { printf("Usage:\r\n"); printf("onewire search\r\n"); -}; +} static void onewire_cli_search(Cli* cli) { UNUSED(cli); diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 2638d2089..5734a8e9f 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -152,7 +152,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); subghz_sleep(subghz); - }; + } //Stop save file subghz_protocol_raw_save_to_file_stop( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); @@ -196,7 +196,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); subghz_sleep(subghz); - }; + } subghz->state_notifications = SubGhzNotificationStateIDLE; consumed = true; break; @@ -305,7 +305,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); subghz_sleep(subghz); - }; + } size_t spl_count = subghz_protocol_raw_get_sample_write( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); @@ -439,7 +439,7 @@ void subghz_scene_read_raw_on_exit(void* context) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); subghz_sleep(subghz); - }; + } subghz->state_notifications = SubGhzNotificationStateIDLE; notification_message(subghz->notifications, &sequence_reset_rgb); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index dd0160213..cbef8fe97 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -603,7 +603,7 @@ void subghz_hopper_update(SubGhz* subghz) { if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { subghz_rx_end(subghz); - }; + } if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { subghz_receiver_reset(subghz->txrx->receiver); subghz->txrx->preset->frequency = subghz_setting_get_hopper_frequency( diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index a8d1b7e67..a652d0f0c 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -79,7 +79,7 @@ void cli_command_help(Cli* cli, FuriString* args, void* context) { printf("%s", furi_string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr)); CliCommandTree_next(it_right); } - }; + } if(furi_string_size(args) > 0) { cli_nl(); diff --git a/applications/services/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c index d91b448ec..34aa34dd3 100644 --- a/applications/services/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -15,7 +15,7 @@ void crypto_cli_print_usage() { printf("\thas_key \t - Check if secure enclave has key in slot\r\n"); printf( "\tstore_key \t - Store key in secure enclave. !!! NON-REVERSIBLE OPERATION - READ MANUAL FIRST !!!\r\n"); -}; +} void crypto_cli_encrypt(Cli* cli, FuriString* args) { int key_slot = 0; diff --git a/applications/services/dolphin/helpers/dolphin_state.c b/applications/services/dolphin/helpers/dolphin_state.c index 190efa5ec..fba3c5a90 100644 --- a/applications/services/dolphin/helpers/dolphin_state.c +++ b/applications/services/dolphin/helpers/dolphin_state.c @@ -101,7 +101,7 @@ bool dolphin_state_is_levelup(int icounter) { if((icounter == DOLPHIN_LEVELS[i])) { return true; } - }; + } return false; } diff --git a/applications/services/gui/modules/popup.c b/applications/services/gui/modules/popup.c index d75abb95f..ad26b2691 100644 --- a/applications/services/gui/modules/popup.c +++ b/applications/services/gui/modules/popup.c @@ -99,7 +99,7 @@ void popup_start_timer(void* context) { if(furi_timer_start(popup->timer, timer_period) != FuriStatusOk) { furi_assert(0); - }; + } } } diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c index 01ccdbf52..6dc558815 100644 --- a/applications/services/gui/modules/text_box.c +++ b/applications/services/gui/modules/text_box.c @@ -85,7 +85,7 @@ static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { // Set text position to 5th line from the end for(uint8_t i = 0; i < line_num - 5; i++) { while(*model->text_pos++ != '\n') { - }; + } } model->scroll_num = line_num - 4; model->scroll_pos = line_num - 5; diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index a26d84ab3..76dc2e052 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -36,7 +36,7 @@ void notification_message_save_settings(NotificationApp* app) { furi_event_flag_wait( m.back_event, NOTIFICATION_EVENT_COMPLETE, FuriFlagWaitAny, FuriWaitForever); furi_event_flag_free(m.back_event); -}; +} // internal layer void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { @@ -336,7 +336,7 @@ void notification_process_notification_message( } notification_message_index++; notification_message = (*message->sequence)[notification_message_index]; - }; + } // send and do minimal delay if(led_active) { @@ -447,7 +447,7 @@ static bool notification_load_settings(NotificationApp* app) { furi_record_close(RECORD_STORAGE); return fs_result; -}; +} static bool notification_save_settings(NotificationApp* app) { NotificationSettings settings; @@ -482,7 +482,7 @@ static bool notification_save_settings(NotificationApp* app) { furi_record_close(RECORD_STORAGE); return fs_result; -}; +} static void input_event_callback(const void* value, void* context) { furi_assert(value); @@ -531,7 +531,7 @@ static NotificationApp* notification_app_alloc() { notification_message(app, &sequence_display_backlight_on); return app; -}; +} // App int32_t notification_srv(void* p) { diff --git a/applications/services/notification/notification_app_api.c b/applications/services/notification/notification_app_api.c index 9bc06b013..9bcf5964f 100644 --- a/applications/services/notification/notification_app_api.c +++ b/applications/services/notification/notification_app_api.c @@ -8,13 +8,13 @@ void notification_message(NotificationApp* app, const NotificationSequence* sequ NotificationAppMessage m = { .type = NotificationLayerMessage, .sequence = sequence, .back_event = NULL}; furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk); -}; +} void notification_internal_message(NotificationApp* app, const NotificationSequence* sequence) { NotificationAppMessage m = { .type = InternalLayerMessage, .sequence = sequence, .back_event = NULL}; furi_check(furi_message_queue_put(app->queue, &m, FuriWaitForever) == FuriStatusOk); -}; +} void notification_message_block(NotificationApp* app, const NotificationSequence* sequence) { NotificationAppMessage m = { @@ -25,7 +25,7 @@ void notification_message_block(NotificationApp* app, const NotificationSequence furi_event_flag_wait( m.back_event, NOTIFICATION_EVENT_COMPLETE, FuriFlagWaitAny, FuriWaitForever); furi_event_flag_free(m.back_event); -}; +} void notification_internal_message_block( NotificationApp* app, @@ -36,4 +36,4 @@ void notification_internal_message_block( furi_event_flag_wait( m.back_event, NOTIFICATION_EVENT_COMPLETE, FuriFlagWaitAny, FuriWaitForever); furi_event_flag_free(m.back_event); -}; +} diff --git a/applications/services/rgb_backlight/rgb_backlight.c b/applications/services/rgb_backlight/rgb_backlight.c index 2dbfa7e8c..9b39b469a 100644 --- a/applications/services/rgb_backlight/rgb_backlight.c +++ b/applications/services/rgb_backlight/rgb_backlight.c @@ -103,7 +103,7 @@ void rgb_backlight_load_settings(void) { storage_file_free(file); furi_record_close(RECORD_STORAGE); rgb_settings.settings_is_loaded = true; -}; +} void rgb_backlight_save_settings(void) { RGBBacklightSettings settings; @@ -134,7 +134,7 @@ void rgb_backlight_save_settings(void) { storage_file_close(file); storage_file_free(file); furi_record_close(RECORD_STORAGE); -}; +} RGBBacklightSettings* rgb_backlight_get_settings(void) { if(!rgb_settings.settings_is_loaded) { diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index 8e2dcdbbb..d720de9d1 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -33,7 +33,7 @@ static void storage_cli_print_usage() { printf("\tmd5\t - md5 hash of the file\r\n"); printf("\tstat\t - info about file or dir\r\n"); printf("\ttimestamp\t - last modification timestamp\r\n"); -}; +} static void storage_cli_print_error(FS_Error error) { printf("Storage error: %s\r\n", storage_error_get_desc(error)); @@ -86,7 +86,7 @@ static void storage_cli_info(Cli* cli, FuriString* path) { } furi_record_close(RECORD_STORAGE); -}; +} static void storage_cli_format(Cli* cli, FuriString* path) { if(furi_string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) { @@ -112,7 +112,7 @@ static void storage_cli_format(Cli* cli, FuriString* path) { } else { storage_cli_print_usage(); } -}; +} static void storage_cli_list(Cli* cli, FuriString* path) { UNUSED(cli); diff --git a/applications/services/storage/storages/storage_int.c b/applications/services/storage/storages/storage_int.c index c1cf2c7de..8e4e2b0fc 100644 --- a/applications/services/storage/storages/storage_int.c +++ b/applications/services/storage/storages/storage_int.c @@ -160,7 +160,7 @@ static LFSData* storage_int_lfs_data_alloc() { lfs_data->config.lookahead_size = 16; return lfs_data; -}; +} // Returns true if fingerprint was invalid and LFS reformatting is needed static bool storage_int_check_and_set_fingerprint(LFSData* lfs_data) { diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 20263e887..2f61ceb2c 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -179,7 +179,7 @@ static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) canvas_draw_box(canvas, x - 4, y + 16, 24, 6); canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); -}; +} static void draw_battery(Canvas* canvas, PowerInfo* info, int x, int y) { char header[20] = {}; @@ -245,7 +245,7 @@ static void draw_battery(Canvas* canvas, PowerInfo* info, int x, int y) { canvas_draw_str_aligned(canvas, x + 92, y + 9, AlignCenter, AlignCenter, header); canvas_draw_str_aligned(canvas, x + 92, y + 19, AlignCenter, AlignCenter, value); } -}; +} static void battery_info_draw_callback(Canvas* canvas, void* context) { furi_assert(context); diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index 0956cae4f..828c518d6 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -18,7 +18,7 @@ static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) canvas_draw_box(canvas, x - 4, y + 16, 24, 6); canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); -}; +} static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { char emote[20] = {}; @@ -87,7 +87,7 @@ static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); -}; +} static void battery_info_draw_callback(Canvas* canvas, void* context) { furi_assert(context); From e8a1718b8eb9de0c4ee7243f3ad0b977c77d6321 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 17:53:54 +0100 Subject: [PATCH 273/282] Update apps --- .../external/asteroids/application.fam | 2 +- .../external/bpmtapper/img/screenshot.png | Bin 1987 -> 0 bytes .../brainfuck/icons/DolphinCommon_56x48.png | Bin 1416 -> 0 bytes .../external/brainfuck/icons/Text_10x10.png | Bin 158 -> 0 bytes .../external/cli_bridge/internal_defs.h | 4 +- .../external/doom/assets/screenshot1.png | Bin 1487 -> 0 bytes .../external/doom/assets/screenshot2.png | Bin 1705 -> 0 bytes .../external/doom/assets/screenshot3.png | Bin 1629 -> 0 bytes applications/external/geiger/flipper_geiger.c | 59 +++++++++----- .../GPIO_reader.c | 0 .../GPIO_reader_item.c | 0 .../GPIO_reader_item.h | 0 .../application.fam | 0 .../{gpio_reader_a => gpioreader_a}/icon.png | Bin .../{gpio_reader_b => gpioreader_b}/LICENSE | 0 .../application.fam | 0 .../gpio_app.c | 0 .../gpio_app.h | 0 .../gpio_app_i.h | 0 .../gpio_custom_event.h | 0 .../gpio_item.c | 0 .../gpio_item.h | 0 .../{gpio_reader_b => gpioreader_b}/icon.png | Bin .../scenes/gpio_scene.c | 0 .../scenes/gpio_scene.h | 0 .../scenes/gpio_scene_config.h | 0 .../scenes/gpio_scene_reader.c | 0 .../scenes/gpio_scene_start.c | 0 .../scenes/gpio_scene_test.c | 0 .../scenes/gpio_scene_usb_uart.c | 0 .../scenes/gpio_scene_usb_uart_close_rpc.c | 0 .../scenes/gpio_scene_usb_uart_config.c | 0 .../usb_uart_bridge.c | 0 .../usb_uart_bridge.h | 0 .../views/gpio_reader.c | 0 .../views/gpio_reader.h | 0 .../views/gpio_test.c | 0 .../views/gpio_test.h | 0 .../views/gpio_usb_uart.c | 2 +- .../views/gpio_usb_uart.h | 0 .../external/metronome/img/screenshot.png | Bin 1978 -> 0 bytes .../external/minesweeper/img/screenshot.png | Bin 1842 -> 0 bytes .../application.fam | 1 + .../tracker_engine/speaker_hal.c | 11 +-- .../tracker_engine/speaker_hal.h | 0 .../tracker_engine/tracker.c | 0 .../tracker_engine/tracker.h | 0 .../tracker_engine/tracker_notes.h | 0 .../tracker_engine/tracker_song.h | 0 .../view/tracker_view.c | 0 .../view/tracker_view.h | 0 .../zero_tracker.c | 0 .../zero_tracker.h | 0 .../zero_tracker.png | Bin .../application.fam | 0 .../clock.png | Bin .../clock_app.c | 0 .../clock_app.h | 0 .../external/pomodoro/flipp_pomodoro_app.c | 1 + .../pomodoro/modules/flipp_pomodoro.c | 5 ++ applications/external/pong/flipper_pong.c | 30 +++---- applications/external/rc2014_coleco/coleco.c | 22 +++--- .../rubiks_cube_scrambler.c | 35 ++++----- .../rubiks_cube_scrambler/scrambler.c | 74 ++++-------------- .../rubiks_cube_scrambler/scrambler.h | 1 - .../timelapse/icons/ButtonDownHollow_7x4.png | Bin 0 -> 173 bytes .../timelapse/icons/ButtonLeftHollow_4x7.png | Bin 0 -> 149 bytes .../timelapse/icons/ButtonRightHollow_4x7.png | Bin 0 -> 147 bytes .../timelapse/icons/ButtonUpHollow_7x4.png | Bin 0 -> 143 bytes applications/external/timelapse/zeitraffer.c | 32 +++++--- .../external/{wii_ec_anal => wiiec}/LICENSE | 0 .../external/{wii_ec_anal => wiiec}/WiiEC.png | Bin .../_image_tool/LICENSE | 0 .../{wii_ec_anal => wiiec}/_image_tool/README | 0 .../_image_tool/_convert.c | 0 .../_image_tool/_convert.sh | 0 .../_image_tool/_convert_images.c | 0 .../_image_tool/_convert_images.h | 0 .../_image_tool/_convert_test.c | 0 .../{wii_ec_anal => wiiec}/application.fam | 0 .../{wii_ec_anal => wiiec}/bc_logging.h | 0 .../external/{wii_ec_anal => wiiec}/err.h | 0 .../{wii_ec_anal => wiiec}/gfx/images.c | 0 .../{wii_ec_anal => wiiec}/gfx/images.h | 0 .../{wii_ec_anal => wiiec}/gfx/img_3x5_0.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_3x5_1.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_3x5_2.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_3x5_3.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_3x5_4.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_3x5_5.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_3x5_6.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_3x5_7.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_3x5_8.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_3x5_9.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_3x5_v.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_0.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_1.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_2.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_3.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_4.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_5.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_6.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_7.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_8.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_9.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_A.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_B.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_C.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_D.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_E.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_5x7_F.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_0.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_1.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_2.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_3.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_4.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_5.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_6.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_7.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_8.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_9.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_A.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_B.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_C.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_D.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_E.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_F.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_G.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_X.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_Y.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_Z.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_d_.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_n_.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_6x8_v_.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_RIP.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_cc_Cable.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_cc_Joy.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_cc_Main.c | 0 .../gfx/img_cc_btn_A1.c | 0 .../gfx/img_cc_btn_B1.c | 0 .../gfx/img_cc_btn_X1.c | 0 .../gfx/img_cc_btn_Y1.c | 0 .../gfx/img_cc_pad_LR1.c | 0 .../gfx/img_cc_pad_UD1.c | 0 .../gfx/img_cc_trg_L1.c | 0 .../gfx/img_cc_trg_L2.c | 0 .../gfx/img_cc_trg_L3.c | 0 .../gfx/img_cc_trg_L4.c | 0 .../gfx/img_cc_trg_R1.c | 0 .../gfx/img_cc_trg_R2.c | 0 .../gfx/img_cc_trg_R3.c | 0 .../gfx/img_cc_trg_R4.c | 0 .../gfx/img_csLogo_FULL.c | 0 .../gfx/img_csLogo_Small.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_ecp_SCL.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_ecp_SDA.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_ecp_port.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_key_Back.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_key_D.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_key_L.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_key_OK.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_key_OKi.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_key_R.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_key_U.c | 0 .../{wii_ec_anal => wiiec}/gfx/img_key_Ui.c | 0 .../{wii_ec_anal => wiiec}/i2c_workaround.h | 0 .../external/{wii_ec_anal => wiiec}/info.sh | 0 .../{wii_ec_anal => wiiec}/wii_anal.c | 25 ++++-- .../{wii_ec_anal => wiiec}/wii_anal.h | 3 +- .../{wii_ec_anal => wiiec}/wii_anal_ec.c | 0 .../{wii_ec_anal => wiiec}/wii_anal_ec.h | 0 .../{wii_ec_anal => wiiec}/wii_anal_keys.c | 0 .../{wii_ec_anal => wiiec}/wii_anal_keys.h | 0 .../{wii_ec_anal => wiiec}/wii_anal_lcd.c | 0 .../{wii_ec_anal => wiiec}/wii_anal_lcd.h | 0 .../{wii_ec_anal => wiiec}/wii_anal_ver.h | 1 + .../external/{wii_ec_anal => wiiec}/wii_ec.c | 0 .../external/{wii_ec_anal => wiiec}/wii_ec.h | 0 .../{wii_ec_anal => wiiec}/wii_ec_classic.c | 0 .../{wii_ec_anal => wiiec}/wii_ec_classic.h | 0 .../{wii_ec_anal => wiiec}/wii_ec_macros.h | 0 .../{wii_ec_anal => wiiec}/wii_ec_nunchuck.c | 0 .../{wii_ec_anal => wiiec}/wii_ec_nunchuck.h | 0 .../{wii_ec_anal => wiiec}/wii_ec_udraw.c | 0 .../{wii_ec_anal => wiiec}/wii_ec_udraw.h | 0 .../external/{wii_ec_anal => wiiec}/wii_i2c.c | 0 .../external/{wii_ec_anal => wiiec}/wii_i2c.h | 0 187 files changed, 149 insertions(+), 159 deletions(-) delete mode 100644 applications/external/bpmtapper/img/screenshot.png delete mode 100644 applications/external/brainfuck/icons/DolphinCommon_56x48.png delete mode 100644 applications/external/brainfuck/icons/Text_10x10.png delete mode 100644 applications/external/doom/assets/screenshot1.png delete mode 100644 applications/external/doom/assets/screenshot2.png delete mode 100644 applications/external/doom/assets/screenshot3.png rename applications/external/{gpio_reader_a => gpioreader_a}/GPIO_reader.c (100%) rename applications/external/{gpio_reader_a => gpioreader_a}/GPIO_reader_item.c (100%) rename applications/external/{gpio_reader_a => gpioreader_a}/GPIO_reader_item.h (100%) rename applications/external/{gpio_reader_a => gpioreader_a}/application.fam (100%) rename applications/external/{gpio_reader_a => gpioreader_a}/icon.png (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/LICENSE (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/application.fam (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/gpio_app.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/gpio_app.h (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/gpio_app_i.h (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/gpio_custom_event.h (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/gpio_item.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/gpio_item.h (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/icon.png (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/scenes/gpio_scene.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/scenes/gpio_scene.h (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/scenes/gpio_scene_config.h (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/scenes/gpio_scene_reader.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/scenes/gpio_scene_start.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/scenes/gpio_scene_test.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/scenes/gpio_scene_usb_uart.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/scenes/gpio_scene_usb_uart_close_rpc.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/scenes/gpio_scene_usb_uart_config.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/usb_uart_bridge.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/usb_uart_bridge.h (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/views/gpio_reader.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/views/gpio_reader.h (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/views/gpio_test.c (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/views/gpio_test.h (100%) rename applications/external/{gpio_reader_b => gpioreader_b}/views/gpio_usb_uart.c (98%) rename applications/external/{gpio_reader_b => gpioreader_b}/views/gpio_usb_uart.h (100%) delete mode 100644 applications/external/metronome/img/screenshot.png delete mode 100644 applications/external/minesweeper/img/screenshot.png rename applications/external/{musictracker => music_tracker}/application.fam (90%) rename applications/external/{musictracker => music_tracker}/tracker_engine/speaker_hal.c (91%) rename applications/external/{musictracker => music_tracker}/tracker_engine/speaker_hal.h (100%) rename applications/external/{musictracker => music_tracker}/tracker_engine/tracker.c (100%) rename applications/external/{musictracker => music_tracker}/tracker_engine/tracker.h (100%) rename applications/external/{musictracker => music_tracker}/tracker_engine/tracker_notes.h (100%) rename applications/external/{musictracker => music_tracker}/tracker_engine/tracker_song.h (100%) rename applications/external/{musictracker => music_tracker}/view/tracker_view.c (100%) rename applications/external/{musictracker => music_tracker}/view/tracker_view.h (100%) rename applications/external/{musictracker => music_tracker}/zero_tracker.c (100%) rename applications/external/{musictracker => music_tracker}/zero_tracker.h (100%) rename applications/external/{musictracker => music_tracker}/zero_tracker.png (100%) rename applications/external/{nightstand_clock => nightstand}/application.fam (100%) rename applications/external/{nightstand_clock => nightstand}/clock.png (100%) rename applications/external/{nightstand_clock => nightstand}/clock_app.c (100%) rename applications/external/{nightstand_clock => nightstand}/clock_app.h (100%) create mode 100644 applications/external/timelapse/icons/ButtonDownHollow_7x4.png create mode 100644 applications/external/timelapse/icons/ButtonLeftHollow_4x7.png create mode 100644 applications/external/timelapse/icons/ButtonRightHollow_4x7.png create mode 100644 applications/external/timelapse/icons/ButtonUpHollow_7x4.png rename applications/external/{wii_ec_anal => wiiec}/LICENSE (100%) rename applications/external/{wii_ec_anal => wiiec}/WiiEC.png (100%) rename applications/external/{wii_ec_anal => wiiec}/_image_tool/LICENSE (100%) rename applications/external/{wii_ec_anal => wiiec}/_image_tool/README (100%) rename applications/external/{wii_ec_anal => wiiec}/_image_tool/_convert.c (100%) rename applications/external/{wii_ec_anal => wiiec}/_image_tool/_convert.sh (100%) rename applications/external/{wii_ec_anal => wiiec}/_image_tool/_convert_images.c (100%) rename applications/external/{wii_ec_anal => wiiec}/_image_tool/_convert_images.h (100%) rename applications/external/{wii_ec_anal => wiiec}/_image_tool/_convert_test.c (100%) rename applications/external/{wii_ec_anal => wiiec}/application.fam (100%) rename applications/external/{wii_ec_anal => wiiec}/bc_logging.h (100%) rename applications/external/{wii_ec_anal => wiiec}/err.h (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/images.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/images.h (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_3x5_0.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_3x5_1.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_3x5_2.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_3x5_3.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_3x5_4.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_3x5_5.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_3x5_6.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_3x5_7.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_3x5_8.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_3x5_9.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_3x5_v.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_0.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_1.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_2.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_3.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_4.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_5.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_6.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_7.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_8.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_9.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_A.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_B.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_C.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_D.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_E.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_5x7_F.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_0.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_1.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_2.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_3.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_4.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_5.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_6.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_7.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_8.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_9.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_A.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_B.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_C.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_D.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_E.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_F.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_G.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_X.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_Y.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_Z.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_d_.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_n_.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_6x8_v_.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_RIP.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_Cable.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_Joy.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_Main.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_btn_A1.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_btn_B1.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_btn_X1.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_btn_Y1.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_pad_LR1.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_pad_UD1.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_trg_L1.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_trg_L2.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_trg_L3.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_trg_L4.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_trg_R1.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_trg_R2.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_trg_R3.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_cc_trg_R4.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_csLogo_FULL.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_csLogo_Small.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_ecp_SCL.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_ecp_SDA.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_ecp_port.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_key_Back.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_key_D.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_key_L.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_key_OK.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_key_OKi.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_key_R.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_key_U.c (100%) rename applications/external/{wii_ec_anal => wiiec}/gfx/img_key_Ui.c (100%) rename applications/external/{wii_ec_anal => wiiec}/i2c_workaround.h (100%) rename applications/external/{wii_ec_anal => wiiec}/info.sh (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_anal.c (96%) rename applications/external/{wii_ec_anal => wiiec}/wii_anal.h (98%) rename applications/external/{wii_ec_anal => wiiec}/wii_anal_ec.c (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_anal_ec.h (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_anal_keys.c (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_anal_keys.h (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_anal_lcd.c (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_anal_lcd.h (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_anal_ver.h (85%) rename applications/external/{wii_ec_anal => wiiec}/wii_ec.c (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_ec.h (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_ec_classic.c (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_ec_classic.h (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_ec_macros.h (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_ec_nunchuck.c (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_ec_nunchuck.h (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_ec_udraw.c (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_ec_udraw.h (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_i2c.c (100%) rename applications/external/{wii_ec_anal => wiiec}/wii_i2c.h (100%) diff --git a/applications/external/asteroids/application.fam b/applications/external/asteroids/application.fam index 5f70a0e1c..5eb43a6e5 100644 --- a/applications/external/asteroids/application.fam +++ b/applications/external/asteroids/application.fam @@ -8,8 +8,8 @@ App( stack_size=8 * 1024, order=50, fap_icon="appicon.png", - fap_icon_assets="assets", # Image assets to compile for this application fap_category="Games", + fap_icon_assets="assets", # Image assets to compile for this application fap_description="An implementation of the classic arcade game Asteroids", fap_author="antirez, SimplyMinimal", fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Asteroids", diff --git a/applications/external/bpmtapper/img/screenshot.png b/applications/external/bpmtapper/img/screenshot.png deleted file mode 100644 index fbba2aad9026c40fd85788ed73ff4279b825cff0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1987 zcmd6odrVVT9LG;VMd}pe#uOzmmpGkOqBfum3r-n`Tfq8&ImJqd)U+y$4q94hL543L zH3sIa(7JU3N*}O_6``$*NEa+tx?t0AB7_6j(SH_E-*WatGgx-G?XuENPv4P!*UZs{ruH zii`gI0IhOtD7maE=7XZn?;bsvoZh~Wt$x{HxpeNxr>hI9(H9*;=tT0a$u8|@HP@%7 zol}lrV9g!I0bpJYz|Zr*j&cCbF9QJY1wPsXKy+Tj6fcT3vvSMA@sD#mxKoO()9ND; z8nObbInVGHRiWRe`GWjfb^uNw9PaOO9LMxgPRCZUG?QdeOH9eZ=Ir$#e;o=eUE|&& zUgooDpjJN^3yr63r?PIRPq#*<{?=)1&Pw1mrL)UYY|a)fZF;T0N6MoP)JbTElu5C| z5{8)3CfOqEhVN+S@@yum5l;BU>mSXerto~66ULMpd>@JACjB7&kifc(|a@y3Q8c^6|1 zLR&CZIEf=g@P~bA?OO}ff#UI-N7LIlVX?CzX}N{e#!1!PbFo#tiklvHIa_?{z4A_R zPmDlkXi*4j1oP)D>V)-s5JPUaGDajeu5p&*FX{|R68TZmwok+#@H8mLFaLEK%jd2e?SdNg^r3ZKH622T}j7VeDxy93?4e6yRY8%^9_KL2{`66C;wm*hfi+tQA>3*2K(*Yn^c#BY zkT|XKTo1j=y;QN@*>o9!no@-dO!mtw3O#tIt)58W_i_|ZYbkPVw#eGLdFlva$4Ki^ z6bE6K8`Qr^{5S0XyUDy}2ACO}Qi@qlSQn*87;>GARYs92`Hu(otK_UQGhC~MzA5lH zvyK8bq_oYn8pEI~%1qgC;X6~yn~VW7II}@&4qwY3UQMI3a*7;Eh>nC%g{rT_;&htX z=AF8h`w%?@LDVY{@@q{3J;~21f(CIqnXVo7Q{V8A&>jdL24II-34p0y3~!;L_7*a|J9u1OY(0B z2VvdH!zn>8L~zuKFFpTjd3gS%8moZU2kYz|Bz^W-yz)fIbNV~WX51p;ZlyP8>UcR; z=Qu*VVHY8b+ibta5WO}v2sfSGDB&Gu*(sCM4)3Y}Zvr1tCIjHHy%aHYimZ&dHkE@z zSTyVn&hPW6ojdXVYtEoCGpeOf9jtJ{TNNxOfkdE|zEOqoXeLJ6#659;>VU+_cVx{* zUGczC%ihT&pA`nZ;4;mflt>bxt~94RJ2AqBE3;isS~1WLKP9Cy(TGAXz@W?q*=Ahr z+-LjkY$A8F&QhMLOYL?nO~7w!tJ&`{q2fpHKKf($wXr}jp|V^JJry$s_w+Ud%X|{$ zW~{*G^3m*lCz}?^7-C}>*BN%LM02sJPRg+Mu|?k4m#Y>OG409lA3?-dUb+f|{K3Hc zT&Av*Ir2WEksmn56FpgUw4Y>jDeADgt<1Fu0+vw#QSOuRs>y;+H1?Enmgi{FZ*z8O z#WU2~UdZ~w^D!OMZTD*%O}x9GFyHhgxsE=p&`WatkXMKyA-7b%xiWJA<-V0c+!yiD JqMgUi{0T658(RPX diff --git a/applications/external/brainfuck/icons/DolphinCommon_56x48.png b/applications/external/brainfuck/icons/DolphinCommon_56x48.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/applications/external/brainfuck/icons/Text_10x10.png b/applications/external/brainfuck/icons/Text_10x10.png deleted file mode 100644 index 8e8a6183dd50535729dc9c9b4f220a12dd4c600f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&u?6^qxB}__|Nk$&IsYz@rRC}3 z7*a7OIiZ2U&CSi=;0cBn1vTatM&Z;3u7g(^G9`qQn09G2aWeNXaKC0S=Q~tg57Z@F z;u=vBoS#-wo>-L1;E+?AmspUPnOCA;ke9BToS%}K{MA`f4ycg9)78&qol`;+00Iau A9smFU diff --git a/applications/external/cli_bridge/internal_defs.h b/applications/external/cli_bridge/internal_defs.h index 9840d008b..09fe6169c 100644 --- a/applications/external/cli_bridge/internal_defs.h +++ b/applications/external/cli_bridge/internal_defs.h @@ -79,8 +79,8 @@ typedef struct { void* view_dispatcher; void* primary_menu; - void* plugins_menu; - void* debug_menu; + // void* plugins_menu; + // void* debug_menu; void* settings_menu; volatile uint8_t lock_count; diff --git a/applications/external/doom/assets/screenshot1.png b/applications/external/doom/assets/screenshot1.png deleted file mode 100644 index 1ecb073a69f3692705a6b5a76947d7ceb6dfab3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1487 zcmeAS@N?(olHy`uVBq!ia0y~yU;;8388|?c*QZDWAjMhW5n0T@z;^_M8K-LVNi#68 zHhQ`^hE&XXdpEZ4vK2!^pjDFPnts;}cGsHUNv|pqJ$dkF@&4j@XufgnYUZ5(uw}k>|NTEd%o!%JFerF2Ff1~=&hTLF*2b+r>I@HYK!kuYr=%Ey&zRmn z{V}~wkilUJ1A_}EkTPZPOU(Nf(Wjnnz(ht!Zjxu1erwnIzxR%Z2mY>#>_##fY5|mb z&G19ReQx=Cn_8|OpcA1kC7yc2e!%_aF8BX8`ww^FG$4lkz|ySecdze@yOzYlAfU>? z5XeLdm1@u6_i*30<3%=>%}72mWmq<2_P*(l&+{PSK_i=iXXRPb`oDkvet#^w|C{K2 z^f0gYIp5S&EI>K^{-jhoIo;n z-hrAa6MueRTm1k04fY#V4E|{d8=hJ*OfxUCpRa3toqf*7njMpn3?=mO6 z-~W1_!y4oSRmz}~`QG-I`c|}@-=njfUINCNluyfcg6EL4^Lj#zL0|OZYpoX@tf8A{+~Jj;ojfI{#nPj%N-Dw zK*UZYGed_FA|_D_msz)jD_+mjU8l=XVTbV3ij}4f+Hd#P|2p%_B{BE$y6SiGUuX3c z|C#^z)c@mc_s%P>XF5;>P3vvf7`~kozi;~7!f3zQ=IgIt{oZlzO}({o`OlTdKcAHO zUmE*!FH%B^dS=DYw{@%E=JLJl7T2a`o4FwpwR1Lua&eIIdG>@mH?LIpi1*e%!5?luH~HN2J?DHr z=X-9+r_mf|+I|`UI7dZ>odmG6zEZ$(8;-8V`B}j!Co)b5;PUJCL%HVb@(O@`d{o#+ zpNXoLrg>RY5niFAK?`}!%J<&T4HcbZd-?2n>2TnD8DFF>OiLIz@h;PoQ4_fNd`+iK zg7r-iMuk~q54?U9p!WX~?YO!Cae*xUUT}L8;DjBx8L%1NCP0UW@AYEoXJa)P~Q7phEag={jZfCBb`0W+=8F1uTS0ken2%i&!e9Gs`Y0Yt7OB4!#Zeuwn& zF0~8elq;?IeCg6N!HUMBh_z|*;_sMpC<4PVmM25y9-;Q1Tw}I$+crGWm&oFjvh@WT zZnFB!jU~3jf3;#=K#KUrj%?*%J~Z;hth!dE_VXQcw>{>=Tf$q;{w>{{Et+`iJKwF0 z(?k(-d@%k&VUx5!9zbp4sOb@E`O{NRD`nh70N<;guLst{h_CljNUY7aBN zCv)zv2R`WijCKtw=`?v}Fno@P@ zyjOwIVnasN304<#Q_4`ad2E=v9AN6oz%})@2Sjl+^&zGW?h}Uzmdu-EzI38EC7h$_ z#N|TqqWQpm!F8dbJtpQ$qUE_DxE|1=cjGY16rq z89qAv|Dg-@N@}r(~GK;}F|lEj6~VwKR_T{r4j+V+hiB=7pi;XwHa-#kc^T>@e&^j z_9G$qLrJaQlxV!dERY^o4oDp}x{>06uaN3fN*WBkxO43${i$Bl@1x_@`f6Np5G&$I zE$a{Q?6O53+hBI$wyg;Uh<4H{bPzZ`R>eQ7p;qZeDk=z}9?89XtC8DCjcYH;P4YcM zlx+B5jj0!F%F#YaEo&-p6*+)cUes&pa#8f->~(Xot~*GH2UB7zcXwgW$&o8eC*JwT Q)_)H~g-3_UKTbaPH)Y0Q@&Et; diff --git a/applications/external/doom/assets/screenshot3.png b/applications/external/doom/assets/screenshot3.png deleted file mode 100644 index b5aec03fdccb77f978d836ea80ecb1ee898154a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1629 zcmc&#drVVj6hDR04n$z0ggRHr%z_#6kY>CD#x`a}7la8+6bc!5%Vw1d_@cIedqf6V z!{!vSLZf>`2cw`t9gnskI>4?SnXz;XI!C37rMFN@OK%^$uFHD;Z~ytn&AB;W&iS45 zJHPXN^;wyW)qd;!0AO{-j`Vi{yzpNVJpaV(T+ENbjW1_MekDM_@RN`9RYbsx0OY`o z^fz)&2K#iSkW)pGW7UP^=bavvck}+Pru)+Ft(X;L+c_Mu0$hk3AP2y zF&_=p%uYT)!CL?qpM$WS&(zGB=O`fiiOF}`EYcdO)L}74(ELFa47Hm{a3dZ zd^e*eGQyqE9atchxFAp{wyGJo#9 z<4Gl~Qkv`t;%g)xwx?uS*$3lh_&$bVzyD-9mY??Rp{YHw6`D~hW<`9Hzx3qGd7o^Q zSAJ}$M#fw915x`iJzX@T<@;ayo7{xgui9N4V z*d^FH%`S!N8k5^OlVFJ`xZ^3}!%-?EkB$5>!gTe@LnVl(-yW@hG1{ijwNN@L1Ve={ zsl^&>Jw3c^AMYGNZsw9}DVJa1D!r1paU3lsw%V2Yk$1|p`}9+q_`qWF*EMwUi5Mu@ z?baR+Cu6YsrOqEP-#lr^dx^2wf^g4MsQGEe@p}VdEKP8inI~K|{OZ#U=~GsfAGOhyCe?+LaJAc`_OVu&O;@@ta?#e|So>S^skYHfqskCwDM-yKkR# z>b6d5wi0T(K$T59{%T*|z*ts|iEdSEP2~c8VQ+|1w^F2{i(5U4yc>&?(xpl~d@%Fs z@#&jFdsN5c7L|gaq%v~MmGIXVQPC(f+QzXN5!iVhN!9VRDUbHERE=F5ZR%;A oeZ;b47yXP5B_qyl1Pz$ulXAWbLp3j}@%I8U-p)+#e6#rU-{AE`xBvhE diff --git a/applications/external/geiger/flipper_geiger.c b/applications/external/geiger/flipper_geiger.c index a5503eb90..9c3d0d3fc 100644 --- a/applications/external/geiger/flipper_geiger.c +++ b/applications/external/geiger/flipper_geiger.c @@ -39,33 +39,53 @@ typedef struct { static void draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); - mutexStruct displayStruct; - mutexStruct* geigerMutex = ctx; - furi_mutex_acquire(geigerMutex->mutex, FuriWaitForever); - memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct)); - furi_mutex_release(geigerMutex->mutex); + mutexStruct* mutexVal = ctx; + mutexStruct mutexDraw; + furi_mutex_acquire(mutexVal->mutex, FuriWaitForever); + memcpy(&mutexDraw, mutexVal, sizeof(mutexStruct)); + furi_mutex_release(mutexVal->mutex); char buffer[32]; - if(displayStruct.data == 0) - snprintf( - buffer, sizeof(buffer), "%ld cps - %ld cpm", displayStruct.cps, displayStruct.cpm); - else if(displayStruct.data == 1) + if(mutexDraw.data == 0) + snprintf(buffer, sizeof(buffer), "%ld cps - %ld cpm", mutexDraw.cps, mutexDraw.cpm); + else if(mutexDraw.data == 1) snprintf( buffer, sizeof(buffer), "%ld cps - %.2f uSv/h", - displayStruct.cps, - ((double)displayStruct.cpm * (double)CONVERSION_FACTOR)); - else + mutexDraw.cps, + ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR)); + else if(mutexDraw.data == 2) snprintf( buffer, sizeof(buffer), "%ld cps - %.2f mSv/y", - displayStruct.cps, - (((double)displayStruct.cpm * (double)CONVERSION_FACTOR)) * (double)8.76); + mutexDraw.cps, + (((double)mutexDraw.cpm * (double)CONVERSION_FACTOR)) * (double)8.76); + else if(mutexDraw.data == 3) + snprintf( + buffer, + sizeof(buffer), + "%ld cps - %.4f Rad/h", + mutexDraw.cps, + ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) / (double)10000); + else if(mutexDraw.data == 4) + snprintf( + buffer, + sizeof(buffer), + "%ld cps - %.2f mR/h", + mutexDraw.cps, + ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) / (double)10); + else + snprintf( + buffer, + sizeof(buffer), + "%ld cps - %.2f uR/h", + mutexDraw.cps, + ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) * (double)100); for(int i = 0; i < SCREEN_SIZE_X; i += 2) { - float Y = SCREEN_SIZE_Y - (displayStruct.line[i / 2] * displayStruct.coef); + float Y = SCREEN_SIZE_Y - (mutexDraw.line[i / 2] * mutexDraw.coef); canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y); canvas_draw_line(canvas, i + 1, Y, i + 1, SCREEN_SIZE_Y); @@ -103,8 +123,7 @@ static void gpiocallback(void* ctx) { furi_message_queue_put(queue, &event, 0); } -int32_t flipper_geiger_app(void* p) { - UNUSED(p); +int32_t flipper_geiger_app() { EventApp event; FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); @@ -127,7 +146,7 @@ int32_t flipper_geiger_app(void* p) { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &mutexVal); + view_port_draw_callback_set(view_port, draw_callback, &mutexVal.mutex); view_port_input_callback_set(view_port, input_callback, event_queue); furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue); @@ -167,7 +186,7 @@ int32_t flipper_geiger_app(void* p) { if(mutexVal.data != 0) mutexVal.data--; else - mutexVal.data = 2; + mutexVal.data = 5; screenRefresh = 1; furi_mutex_release(mutexVal.mutex); @@ -175,7 +194,7 @@ int32_t flipper_geiger_app(void* p) { event.input.type == InputTypeShort)) { furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); - if(mutexVal.data != 2) + if(mutexVal.data != 5) mutexVal.data++; else mutexVal.data = 0; diff --git a/applications/external/gpio_reader_a/GPIO_reader.c b/applications/external/gpioreader_a/GPIO_reader.c similarity index 100% rename from applications/external/gpio_reader_a/GPIO_reader.c rename to applications/external/gpioreader_a/GPIO_reader.c diff --git a/applications/external/gpio_reader_a/GPIO_reader_item.c b/applications/external/gpioreader_a/GPIO_reader_item.c similarity index 100% rename from applications/external/gpio_reader_a/GPIO_reader_item.c rename to applications/external/gpioreader_a/GPIO_reader_item.c diff --git a/applications/external/gpio_reader_a/GPIO_reader_item.h b/applications/external/gpioreader_a/GPIO_reader_item.h similarity index 100% rename from applications/external/gpio_reader_a/GPIO_reader_item.h rename to applications/external/gpioreader_a/GPIO_reader_item.h diff --git a/applications/external/gpio_reader_a/application.fam b/applications/external/gpioreader_a/application.fam similarity index 100% rename from applications/external/gpio_reader_a/application.fam rename to applications/external/gpioreader_a/application.fam diff --git a/applications/external/gpio_reader_a/icon.png b/applications/external/gpioreader_a/icon.png similarity index 100% rename from applications/external/gpio_reader_a/icon.png rename to applications/external/gpioreader_a/icon.png diff --git a/applications/external/gpio_reader_b/LICENSE b/applications/external/gpioreader_b/LICENSE similarity index 100% rename from applications/external/gpio_reader_b/LICENSE rename to applications/external/gpioreader_b/LICENSE diff --git a/applications/external/gpio_reader_b/application.fam b/applications/external/gpioreader_b/application.fam similarity index 100% rename from applications/external/gpio_reader_b/application.fam rename to applications/external/gpioreader_b/application.fam diff --git a/applications/external/gpio_reader_b/gpio_app.c b/applications/external/gpioreader_b/gpio_app.c similarity index 100% rename from applications/external/gpio_reader_b/gpio_app.c rename to applications/external/gpioreader_b/gpio_app.c diff --git a/applications/external/gpio_reader_b/gpio_app.h b/applications/external/gpioreader_b/gpio_app.h similarity index 100% rename from applications/external/gpio_reader_b/gpio_app.h rename to applications/external/gpioreader_b/gpio_app.h diff --git a/applications/external/gpio_reader_b/gpio_app_i.h b/applications/external/gpioreader_b/gpio_app_i.h similarity index 100% rename from applications/external/gpio_reader_b/gpio_app_i.h rename to applications/external/gpioreader_b/gpio_app_i.h diff --git a/applications/external/gpio_reader_b/gpio_custom_event.h b/applications/external/gpioreader_b/gpio_custom_event.h similarity index 100% rename from applications/external/gpio_reader_b/gpio_custom_event.h rename to applications/external/gpioreader_b/gpio_custom_event.h diff --git a/applications/external/gpio_reader_b/gpio_item.c b/applications/external/gpioreader_b/gpio_item.c similarity index 100% rename from applications/external/gpio_reader_b/gpio_item.c rename to applications/external/gpioreader_b/gpio_item.c diff --git a/applications/external/gpio_reader_b/gpio_item.h b/applications/external/gpioreader_b/gpio_item.h similarity index 100% rename from applications/external/gpio_reader_b/gpio_item.h rename to applications/external/gpioreader_b/gpio_item.h diff --git a/applications/external/gpio_reader_b/icon.png b/applications/external/gpioreader_b/icon.png similarity index 100% rename from applications/external/gpio_reader_b/icon.png rename to applications/external/gpioreader_b/icon.png diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene.c b/applications/external/gpioreader_b/scenes/gpio_scene.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene.c rename to applications/external/gpioreader_b/scenes/gpio_scene.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene.h b/applications/external/gpioreader_b/scenes/gpio_scene.h similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene.h rename to applications/external/gpioreader_b/scenes/gpio_scene.h diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_config.h b/applications/external/gpioreader_b/scenes/gpio_scene_config.h similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_config.h rename to applications/external/gpioreader_b/scenes/gpio_scene_config.h diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_reader.c b/applications/external/gpioreader_b/scenes/gpio_scene_reader.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_reader.c rename to applications/external/gpioreader_b/scenes/gpio_scene_reader.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_start.c b/applications/external/gpioreader_b/scenes/gpio_scene_start.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_start.c rename to applications/external/gpioreader_b/scenes/gpio_scene_start.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_test.c b/applications/external/gpioreader_b/scenes/gpio_scene_test.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_test.c rename to applications/external/gpioreader_b/scenes/gpio_scene_test.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart.c b/applications/external/gpioreader_b/scenes/gpio_scene_usb_uart.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart.c rename to applications/external/gpioreader_b/scenes/gpio_scene_usb_uart.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c b/applications/external/gpioreader_b/scenes/gpio_scene_usb_uart_close_rpc.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_close_rpc.c rename to applications/external/gpioreader_b/scenes/gpio_scene_usb_uart_close_rpc.c diff --git a/applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c b/applications/external/gpioreader_b/scenes/gpio_scene_usb_uart_config.c similarity index 100% rename from applications/external/gpio_reader_b/scenes/gpio_scene_usb_uart_config.c rename to applications/external/gpioreader_b/scenes/gpio_scene_usb_uart_config.c diff --git a/applications/external/gpio_reader_b/usb_uart_bridge.c b/applications/external/gpioreader_b/usb_uart_bridge.c similarity index 100% rename from applications/external/gpio_reader_b/usb_uart_bridge.c rename to applications/external/gpioreader_b/usb_uart_bridge.c diff --git a/applications/external/gpio_reader_b/usb_uart_bridge.h b/applications/external/gpioreader_b/usb_uart_bridge.h similarity index 100% rename from applications/external/gpio_reader_b/usb_uart_bridge.h rename to applications/external/gpioreader_b/usb_uart_bridge.h diff --git a/applications/external/gpio_reader_b/views/gpio_reader.c b/applications/external/gpioreader_b/views/gpio_reader.c similarity index 100% rename from applications/external/gpio_reader_b/views/gpio_reader.c rename to applications/external/gpioreader_b/views/gpio_reader.c diff --git a/applications/external/gpio_reader_b/views/gpio_reader.h b/applications/external/gpioreader_b/views/gpio_reader.h similarity index 100% rename from applications/external/gpio_reader_b/views/gpio_reader.h rename to applications/external/gpioreader_b/views/gpio_reader.h diff --git a/applications/external/gpio_reader_b/views/gpio_test.c b/applications/external/gpioreader_b/views/gpio_test.c similarity index 100% rename from applications/external/gpio_reader_b/views/gpio_test.c rename to applications/external/gpioreader_b/views/gpio_test.c diff --git a/applications/external/gpio_reader_b/views/gpio_test.h b/applications/external/gpioreader_b/views/gpio_test.h similarity index 100% rename from applications/external/gpio_reader_b/views/gpio_test.h rename to applications/external/gpioreader_b/views/gpio_test.h diff --git a/applications/external/gpio_reader_b/views/gpio_usb_uart.c b/applications/external/gpioreader_b/views/gpio_usb_uart.c similarity index 98% rename from applications/external/gpio_reader_b/views/gpio_usb_uart.c rename to applications/external/gpioreader_b/views/gpio_usb_uart.c index f71dcccab..14f8c12fe 100644 --- a/applications/external/gpio_reader_b/views/gpio_usb_uart.c +++ b/applications/external/gpioreader_b/views/gpio_usb_uart.c @@ -82,7 +82,7 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) { if(model->rx_active) canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180); else - canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180); + canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180); } static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) { diff --git a/applications/external/gpio_reader_b/views/gpio_usb_uart.h b/applications/external/gpioreader_b/views/gpio_usb_uart.h similarity index 100% rename from applications/external/gpio_reader_b/views/gpio_usb_uart.h rename to applications/external/gpioreader_b/views/gpio_usb_uart.h diff --git a/applications/external/metronome/img/screenshot.png b/applications/external/metronome/img/screenshot.png deleted file mode 100644 index 7b6916e81ac8e1984370d33397ac6664f57b5f6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1978 zcmbVNeNfV89Dfj<<<4w%mCJ-~)oST!>0D|8uCo_4JJST;>CPr6x~!O@5@4OHZKbU% z(tOEW(<~G%MDYdjT9bJSxP_UR=*`UWB1{o}28xqO+ppU{tv`PE{GR9g{(QdQ&+~2M zk%P`o8=U|E&Y_1wq5!a4`bB^h)+KBvZr9SX^3mGKT`MRo!`@6Z~^7qY?jg_o`{I%fhMoE?uum{0A5MZ^6Vg#Mh@q@`#mYhv`r}1;{EQ`BguAMzJ z={1VP15jmCBr8!}zt#nA?n;V7fPyv4g4!?hfFEb}y|9)5G7mGzG8T}HL`p=U`j2Ud zZ%#~rX{KWG6@z2Mdpra7q|%aZa_;b02$HJi+BX#!mr5IB78_D4>u2W_jdozgHURdm z1o@FMn0#&^p*Viv$M{^aVeIlAwVY`R?Sy`SsCDa>^A7`{V5b&w{i$4gEIejQadZozvGR4?%v8jj$ zi!2>N-YzVNr|6)778N2fK%1m2och@bY`zYb)AOTk-!)!6P@HhOf`PkrmAAoTDPOx~ z`2;p!TpK5pb!>00ar}OsA>U|1q(6jtC*}ciF@TH{BkR_R5M3cQyVqsE%hnJ;1Gv*? z?@q8FS4hFQG47v!-BMIvlt7+n^_NfRZ)RC8zi9uR21m3E6`Uq`2pPGvu9MoV!<=v| za~7$9!#g>RW)IX$R4mfCMzG*XP&H#Xh)P3%#>bn4cVo(5QM#d?-qYo8cr}9@LUclq zMdJkp_dg}`(z>~@+ItNkk=>g#Wtm?Xq3tKp8@QyoSp~xZ?xGb1ba}G%1ohXNWFji< zM8r_X^D-FVzagLXu^7Q(>_PeT%^AY1tA2w=o`VUyL6(F5MWOV#o@HK|h=&iF`LC*I8kmjY|aTOy_>U2s1= z2Yuz>qCEY|D3vG8evyMOYZ&N$Ql=}U61B`y|5f1Qf5uIV+(Bp1byFneZV5}x!d#xG z6Rg_4QA&SWrqB*n15Zo`Kt@4ZIc77<*0$EEdbjpccZr}VyRBsgY&I;2Uz6i)txHPZ z{ZI71B3<`Y3JF$T8w6wvh)!yq`HIFd_Fpcr^3Ln2w6V)uZ)>$Wdn|2m8}QyTikzR| z|5Rk(74@3Sa_2?;@@ovxH9lPejlhS$ZPf8p>Iw1t`BWH;Zyof1C#+hU#(5!=$q=I+ zZLrZUelJtv-|+Iy_H-+Wk;YUCE)JLw5_T5sj%t_qGNs}{Q#ZBxKyI_NbX;3Y#k35N zVtwy&i+D4wGIB;cbdB6Cd{o|Sx|LyQhuca^(VTlC)>AH?Bc1U%bEE>dRhq)q55f3r z9R#vFismx<{Gjzs@>D8Gn4;vp65!Y#l&_47)i_%AbNul3a>5o~n~O5TtkB1D(JKVj6sxDd;hFZ`10iAvCa02&=^aJR{jJL+3* z%y@1f`!jvSr6Ly1SBnH=Yi#nJjP;RhMi-gq%c!YAwaLv#t7J|KK!*gkOKj~MBUsC? zLHC>Ef(b^V>1+w!EMzMvf$CU4(H9YZdj6wP8du-ilMJ2}*(@+5mwX?AT9QV;^y%Kt oCuNK}+uQP=Dfs)r1Y-v*$QL~Uk1Kkr_|}uq{YOGL`;MRg3sjKxR{#J2 diff --git a/applications/external/minesweeper/img/screenshot.png b/applications/external/minesweeper/img/screenshot.png deleted file mode 100644 index 65b307c550cdfc13c388135b4e286d1be56957d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1842 zcmbtVc~BEq7=IyRgcgu0lNROZV5T#7|k6~v9}^R?EPJl zBCckac0tDKDHDB)&E87M8+q{t=h*b7A$KRhV%w@b}PgcgWS4FPo7UFZGEXhdN81u1de_LK+PP$ zsQ-lrLXhnzD}BG1!on)ucSr&5&)qBH2AsQ8O9t~kdlSfcO9bQd0KiyA0`oTkaCD$* zYv;&Vc~b;u-O9C;8JSC^LE_FVG}yv)al|S=f}-Piv@|@)jMR1z+6vs<-w#_hljONO z0y(-b+wpdTA<@2EE3JbPcZMdMt1(zwv-tGq7Slq=_!UaQ;gPGcAapi5eE&Xruf!V! zpK)f><@h6VOJ|yyOzwdL9)IaB*Nj!2ZH*It5;K!Y7hY#C0)y;nUF72lEu|g~9eoWH zVk*m6&SN@cT)ZnLOyjszN@qLv+_4|VOE8tgDia9!kbn7(XrV9b-ZVR3(}T*;MfLXO zM{uqFcnD-V^x8 z-7(UvLMBPwi*?*-KLlypY%534XE{Fi*P(OwbB?`QAzkNpVq&I@1M2Lr}c&4dcO`0E>xN~O!>eF(XAfJP?lz7M+ z{8TLMyVVV$L-+Yq|4AbyOTu^99Qx=h1GwsOVWT|;D|x3ebem5#-gc%84>74@kwJE* zBMPy+g8Q=UA^D+}-GJN)Kw9+<`NU{lIMfx+uP+q%o}cseKD+O~n}cOMa6+bTt`=jr zaLA+XKn6K^Fq!8!dVJMOh)wbic<}$%y3dM$w7%+c270qNN5ILOmVXTuKM5rV zZ4LU!2&y7VaFM__WMhuxyNFVavqwiE-MxVfj3-(!!srt~tfN5MLIkp5Tb;A^0M@y&|tLV8cCM1-cd+b59W z;+(jX&}2Fjk;9}>73wszruDURZgYMk@L4mlH?JaSv{b+_V;rQ^E#u;U`kns)=Ov_G z85b1lcsCl*l=K`vreg`zY7U9?mh0Yf*Bk14kL`@TnP=zI>$Y&a6)#NaYnFC62x6qc znb;9!nsMo2(!ak7Cs(@|<}H=J-*(36lkctWE&l1cC7M!Smn30bQoLeK?vZ~0WMt2h diff --git a/applications/external/musictracker/application.fam b/applications/external/music_tracker/application.fam similarity index 90% rename from applications/external/musictracker/application.fam rename to applications/external/music_tracker/application.fam index fe4355e86..f3bac02c2 100644 --- a/applications/external/musictracker/application.fam +++ b/applications/external/music_tracker/application.fam @@ -10,4 +10,5 @@ App( order=20, fap_icon="zero_tracker.png", fap_category="Music", + fap_icon_assets="icons", ) diff --git a/applications/external/musictracker/tracker_engine/speaker_hal.c b/applications/external/music_tracker/tracker_engine/speaker_hal.c similarity index 91% rename from applications/external/musictracker/tracker_engine/speaker_hal.c rename to applications/external/music_tracker/tracker_engine/speaker_hal.c index 94489f1b6..208fee04c 100644 --- a/applications/external/musictracker/tracker_engine/speaker_hal.c +++ b/applications/external/music_tracker/tracker_engine/speaker_hal.c @@ -40,17 +40,12 @@ void tracker_speaker_stop() { } void tracker_speaker_init() { - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { - furi_hal_speaker_start(200.0f, 0.01f); - tracker_speaker_stop(); - } + furi_hal_speaker_start(200.0f, 0.01f); + tracker_speaker_stop(); } void tracker_speaker_deinit() { - if(furi_hal_speaker_is_mine()) { - furi_hal_speaker_stop(); - furi_hal_speaker_release(); - } + furi_hal_speaker_stop(); } static FuriHalInterruptISR tracker_isr; diff --git a/applications/external/musictracker/tracker_engine/speaker_hal.h b/applications/external/music_tracker/tracker_engine/speaker_hal.h similarity index 100% rename from applications/external/musictracker/tracker_engine/speaker_hal.h rename to applications/external/music_tracker/tracker_engine/speaker_hal.h diff --git a/applications/external/musictracker/tracker_engine/tracker.c b/applications/external/music_tracker/tracker_engine/tracker.c similarity index 100% rename from applications/external/musictracker/tracker_engine/tracker.c rename to applications/external/music_tracker/tracker_engine/tracker.c diff --git a/applications/external/musictracker/tracker_engine/tracker.h b/applications/external/music_tracker/tracker_engine/tracker.h similarity index 100% rename from applications/external/musictracker/tracker_engine/tracker.h rename to applications/external/music_tracker/tracker_engine/tracker.h diff --git a/applications/external/musictracker/tracker_engine/tracker_notes.h b/applications/external/music_tracker/tracker_engine/tracker_notes.h similarity index 100% rename from applications/external/musictracker/tracker_engine/tracker_notes.h rename to applications/external/music_tracker/tracker_engine/tracker_notes.h diff --git a/applications/external/musictracker/tracker_engine/tracker_song.h b/applications/external/music_tracker/tracker_engine/tracker_song.h similarity index 100% rename from applications/external/musictracker/tracker_engine/tracker_song.h rename to applications/external/music_tracker/tracker_engine/tracker_song.h diff --git a/applications/external/musictracker/view/tracker_view.c b/applications/external/music_tracker/view/tracker_view.c similarity index 100% rename from applications/external/musictracker/view/tracker_view.c rename to applications/external/music_tracker/view/tracker_view.c diff --git a/applications/external/musictracker/view/tracker_view.h b/applications/external/music_tracker/view/tracker_view.h similarity index 100% rename from applications/external/musictracker/view/tracker_view.h rename to applications/external/music_tracker/view/tracker_view.h diff --git a/applications/external/musictracker/zero_tracker.c b/applications/external/music_tracker/zero_tracker.c similarity index 100% rename from applications/external/musictracker/zero_tracker.c rename to applications/external/music_tracker/zero_tracker.c diff --git a/applications/external/musictracker/zero_tracker.h b/applications/external/music_tracker/zero_tracker.h similarity index 100% rename from applications/external/musictracker/zero_tracker.h rename to applications/external/music_tracker/zero_tracker.h diff --git a/applications/external/musictracker/zero_tracker.png b/applications/external/music_tracker/zero_tracker.png similarity index 100% rename from applications/external/musictracker/zero_tracker.png rename to applications/external/music_tracker/zero_tracker.png diff --git a/applications/external/nightstand_clock/application.fam b/applications/external/nightstand/application.fam similarity index 100% rename from applications/external/nightstand_clock/application.fam rename to applications/external/nightstand/application.fam diff --git a/applications/external/nightstand_clock/clock.png b/applications/external/nightstand/clock.png similarity index 100% rename from applications/external/nightstand_clock/clock.png rename to applications/external/nightstand/clock.png diff --git a/applications/external/nightstand_clock/clock_app.c b/applications/external/nightstand/clock_app.c similarity index 100% rename from applications/external/nightstand_clock/clock_app.c rename to applications/external/nightstand/clock_app.c diff --git a/applications/external/nightstand_clock/clock_app.h b/applications/external/nightstand/clock_app.h similarity index 100% rename from applications/external/nightstand_clock/clock_app.h rename to applications/external/nightstand/clock_app.h diff --git a/applications/external/pomodoro/flipp_pomodoro_app.c b/applications/external/pomodoro/flipp_pomodoro_app.c index 5adca1edb..2ddf8cf68 100644 --- a/applications/external/pomodoro/flipp_pomodoro_app.c +++ b/applications/external/pomodoro/flipp_pomodoro_app.c @@ -78,6 +78,7 @@ void flipp_pomodoro_app_free(FlippPomodoroApp* app) { view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); flipp_pomodoro_view_timer_free(app->timer_view); + flipp_pomodoro__destroy(app->state); free(app); furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); diff --git a/applications/external/pomodoro/modules/flipp_pomodoro.c b/applications/external/pomodoro/modules/flipp_pomodoro.c index 161e862f8..9915025fb 100644 --- a/applications/external/pomodoro/modules/flipp_pomodoro.c +++ b/applications/external/pomodoro/modules/flipp_pomodoro.c @@ -55,6 +55,11 @@ char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) { return next_stage_label[flipp_pomodoro__stage_by_index(state->current_stage_index + 1)]; }; +void flipp_pomodoro__destroy(FlippPomodoroState* state) { + furi_assert(state); + free(state); +}; + uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) { const int32_t stage_duration_seconds_map[] = { [FlippPomodoroStageFocus] = 25 * TIME_SECONDS_IN_MINUTE, diff --git a/applications/external/pong/flipper_pong.c b/applications/external/pong/flipper_pong.c index 55b371ad5..53c6a7e27 100644 --- a/applications/external/pong/flipper_pong.c +++ b/applications/external/pong/flipper_pong.c @@ -15,8 +15,7 @@ #define PAD_SIZE_X 3 #define PAD_SIZE_Y 8 -#define PLAYER1_PAD_SPEED 4 - +#define PLAYER1_PAD_SPEED 2 #define PLAYER2_PAD_SPEED 2 #define BALL_SIZE 4 @@ -39,29 +38,22 @@ typedef struct Players { static void draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); - Players* playersMutex = ctx; - furi_mutex_acquire(playersMutex->mutex, FuriWaitForever); + Players* players = ctx; + furi_mutex_acquire(players->mutex, FuriWaitForever); canvas_draw_frame(canvas, 0, 0, 128, 64); - canvas_draw_box( - canvas, playersMutex->player1_X, playersMutex->player1_Y, PAD_SIZE_X, PAD_SIZE_Y); - canvas_draw_box( - canvas, playersMutex->player2_X, playersMutex->player2_Y, PAD_SIZE_X, PAD_SIZE_Y); - canvas_draw_box(canvas, playersMutex->ball_X, playersMutex->ball_Y, BALL_SIZE, BALL_SIZE); + canvas_draw_box(canvas, players->player1_X, players->player1_Y, PAD_SIZE_X, PAD_SIZE_Y); + canvas_draw_box(canvas, players->player2_X, players->player2_Y, PAD_SIZE_X, PAD_SIZE_Y); + canvas_draw_box(canvas, players->ball_X, players->ball_Y, BALL_SIZE, BALL_SIZE); canvas_set_font(canvas, FontPrimary); canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); char buffer[16]; - snprintf( - buffer, - sizeof(buffer), - "%u - %u", - playersMutex->player1_score, - playersMutex->player2_score); + snprintf(buffer, sizeof(buffer), "%u - %u", players->player1_score, players->player2_score); canvas_draw_str_aligned( canvas, SCREEN_SIZE_X / 2 + 15, SCREEN_SIZE_Y / 2 + 2, AlignCenter, AlignTop, buffer); - furi_mutex_release(playersMutex->mutex); + furi_mutex_release(players->mutex); } static void input_callback(InputEvent* input_event, void* ctx) { @@ -101,8 +93,7 @@ uint8_t changeDirection() { return randomuint8[0]; } -int32_t flipper_pong_app(void* p) { - UNUSED(p); +int32_t flipper_pong_app() { EventApp event; FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); @@ -129,7 +120,7 @@ int32_t flipper_pong_app(void* p) { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &players); + view_port_draw_callback_set(view_port, draw_callback, &players.mutex); view_port_input_callback_set(view_port, input_callback, event_queue); Gui* gui = furi_record_open(RECORD_GUI); @@ -152,7 +143,6 @@ int32_t flipper_pong_app(void* p) { if(event.type == EventTypeInput) { if(event.input.key == InputKeyBack) { furi_mutex_release(players.mutex); - notification_message(notification, &sequence_set_only_green_255); break; } else if(event.input.key == InputKeyUp) { if(players.player1_Y >= 1 + PLAYER1_PAD_SPEED) diff --git a/applications/external/rc2014_coleco/coleco.c b/applications/external/rc2014_coleco/coleco.c index 311b0ceac..f0a4c6188 100644 --- a/applications/external/rc2014_coleco/coleco.c +++ b/applications/external/rc2014_coleco/coleco.c @@ -40,15 +40,14 @@ typedef struct { } PluginEvent; typedef struct { - FuriMutex* mutex; bool dpad; int row; int column; + FuriMutex* mutex; } Coleco; static void render_callback(Canvas* const canvas, void* context) { - furi_assert(context); - Coleco* coleco = context; + Coleco* coleco = (Coleco*)context; furi_mutex_acquire(coleco->mutex, FuriWaitForever); if(coleco->dpad) { @@ -175,12 +174,20 @@ static Coleco* coleco_alloc() { coleco->row = 0; coleco->column = 1; + coleco->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!coleco->mutex) { + FURI_LOG_E("Coleco", "cannot create mutex\r\n"); + free(coleco); + return NULL; + } + return coleco; } static void coleco_free(Coleco* coleco) { furi_assert(coleco); + furi_mutex_free(coleco->mutex); free(coleco); } @@ -190,11 +197,7 @@ int32_t coleco_app(void* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); Coleco* coleco = coleco_alloc(); - - coleco->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!coleco->mutex) { - FURI_LOG_E("Coleco", "cannot create mutex\r\n"); - coleco_free(coleco); + if(coleco == NULL) { return 255; } @@ -346,6 +349,8 @@ int32_t coleco_app(void* p) { view_port_update(view_port); } + } else { + FURI_LOG_D("Coleco", "FuriMessageQueue: event timeout"); } furi_mutex_release(coleco->mutex); @@ -358,7 +363,6 @@ int32_t coleco_app(void* p) { furi_record_close("gui"); view_port_free(view_port); furi_message_queue_free(event_queue); - furi_mutex_free(coleco->mutex); coleco_free(coleco); return 0; } diff --git a/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c b/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c index 4c845b883..17e48473a 100644 --- a/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c +++ b/applications/external/rubiks_cube_scrambler/rubiks_cube_scrambler.c @@ -8,11 +8,11 @@ #include "scrambler.h" #include "furi_hal_random.h" -int scrambleStarted = 0; +bool scrambleStarted = false; char scramble_str[100] = {0}; char scramble_start[100] = {0}; char scramble_end[100] = {0}; -int notifications_enabled = 0; +bool notifications_enabled = false; static void success_vibration() { furi_hal_vibro_on(false); @@ -22,12 +22,12 @@ static void success_vibration() { return; } void split_array(char original[], int size, char first[], char second[]) { - int mid = size / 2; + int32_t mid = size / 2; if(size % 2 != 0) { mid++; } - int first_index = 0, second_index = 0; - for(int i = 0; i < size; i++) { + int32_t first_index = 0, second_index = 0; + for(int32_t i = 0; i < size; i++) { if(i < mid) { first[first_index++] = original[i]; } else { @@ -40,23 +40,17 @@ void split_array(char original[], int size, char first[], char second[]) { first[first_index] = '\0'; second[second_index] = '\0'; } +void genScramble() { + scrambleReplace(); + strcpy(scramble_str, printData()); + split_array(scramble_str, strlen(scramble_str), scramble_start, scramble_end); +} static void draw_callback(Canvas* canvas, void* ctx) { UNUSED(ctx); canvas_clear(canvas); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 4, 13, "Rubik's Cube Scrambler"); - - if(scrambleStarted) { - genScramble(); - scrambleReplace(); - strcpy(scramble_str, printData()); - if(notifications_enabled) { - success_vibration(); - } - split_array(scramble_str, strlen(scramble_str), scramble_start, scramble_end); - scrambleStarted = 0; - } canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, scramble_start); canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignCenter, scramble_end); @@ -90,13 +84,16 @@ int32_t rubiks_cube_scrambler_main(void* p) { furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); if(event.key == InputKeyOk && event.type == InputTypeShort) { - scrambleStarted = 1; + genScramble(); + if(notifications_enabled) { + success_vibration(); + } } if(event.key == InputKeyLeft && event.type == InputTypeShort) { if(notifications_enabled) { - notifications_enabled = 0; + notifications_enabled = false; } else { - notifications_enabled = 1; + notifications_enabled = true; success_vibration(); } } diff --git a/applications/external/rubiks_cube_scrambler/scrambler.c b/applications/external/rubiks_cube_scrambler/scrambler.c index ea5291940..b97a67400 100644 --- a/applications/external/rubiks_cube_scrambler/scrambler.c +++ b/applications/external/rubiks_cube_scrambler/scrambler.c @@ -12,66 +12,37 @@ Authors: Tanish Bhongade and RaZe // 6 moves along with direction char moves[6] = {'R', 'U', 'F', 'B', 'L', 'D'}; -char dir[4] = {' ', '\'', '2'}; -const int SLEN = 20; +char dir[4] = {'\'', '2'}; +const int32_t SLEN = 20; #define RESULT_SIZE 100 -// Structure which holds main scramble + struct GetScramble { char mainScramble[25][3]; }; -struct GetScramble a; // Its object - -// Function prototypes to avoid bugs -void scrambleReplace(); -void genScramble(); -void valid(); -int getRand(int upr, int lwr); -char* printData(); -void writeToFile(); - -// Main function -/* int main(){ - genScramble ();//Calling genScramble - scrambleReplace();//Calling scrambleReplace - valid();//Calling valid to validate the scramble - printData ();//Printing the final scramble - //writeToFile();//If you want to write to a file, please uncomment this - - return 0; -} */ - -void genScramble() { - // Stage 1 - for(int i = 0; i < SLEN; i++) { - strcpy(a.mainScramble[i], "00"); - } - // This makes array like this 00 00 00....... -} +struct GetScramble a; void scrambleReplace() { - // Stage 2 - // Actual process begins here - // Initialize the mainScramble array with all the possible moves - for(int i = 0; i < SLEN; i++) { + for(int32_t i = 0; i < SLEN; i++) { a.mainScramble[i][0] = moves[furi_hal_random_get() % 6]; a.mainScramble[i][1] = dir[furi_hal_random_get() % 3]; } - // Perform the Fisher-Yates shuffle - for(int i = 6 - 1; i > 0; i--) { - int j = rand() % (i + 1); + /* // Perform the Fisher-Yates shuffle + for (int32_t i = 6 - 1; i > 0; i--) + { + int32_t j = rand() % (i + 1); char temp[3]; strcpy(temp, a.mainScramble[i]); strcpy(a.mainScramble[i], a.mainScramble[j]); strcpy(a.mainScramble[j], temp); - } + } */ - // Select the first 10 elements as the scramble, using only the first three elements of the dir array - for(int i = 0; i < SLEN; i++) { + // Select the first 10 elements as the scramble, using only the first two elements of the dir array + for(int32_t i = 0; i < SLEN; i++) { a.mainScramble[i][1] = dir[furi_hal_random_get() % 3]; } - for(int i = 1; i < SLEN; i++) { + for(int32_t i = 1; i < SLEN; i++) { while(a.mainScramble[i][0] == a.mainScramble[i - 2][0] || a.mainScramble[i][0] == a.mainScramble[i - 1][0]) { a.mainScramble[i][0] = moves[furi_hal_random_get() % 5]; @@ -79,24 +50,11 @@ void scrambleReplace() { } } -// Let this function be here for now till I find out what is causing the extra space bug in the scrambles -void remove_double_spaces(char* str) { - int i, j; - int len = strlen(str); - for(i = 0, j = 0; i < len; i++, j++) { - if(str[i] == ' ' && str[i + 1] == ' ') { - i++; - } - str[j] = str[i]; - } - str[j] = '\0'; -} char* printData() { static char result[RESULT_SIZE]; - int offset = 0; - for(int loop = 0; loop < SLEN; loop++) { + int32_t offset = 0; + for(int32_t loop = 0; loop < SLEN; loop++) { offset += snprintf(result + offset, RESULT_SIZE - offset, "%s ", a.mainScramble[loop]); } - remove_double_spaces(result); return result; -} +} \ No newline at end of file diff --git a/applications/external/rubiks_cube_scrambler/scrambler.h b/applications/external/rubiks_cube_scrambler/scrambler.h index 4b56c565d..557ef20ae 100644 --- a/applications/external/rubiks_cube_scrambler/scrambler.h +++ b/applications/external/rubiks_cube_scrambler/scrambler.h @@ -1,3 +1,2 @@ void scrambleReplace(); -void genScramble(); char* printData(); diff --git a/applications/external/timelapse/icons/ButtonDownHollow_7x4.png b/applications/external/timelapse/icons/ButtonDownHollow_7x4.png new file mode 100644 index 0000000000000000000000000000000000000000..2b87c43647d56c78a2c5ff5ecee6b3c9641c57fa GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!2~4FF4?37q!^2X+?^QKos)S9l1X>;b!1@J z*w6hZkrl}2EbxddW?{W77j*6W#z~B_y7O*=kMVYCpZ{Ri)wIhc380j PXc~j3tDnm{r-UW|ZJjr; literal 0 HcmV?d00001 diff --git a/applications/external/timelapse/icons/ButtonLeftHollow_4x7.png b/applications/external/timelapse/icons/ButtonLeftHollow_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..374dc7d1ac7aa9c4725681c145ef2f61b7869b77 GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^EI`c8!2~21uvfPODaPU;cPEB*=VV@jWYZme9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucK`l=g#}J9|v pae)j+>jQ^K9-aVZhZ;6Eh9@nu@2ZLtD}cHfJYD@<);T3K0RWG@CDZ@_ literal 0 HcmV?d00001 diff --git a/applications/external/timelapse/icons/ButtonRightHollow_4x7.png b/applications/external/timelapse/icons/ButtonRightHollow_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..acbb0859267d431885e1998146e04f2cdfdb46b9 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^EI`c8!2~21uvfPODaPU;cPEB*=VV@jWYZme9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucK@CqA#}J9|gTe~DWM4fbu1?j literal 0 HcmV?d00001 diff --git a/applications/external/timelapse/icons/ButtonUpHollow_7x4.png b/applications/external/timelapse/icons/ButtonUpHollow_7x4.png new file mode 100644 index 0000000000000000000000000000000000000000..e88ee332230e98938ba769e8813a671040140987 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!2~4FF4?37q!^2X+?^QKos)S9l1X>;b!1@J z*w6hZkrl}2EbxddW?mutex, FuriWaitForever); + + // Try to acquire the mutex for the plugin state variables, timeout = 25mS + if(furi_mutex_acquire(state->mutex, 25) != FuriStatusOk) return; switch(state->scene) { //--------------------------------------------------------------------- @@ -344,8 +347,7 @@ int32_t wii_ec_anal(void) { goto bail; } // 5. Create a mutex for (reading/writing) the plugin state variables - state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!state->mutex) { + if(!(state->mutex = furi_mutex_alloc(FuriMutexTypeNormal))) { ERROR(wii_errs[(error = ERR_NO_MUTEX)]); goto bail; } @@ -434,7 +436,10 @@ int32_t wii_ec_anal(void) { // Read successful // *** Try to lock the plugin state variables *** - furi_mutex_acquire(state->mutex, FuriWaitForever); + if(furi_mutex_acquire(state->mutex, FuriWaitForever) != FuriStatusOk) { + ERROR(wii_errs[(error = ERR_MUTEX_BLOCK)]); + goto bail; + } // *** Handle events *** switch(msg.id) { @@ -472,7 +477,10 @@ int32_t wii_ec_anal(void) { if(redraw) view_port_update(vpp); // *** Try to release the plugin state variables *** - furi_mutex_release(state->mutex); + if(furi_mutex_release(state->mutex) != FuriStatusOk) { + ERROR(wii_errs[(error = ERR_MUTEX_RELEASE)]); + goto bail; + } } while(state->run); // ===== Game Over ===== @@ -507,7 +515,10 @@ bail: } // 5. Free the mutex - furi_mutex_free(state->mutex); + if(state->mutex) { + furi_mutex_free(state->mutex); + state->mutex = NULL; + } // 4. Free up state pointer(s) // none diff --git a/applications/external/wii_ec_anal/wii_anal.h b/applications/external/wiiec/wii_anal.h similarity index 98% rename from applications/external/wii_ec_anal/wii_anal.h rename to applications/external/wiiec/wii_anal.h index d8997b030..3be398a54 100644 --- a/applications/external/wii_ec_anal/wii_anal.h +++ b/applications/external/wiiec/wii_anal.h @@ -56,7 +56,8 @@ typedef struct eventMsg { // Access to this memory is controlled by mutex // typedef struct state { - FuriMutex* mutex; + FuriMutex* mutex; // mutex for using this struct + bool run; // true : plugin is running bool timerEn; // controller scanning enabled diff --git a/applications/external/wii_ec_anal/wii_anal_ec.c b/applications/external/wiiec/wii_anal_ec.c similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_ec.c rename to applications/external/wiiec/wii_anal_ec.c diff --git a/applications/external/wii_ec_anal/wii_anal_ec.h b/applications/external/wiiec/wii_anal_ec.h similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_ec.h rename to applications/external/wiiec/wii_anal_ec.h diff --git a/applications/external/wii_ec_anal/wii_anal_keys.c b/applications/external/wiiec/wii_anal_keys.c similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_keys.c rename to applications/external/wiiec/wii_anal_keys.c diff --git a/applications/external/wii_ec_anal/wii_anal_keys.h b/applications/external/wiiec/wii_anal_keys.h similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_keys.h rename to applications/external/wiiec/wii_anal_keys.h diff --git a/applications/external/wii_ec_anal/wii_anal_lcd.c b/applications/external/wiiec/wii_anal_lcd.c similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_lcd.c rename to applications/external/wiiec/wii_anal_lcd.c diff --git a/applications/external/wii_ec_anal/wii_anal_lcd.h b/applications/external/wiiec/wii_anal_lcd.h similarity index 100% rename from applications/external/wii_ec_anal/wii_anal_lcd.h rename to applications/external/wiiec/wii_anal_lcd.h diff --git a/applications/external/wii_ec_anal/wii_anal_ver.h b/applications/external/wiiec/wii_anal_ver.h similarity index 85% rename from applications/external/wii_ec_anal/wii_anal_ver.h rename to applications/external/wiiec/wii_anal_ver.h index 3f2c8c0e6..df2659d57 100644 --- a/applications/external/wii_ec_anal/wii_anal_ver.h +++ b/applications/external/wiiec/wii_anal_ver.h @@ -5,5 +5,6 @@ #define VER_MAJ &img_3x5_1 #define VER_MIN &img_3x5_0 +#define VER_SUB &img_3x5_1 #endif //WII_ANAL_VER_H_ diff --git a/applications/external/wii_ec_anal/wii_ec.c b/applications/external/wiiec/wii_ec.c similarity index 100% rename from applications/external/wii_ec_anal/wii_ec.c rename to applications/external/wiiec/wii_ec.c diff --git a/applications/external/wii_ec_anal/wii_ec.h b/applications/external/wiiec/wii_ec.h similarity index 100% rename from applications/external/wii_ec_anal/wii_ec.h rename to applications/external/wiiec/wii_ec.h diff --git a/applications/external/wii_ec_anal/wii_ec_classic.c b/applications/external/wiiec/wii_ec_classic.c similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_classic.c rename to applications/external/wiiec/wii_ec_classic.c diff --git a/applications/external/wii_ec_anal/wii_ec_classic.h b/applications/external/wiiec/wii_ec_classic.h similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_classic.h rename to applications/external/wiiec/wii_ec_classic.h diff --git a/applications/external/wii_ec_anal/wii_ec_macros.h b/applications/external/wiiec/wii_ec_macros.h similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_macros.h rename to applications/external/wiiec/wii_ec_macros.h diff --git a/applications/external/wii_ec_anal/wii_ec_nunchuck.c b/applications/external/wiiec/wii_ec_nunchuck.c similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_nunchuck.c rename to applications/external/wiiec/wii_ec_nunchuck.c diff --git a/applications/external/wii_ec_anal/wii_ec_nunchuck.h b/applications/external/wiiec/wii_ec_nunchuck.h similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_nunchuck.h rename to applications/external/wiiec/wii_ec_nunchuck.h diff --git a/applications/external/wii_ec_anal/wii_ec_udraw.c b/applications/external/wiiec/wii_ec_udraw.c similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_udraw.c rename to applications/external/wiiec/wii_ec_udraw.c diff --git a/applications/external/wii_ec_anal/wii_ec_udraw.h b/applications/external/wiiec/wii_ec_udraw.h similarity index 100% rename from applications/external/wii_ec_anal/wii_ec_udraw.h rename to applications/external/wiiec/wii_ec_udraw.h diff --git a/applications/external/wii_ec_anal/wii_i2c.c b/applications/external/wiiec/wii_i2c.c similarity index 100% rename from applications/external/wii_ec_anal/wii_i2c.c rename to applications/external/wiiec/wii_i2c.c diff --git a/applications/external/wii_ec_anal/wii_i2c.h b/applications/external/wiiec/wii_i2c.h similarity index 100% rename from applications/external/wii_ec_anal/wii_i2c.h rename to applications/external/wiiec/wii_i2c.h From ffa7b74e18d114c0143d03a67b3ad3df71ce8f6a Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 18:03:55 +0100 Subject: [PATCH 274/282] Fix build --- applications/external/music_tracker/application.fam | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/external/music_tracker/application.fam b/applications/external/music_tracker/application.fam index f3bac02c2..fe4355e86 100644 --- a/applications/external/music_tracker/application.fam +++ b/applications/external/music_tracker/application.fam @@ -10,5 +10,4 @@ App( order=20, fap_icon="zero_tracker.png", fap_category="Music", - fap_icon_assets="icons", ) From 8a5cef00795d02eaa5a892b708432f95164a1e22 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 19:56:06 +0100 Subject: [PATCH 275/282] Fix zero tracker app (revert "update") --- .../music_tracker/tracker_engine/speaker_hal.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/applications/external/music_tracker/tracker_engine/speaker_hal.c b/applications/external/music_tracker/tracker_engine/speaker_hal.c index 208fee04c..0a506a424 100644 --- a/applications/external/music_tracker/tracker_engine/speaker_hal.c +++ b/applications/external/music_tracker/tracker_engine/speaker_hal.c @@ -40,12 +40,17 @@ void tracker_speaker_stop() { } void tracker_speaker_init() { - furi_hal_speaker_start(200.0f, 0.01f); - tracker_speaker_stop(); + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + furi_hal_speaker_start(200.0f, 0.01f); + tracker_speaker_stop(); + } } void tracker_speaker_deinit() { - furi_hal_speaker_stop(); + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } } static FuriHalInterruptISR tracker_isr; @@ -99,4 +104,4 @@ void tracker_debug_set(bool value) { void tracker_debug_deinit() { furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -} \ No newline at end of file +} From ae321fb5f4c616d3965546926b1b4b446eef8d86 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 20:40:23 +0100 Subject: [PATCH 276/282] Mfw NULL pointer dereference (fix IFTTT) --- .../external/ifttt/ifttt_virtual_button.c | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/applications/external/ifttt/ifttt_virtual_button.c b/applications/external/ifttt/ifttt_virtual_button.c index e23b8715d..d518a1109 100644 --- a/applications/external/ifttt/ifttt_virtual_button.c +++ b/applications/external/ifttt/ifttt_virtual_button.c @@ -38,6 +38,11 @@ void save_settings_file(FlipperFormat* file, Settings* settings) { Settings* load_settings() { Settings* settings = malloc(sizeof(Settings)); + settings->save_ssid = ""; + settings->save_password = ""; + settings->save_key = ""; + settings->save_event = ""; + Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -53,29 +58,14 @@ Settings* load_settings() { text_event_value = furi_string_alloc(); if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) { - if(!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) { - flipper_format_file_close(file); - } else { - settings->save_ssid = malloc(1); - settings->save_password = malloc(1); - settings->save_key = malloc(1); - settings->save_event = malloc(1); - - settings->save_ssid[0] = '\0'; - settings->save_password[0] = '\0'; - settings->save_key[0] = '\0'; - settings->save_event[0] = '\0'; - + if(flipper_format_file_open_new(file, CONFIG_FILE_PATH)) { save_settings_file(file, settings); - flipper_format_file_close(file); } + flipper_format_file_close(file); } else { - if(!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { - flipper_format_file_close(file); - } else { + if(flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { uint32_t value; - if(!flipper_format_read_header(file, string_value, &value)) { - } else { + if(flipper_format_read_header(file, string_value, &value)) { if(flipper_format_read_string(file, CONF_SSID, text_ssid_value)) { settings->save_ssid = malloc(furi_string_size(text_ssid_value) + 1); strcpy(settings->save_ssid, furi_string_get_cstr(text_ssid_value)); @@ -93,8 +83,8 @@ Settings* load_settings() { strcpy(settings->save_event, furi_string_get_cstr(text_event_value)); } } - flipper_format_file_close(file); } + flipper_format_file_close(file); } furi_string_free(text_ssid_value); @@ -248,4 +238,4 @@ int32_t ifttt_virtual_button_app(void* p) { view_dispatcher_run(app->view_dispatcher); ifttt_virtual_button_app_free(app); return 0; -} \ No newline at end of file +} From 5d62ce919bf47a74a50aa421b6936809f2bfbc54 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 21:29:46 +0100 Subject: [PATCH 277/282] :doomed: --- .../services/storage/storages/storage_int.c | 92 +++++++++---------- firmware/targets/f7/src/recovery.c | 1 - 2 files changed, 46 insertions(+), 47 deletions(-) diff --git a/applications/services/storage/storages/storage_int.c b/applications/services/storage/storages/storage_int.c index 8e4e2b0fc..5342c2bdd 100644 --- a/applications/services/storage/storages/storage_int.c +++ b/applications/services/storage/storages/storage_int.c @@ -194,41 +194,41 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) { if(need_format) { // Backup U2F keys - lfs_file_t file; - uint8_t* cnt = NULL; - uint32_t cnt_size; - uint8_t* key = NULL; - uint32_t key_size; - if(lfs_mount(lfs, &lfs_data->config) == 0) { - FURI_LOG_I(TAG, "Factory reset: Mounted for backup"); + // lfs_file_t file; + // uint8_t* cnt = NULL; + // uint32_t cnt_size; + // uint8_t* key = NULL; + // uint32_t key_size; + // if(lfs_mount(lfs, &lfs_data->config) == 0) { + // FURI_LOG_I(TAG, "Factory reset: Mounted for backup"); - if(lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_RDONLY) == 0) { - cnt_size = file.ctz.size; - cnt = malloc(cnt_size); - if(lfs_file_read(lfs, &file, cnt, cnt_size) != (int32_t)cnt_size) { - free(cnt); - cnt = NULL; - } - lfs_file_close(lfs, &file); - if(lfs_file_open(lfs, &file, ".key.u2f", LFS_O_RDONLY) == 0) { - key_size = file.ctz.size; - key = malloc(key_size); - if(lfs_file_read(lfs, &file, key, key_size) != (int32_t)key_size) { - free(key); - key = NULL; - } - lfs_file_close(lfs, &file); - } - } + // if(lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_RDONLY) == 0) { + // cnt_size = file.ctz.size; + // cnt = malloc(cnt_size); + // if(lfs_file_read(lfs, &file, cnt, cnt_size) != (int32_t)cnt_size) { + // free(cnt); + // cnt = NULL; + // } + // lfs_file_close(lfs, &file); + // if(lfs_file_open(lfs, &file, ".key.u2f", LFS_O_RDONLY) == 0) { + // key_size = file.ctz.size; + // key = malloc(key_size); + // if(lfs_file_read(lfs, &file, key, key_size) != (int32_t)key_size) { + // free(key); + // key = NULL; + // } + // lfs_file_close(lfs, &file); + // } + // } - if(lfs_unmount(lfs) == 0) { - FURI_LOG_E(TAG, "Factory reset: Unmounted after backup"); - } else { - FURI_LOG_E(TAG, "Factory reset: Unmount after backup failed"); - } - } else { - FURI_LOG_E(TAG, "Factory reset: Mount for backup failed"); - } + // if(lfs_unmount(lfs) == 0) { + // FURI_LOG_E(TAG, "Factory reset: Unmounted after backup"); + // } else { + // FURI_LOG_E(TAG, "Factory reset: Unmount after backup failed"); + // } + // } else { + // FURI_LOG_E(TAG, "Factory reset: Mount for backup failed"); + // } // Format storage if(lfs_format(lfs, &lfs_data->config) == 0) { @@ -239,19 +239,19 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) { storage->status = StorageStatusOK; // Restore U2F keys - if(cnt != NULL && key != NULL) { - if(lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_WRONLY | LFS_O_CREAT) == 0) { - lfs_file_write(lfs, &file, cnt, cnt_size); - lfs_file_close(lfs, &file); - if(lfs_file_open(lfs, &file, ".key.u2f", LFS_O_WRONLY | LFS_O_CREAT) == - 0) { - lfs_file_write(lfs, &file, key, key_size); - lfs_file_close(lfs, &file); - } - } - } - if(cnt != NULL) free(cnt); - if(key != NULL) free(key); + // if(cnt != NULL && key != NULL) { + // if(lfs_file_open(lfs, &file, ".cnt.u2f", LFS_O_WRONLY | LFS_O_CREAT) == 0) { + // lfs_file_write(lfs, &file, cnt, cnt_size); + // lfs_file_close(lfs, &file); + // if(lfs_file_open(lfs, &file, ".key.u2f", LFS_O_WRONLY | LFS_O_CREAT) == + // 0) { + // lfs_file_write(lfs, &file, key, key_size); + // lfs_file_close(lfs, &file); + // } + // } + // } + // if(cnt != NULL) free(cnt); + // if(key != NULL) free(key); } else { FURI_LOG_E(TAG, "Factory reset: Mount after format failed"); storage->status = StorageStatusNotMounted; diff --git a/firmware/targets/f7/src/recovery.c b/firmware/targets/f7/src/recovery.c index 700fb03c6..f1bfe5383 100644 --- a/firmware/targets/f7/src/recovery.c +++ b/firmware/targets/f7/src/recovery.c @@ -57,7 +57,6 @@ void flipper_boot_recovery_exec() { if(!counter) { furi_hal_rtc_set_flag(FuriHalRtcFlagResetPin); - furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); furi_hal_rtc_set_pin_fails(0); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); } From 331f26547bdbf7fab2ec96fe8741693f628ca635 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 22:54:32 +0100 Subject: [PATCH 278/282] Add XTREME_SETTINGS_WAIT() for race conditions --- firmware/targets/f7/api_symbols.csv | 1 + lib/xtreme/settings.c | 14 +++++++++++++- lib/xtreme/xtreme.h | 4 +++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 784be14a9..2ccfe3ed5 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -323,6 +323,7 @@ Function,-,SystemInit,void, Function,+,XTREME_ASSETS,XtremeAssets*, Function,+,XTREME_SETTINGS,XtremeSettings*, Function,+,XTREME_SETTINGS_SAVE,void, +Function,+,XTREME_SETTINGS_WAIT,XtremeSettings*, Function,-,_Exit,void,int Function,-,__assert,void,"const char*, int, const char*" Function,+,__assert_func,void,"const char*, int, const char*, const char*" diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index fe6520808..88aa55b99 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -6,6 +6,7 @@ #define TAG "XtremeSettings" XtremeSettings xtreme_settings = { + .loaded = false, .asset_pack = "", .anim_speed = 100, // 100% .cycle_anims = 0, // Meta.txt @@ -27,7 +28,7 @@ XtremeSettings xtreme_settings = { .favorite_timeout = 0, // OFF .bad_bt = false, // USB .bad_bt_remember = false, // OFF - .butthurt_timer = 43200, // 12 H + .butthurt_timer = 21600, // 6 H .rgb_backlight = false, // OFF }; @@ -88,6 +89,8 @@ void XTREME_SETTINGS_LOAD() { } flipper_format_free(file); furi_record_close(RECORD_STORAGE); + + xtreme_settings.loaded = true; } void XTREME_SETTINGS_SAVE() { @@ -128,3 +131,12 @@ void XTREME_SETTINGS_SAVE() { XtremeSettings* XTREME_SETTINGS() { return &xtreme_settings; } + +XtremeSettings* XTREME_SETTINGS_WAIT() { + if(furi_hal_is_normal_boot()) { + while(!xtreme_settings.loaded) { + furi_delay_ms(50); + } + } + return &xtreme_settings; +} diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 54c208ae7..1e63f10ec 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -13,6 +13,7 @@ extern "C" { #define XTREME_ASSETS_PACK_NAME_LEN 32 typedef struct { + bool loaded; char asset_pack[XTREME_ASSETS_PACK_NAME_LEN]; uint32_t anim_speed; int32_t cycle_anims; @@ -38,8 +39,9 @@ typedef struct { bool rgb_backlight; } XtremeSettings; -XtremeSettings* XTREME_SETTINGS(); void XTREME_SETTINGS_SAVE(); +XtremeSettings* XTREME_SETTINGS(); +XtremeSettings* XTREME_SETTINGS_WAIT(); typedef struct { bool is_nsfw; From d4eef8e3f860566515386dce796ce3eb9282bed3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 22:55:06 +0100 Subject: [PATCH 279/282] Properly read butthurt timer setting --- applications/services/dolphin/dolphin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index 5e5ff07d3..145224542 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -80,7 +80,7 @@ Dolphin* dolphin_alloc() { dolphin->state = dolphin_state_alloc(); dolphin->event_queue = furi_message_queue_alloc(8, sizeof(DolphinEvent)); dolphin->pubsub = furi_pubsub_alloc(); - int32_t butthurt = XTREME_SETTINGS()->butthurt_timer; + int32_t butthurt = XTREME_SETTINGS_WAIT()->butthurt_timer; dolphin->butthurt_timer = xTimerCreate( NULL, (butthurt > 0) ? (butthurt * 1000) : -1, From f2f46e42f616b3ff0437a643f29cccfaf2a676d6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 2 May 2023 22:59:19 +0100 Subject: [PATCH 280/282] Bump v45 --- fbt_options.py | 2 +- scripts/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fbt_options.py b/fbt_options.py index 0e87521a1..5c874357a 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -14,7 +14,7 @@ DEBUG = 0 # Suffix to add to files when building distribution # If OS environment has DIST_SUFFIX set, it will be used instead -DIST_SUFFIX = "XFW-0044_09042023" +DIST_SUFFIX = "XFW-0045_02052023" # Coprocessor firmware COPRO_OB_DATA = "scripts/ob.data" diff --git a/scripts/version.py b/scripts/version.py index 4fd5fd3f9..6011dea26 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -1,5 +1,5 @@ #!/usb/bin/env python3 -VERSION = "XFW-0044" +VERSION = "XFW-0045" from flipper.app import App From b5dc2f239e3e7c92080c4c29e9ab8627209622d1 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 3 May 2023 00:11:40 +0100 Subject: [PATCH 281/282] Asset pack support status bar background --- applications/main/archive/views/archive_browser_view.c | 2 +- applications/services/gui/gui.c | 2 +- lib/xtreme/assets.c | 2 ++ lib/xtreme/xtreme.h | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 6e42cb40b..cc2ed6263 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -324,7 +324,7 @@ static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* m const char* tab_name = ArchiveTabNames[model->tab_idx]; - canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); + canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Background_128x11); canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 0, 0, 50, 13); diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index e914e807f..8f0deaa6f 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -75,7 +75,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { canvas_draw_box(gui->canvas, 89, 3, 38, 6); canvas_set_color(gui->canvas, ColorBlack); canvas_set_bitmap_mode(gui->canvas, 1); - canvas_draw_icon(gui->canvas, 0, 0, &I_Background_128x11); + canvas_draw_icon(gui->canvas, 0, 0, XTREME_ASSETS()->I_Background_128x11); } else { canvas_set_color(gui->canvas, ColorBlack); } diff --git a/lib/xtreme/assets.c b/lib/xtreme/assets.c index 3c27f838c..cc1a6fe0d 100644 --- a/lib/xtreme/assets.c +++ b/lib/xtreme/assets.c @@ -28,6 +28,7 @@ XtremeAssets xtreme_assets = { .I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61, .I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57, .I_Cry_dolph_55x52 = &I_Cry_dolph_55x52, + .I_Background_128x11 = &I_Background_128x11, .I_Fishing_123x52 = &I_Fishing_123x52, .I_Scanning_123x52 = &I_Scanning_123x52, .I_Auth_62x31 = &I_Auth_62x31, @@ -132,6 +133,7 @@ void swap(XtremeAssets* x, FuriString* p, File* f) { icon(&x->I_RFIDDolphinSend_97x61, "RFID/RFIDDolphinSend_97x61", p, f); icon(&x->I_RFIDDolphinSuccess_108x57, "RFID/RFIDDolphinSuccess_108x57", p, f); icon(&x->I_Cry_dolph_55x52, "Settings/Cry_dolph_55x52", p, f); + icon(&x->I_Background_128x11, "StatusBar/Background_128x11", p, f); icon(&x->I_Fishing_123x52, "SubGhz/Fishing_123x52", p, f); icon(&x->I_Scanning_123x52, "SubGhz/Scanning_123x52", p, f); icon(&x->I_Auth_62x31, "U2F/Auth_62x31", p, f); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 1e63f10ec..e4baeeeba 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -64,6 +64,7 @@ typedef struct { const Icon* I_RFIDDolphinSend_97x61; const Icon* I_RFIDDolphinSuccess_108x57; const Icon* I_Cry_dolph_55x52; + const Icon* I_Background_128x11; const Icon* I_Fishing_123x52; const Icon* I_Scanning_123x52; const Icon* I_Auth_62x31; From e37dedd477a18edc99201277f98472c3c12e617b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 3 May 2023 00:36:54 +0100 Subject: [PATCH 282/282] Fix some memory management bugs in apps --- applications/external/flipfrid/flipfrid.c | 3 --- applications/external/ibtn_fuzzer/ibtnfuzzer.c | 3 --- .../wifi_marauder_companion/wifi_marauder_text_input.c | 9 --------- applications/external/wiiec/wii_anal.c | 6 +++--- 4 files changed, 3 insertions(+), 18 deletions(-) diff --git a/applications/external/flipfrid/flipfrid.c b/applications/external/flipfrid/flipfrid.c index 25bdac024..172a98ef3 100644 --- a/applications/external/flipfrid/flipfrid.c +++ b/applications/external/flipfrid/flipfrid.c @@ -121,9 +121,6 @@ void flipfrid_free(FlipFridState* flipfrid) { furi_string_free(flipfrid->main_menu_proto_items[i]); } - free(flipfrid->data); - free(flipfrid->payload); - // The rest free(flipfrid); } diff --git a/applications/external/ibtn_fuzzer/ibtnfuzzer.c b/applications/external/ibtn_fuzzer/ibtnfuzzer.c index 880d3983b..9e6239b69 100644 --- a/applications/external/ibtn_fuzzer/ibtnfuzzer.c +++ b/applications/external/ibtn_fuzzer/ibtnfuzzer.c @@ -121,9 +121,6 @@ void ibtnfuzzer_free(iBtnFuzzerState* ibtnfuzzer) { furi_string_free(ibtnfuzzer->main_menu_proto_items[i]); } - free(ibtnfuzzer->data); - free(ibtnfuzzer->payload); - // The rest free(ibtnfuzzer); } diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c index d9eee83c9..e17e5aaee 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c @@ -168,31 +168,22 @@ static char char_to_uppercase(const char letter) { switch(letter) { case '_': return 0x20; - break; case '(': return 0x29; - break; case '{': return 0x7d; - break; case '[': return 0x5d; - break; case '/': return 0x5c; - break; case ';': return 0x3a; - break; case '.': return 0x2c; - break; case '!': return 0x3f; - break; case '<': return 0x3e; - break; } if(char_is_lowercase(letter)) { return (letter - 0x20); diff --git a/applications/external/wiiec/wii_anal.c b/applications/external/wiiec/wii_anal.c index e3bb21f0f..5882dd60b 100644 --- a/applications/external/wiiec/wii_anal.c +++ b/applications/external/wiiec/wii_anal.c @@ -488,13 +488,13 @@ int32_t wii_ec_anal(void) { bail: // 10. Release system notification queue - if(state->notify) { + if(state && state->notify) { furi_record_close(RECORD_NOTIFICATION); state->notify = NULL; } // 9. Stop the timer - if(state->timer) { + if(state && state->timer) { (void)furi_timer_stop(state->timer); furi_timer_free(state->timer); state->timer = NULL; @@ -515,7 +515,7 @@ bail: } // 5. Free the mutex - if(state->mutex) { + if(state && state->mutex) { furi_mutex_free(state->mutex); state->mutex = NULL; }