[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:
RebornedBrain 2023-12-02 07:45:47 +03:00 committed by GitHub
parent b51a754fd9
commit 6a5d63803a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 462 additions and 4 deletions

View file

@ -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;
}

View file

@ -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)

View 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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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,
};

View file

@ -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;
/**

View file

@ -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;