[FL-3675] Ntag21x write (#3246)
* 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 <n.gorbadey@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									b51a754fd9
								
							
						
					
					
						commit
						6a5d63803a
					
				| @ -12,6 +12,7 @@ enum { | |||||||
|     SubmenuIndexUnlock = SubmenuIndexCommonMax, |     SubmenuIndexUnlock = SubmenuIndexCommonMax, | ||||||
|     SubmenuIndexUnlockByReader, |     SubmenuIndexUnlockByReader, | ||||||
|     SubmenuIndexUnlockByPassword, |     SubmenuIndexUnlockByPassword, | ||||||
|  |     SubmenuIndexWrite, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) { | 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, |             SubmenuIndexUnlock, | ||||||
|             nfc_protocol_support_common_submenu_callback, |             nfc_protocol_support_common_submenu_callback, | ||||||
|             instance); |             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) { |     if(event == SubmenuIndexUnlock) { | ||||||
|         scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); |         scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); | ||||||
|         return true; |         return true; | ||||||
|  |     } else if(event == SubmenuIndexWrite) { | ||||||
|  |         scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite); | ||||||
|  |         return true; | ||||||
|     } |     } | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  | |||||||
| @ -24,6 +24,10 @@ ADD_SCENE(nfc, field, Field) | |||||||
| ADD_SCENE(nfc, retry_confirm, RetryConfirm) | ADD_SCENE(nfc, retry_confirm, RetryConfirm) | ||||||
| ADD_SCENE(nfc, exit_confirm, ExitConfirm) | 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_menu, MfUltralightUnlockMenu) | ||||||
| ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) | ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) | ||||||
| ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) | ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) | ||||||
|  | |||||||
							
								
								
									
										119
									
								
								applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,119 @@ | |||||||
|  | #include "../nfc_app_i.h" | ||||||
|  | 
 | ||||||
|  | #include <nfc/protocols/mf_ultralight/mf_ultralight_poller.h> | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -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); | ||||||
|  | } | ||||||
| @ -224,11 +224,24 @@ static NfcCommand mf_ultralight_poller_handler_idle(MfUltralightPoller* instance | |||||||
|     instance->tearing_flag_read = 0; |     instance->tearing_flag_read = 0; | ||||||
|     instance->tearing_flag_total = 3; |     instance->tearing_flag_total = 3; | ||||||
|     instance->pages_read = 0; |     instance->pages_read = 0; | ||||||
|     instance->state = MfUltralightPollerStateReadVersion; |     instance->state = MfUltralightPollerStateRequestMode; | ||||||
| 
 |     instance->current_page = 0; | ||||||
|     return NfcCommandContinue; |     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) { | static NfcCommand mf_ultralight_poller_handler_read_version(MfUltralightPoller* instance) { | ||||||
|     instance->error = mf_ultralight_poller_read_version(instance, &instance->data->version); |     instance->error = mf_ultralight_poller_read_version(instance, &instance->data->version); | ||||||
|     if(instance->error == MfUltralightErrorNone) { |     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) { | static NfcCommand mf_ultralight_poller_handler_check_ntag_203(MfUltralightPoller* instance) { | ||||||
|  |     MfUltralightPollerState next_state = MfUltralightPollerStateGetFeatureSet; | ||||||
|     MfUltralightPageReadCommandData data = {}; |     MfUltralightPageReadCommandData data = {}; | ||||||
|     instance->error = mf_ultralight_poller_read_page(instance, 41, &data); |     instance->error = mf_ultralight_poller_read_page(instance, 41, &data); | ||||||
|     if(instance->error == MfUltralightErrorNone) { |     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"); |         FURI_LOG_D(TAG, "Original Ultralight detected"); | ||||||
|         iso14443_3a_poller_halt(instance->iso14443_3a_poller); |         iso14443_3a_poller_halt(instance->iso14443_3a_poller); | ||||||
|         instance->data->type = MfUltralightTypeUnknown; |         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; |     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) { | static NfcCommand mf_ultralight_poller_handler_read_fail(MfUltralightPoller* instance) { | ||||||
|     FURI_LOG_D(TAG, "Read Failed"); |     FURI_LOG_D(TAG, "Read Failed"); | ||||||
|     iso14443_3a_poller_halt(instance->iso14443_3a_poller); |     iso14443_3a_poller_halt(instance->iso14443_3a_poller); | ||||||
|  |     instance->mfu_event.type = MfUltralightPollerEventTypeReadFailed; | ||||||
|     instance->mfu_event.data->error = instance->error; |     instance->mfu_event.data->error = instance->error; | ||||||
|     NfcCommand command = instance->callback(instance->general_event, instance->context); |     NfcCommand command = instance->callback(instance->general_event, instance->context); | ||||||
|     instance->state = MfUltralightPollerStateIdle; |     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) { | static NfcCommand mf_ultralight_poller_handler_read_success(MfUltralightPoller* instance) { | ||||||
|     FURI_LOG_D(TAG, "Read success"); |     FURI_LOG_D(TAG, "Read success"); | ||||||
|     iso14443_3a_poller_halt(instance->iso14443_3a_poller); |  | ||||||
|     instance->mfu_event.type = MfUltralightPollerEventTypeReadSuccess; |     instance->mfu_event.type = MfUltralightPollerEventTypeReadSuccess; | ||||||
|     NfcCommand command = instance->callback(instance->general_event, instance->context); |     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; |     return command; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const MfUltralightPollerReadHandler | static const MfUltralightPollerReadHandler | ||||||
|     mf_ultralight_poller_read_handler[MfUltralightPollerStateNum] = { |     mf_ultralight_poller_read_handler[MfUltralightPollerStateNum] = { | ||||||
|         [MfUltralightPollerStateIdle] = mf_ultralight_poller_handler_idle, |         [MfUltralightPollerStateIdle] = mf_ultralight_poller_handler_idle, | ||||||
|  |         [MfUltralightPollerStateRequestMode] = mf_ultralight_poller_handler_request_mode, | ||||||
|         [MfUltralightPollerStateReadVersion] = mf_ultralight_poller_handler_read_version, |         [MfUltralightPollerStateReadVersion] = mf_ultralight_poller_handler_read_version, | ||||||
|         [MfUltralightPollerStateDetectMfulC] = mf_ultralight_poller_handler_check_ultralight_c, |         [MfUltralightPollerStateDetectMfulC] = mf_ultralight_poller_handler_check_ultralight_c, | ||||||
|         [MfUltralightPollerStateDetectNtag203] = mf_ultralight_poller_handler_check_ntag_203, |         [MfUltralightPollerStateDetectNtag203] = mf_ultralight_poller_handler_check_ntag_203, | ||||||
| @ -538,6 +664,11 @@ static const MfUltralightPollerReadHandler | |||||||
|         [MfUltralightPollerStateReadPages] = mf_ultralight_poller_handler_read_pages, |         [MfUltralightPollerStateReadPages] = mf_ultralight_poller_handler_read_pages, | ||||||
|         [MfUltralightPollerStateReadFailed] = mf_ultralight_poller_handler_read_fail, |         [MfUltralightPollerStateReadFailed] = mf_ultralight_poller_handler_read_fail, | ||||||
|         [MfUltralightPollerStateReadSuccess] = mf_ultralight_poller_handler_read_success, |         [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, | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -16,13 +16,27 @@ typedef struct MfUltralightPoller MfUltralightPoller; | |||||||
|  * @brief Enumeration of possible MfUltralight poller event types. |  * @brief Enumeration of possible MfUltralight poller event types. | ||||||
|  */ |  */ | ||||||
| typedef enum { | typedef enum { | ||||||
|  |     MfUltralightPollerEventTypeRequestMode, /**< Poller requests for operating mode. */ | ||||||
|     MfUltralightPollerEventTypeAuthRequest, /**< Poller requests to fill authentication context. */ |     MfUltralightPollerEventTypeAuthRequest, /**< Poller requests to fill authentication context. */ | ||||||
|     MfUltralightPollerEventTypeAuthSuccess, /**< Authentication succeeded. */ |     MfUltralightPollerEventTypeAuthSuccess, /**< Authentication succeeded. */ | ||||||
|     MfUltralightPollerEventTypeAuthFailed, /**< Authentication failed. */ |     MfUltralightPollerEventTypeAuthFailed, /**< Authentication failed. */ | ||||||
|     MfUltralightPollerEventTypeReadSuccess, /**< Poller read card successfully. */ |     MfUltralightPollerEventTypeReadSuccess, /**< Poller read card successfully. */ | ||||||
|     MfUltralightPollerEventTypeReadFailed, /**< Poller failed to read card. */ |     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; | } 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. |  * @brief MfUltralight poller authentication context. | ||||||
|  */ |  */ | ||||||
| @ -39,6 +53,8 @@ typedef struct { | |||||||
| typedef union { | typedef union { | ||||||
|     MfUltralightPollerAuthContext auth_context; /**< Authentication context. */ |     MfUltralightPollerAuthContext auth_context; /**< Authentication context. */ | ||||||
|     MfUltralightError error; /**< Error code indicating reading fail reason. */ |     MfUltralightError error; /**< Error code indicating reading fail reason. */ | ||||||
|  |     const MfUltralightData* write_data; | ||||||
|  |     MfUltralightPollerMode poller_mode; | ||||||
| } MfUltralightPollerEventData; | } MfUltralightPollerEventData; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | |||||||
| @ -49,6 +49,7 @@ typedef union { | |||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     MfUltralightPollerStateIdle, |     MfUltralightPollerStateIdle, | ||||||
|  |     MfUltralightPollerStateRequestMode, | ||||||
|     MfUltralightPollerStateReadVersion, |     MfUltralightPollerStateReadVersion, | ||||||
|     MfUltralightPollerStateDetectMfulC, |     MfUltralightPollerStateDetectMfulC, | ||||||
|     MfUltralightPollerStateDetectNtag203, |     MfUltralightPollerStateDetectNtag203, | ||||||
| @ -61,6 +62,10 @@ typedef enum { | |||||||
|     MfUltralightPollerStateTryDefaultPass, |     MfUltralightPollerStateTryDefaultPass, | ||||||
|     MfUltralightPollerStateReadFailed, |     MfUltralightPollerStateReadFailed, | ||||||
|     MfUltralightPollerStateReadSuccess, |     MfUltralightPollerStateReadSuccess, | ||||||
|  |     MfUltralightPollerStateRequestWriteData, | ||||||
|  |     MfUltralightPollerStateWritePages, | ||||||
|  |     MfUltralightPollerStateWriteFail, | ||||||
|  |     MfUltralightPollerStateWriteSuccess, | ||||||
| 
 | 
 | ||||||
|     MfUltralightPollerStateNum, |     MfUltralightPollerStateNum, | ||||||
| } MfUltralightPollerState; | } MfUltralightPollerState; | ||||||
| @ -68,6 +73,7 @@ typedef enum { | |||||||
| struct MfUltralightPoller { | struct MfUltralightPoller { | ||||||
|     Iso14443_3aPoller* iso14443_3a_poller; |     Iso14443_3aPoller* iso14443_3a_poller; | ||||||
|     MfUltralightPollerState state; |     MfUltralightPollerState state; | ||||||
|  |     MfUltralightPollerMode mode; | ||||||
|     BitBuffer* tx_buffer; |     BitBuffer* tx_buffer; | ||||||
|     BitBuffer* rx_buffer; |     BitBuffer* rx_buffer; | ||||||
|     MfUltralightData* data; |     MfUltralightData* data; | ||||||
| @ -79,6 +85,7 @@ struct MfUltralightPoller { | |||||||
|     uint8_t counters_total; |     uint8_t counters_total; | ||||||
|     uint8_t tearing_flag_read; |     uint8_t tearing_flag_read; | ||||||
|     uint8_t tearing_flag_total; |     uint8_t tearing_flag_total; | ||||||
|  |     uint16_t current_page; | ||||||
|     MfUltralightError error; |     MfUltralightError error; | ||||||
| 
 | 
 | ||||||
|     NfcGenericEvent general_event; |     NfcGenericEvent general_event; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 RebornedBrain
						RebornedBrain