From 6a5d63803aeadc9f2b3cb4f150bb32dee841dc03 Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Sat, 2 Dec 2023 07:45:47 +0300 Subject: [PATCH 1/5] [FL-3675] Ntag21x write (#3246) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * New scenes for ultralight poller write mode * Added new button and transition logic for write operation For now write is only possible for NTAG21x cards with default password and no AUTHLIM set * Poller states extended * Enums and datatypes extended for new poller mode * Added mode field to poller instance datatype * New states for poller added in order to implement write mode * Added new event type for locked cards in order to simplify state flow * New logic for poller write commands * Scenes adjustments * Scenes renamed * New field added to poller instance * Now we write in 'page per call' mode * Now function takes callback return value into account * Callback will be called only in write mode * Event type added * Log adjusted and start page to write set * Logs added and check in now false at start, then it moves to true * Now mf_ultralight_poller_handler_request_write_data halts card in case of check failure and stops poller * All fail events now returns NfcCommandStop callback * In case of fail we move back properly * Remove garbage Co-authored-by: gornekich Co-authored-by: あく --- .../mf_ultralight/mf_ultralight.c | 13 ++ .../main/nfc/scenes/nfc_scene_config.h | 4 + .../scenes/nfc_scene_mf_ultralight_write.c | 119 +++++++++++++++ .../nfc_scene_mf_ultralight_write_fail.c | 67 +++++++++ .../nfc_scene_mf_ultralight_write_success.c | 43 ++++++ .../nfc_scene_mf_ultralight_wrong_card.c | 58 ++++++++ .../mf_ultralight/mf_ultralight_poller.c | 139 +++++++++++++++++- .../mf_ultralight/mf_ultralight_poller.h | 16 ++ .../mf_ultralight/mf_ultralight_poller_i.h | 7 + 9 files changed, 462 insertions(+), 4 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c create mode 100644 applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index c4fd04c7e..3e27fc539 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -12,6 +12,7 @@ enum { SubmenuIndexUnlock = SubmenuIndexCommonMax, SubmenuIndexUnlockByReader, SubmenuIndexUnlockByPassword, + SubmenuIndexWrite, }; static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) { @@ -106,6 +107,15 @@ static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instanc SubmenuIndexUnlock, nfc_protocol_support_common_submenu_callback, instance); + } else if( + data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 || + data->type == MfUltralightTypeNTAG216) { + submenu_add_item( + submenu, + "Write", + SubmenuIndexWrite, + nfc_protocol_support_common_submenu_callback, + instance); } } @@ -146,6 +156,9 @@ static bool if(event == SubmenuIndexUnlock) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); return true; + } else if(event == SubmenuIndexWrite) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite); + return true; } return false; } diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index f415c66a6..a9887996d 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -24,6 +24,10 @@ ADD_SCENE(nfc, field, Field) ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, exit_confirm, ExitConfirm) +ADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite) +ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess) +ADD_SCENE(nfc, mf_ultralight_write_fail, MfUltralightWriteFail) +ADD_SCENE(nfc, mf_ultralight_wrong_card, MfUltralightWrongCard) ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c new file mode 100644 index 000000000..b3c1beef5 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c @@ -0,0 +1,119 @@ +#include "../nfc_app_i.h" + +#include + +enum { + NfcSceneMfUltralightWriteStateCardSearch, + NfcSceneMfUltralightWriteStateCardFound, +}; + +NfcCommand nfc_scene_mf_ultralight_write_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolMfUltralight); + + NfcCommand command = NfcCommandContinue; + NfcApp* instance = context; + MfUltralightPollerEvent* mfu_event = event.event_data; + + if(mfu_event->type == MfUltralightPollerEventTypeRequestMode) { + mfu_event->data->poller_mode = MfUltralightPollerModeWrite; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) { + mfu_event->data->auth_context.skip_auth = true; + } else if(mfu_event->type == MfUltralightPollerEventTypeRequestWriteData) { + mfu_event->data->write_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + } else if(mfu_event->type == MfUltralightPollerEventTypeCardMismatch) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } else if(mfu_event->type == MfUltralightPollerEventTypeCardLocked) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } else if(mfu_event->type == MfUltralightPollerEventTypeWriteFail) { + command = NfcCommandStop; + } else if(mfu_event->type == MfUltralightPollerEventTypeWriteSuccess) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } + return command; +} + +static void nfc_scene_mf_ultralight_write_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + popup_reset(popup); + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightWrite); + + if(state == NfcSceneMfUltralightWriteStateCardSearch) { + popup_set_text( + instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_ultralight_write_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfUltralightWrite, + NfcSceneMfUltralightWriteStateCardSearch); + nfc_scene_mf_ultralight_write_setup_view(instance); + + // Setup and start worker + FURI_LOG_D("WMFU", "Card searching..."); + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight); + nfc_poller_start(instance->poller, nfc_scene_mf_ultralight_write_worker_callback, instance); + + nfc_blink_emulate_start(instance); +} + +bool nfc_scene_mf_ultralight_write_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfUltralightWrite, + NfcSceneMfUltralightWriteStateCardFound); + nfc_scene_mf_ultralight_write_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventWrongCard) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrongCard); + consumed = true; + } else if(event.event == NfcCustomEventPollerSuccess) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteSuccess); + consumed = true; + } else if(event.event == NfcCustomEventPollerFailure) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteFail); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_ultralight_write_on_exit(void* context) { + NfcApp* instance = context; + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfUltralightWrite, + NfcSceneMfUltralightWriteStateCardSearch); + // Clear view + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c new file mode 100644 index 000000000..dff5f2781 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c @@ -0,0 +1,67 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_ultralight_write_fail_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_ultralight_write_fail_on_enter(void* context) { + NfcApp* instance = context; + Widget* widget = instance->widget; + + notification_message(instance->notifications, &sequence_error); + + widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Card protected by\npassword, AUTH0\nor lock bits"); + + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Finish", + nfc_scene_mf_ultralight_write_fail_widget_callback, + instance); + + // Setup and start worker + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +static bool nfc_scene_mf_ultralight_write_fail_move_to_back_scene(const NfcApp* const instance) { + bool was_saved = scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu); + uint32_t scene_id = was_saved ? NfcSceneSavedMenu : NfcSceneReadMenu; + + return scene_manager_search_and_switch_to_previous_scene(instance->scene_manager, scene_id); +} + +bool nfc_scene_mf_ultralight_write_fail_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance); + } + return consumed; +} + +void nfc_scene_mf_ultralight_write_fail_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c new file mode 100644 index 000000000..c1fbc35ee --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c @@ -0,0 +1,43 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_ultralight_write_success_popup_callback(void* context) { + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_ultralight_write_success_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcSave); + + notification_message(instance->notifications, &sequence_success); + + Popup* popup = instance->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_mf_ultralight_write_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_ultralight_write_success_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_write_success_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c new file mode 100644 index 000000000..a225c474d --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c @@ -0,0 +1,58 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_ultralight_wrong_card_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_ultralight_wrong_card_on_enter(void* context) { + NfcApp* instance = context; + Widget* widget = instance->widget; + + notification_message(instance->notifications, &sequence_error); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); + widget_add_string_multiline_element( + widget, + 4, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Card of the same\ntype should be\n presented"); + //"Data management\nis only possible\nwith card of same type"); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_scene_mf_ultralight_wrong_card_widget_callback, + instance); + + // Setup and start worker + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_ultralight_wrong_card_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(instance->scene_manager); + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_wrong_card_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} \ No newline at end of file diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c index 86ab68c8b..619cd8c5f 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c @@ -224,11 +224,24 @@ static NfcCommand mf_ultralight_poller_handler_idle(MfUltralightPoller* instance instance->tearing_flag_read = 0; instance->tearing_flag_total = 3; instance->pages_read = 0; - instance->state = MfUltralightPollerStateReadVersion; - + instance->state = MfUltralightPollerStateRequestMode; + instance->current_page = 0; return NfcCommandContinue; } +static NfcCommand mf_ultralight_poller_handler_request_mode(MfUltralightPoller* instance) { + NfcCommand command = NfcCommandContinue; + + instance->mfu_event.type = MfUltralightPollerEventTypeRequestMode; + instance->mfu_event.data->poller_mode = MfUltralightPollerModeRead; + + command = instance->callback(instance->general_event, instance->context); + instance->mode = instance->mfu_event.data->poller_mode; + + instance->state = MfUltralightPollerStateReadVersion; + return command; +} + static NfcCommand mf_ultralight_poller_handler_read_version(MfUltralightPoller* instance) { instance->error = mf_ultralight_poller_read_version(instance, &instance->data->version); if(instance->error == MfUltralightErrorNone) { @@ -259,6 +272,7 @@ static NfcCommand mf_ultralight_poller_handler_check_ultralight_c(MfUltralightPo } static NfcCommand mf_ultralight_poller_handler_check_ntag_203(MfUltralightPoller* instance) { + MfUltralightPollerState next_state = MfUltralightPollerStateGetFeatureSet; MfUltralightPageReadCommandData data = {}; instance->error = mf_ultralight_poller_read_page(instance, 41, &data); if(instance->error == MfUltralightErrorNone) { @@ -268,8 +282,13 @@ static NfcCommand mf_ultralight_poller_handler_check_ntag_203(MfUltralightPoller FURI_LOG_D(TAG, "Original Ultralight detected"); iso14443_3a_poller_halt(instance->iso14443_3a_poller); instance->data->type = MfUltralightTypeUnknown; + if(instance->mode == MfUltralightPollerModeWrite) { + instance->mfu_event.type = MfUltralightPollerEventTypeCardMismatch; + instance->callback(instance->general_event, instance->context); + next_state = MfUltralightPollerStateWriteFail; + } } - instance->state = MfUltralightPollerStateGetFeatureSet; + instance->state = next_state; return NfcCommandContinue; } @@ -508,6 +527,7 @@ static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoll static NfcCommand mf_ultralight_poller_handler_read_fail(MfUltralightPoller* instance) { FURI_LOG_D(TAG, "Read Failed"); iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->mfu_event.type = MfUltralightPollerEventTypeReadFailed; instance->mfu_event.data->error = instance->error; NfcCommand command = instance->callback(instance->general_event, instance->context); instance->state = MfUltralightPollerStateIdle; @@ -516,15 +536,121 @@ static NfcCommand mf_ultralight_poller_handler_read_fail(MfUltralightPoller* ins static NfcCommand mf_ultralight_poller_handler_read_success(MfUltralightPoller* instance) { FURI_LOG_D(TAG, "Read success"); - iso14443_3a_poller_halt(instance->iso14443_3a_poller); instance->mfu_event.type = MfUltralightPollerEventTypeReadSuccess; NfcCommand command = instance->callback(instance->general_event, instance->context); + + if(instance->mode == MfUltralightPollerModeRead) { + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->state = MfUltralightPollerStateIdle; + } else { + instance->state = MfUltralightPollerStateRequestWriteData; + } + + return command; +} + +static NfcCommand mf_ultralight_poller_handler_request_write_data(MfUltralightPoller* instance) { + FURI_LOG_D(TAG, "Check writing capability"); + NfcCommand command = NfcCommandContinue; + MfUltralightPollerState next_state = MfUltralightPollerStateWritePages; + instance->current_page = 4; + + instance->mfu_event.type = MfUltralightPollerEventTypeRequestWriteData; + instance->callback(instance->general_event, instance->context); + + const MfUltralightData* write_data = instance->mfu_event.data->write_data; + const MfUltralightData* tag_data = instance->data; + uint32_t features = mf_ultralight_get_feature_support_set(tag_data->type); + + bool check_passed = false; + do { + if(write_data->type != tag_data->type) { + FURI_LOG_D(TAG, "Incorrect tag type"); + instance->mfu_event.type = MfUltralightPollerEventTypeCardMismatch; + break; + } + + if(!instance->auth_context.auth_success) { + FURI_LOG_D(TAG, "Unknown password"); + instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked; + break; + } + + const MfUltralightPage staticlock_page = tag_data->page[2]; + if(staticlock_page.data[2] != 0 || staticlock_page.data[3] != 0) { + FURI_LOG_D(TAG, "Static lock bits are set"); + instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked; + break; + } + + if(mf_ultralight_support_feature(features, MfUltralightFeatureSupportDynamicLock)) { + uint8_t dynlock_num = mf_ultralight_get_config_page_num(tag_data->type) - 1; + const MfUltralightPage dynlock_page = tag_data->page[dynlock_num]; + if(dynlock_page.data[0] != 0 || dynlock_page.data[1] != 0) { + FURI_LOG_D(TAG, "Dynamic lock bits are set"); + instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked; + break; + } + } + + check_passed = true; + } while(false); + + if(!check_passed) { + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + command = instance->callback(instance->general_event, instance->context); + next_state = MfUltralightPollerStateWriteFail; + } + + instance->state = next_state; + return command; +} + +static NfcCommand mf_ultralight_poller_handler_write_pages(MfUltralightPoller* instance) { + NfcCommand command = NfcCommandContinue; + + do { + const MfUltralightData* write_data = instance->mfu_event.data->write_data; + uint8_t end_page = mf_ultralight_get_config_page_num(write_data->type) - 1; + if(instance->current_page == end_page) { + instance->state = MfUltralightPollerStateWriteSuccess; + break; + } + FURI_LOG_D(TAG, "Writing page %d", instance->current_page); + MfUltralightError error = mf_ultralight_poller_write_page( + instance, instance->current_page, &write_data->page[instance->current_page]); + if(error != MfUltralightErrorNone) { + instance->state = MfUltralightPollerStateWriteFail; + instance->error = error; + break; + } + instance->current_page++; + } while(false); + + return command; +} + +static NfcCommand mf_ultralight_poller_handler_write_fail(MfUltralightPoller* instance) { + FURI_LOG_D(TAG, "Write failed"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->mfu_event.data->error = instance->error; + instance->mfu_event.type = MfUltralightPollerEventTypeWriteFail; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static NfcCommand mf_ultralight_poller_handler_write_success(MfUltralightPoller* instance) { + FURI_LOG_D(TAG, "Write success"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->mfu_event.type = MfUltralightPollerEventTypeWriteSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); return command; } static const MfUltralightPollerReadHandler mf_ultralight_poller_read_handler[MfUltralightPollerStateNum] = { [MfUltralightPollerStateIdle] = mf_ultralight_poller_handler_idle, + [MfUltralightPollerStateRequestMode] = mf_ultralight_poller_handler_request_mode, [MfUltralightPollerStateReadVersion] = mf_ultralight_poller_handler_read_version, [MfUltralightPollerStateDetectMfulC] = mf_ultralight_poller_handler_check_ultralight_c, [MfUltralightPollerStateDetectNtag203] = mf_ultralight_poller_handler_check_ntag_203, @@ -538,6 +664,11 @@ static const MfUltralightPollerReadHandler [MfUltralightPollerStateReadPages] = mf_ultralight_poller_handler_read_pages, [MfUltralightPollerStateReadFailed] = mf_ultralight_poller_handler_read_fail, [MfUltralightPollerStateReadSuccess] = mf_ultralight_poller_handler_read_success, + [MfUltralightPollerStateRequestWriteData] = + mf_ultralight_poller_handler_request_write_data, + [MfUltralightPollerStateWritePages] = mf_ultralight_poller_handler_write_pages, + [MfUltralightPollerStateWriteFail] = mf_ultralight_poller_handler_write_fail, + [MfUltralightPollerStateWriteSuccess] = mf_ultralight_poller_handler_write_success, }; diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h index 665d90cb7..2343be089 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h @@ -16,13 +16,27 @@ typedef struct MfUltralightPoller MfUltralightPoller; * @brief Enumeration of possible MfUltralight poller event types. */ typedef enum { + MfUltralightPollerEventTypeRequestMode, /**< Poller requests for operating mode. */ MfUltralightPollerEventTypeAuthRequest, /**< Poller requests to fill authentication context. */ MfUltralightPollerEventTypeAuthSuccess, /**< Authentication succeeded. */ MfUltralightPollerEventTypeAuthFailed, /**< Authentication failed. */ MfUltralightPollerEventTypeReadSuccess, /**< Poller read card successfully. */ MfUltralightPollerEventTypeReadFailed, /**< Poller failed to read card. */ + MfUltralightPollerEventTypeRequestWriteData, /**< Poller request card data for write operation. */ + MfUltralightPollerEventTypeCardMismatch, /**< Type of card for writing differs from presented one. */ + MfUltralightPollerEventTypeCardLocked, /**< Presented card is locked by password, AUTH0 or lock bytes. */ + MfUltralightPollerEventTypeWriteSuccess, /**< Poller wrote card successfully. */ + MfUltralightPollerEventTypeWriteFail, /**< Poller failed to write card. */ } MfUltralightPollerEventType; +/** + * @brief Enumeration of possible MfUltralight poller operating modes. + */ +typedef enum { + MfUltralightPollerModeRead, /**< Poller will only read card. It's a default mode. */ + MfUltralightPollerModeWrite, /**< Poller will write already saved card to another presented card. */ +} MfUltralightPollerMode; + /** * @brief MfUltralight poller authentication context. */ @@ -39,6 +53,8 @@ typedef struct { typedef union { MfUltralightPollerAuthContext auth_context; /**< Authentication context. */ MfUltralightError error; /**< Error code indicating reading fail reason. */ + const MfUltralightData* write_data; + MfUltralightPollerMode poller_mode; } MfUltralightPollerEventData; /** diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h index c89402b42..7c7354b1c 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h @@ -49,6 +49,7 @@ typedef union { typedef enum { MfUltralightPollerStateIdle, + MfUltralightPollerStateRequestMode, MfUltralightPollerStateReadVersion, MfUltralightPollerStateDetectMfulC, MfUltralightPollerStateDetectNtag203, @@ -61,6 +62,10 @@ typedef enum { MfUltralightPollerStateTryDefaultPass, MfUltralightPollerStateReadFailed, MfUltralightPollerStateReadSuccess, + MfUltralightPollerStateRequestWriteData, + MfUltralightPollerStateWritePages, + MfUltralightPollerStateWriteFail, + MfUltralightPollerStateWriteSuccess, MfUltralightPollerStateNum, } MfUltralightPollerState; @@ -68,6 +73,7 @@ typedef enum { struct MfUltralightPoller { Iso14443_3aPoller* iso14443_3a_poller; MfUltralightPollerState state; + MfUltralightPollerMode mode; BitBuffer* tx_buffer; BitBuffer* rx_buffer; MfUltralightData* data; @@ -79,6 +85,7 @@ struct MfUltralightPoller { uint8_t counters_total; uint8_t tearing_flag_read; uint8_t tearing_flag_total; + uint16_t current_page; MfUltralightError error; NfcGenericEvent general_event; From 93732865acac9053cb0c580a2bf3e58962b3a678 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Sat, 2 Dec 2023 08:52:04 +0400 Subject: [PATCH 2/5] [FL-3132] HID app: Add new function key icons (#3236) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add new function key icons * Fix graphical glitches on the buttons Co-authored-by: あく --- applications/system/hid_app/assets/Alt_11x7.png | Bin 2417 -> 0 bytes applications/system/hid_app/assets/Alt_17x10.png | Bin 0 -> 550 bytes applications/system/hid_app/assets/Cmd_15x7.png | Bin 2426 -> 0 bytes applications/system/hid_app/assets/Cmd_17x10.png | Bin 0 -> 556 bytes applications/system/hid_app/assets/Ctrl_15x7.png | Bin 2433 -> 0 bytes .../system/hid_app/assets/Ctrl_17x10.png | Bin 0 -> 552 bytes applications/system/hid_app/assets/Del_12x7.png | Bin 2417 -> 0 bytes applications/system/hid_app/assets/Del_17x10.png | Bin 0 -> 551 bytes applications/system/hid_app/assets/Esc_14x7.png | Bin 2430 -> 0 bytes applications/system/hid_app/assets/Esc_17x10.png | Bin 0 -> 550 bytes applications/system/hid_app/assets/Tab_15x7.png | Bin 2419 -> 0 bytes applications/system/hid_app/assets/Tab_17x10.png | Bin 0 -> 549 bytes applications/system/hid_app/views/hid_keyboard.c | 14 +++++++------- 13 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 applications/system/hid_app/assets/Alt_11x7.png create mode 100644 applications/system/hid_app/assets/Alt_17x10.png delete mode 100644 applications/system/hid_app/assets/Cmd_15x7.png create mode 100644 applications/system/hid_app/assets/Cmd_17x10.png delete mode 100644 applications/system/hid_app/assets/Ctrl_15x7.png create mode 100644 applications/system/hid_app/assets/Ctrl_17x10.png delete mode 100644 applications/system/hid_app/assets/Del_12x7.png create mode 100644 applications/system/hid_app/assets/Del_17x10.png delete mode 100644 applications/system/hid_app/assets/Esc_14x7.png create mode 100644 applications/system/hid_app/assets/Esc_17x10.png delete mode 100644 applications/system/hid_app/assets/Tab_15x7.png create mode 100644 applications/system/hid_app/assets/Tab_17x10.png diff --git a/applications/system/hid_app/assets/Alt_11x7.png b/applications/system/hid_app/assets/Alt_11x7.png deleted file mode 100644 index 3e4bf320ee313c725e228356b6bf58989e804441..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2417 zcmbtVdsGu=79SqVBOtIXmWphdCPf~SC-O=|SV#a}0MIvsdA#66UH}hCLeQNGkr)bq zcTa>1R32bv_F?`7bNu@j92v z`l8yzhW7qnMoXqL9u8oW115)RouiKo*y%c3U!Xp?HC)*;s#de{#`Mk&>7CTA2M#+O z-lDTv({%Ojqbaa?pl7CLb}3=}LU4di?!BDea*vzjVF*~Jz*o@)l&bxPA+j_S9Sc1i1mNL1KbF+DpzfjGLEg>amgp`<$n!g7O z4LeaQ_RUh>s}RCYx$OlJQn!|$bLVPD--*{vpQI+h?4C}~Gx1;gI8Wwje*SIvS*|+b z+_%NP&ts`Imd$ovpY19!#akK43s0ZzjW~YHXt>bR%EUU~jvih;?c16eQEmU7MeJof z^WboQtFW)=rjF{W%KAZYk!{hVKXKI?j~2B3zA-y>Vk2Ys+x$bIIh+=~`}A3o^_cYJ z*cs``x!Xe%gH4XVECMy5dlp;A$GRB4rAw5@o#&gTq+ygRrWvWyvgA(Vcm<`KDWD4q zv*v8bMI($&@1F(>?b~)r@+DpDkIYy&_sDm#)8!F)_5R_i`a9T9_y-Brd#HHp-R|RP$5K=5!=8%Rhwf3P zi-s9`nd5!oHLl~^{uxe6{{e|s2R!i#lyJ{b!;(amr%(OSHT;>bZ99-&r>r+hFo<3l znQikfOl+GiwB3@a85rT-{}EH6s!s;@x5f<7&{#C~6I)Cbu%|n9YFpyu#nXQ$jl#tr z_p5xPdZ`=-Nsd?3^(M)Vps|ggWgCm=`}Vq*yKO=A8F(z%e>fX;+0%TeT(5Ip+U~YLLDMh=lygg!Ga*WQb=;t? z$L*}^jS)fC9c8xTPotG`y8)m#tzp;F{PTV3PxQJ6f!Y&GdP{anlN;hY?Zkk{hav^> zLLuNp$VPyk&Rc*UA?Xk&pu+@o37JD&tj}RUe_0Oza^ea2NRT)P43;7|f=Lkt$a1AI zKnE6s<-+h_y3=Gd7R!?XGF>I{Viwa81RaDTF)Y^_I|6|23EfpRlM{NYvY{54=Dj_T zm{@c;G!l;#{(&tB%U3@_g`@*-n__C99OXE^punoT8aw|K@;dqPft%e zgGFbtsDuR-OO@jyB~^}5UVyyB;X{}hg%voA$U!ZxC=N-+y~t#3pw(lAr%WLfu7;9h zD|rza(v>0wok?TRWitBfJTW{3S|j;dPb@T50ntMs3`s`C5MfSv9S~8t|4ra^PBLZr+gf(V6dJ|a$*L2_Kc=MoB<1eUP3G5y?J`2Kz_?wSZMbQI|zk|ts&BO4VTHGzoJ{Q=g_q+wXVfp^zX8k zXkadhi1cz8a7jU`yfR`w>=5vMLf_q#b0C@ofJXY2^MiFZqHE;VgEL)Go44AN4T`EP zs4c-k#>KST&6=a;Qx$1IIfMUXdufGPq$qdvcazVeHJe9^ckO70@?kEP)L%(Hre^b&aHEonE(xCqcjVkM=k!t!!_ zYC5O<+{b=NO*}I*EwN3!1%ohS1o1M#C?f>uWl-h-e){N^6(#a}zVj!^O0FWaMjoAI zlB6){5;Z5);}R31$7Q6++x`FBS0mBi=Txr=LdBzHy|zQ;CKNU->*K((3Wrek0Oy%v zes&)Q-bCLhYQ8|eBA@6YN^=FSz=Hm(eG{(jQj8)Dum3s32i>k78GhN>N&o-=07*qoM6N<$g1ch(GXMYp literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Cmd_15x7.png b/applications/system/hid_app/assets/Cmd_15x7.png deleted file mode 100644 index a63a4be747a89745cbb32b5c9fb55d754f600dae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2426 zcmbtVdsGu=79So3iM+POA}pt*Ns))-i6kTqu|NWi-5{nsTv^1B3}k?0(##-1d9^^x z3RnxPRtt@fibYsq1q2#h1rb5^6xgQ15d`EVT@a)$LKR^r5rI9}?jJjIX6DYFdw=)d z-}l`+e+~%_vcfsy006KOyw4M1PaEtq-(ZH_yL0Sa0l?H63J3^E2nY&*6JgbdO0fhC z0u`VNl8A!*0l=rAAS_De6lTZ$s-b%8JKtyNN=pl^7rp;dlPK!SzB|4MR?GW5ixO-6 zjNxQQJvW-~>2@K1vAH+b`~mUF9#eDVQSV`n_qTs_C$#h1 z809B}i9cM;dX{mSe=niAH*4qT)zmb*0h9VC$30ZX?tf;h2^;A{b1TVE^-bF)umQ4| zAp%j_D>*9M47XVlcJPYRI9hgr*>!#EdEJRP+kq@~QX}Vf%M@>+mKI%%OG+0NTaMYh z1q=>(kg5*M5#Op3LLRy81rajFt)Fw}>&M=VHBOr%#X;=uPS&~DLw=r9dBZ>dF8n;V zIil?A zGMe%EP+zOCx9GN!=9ADjBX}DqHFDqC^Ek(G z+3E3fveWZF4o(gtoiG)>FGh*vDrQ)J~yofff5qK+n@ z2nw_2od`uEREM|E;tO5dK9uH5yVyIdy>A^OAkxPTUAhz_qDaHcyOKAa?ANjwC;f7; zrsL)g4b!%#qo*U`_R(&|UUr%yB2OC_uF>8>enQ`gbBrgUt#@@Ls*a_m=m*_Z(GQq= zEEmnwO)@5W!|S{!w*4a{(;Te?5}1K+!qAGaVgiJaGBQlnG1aqYG@ zDYF~~^8UVSaz>~6=~BD(P(D;rQ+x7Xhi{8I_1(EWmsvK06(*g}WbKU1xZ$hQKn1r{{Y0d8ZH^f* z_W3dEMq|WI$BuI6iP=cC!lfVRNxiyv>DluGX^%~G13>+$C6gr=^~Rf$JMGwj$Om@| zVueD$1CuuZCPuFVCYWS|oq!PyFvesKcF{kB08@h=02J60@RPzZAU;F}qc9?am3X>0 znTmI#Q|Vq*9~zS^C)4RHDxT&ojg`=8?s&YLP%MEIdSiD0Fg|6xf@X8dWJNaDBS$ge zkFkkGfkJnqyM=#XOJF%!tdB-k%awWqfTLz(q8voUc(puHfw0xS1cL<|ll5u}0dFut z6MPA~g(3I=SOwx4WGb0T;NtLjJVzysWs7)$%kJ2lFF}T)N;ZX(oSaNf_9DZoI0}u$ zVo|7c3Y|{EEJ#R-0u`%C3WWFqy^GhUgNro zyu$8ksfKoVu}k&zvIA707==}#Fr3K!KU&-n>A%3%WTD?P?42qI+Ztkh*KlbZ$~xV$ zJ%^%isWmP3qJIrqtbw^WEYiziz{SOuRSshFAymNg3*GEca5xVK*xH(wEG$tZI^C|J zL$f^%z2nt{Ga--b{~ecAmiV6MR}0tG@eKBfEez4@=ryZ5nJ0OpR!6h}7pY?r@<$Fc huhluV`!L-O&R7cBE}ON;j!OLi67Yk07yb8r@*g!;qu&4k diff --git a/applications/system/hid_app/assets/Cmd_17x10.png b/applications/system/hid_app/assets/Cmd_17x10.png new file mode 100644 index 0000000000000000000000000000000000000000..26ca3395c699cd19f2ae3c12d46954a45310eab7 GIT binary patch literal 556 zcmV+{0@MA8P)L%(Hre^b&aHEonE(xCqcjVkM=k!t!!_ zYC5O<+{b=NO*}I*EwN3!1%ohS1o1M#C?f>uWl-h-e){N^6(#a}zVj!^O0FWaMjoAI zlB6){5;Z5);}R31$7Q6++x`FBS0mBi=Txr=LdBzHy|zQ;CKNU->*K((3Wrek0Oy%v zes&)Q-bCLhYQ8n!+r=$Vtj3glk1^)H`0000501vCmP2ndL{yrdq5DkeKY1oo)gKX&HK%$+;;{_gMI z@4I*Y9vTw7oajgd0ARW3FTybVX^USLmge}qGsnRd0L*NlfPm1%fZzZ)2}V9rNu*#f zs00y68W!vi0KNqU5ixS72z&mwH5F^#`z1?PQc`H6_x?v^Qdmd!tr0y~q3H1}N~#p> zX&$?M5L)duIC~6o=i3YH>c8(V>NiB!yS}@vbUcrGq%!n~S(e*O`;*|q*Bq=yYrHCI zi>ebZwD$ZqTs)a^uNR%@UACv%GkRaIi>asMY3kkU0~J??wOTG#pWePay`8$|%e@|Z zSDXBCu;JpZ@kvr+Z|CFl(}sl6)1kp8xwmt=%6wbM10bMZdF5=(PBX#NCacc487uAk zNyVgr6_=}ksJFkfeQ>tzz*nr?*g#LWQ~7#rcdo@f%Hz#u7U+ZSFL~bI|KLt;4Q%!) z{U(HR{X*8$%yWUa6Sdu0>vvZ$((U^Q)sOdkAp7p@veQHi^@gT602oAi=ILOPyTLYiXPi&Q>vFi;2k@Va3+Nw(kJ_ z10K}!9n+L|%EgdJZfil5+~?}Axii(nZ^fIYPf!yeZf85^c>EUv&xyRjU*Cv4!PiEe z{BFPhvsmhR`$iYP&$kp?V@-_Y*+);dM;$z1IZ()PSmv1TLXWJP@^8wFs&YMM7kduN z+&$3KBw@M#*Jr1Wtz>L{Q@Dp@3#CPGJ$i!ZI3hnh za$J6R=0^WmUxWK^dfz{dOPJ~AqMSks|PHKBxQ*m-q%BPoJLeS+-k{>rv1 zA86QiU2R=$i7k6Om0P&!%BZ~Bph!*aP4qtYUV`H`D%NyMcMxeaG{oNPERVg%-fXS6 z$RK2nc1ND~9$ou$Br*L*I9eP0&@V*BKTeHEl2jZ$vdKI0iK%ldkY1y%wM;ONo*kNQ zURRjdJYDF#I&-jhh=1o7RD)8o%ys8ol4^UAucU8Z7tt^U4trX zRbSpu8^)$|$o;deHUs(4!OE&{{{6+}=Apqj;u)65Th@0k9daA}airF%qr|3azx*yx z{t3&s0T$_>IdrEU2T_f=@2u zM%Lx}sP&F*rOu;|qSZ>5UZ5-O!WP5RXFJj#5_Ek)^&tbn;G$l3MS8Op9}xNAMp3+2 z40zzOB|tD)0T6J>1U~^2CSZ!oJp5wqf&jBQJpd^2Cm@i)aUdyF4r4GXhgBq&H;qAZ zV=-7>3|}Ujrl7G{90rN$EsK}3nC>K!n^+=+ltyEB05Cmdx`1YTh_E1A7?ESx@Td61 zqC?>uv5n%ta;30>CNV~%sTC@t0l-spaZv$c5|Ubxq(r%DKk}Rf7nhA{I+-+Qf+hNq zH;O|^0WboRd}s_BgUlzANF*L2i|2+3gXZ1wH$So*!&F>4JtZZDmf}T&kpw!E!{N{w zEINxt#Vx34suGi^sY;ac9OMO#5JaU2q{1LrNiyO};^1V=k4!cOS~%u;DpX?eLMSCV zpBJ7XT`f`3nKTAnp`gFY6UD;7MUt=dM8i{6AUzC3;bcS#;^vfB0pW%F-vnOeghBDI z32yu{$9thBc*;CHh*-=O!qQ{~sKi7k$aw8WLmI@R>aNOS*Y`9^KedOIqxC|ITUg z2Idp-NH2x~pLi|e*bRI>M2m!i@V8gx>=vxJM6x9084PH0_rF5MbS?FO2pDzR5MjZ* zwk&l7d}deg9ccNd!iI}=F85dO%9S@&l&}v#b~bM?zl!cVm9q)ib$xhxQ?%EK84a&h c-wD*%=rG#yo8xYw#yKPk3=y98-}>2q0D4cRIRF3v diff --git a/applications/system/hid_app/assets/Ctrl_17x10.png b/applications/system/hid_app/assets/Ctrl_17x10.png new file mode 100644 index 0000000000000000000000000000000000000000..0eda72160009907c74a5f44e2dd90cd923f1fdfb GIT binary patch literal 552 zcmV+@0@wYCP)L%(Hre^b&aHEonE(xCqcjVkM=k!t!!_ zYC5O<+{b=NO*}I*EwN3!1%ohS1o1M#C?f>uWl-h-e){N^6(#a}zVj!^O0FWaMjoAI zlB6){5;Z5);}R31$7Q6++x`FBS0mBi=Txr=LdBzHy|zQ;CKNU->*K((3Wrek0Oy%v zes&)Q-bCLhYQ8xs5R007ntcL;*T1E?xm8=0AOSR^Z7w>e1AR?kD%|VBvQy9 zQbH&!4fgi|09IaJXq0?Ys5S5J)n!{Z|CG^LRFrQq@BU?Zd~iqRtAM6VKY)s_wgif`Ns|I)}IS72nUHoGK4GWt8DK(?08e@?SP)W7V!@ zH3e01S6h33A1RznyVs|l=v#NF$|dqhpS_`r?FGu+p9jlsjOcV6tS+^EeQG;p>*t4^ z4{tH}^HBY@TlXiy#=fq{m!}tE$1Vi<8)VMmimkOm=Oe#7-kQ3s5?pEQ|uMbDV& zw#65MgReGJ0O4aI^2t8U!=li*R2+yYS?Uw z3`Et`W}(C>o=zJ2LG7kd?A&?Jjvuz3?K~E1)t{kBsN=RYP6*~I7~(==LRxU4`H1CP zz`&q0rF8!^`K?kB?3~@27cTd>@muyx)yV5HhN%;jSeVn*&OQ_KnYYVC&d_gv3Omcw zg`fN8sLxX|<+637z1PQk3eB-5dcxeJ*~IYU$4m$F**5EJbM0wi6;nP<>ERU)-&%<) zvGjw3y-lK?f))eqHLdO4paQ#s`+wuc81Jul`h6a_9I|h|X>7EE?o+fte$alteo`Jvt~F0fmKP_qo5iTeoeUvO zn4dATiBvF5-}v@vFyEp1Jz1`-gR|4Z{l*cJTK2HIqchon9IW-ssm&Qp@@|}um3=i( z-gbSb*3+uHt-CF*<#3mB4<}U_o>Su&rmeZD{uSFCYrBtvHQnkwjLxr-)PBc-GtN zRT&%mbN>16_*5tQ>!ntU!Cd%wdBxZN{;Z*Scqp29k?H)p`Q0B*IF4Nzt=ZI3WKnTc zeitbHfXS*yg!4~rdXmpTYOOJA&SBg1?n<`hKnbD!iM-Y0Kc1EIfI-KlLG@vmdBBu|Qa`*Q~oPz1fNnh+HT_ z7$Xt^&bVv}5DZ=g2)JZ`pMU`aFw~C_{9=9r14fH_08rvjz*~kyLtu~`!4S0^QGrZ% zDjjrW(wVMw7Q>UOpfZ_kI>>OB#YmY9ClGWLNu;n+Z|npBh9?Y{(acW}mSqz?vc>mq zuiz7l28Tpo5u$fEQba+O=%Z0J3YFdf;A%LysDLmDs8Pf#)f|l%Y0-j%%X&491TLCj zabBbdQ4q*SPzdy((y4S3j|hSw7nQ|uf(3p{?)aM*NseJE4vm(SltfK(r6Oo7jlpKK zX>=xy$)w;G6m_x^lV~VPHTfCHa~uJrmZGoAzP zIOZk6^~q?|AvW>%nwBri_55z zplV28QZLf;ny$`KpXTDCaNxCkJiX~`7Tufmaz1NZSNN$a$P}QuI@^z zi|z1im+H&Q;iHfQL(mWeiRb+vExs7(zra?p(C-;eAPVDKL!$2*9)n9;qg%4)()2C0 zs>Po5@1hoOU>*^V^n4ibh&vkWJn{LkODOOTd1GVN!44t-niAY+7sQnTK`9ZNcc54D zdVd^D3=VgLXD diff --git a/applications/system/hid_app/assets/Del_17x10.png b/applications/system/hid_app/assets/Del_17x10.png new file mode 100644 index 0000000000000000000000000000000000000000..13d736983079f920184d157d83054efe809a6981 GIT binary patch literal 551 zcmV+?0@(eDP)L%(Hre^b&aHEonE(xCqcjVkM=k!t!!_ zYC5O<+{b=NO*}I*EwN3!1%ohS1o1M#C?f>uWl-h-e){N^6(#a}zVj!^O0FWaMjoAI zlB6){5;Z5);}R31$7Q6++x`FBS0mBi=Txr=LdBzHy|zQ;CKNU->*K((3Wrek0Oy%v zes&)Q-bCLhYQ8Askdnas~hZ002ovPDHLkV1h}~`Pcve literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/assets/Esc_14x7.png b/applications/system/hid_app/assets/Esc_14x7.png deleted file mode 100644 index 3be4691d8a3d050bf14f5bd29099705d984eead7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2430 zcmbtVd0Z2B79Wn~hzL|c3X3HsMGnb{97#jikN`{DAf;Rjix`rD43JEk86Z#&yI@6u zYN2ZFVxuB>z$$A%py*mKBFC=4mI@092#B~KNL_&nLMMp8e$?$BJM)>DH*enizVCa# z-+OZ)z~6Tx&I$(rz(&8{^8&G_F?Q)2=wWwv-!w}A&^3m*+<v=Simijq*nh57?RfWgR%dBxf$^ftSCt8Y9oao&i=ax<=Tw+b$vNCS zarXqY-FbN74CKHzK^?M0L z_@PZVs({e9zcqP$x$XFu^qeSfC;JO|i|XDS{RhOEa9w@maqkx_mvet{AhdcnyOw?9 zPyFFp)_mp_@4NBp-mG1RRMd2{0iBweV~+Bp_x77AgGc(%oJ(X#b<=bStc47xi9lrC z)qQf@G+QkU{`9K#7<&GKTgUf1&UPM;H66%OB-XQ98Yg)RRkWxgTw+FGkFmiSHuA9BoT%?}m3-ufwLu4Z&gjCT4YDHdXOwKGa$4sx6(bBBL=E95L&9eV!T zV_wgrNY!SIwjQ7FE;2%!sEG?tpCyH!IBqahz_8e0m1j!{shaj`$_%ZtJ7XGk11Nv9*4-B0a->VTo0%>R&8_nZQP$l-9-{BYTE&sjrk>6d^0uXhs0UpYQ4ie0jTZGY zbTY?#L#kcIcm6#Dm;N0bq4s^^;V)vBkb)Bgs^Zgsa0z*)ZPN;**DC4^V)Y^xMrNBm z3*wt+3v9M$4)>3+@7+d}a^)ujdz&H$$?_<9)F(EG(l@R%Wol>qe;204o^Aj_)CYgiGH4zgng2_)a*x3OOrA_@aVbpStBx?!1_&eT2$IjwwY&xEX#5r z_irCfOn1s3UT!rW%7ac+R(A?tg#Me*C+!I_r*7Z}GWBZ%PS>DD~VI?B1IVzrrvXEZ1qD7q`0ZjUB!=1(dT(6*GYfJ9YGcw)>A+ zH|j%oS+$kfj6aP~NNxLp?zC&Wm*$_Rra#f?8~|!gF6k`UDmL5{-f6`KL>?IC7sKZR zj+kr!=xA*MbTCN^I{_^kppD5a?4s|70NrIh07$VXz!AaGAU;40qc9?dWq7&^nTofk zQ|ZoBcbXenLZ;IhR6NZ^6eFb59PoI1zCZ{`HO3AApnXz%70vjh&Z?}hLGJnTOanHt zC{R!s8pi*SDTF0tfhHPRA(3ee0G5J@i4qVM;1!YtDZ*5E5SA^Nn54|aG~R;{ z#t*=AVL6C*B~!^%0vm_N<5_Z13^S1Dv*M1uc@V@XDq~V8$;rv&WM?ugkEPHU3M)^8&i-)kcjEVp)%a595-h-h7XPA?ako2x^rFW z9Cs?shfDW%Wl>&nT`S`Xte+G?1yUin0t>Cdy8asMI@ecVdD!1D2^9IrVaf6`$^>!* z)RfeN@Uo`sbJV1{yeLfQjeK^#na?Y(Ykby_MQ<#i%da7?b6rDTt?pW?%kA)Dmukw( zyW|kIH3XWjVbfTYS9B}(EQ+S3*0tD+{$18$ z4a~-2kzNi1Htt}oi#0YM!u)ugptmja4}FFMwi$5ECzmq+75*R5*5m-we>@y*$vKxY zAG8&G0zb~F*_t%HiK%t_!@*SA{?KYw%I1l<1B0H$7Y?Yhjp~}=Ae$L}&Y^QP^R6{p bCa#;5Jap7X#>00@?kEP)L%(Hre^b&aHEonE(xCqcjVkM=k!t!!_ zYC5O<+{b=NO*}I*EwN3!1%ohS1o1M#C?f>uWl-h-e){N^6(#a}zVj!^O0FWaMjoAI zlB6){5;Z5);}R31$7Q6++x`FBS0mBi=Txr=LdBzHy|zQ;CKNU->*K((3Wrek0Oy%v zes&)Q-bCLhYQ8X71d%_jiBy ze&4Z;t|yP45ZTAFV?=k--}LTG#D?a?`?O5Wp9kWkG% z+&q5g7`(}IaP~Xcm1D=Xt^dBipnpE9-ud0VW%qN)C#yqFnr67nv^@zr@h^MJu{zHx zT|sU9m6o30hl?lEMtaeS-c^TdJ)(+wos2yk&XMo^JWzFgSg&Vc^_sTTnl`fgp+dL9 zO-AnwHvD+|{v_Dg+xht7^nBddxsV{E>^oUq<-RwF0}wE`_S*UA{ifWfO_rT8GnV@I z6Nd2qh<=wBJxq5uz zeG^Rj`AWvq^a}xZ;`QAb+rF%#Y3%w;Y9AkQQx)C)+*TVt)PrSLP~fU-w)0ROY&k^& zqIH+ERD>yxUK0N4kBOYY-`E4qNC ze>u?8Bu@imz_H-|pE>%qU*|P{P@kDRzLvJ;CH2 zYaeQTY`a>!TH|jNb}DwWG>XU^U0{S(cMJU$doRvmFBxmP-EmCSI^Q4~>8uou`0TQp zGfy)~AM1{|=ry+apAiJjB_v88^w2L@$|)g-Cy1*~o&4A<;)$_i3!te}>n!5TqGpGt zo44f0H&5p~Zb~2Q9pc=*iE35aPx|&ZMfX!wB9-V9M^qcMw)vxDUtOs)8W7Rd^{QKb5=ApqDLIuNZoz=acj=PLq8r5xRFSV{YBD)7v ze#-D|Km>Eo?7NdoAXIDSJL~-Z^sWm`oBnc>wx_Zd@BMLuS0;gSPO18FsM=W{(`W4a zOUBju$ZZa-WsYNyqSOkfUZ5-W%I^86&r&oGO*;C3+T-&k^G@nj*Ce-E@Bxtv?G(fc zg@79_TL30TYXB2mGQv;5hz=OzG8?}bpTmIZf*t@A_!Hntkr)UJks%m@$`B>U@S@N_ z7Y2>tN%N)qP~;Q_gGmGFUeZ_zgYF7~E<&*cRv3(30l@gU@e-QNag!z4+<+YIgB-vo z78MTLiR~1A#F8L#ir5g1qLwQS1^`>l!bLfRi9xkIL4mT=e#8X}7A_mqR3f-wg2nq0 zcM3y59-@LkZwieRfCgM~43dcX5s8LCOUEKlxl$-x3Z+07 z^TIQvs>MnwokF9^<oW3Eb(d3JXou&!)KFd) zPX&oFL=}c037r3<#TO#|7uX6GhCRdDrh@UUAvSakht8(Hq+7ISQw=S(qQ#!~?}8R@ zU=9I~^kNur2&4j+L3}<41$=JU+v`gXZ6^Sr1<&i!eBlpoisu6aj&a4RFF$vo+%;~I;vL%(Hre^b&aHEonE(xCqcjVkM=k!t!!_ zYC5O<+{b=NO*}I*EwN3!1%ohS1o1M#C?f>uWl-h-e){N^6(#a}zVj!^O0FWaMjoAI zlB6){5;Z5);}R31$7Q6++x`FBS0mBi=Txr=LdBzHy|zQ;CKNU->*K((3Wrek0Oy%v zes&)Q-bCLhYQ805AxW#{Ykr9-0u-+Re!Ys32*Va5DoCOQQ;aSac0_jg3zVnM+Y|nGr}v+m<$F n&ROGyli#B?gm};CuYPx{fw(FRB*KjF00000NkvXXu0mjf2deXy literal 0 HcmV?d00001 diff --git a/applications/system/hid_app/views/hid_keyboard.c b/applications/system/hid_app/views/hid_keyboard.c index 17ff754f5..9060c1d6a 100644 --- a/applications/system/hid_app/views/hid_keyboard.c +++ b/applications/system/hid_app/views/hid_keyboard.c @@ -49,7 +49,7 @@ typedef struct { #define ROW_COUNT 7 #define COLUMN_COUNT 12 -// 0 width items are not drawn, but there value is used +// 0 width items are not drawn, but their value is used const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { { {.width = 1, .icon = &I_ButtonF1_5x8, .value = HID_KEYBOARD_F1}, @@ -140,17 +140,17 @@ const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { {.width = 1, .icon = &I_ButtonRight_4x7, .value = HID_KEYBOARD_RIGHT_ARROW}, }, { - {.width = 2, .icon = &I_Ctrl_15x7, .value = HID_KEYBOARD_L_CTRL}, + {.width = 2, .icon = &I_Ctrl_17x10, .value = HID_KEYBOARD_L_CTRL}, {.width = 0, .value = HID_KEYBOARD_L_CTRL}, - {.width = 2, .icon = &I_Alt_11x7, .value = HID_KEYBOARD_L_ALT}, + {.width = 2, .icon = &I_Alt_17x10, .value = HID_KEYBOARD_L_ALT}, {.width = 0, .value = HID_KEYBOARD_L_ALT}, - {.width = 2, .icon = &I_Cmd_15x7, .value = HID_KEYBOARD_L_GUI}, + {.width = 2, .icon = &I_Cmd_17x10, .value = HID_KEYBOARD_L_GUI}, {.width = 0, .value = HID_KEYBOARD_L_GUI}, - {.width = 2, .icon = &I_Tab_15x7, .value = HID_KEYBOARD_TAB}, + {.width = 2, .icon = &I_Tab_17x10, .value = HID_KEYBOARD_TAB}, {.width = 0, .value = HID_KEYBOARD_TAB}, - {.width = 2, .icon = &I_Esc_14x7, .value = HID_KEYBOARD_ESCAPE}, + {.width = 2, .icon = &I_Esc_17x10, .value = HID_KEYBOARD_ESCAPE}, {.width = 0, .value = HID_KEYBOARD_ESCAPE}, - {.width = 2, .icon = &I_Del_12x7, .value = HID_KEYBOARD_DELETE_FORWARD}, + {.width = 2, .icon = &I_Del_17x10, .value = HID_KEYBOARD_DELETE_FORWARD}, {.width = 0, .value = HID_KEYBOARD_DELETE_FORWARD}, }, }; From 04cead1fc56d4093bffee8d3a4a7b47fba93061f Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Sat, 2 Dec 2023 09:03:10 +0400 Subject: [PATCH 3/5] [FL-3620] Add the "remove pairing" button to BLE hid (#3237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/system/hid_app/hid.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/applications/system/hid_app/hid.c b/applications/system/hid_app/hid.c index a42fc6091..88a68f09d 100644 --- a/applications/system/hid_app/hid.c +++ b/applications/system/hid_app/hid.c @@ -14,8 +14,22 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexMouse, HidSubmenuIndexMouseClicker, HidSubmenuIndexMouseJiggler, + HidSubmenuIndexRemovePairing, }; +static void bt_hid_remove_pairing(Bt* bt) { + bt_disconnect(bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + furi_hal_bt_stop_advertising(); + + bt_forget_bonded_devices(bt); + + furi_hal_bt_start_advertising(); +} + static void hid_submenu_callback(void* context, uint32_t index) { furi_assert(context); Hid* app = context; @@ -45,6 +59,8 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexMouseJiggler) { app->view_id = HidViewMouseJiggler; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); + } else if(index == HidSubmenuIndexRemovePairing) { + bt_hid_remove_pairing(app->bt); } } @@ -143,6 +159,14 @@ Hid* hid_alloc(HidTransport transport) { HidSubmenuIndexMouseJiggler, hid_submenu_callback, app); + if(transport == HidTransportBle) { + submenu_add_item( + app->device_type_submenu, + "Remove Pairing", + HidSubmenuIndexRemovePairing, + hid_submenu_callback, + app); + } view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); view_dispatcher_add_view( app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); From c6a14e1a6779ded87b848b06b2cbc51a147f96b3 Mon Sep 17 00:00:00 2001 From: pborsutzki Date: Sat, 2 Dec 2023 08:27:58 +0100 Subject: [PATCH 4/5] Fixed a zero allocation error when reading an iso15693 nfc tag with no additional blocks. (#3229) Co-authored-by: gornekich --- .../iso15693_3/iso15693_3_poller_i.c | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c index 917f7dbb8..ca6f5435e 100644 --- a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c @@ -108,28 +108,30 @@ Iso15693_3Error iso15693_3_poller_activate(Iso15693_3Poller* instance, Iso15693_ break; } - // Read blocks: Optional command - simple_array_init(data->block_data, system_info->block_count * system_info->block_size); - ret = iso15693_3_poller_read_blocks( - instance, - simple_array_get_data(data->block_data), - system_info->block_count, - system_info->block_size); - if(ret != Iso15693_3ErrorNone) { - ret = iso15693_3_poller_filter_error(ret); - break; + if(system_info->block_count > 0) { + // Read blocks: Optional command + simple_array_init( + data->block_data, system_info->block_count * system_info->block_size); + ret = iso15693_3_poller_read_blocks( + instance, + simple_array_get_data(data->block_data), + system_info->block_count, + system_info->block_size); + if(ret != Iso15693_3ErrorNone) { + ret = iso15693_3_poller_filter_error(ret); + break; + } + + // Get block security status: Optional command + simple_array_init(data->block_security, system_info->block_count); + + ret = iso15693_3_poller_get_blocks_security( + instance, simple_array_get_data(data->block_security), system_info->block_count); + if(ret != Iso15693_3ErrorNone) { + ret = iso15693_3_poller_filter_error(ret); + break; + } } - - // Get block security status: Optional command - simple_array_init(data->block_security, system_info->block_count); - - ret = iso15693_3_poller_get_blocks_security( - instance, simple_array_get_data(data->block_security), system_info->block_count); - if(ret != Iso15693_3ErrorNone) { - ret = iso15693_3_poller_filter_error(ret); - break; - } - } while(false); return ret; From eb6fe0a4dbe2e6d3776483e6a191de2307b0060e Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Sat, 2 Dec 2023 11:34:02 +0400 Subject: [PATCH 5/5] SubGhz: fix count bit for detect gate_tx protocol (#3253) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- lib/subghz/protocols/gate_tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/subghz/protocols/gate_tx.c b/lib/subghz/protocols/gate_tx.c index 51a424fed..2ebd6bb03 100644 --- a/lib/subghz/protocols/gate_tx.c +++ b/lib/subghz/protocols/gate_tx.c @@ -227,7 +227,7 @@ void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t du if(duration >= ((uint32_t)subghz_protocol_gate_tx_const.te_short * 10 + subghz_protocol_gate_tx_const.te_delta)) { instance->decoder.parser_step = GateTXDecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit >= + if(instance->decoder.decode_count_bit == subghz_protocol_gate_tx_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit;