#include "mf_ultralight_poller_i.h" #include #include #define TAG "MfUltralightPoller" typedef NfcCommand (*MfUltralightPollerReadHandler)(MfUltralightPoller* instance); static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_1k( uint16_t lin_addr, uint8_t* sector, uint8_t* tag, uint8_t* pages_left) { bool tag_calculated = false; // 0 - 226: sector 0 // 227 - 228: config registers // 229 - 230: session registers if(lin_addr > 230) { *pages_left = 0; } else if(lin_addr >= 229) { *sector = 3; *pages_left = 2 - (lin_addr - 229); *tag = lin_addr - 229 + 248; tag_calculated = true; } else if(lin_addr >= 227) { *sector = 0; *pages_left = 2 - (lin_addr - 227); *tag = lin_addr - 227 + 232; tag_calculated = true; } else { *sector = 0; *pages_left = 227 - lin_addr; *tag = lin_addr; tag_calculated = true; } return tag_calculated; } static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_2k( uint16_t lin_addr, uint8_t* sector, uint8_t* tag, uint8_t* pages_left) { bool tag_calculated = false; // 0 - 255: sector 0 // 256 - 480: sector 1 // 481 - 482: config registers // 483 - 484: session registers if(lin_addr > 484) { *pages_left = 0; } else if(lin_addr >= 483) { *sector = 3; *pages_left = 2 - (lin_addr - 483); *tag = lin_addr - 483 + 248; tag_calculated = true; } else if(lin_addr >= 481) { *sector = 1; *pages_left = 2 - (lin_addr - 481); *tag = lin_addr - 481 + 232; tag_calculated = true; } else if(lin_addr >= 256) { *sector = 1; *pages_left = 225 - (lin_addr - 256); *tag = lin_addr - 256; tag_calculated = true; } else { *sector = 0; *pages_left = 256 - lin_addr; *tag = lin_addr; tag_calculated = true; } return tag_calculated; } static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_1k( uint16_t lin_addr, uint8_t* sector, uint8_t* tag, uint8_t* pages_left) { bool tag_calculated = false; // 0 - 233: sector 0 + registers // 234 - 235: session registers if(lin_addr > 235) { *pages_left = 0; } else if(lin_addr >= 234) { *sector = 0; *pages_left = 2 - (lin_addr - 234); *tag = lin_addr - 234 + 236; tag_calculated = true; } else { *sector = 0; *pages_left = 234 - lin_addr; *tag = lin_addr; tag_calculated = true; } return tag_calculated; } static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_2k( uint16_t lin_addr, uint8_t* sector, uint8_t* tag, uint8_t* pages_left) { bool tag_calculated = false; // 0 - 233: sector 0 + registers // 234 - 235: session registers // 236 - 491: sector 1 if(lin_addr > 491) { *pages_left = 0; } else if(lin_addr >= 236) { *sector = 1; *pages_left = 256 - (lin_addr - 236); *tag = lin_addr - 236; tag_calculated = true; } else if(lin_addr >= 234) { *sector = 0; *pages_left = 2 - (lin_addr - 234); *tag = lin_addr - 234 + 236; tag_calculated = true; } else { *sector = 0; *pages_left = 234 - lin_addr; *tag = lin_addr; tag_calculated = true; } return tag_calculated; } bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag( MfUltralightPoller* instance, uint16_t lin_addr, uint8_t* sector, uint8_t* tag, uint8_t* pages_left) { furi_assert(instance); furi_assert(sector); furi_assert(tag); furi_assert(pages_left); bool tag_calculated = false; if(instance->data->type == MfUltralightTypeNTAGI2C1K) { tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_1k( lin_addr, sector, tag, pages_left); } else if(instance->data->type == MfUltralightTypeNTAGI2C2K) { tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_2k( lin_addr, sector, tag, pages_left); } else if(instance->data->type == MfUltralightTypeNTAGI2CPlus1K) { tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_1k( lin_addr, sector, tag, pages_left); } else if(instance->data->type == MfUltralightTypeNTAGI2CPlus2K) { tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_2k( lin_addr, sector, tag, pages_left); } return tag_calculated; } MfUltralightPoller* mf_ultralight_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) { furi_assert(iso14443_3a_poller); MfUltralightPoller* instance = malloc(sizeof(MfUltralightPoller)); instance->iso14443_3a_poller = iso14443_3a_poller; instance->tx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_MAX_BUFF_SIZE); instance->rx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_MAX_BUFF_SIZE); instance->data = mf_ultralight_alloc(); instance->mfu_event.data = &instance->mfu_event_data; instance->general_event.protocol = NfcProtocolMfUltralight; instance->general_event.event_data = &instance->mfu_event; instance->general_event.instance = instance; return instance; } void mf_ultralight_poller_free(MfUltralightPoller* instance) { furi_assert(instance); furi_assert(instance->data); furi_assert(instance->tx_buffer); furi_assert(instance->rx_buffer); bit_buffer_free(instance->tx_buffer); bit_buffer_free(instance->rx_buffer); mf_ultralight_free(instance->data); free(instance); } static void mf_ultralight_poller_set_callback( MfUltralightPoller* instance, NfcGenericCallback callback, void* context) { furi_assert(instance); furi_assert(callback); instance->callback = callback; instance->context = context; } const MfUltralightData* mf_ultralight_poller_get_data(MfUltralightPoller* instance) { furi_assert(instance); return instance->data; } static NfcCommand mf_ultralight_poller_handler_idle(MfUltralightPoller* instance) { bit_buffer_reset(instance->tx_buffer); bit_buffer_reset(instance->rx_buffer); iso14443_3a_copy( instance->data->iso14443_3a_data, iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); instance->counters_read = 0; instance->counters_total = 3; instance->tearing_flag_read = 0; instance->tearing_flag_total = 3; instance->pages_read = 0; 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) { FURI_LOG_D(TAG, "Read version success"); instance->data->type = mf_ultralight_get_type_by_version(&instance->data->version); instance->state = MfUltralightPollerStateGetFeatureSet; } else { FURI_LOG_D(TAG, "Didn't response. Check Ultralight C"); iso14443_3a_poller_halt(instance->iso14443_3a_poller); instance->state = MfUltralightPollerStateDetectMfulC; } return NfcCommandContinue; } static NfcCommand mf_ultralight_poller_handler_check_ultralight_c(MfUltralightPoller* instance) { instance->error = mf_ultralight_poller_authenticate(instance); if(instance->error == MfUltralightErrorNone) { FURI_LOG_D(TAG, "Ultralight C detected"); instance->data->type = MfUltralightTypeMfulC; instance->state = MfUltralightPollerStateGetFeatureSet; } else { FURI_LOG_D(TAG, "Didn't response. Check NTAG 203"); instance->state = MfUltralightPollerStateDetectNtag203; } iso14443_3a_poller_halt(instance->iso14443_3a_poller); return NfcCommandContinue; } 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) { FURI_LOG_D(TAG, "NTAG203 detected"); instance->data->type = MfUltralightTypeNTAG203; } else { 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 = next_state; return NfcCommandContinue; } static NfcCommand mf_ultralight_poller_handler_get_feature_set(MfUltralightPoller* instance) { instance->feature_set = mf_ultralight_get_feature_support_set(instance->data->type); instance->pages_total = mf_ultralight_get_pages_total(instance->data->type); instance->data->pages_total = instance->pages_total; FURI_LOG_D( TAG, "%s detected. Total pages: %d", mf_ultralight_get_device_name(instance->data, NfcDeviceNameTypeFull), instance->pages_total); instance->state = MfUltralightPollerStateReadSignature; return NfcCommandContinue; } static NfcCommand mf_ultralight_poller_handler_read_signature(MfUltralightPoller* instance) { MfUltralightPollerState next_state = MfUltralightPollerStateAuth; if(mf_ultralight_support_feature( instance->feature_set, MfUltralightFeatureSupportReadSignature)) { FURI_LOG_D(TAG, "Reading signature"); instance->error = mf_ultralight_poller_read_signature(instance, &instance->data->signature); if(instance->error != MfUltralightErrorNone) { FURI_LOG_D(TAG, "Read signature failed"); next_state = MfUltralightPollerStateReadFailed; } } else { FURI_LOG_D(TAG, "Skip reading signature"); } instance->state = next_state; return NfcCommandContinue; } static NfcCommand mf_ultralight_poller_handler_read_counters(MfUltralightPoller* instance) { do { if(!mf_ultralight_support_feature( instance->feature_set, MfUltralightFeatureSupportReadCounter) || !mf_ultralight_is_counter_configured(instance->data)) { FURI_LOG_D(TAG, "Skip reading counters"); instance->state = MfUltralightPollerStateReadTearingFlags; break; } MfUltralightConfigPages* config = NULL; mf_ultralight_get_config_page(instance->data, &config); if(config->access.nfc_cnt_pwd_prot && !instance->auth_context.auth_success) { FURI_LOG_D(TAG, "Counter reading is protected with password"); instance->state = MfUltralightPollerStateReadTearingFlags; break; } if(instance->counters_read == instance->counters_total) { instance->state = MfUltralightPollerStateReadTearingFlags; break; } if(mf_ultralight_support_feature( instance->feature_set, MfUltralightFeatureSupportSingleCounter)) { instance->counters_read = 2; } FURI_LOG_D(TAG, "Reading counter %d", instance->counters_read); instance->error = mf_ultralight_poller_read_counter( instance, instance->counters_read, &instance->data->counter[instance->counters_read]); if(instance->error != MfUltralightErrorNone) { FURI_LOG_D(TAG, "Failed to read %d counter", instance->counters_read); instance->state = MfUltralightPollerStateReadTearingFlags; } else { instance->counters_read++; } } while(false); return NfcCommandContinue; } static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPoller* instance) { NfcCommand command = NfcCommandContinue; if(mf_ultralight_support_feature( instance->feature_set, MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportSingleCounter)) { if(instance->tearing_flag_read == instance->tearing_flag_total) { instance->state = MfUltralightPollerStateTryDefaultPass; command = NfcCommandReset; } else { bool single_counter = mf_ultralight_support_feature( instance->feature_set, MfUltralightFeatureSupportSingleCounter); if(single_counter) instance->tearing_flag_read = 2; FURI_LOG_D(TAG, "Reading tearing flag %d", instance->tearing_flag_read); instance->error = mf_ultralight_poller_read_tearing_flag( instance, instance->tearing_flag_read, &instance->data->tearing_flag[instance->tearing_flag_read]); if((instance->error == MfUltralightErrorProtocol) && single_counter) { instance->tearing_flag_read++; } else if(instance->error != MfUltralightErrorNone) { FURI_LOG_D(TAG, "Reading tearing flag %d failed", instance->tearing_flag_read); instance->state = MfUltralightPollerStateTryDefaultPass; command = NfcCommandReset; } else { instance->tearing_flag_read++; } } } else { FURI_LOG_D(TAG, "Skip reading tearing flags"); instance->state = MfUltralightPollerStateTryDefaultPass; command = NfcCommandReset; } return command; } static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance) { NfcCommand command = NfcCommandContinue; if(mf_ultralight_support_feature( instance->feature_set, MfUltralightFeatureSupportPasswordAuth)) { instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest; command = instance->callback(instance->general_event, instance->context); if(!instance->mfu_event.data->auth_context.skip_auth) { instance->auth_context.password = instance->mfu_event.data->auth_context.password; uint32_t pass = nfc_util_bytes2num( instance->auth_context.password.data, sizeof(MfUltralightAuthPassword)); FURI_LOG_D(TAG, "Trying to authenticate with password %08lX", pass); instance->error = mf_ultralight_poller_auth_pwd(instance, &instance->auth_context); if(instance->error == MfUltralightErrorNone) { FURI_LOG_D(TAG, "Auth success"); instance->auth_context.auth_success = true; instance->mfu_event.data->auth_context = instance->auth_context; instance->mfu_event.type = MfUltralightPollerEventTypeAuthSuccess; command = instance->callback(instance->general_event, instance->context); } else { FURI_LOG_D(TAG, "Auth failed"); instance->auth_context.auth_success = false; instance->mfu_event.type = MfUltralightPollerEventTypeAuthFailed; command = instance->callback(instance->general_event, instance->context); iso14443_3a_poller_halt(instance->iso14443_3a_poller); } } } instance->state = MfUltralightPollerStateReadPages; return command; } static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* instance) { MfUltralightPageReadCommandData data = {}; uint16_t start_page = instance->pages_read; if(MF_ULTRALIGHT_IS_NTAG_I2C(instance->data->type)) { uint8_t tag = 0; uint8_t sector = 0; uint8_t pages_left = 0; if(mf_ultralight_poller_ntag_i2c_addr_lin_to_tag( instance, start_page, §or, &tag, &pages_left)) { instance->error = mf_ultralight_poller_read_page_from_sector(instance, sector, tag, &data); } else { FURI_LOG_D(TAG, "Failed to calculate sector and tag from %d page", start_page); instance->error = MfUltralightErrorProtocol; } } else { instance->error = mf_ultralight_poller_read_page(instance, start_page, &data); } if(instance->error == MfUltralightErrorNone) { for(size_t i = 0; i < 4; i++) { if(start_page + i < instance->pages_total) { FURI_LOG_D(TAG, "Read page %d success", start_page + i); instance->data->page[start_page + i] = data.page[i]; instance->pages_read++; instance->data->pages_read = instance->pages_read; } } if(instance->pages_read == instance->pages_total) { instance->state = MfUltralightPollerStateReadCounters; } } else { FURI_LOG_D(TAG, "Read page %d failed", instance->pages_read); if(instance->pages_read) { instance->state = MfUltralightPollerStateReadCounters; } else { instance->state = MfUltralightPollerStateReadFailed; } } return NfcCommandContinue; } static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoller* instance) { do { if(!mf_ultralight_support_feature( instance->feature_set, MfUltralightFeatureSupportPasswordAuth)) break; MfUltralightConfigPages* config = NULL; mf_ultralight_get_config_page(instance->data, &config); if(instance->auth_context.auth_success) { config->password = instance->auth_context.password; config->pack = instance->auth_context.pack; } else if(config->access.authlim == 0) { FURI_LOG_D(TAG, "No limits in authentication. Trying default password"); nfc_util_num2bytes( MF_ULTRALIGHT_DEFAULT_PASSWORD, sizeof(MfUltralightAuthPassword), instance->auth_context.password.data); instance->error = mf_ultralight_poller_auth_pwd(instance, &instance->auth_context); if(instance->error == MfUltralightErrorNone) { FURI_LOG_D(TAG, "Default password detected"); nfc_util_num2bytes( MF_ULTRALIGHT_DEFAULT_PASSWORD, sizeof(MfUltralightAuthPassword), config->password.data); config->pack = instance->auth_context.pack; instance->auth_context.auth_success = true; } } if(instance->pages_read != instance->pages_total) { // Probably password protected, fix AUTH0 and PROT so before AUTH0 // can be written and since AUTH0 won't be readable, like on the // original card config->auth0 = instance->pages_read; config->access.prot = true; } else if(!instance->auth_context.auth_success) { instance->pages_read -= 2; instance->data->pages_read -= 2; } } while(false); instance->state = MfUltralightPollerStateReadSuccess; return NfcCommandContinue; } 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; return command; } static NfcCommand mf_ultralight_poller_handler_read_success(MfUltralightPoller* instance) { FURI_LOG_D(TAG, "Read success"); 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, [MfUltralightPollerStateGetFeatureSet] = mf_ultralight_poller_handler_get_feature_set, [MfUltralightPollerStateReadSignature] = mf_ultralight_poller_handler_read_signature, [MfUltralightPollerStateReadCounters] = mf_ultralight_poller_handler_read_counters, [MfUltralightPollerStateReadTearingFlags] = mf_ultralight_poller_handler_read_tearing_flags, [MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth, [MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass, [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, }; static NfcCommand mf_ultralight_poller_run(NfcGenericEvent event, void* context) { furi_assert(context); furi_assert(event.event_data); furi_assert(event.protocol == NfcProtocolIso14443_3a); MfUltralightPoller* instance = context; furi_assert(instance->callback); const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data; NfcCommand command = NfcCommandContinue; if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { command = mf_ultralight_poller_read_handler[instance->state](instance); } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { instance->mfu_event.type = MfUltralightPollerEventTypeReadFailed; command = instance->callback(instance->general_event, instance->context); } return command; } static bool mf_ultralight_poller_detect(NfcGenericEvent event, void* context) { furi_assert(context); furi_assert(event.event_data); furi_assert(event.protocol == NfcProtocolIso14443_3a); bool protocol_detected = false; MfUltralightPoller* instance = context; const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data; if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { MfUltralightPageReadCommandData read_page_cmd_data = {}; MfUltralightError error = mf_ultralight_poller_read_page(instance, 0, &read_page_cmd_data); protocol_detected = (error == MfUltralightErrorNone); iso14443_3a_poller_halt(instance->iso14443_3a_poller); } return protocol_detected; } const NfcPollerBase mf_ultralight_poller = { .alloc = (NfcPollerAlloc)mf_ultralight_poller_alloc, .free = (NfcPollerFree)mf_ultralight_poller_free, .set_callback = (NfcPollerSetCallback)mf_ultralight_poller_set_callback, .run = (NfcPollerRun)mf_ultralight_poller_run, .detect = (NfcPollerDetect)mf_ultralight_poller_detect, .get_data = (NfcPollerGetData)mf_ultralight_poller_get_data, };