diff --git a/hf_interface_fal/hf.c b/hf_interface_fal/hf.c index 5cfe60f..91d346d 100644 --- a/hf_interface_fal/hf.c +++ b/hf_interface_fal/hf.c @@ -51,6 +51,28 @@ static const uint8_t plugin_hf_select_desfire_app_no_le[] = {0x00, 0xA4, 0x04, 0x00, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x00}; static const uint8_t plugin_hf_file_not_found[] = {0x6a, 0x82}; +static NfcCommand plugin_hf_run_conversation(PluginHfContext* ctx) { + if(!ctx || !ctx->api) { + FURI_LOG_E(TAG, "Cannot run HF conversation without valid context"); + return NfcCommandStop; + } + + furi_thread_set_current_priority(FuriThreadPriorityLowest); + ctx->api->run_conversation(ctx->host_ctx); + + PluginHfStage stage = ctx->api->get_stage(ctx->host_ctx); + if(stage == PluginHfStageComplete) { + return NfcCommandStop; + } + + if(stage == PluginHfStageFail) { + ctx->api->notify_worker_exit(ctx->host_ctx); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + static bool plugin_hf_validate_host_api(const PluginHfHostApi* api) { if(!api) { FURI_LOG_E(TAG, "Missing HF host API"); @@ -65,10 +87,8 @@ static bool plugin_hf_validate_host_api(const PluginHfHostApi* api) { } \ } while(false) - HF_REQUIRE_API(notify_card_detected); HF_REQUIRE_API(notify_worker_exit); - HF_REQUIRE_API(sam_can_accept_card); - HF_REQUIRE_API(send_card_detected); + HF_REQUIRE_API(begin_card_session); HF_REQUIRE_API(send_nfc_rx); HF_REQUIRE_API(run_conversation); HF_REQUIRE_API(set_stage); @@ -91,18 +111,27 @@ static bool plugin_hf_validate_host_api(const PluginHfHostApi* api) { return true; } -static PluginHfContext* plugin_hf_require_ctx(void* plugin_ctx) { +static PluginHfContext* plugin_hf_get_ctx(void* plugin_ctx) { PluginHfContext* ctx = plugin_ctx; - furi_check(ctx); - furi_check(ctx->api); - furi_check(ctx->host_ctx); - furi_check(ctx->nfc); - furi_check(ctx->nfc_device); + if(!ctx || !ctx->api || !ctx->host_ctx || !ctx->nfc || !ctx->nfc_device) { + FURI_LOG_W( + TAG, + "Invalid HF plugin context ctx=%p api=%p host=%p nfc=%p device=%p", + (void*)ctx, + ctx ? (void*)ctx->api : NULL, + ctx ? ctx->host_ctx : NULL, + ctx ? (void*)ctx->nfc : NULL, + ctx ? (void*)ctx->nfc_device : NULL); + return NULL; + } return ctx; } static void plugin_hf_cleanup_pollers(PluginHfContext* ctx) { - ctx = plugin_hf_require_ctx(ctx); + ctx = plugin_hf_get_ctx(ctx); + if(!ctx) { + return; + } if(ctx->poller) { nfc_poller_stop(ctx->poller); nfc_poller_free(ctx->poller); @@ -116,7 +145,10 @@ static void plugin_hf_cleanup_pollers(PluginHfContext* ctx) { } static void plugin_hf_set_read_error(PluginHfContext* ctx, const char* text) { - ctx = plugin_hf_require_ctx(ctx); + ctx = plugin_hf_get_ctx(ctx); + if(!ctx) { + return; + } if(ctx->api->set_read_error) { ctx->api->set_read_error(ctx->host_ctx, text); } @@ -154,9 +186,10 @@ static PicopassError plugin_hf_fake_epurse_update(BitBuffer* tx_buffer, BitBuffe static void plugin_hf_capture_sio(PluginHfContext* ctx, BitBuffer* tx_buffer, BitBuffer* rx_buffer) { - ctx = plugin_hf_require_ctx(ctx); - furi_check(tx_buffer); - furi_check(rx_buffer); + ctx = plugin_hf_get_ctx(ctx); + if(!ctx || !tx_buffer || !rx_buffer) { + return; + } const uint8_t* buffer = bit_buffer_get_data(tx_buffer); size_t len = bit_buffer_get_size_bytes(tx_buffer); const uint8_t* rx_buffer_data = bit_buffer_get_data(rx_buffer); @@ -179,7 +212,10 @@ static void } static void plugin_hf_iso15693_transmit(PluginHfContext* ctx, uint8_t* buffer, size_t len) { - ctx = plugin_hf_require_ctx(ctx); + ctx = plugin_hf_get_ctx(ctx); + if(!ctx) { + return; + } if(!buffer || len == 0U) { FURI_LOG_W(TAG, "Skip picopass transmit invalid input"); ctx->api->set_stage(ctx->host_ctx, PluginHfStageFail); @@ -240,7 +276,10 @@ static void plugin_hf_iso14443a_transmit( UNUSED(timeout); UNUSED(format); - ctx = plugin_hf_require_ctx(ctx); + ctx = plugin_hf_get_ctx(ctx); + if(!ctx) { + return; + } if(!buffer || len == 0U || !ctx->iso14443_4a_poller) { FURI_LOG_W(TAG, "Skip 14A transmit invalid state"); ctx->api->set_stage(ctx->host_ctx, PluginHfStageFail); @@ -306,7 +345,10 @@ static void plugin_hf_mfc_transmit( uint8_t format[3]) { UNUSED(timeout); - ctx = plugin_hf_require_ctx(ctx); + ctx = plugin_hf_get_ctx(ctx); + if(!ctx) { + return; + } if(!buffer || len == 0U || !ctx->mfc_poller) { FURI_LOG_W(TAG, "Skip MFC transmit invalid state"); ctx->api->set_stage(ctx->host_ctx, PluginHfStageFail); @@ -424,7 +466,10 @@ static void plugin_hf_mfc_transmit( } static NfcCommand plugin_hf_poller_callback_iso14443_4a(NfcGenericEvent event, void* context) { - PluginHfContext* ctx = plugin_hf_require_ctx(context); + PluginHfContext* ctx = plugin_hf_get_ctx(context); + if(!ctx) { + return NfcCommandStop; + } NfcCommand ret = NfcCommandContinue; const Iso14443_4aPollerEvent* iso_event = event.event_data; if(event.protocol != NfcProtocolIso14443_4a || !iso_event) { @@ -438,13 +483,15 @@ static NfcCommand plugin_hf_poller_callback_iso14443_4a(NfcGenericEvent event, v if(iso_event->type == Iso14443_4aPollerEventTypeReady) { HF_DIAG_D("14A ready stage=%d", stage); if(stage == PluginHfStageCardDetect) { - ctx->api->notify_card_detected(ctx->host_ctx); - if(!ctx->api->sam_can_accept_card(ctx->host_ctx)) { - return NfcCommandContinue; + if(!ctx->poller || !ctx->nfc_device) { + FURI_LOG_E( + TAG, + "14A detect without poller/device poller=%p device=%p", + (void*)ctx->poller, + (void*)ctx->nfc_device); + ctx->api->set_stage(ctx->host_ctx, PluginHfStageFail); + return NfcCommandStop; } - - furi_check(ctx->poller); - furi_check(ctx->nfc_device); const void* poller_data = nfc_poller_get_data(ctx->poller); if(!poller_data) { FURI_LOG_E(TAG, "14A ready without poller data"); @@ -501,20 +548,15 @@ static NfcCommand plugin_hf_poller_callback_iso14443_4a(NfcGenericEvent event, v } } - ctx->api->send_card_detected( - ctx->host_ctx, iso14443_3a_get_sak(iso3a), uid, uid_len, ats, ats_len); - FURI_LOG_D(TAG, "14A cardDetected delivered uid_len=%u ats_len=%u", uid_len, ats_len); + if(!ctx->api->begin_card_session( + ctx->host_ctx, iso14443_3a_get_sak(iso3a), uid, uid_len, ats, ats_len)) { + ctx->api->set_stage(ctx->host_ctx, PluginHfStageFail); + return NfcCommandStop; + } ctx->api->set_stage(ctx->host_ctx, PluginHfStageConversation); } else if(stage == PluginHfStageConversation) { FURI_LOG_D(TAG, "14A enter conversation"); - ctx->api->run_conversation(ctx->host_ctx); - stage = ctx->api->get_stage(ctx->host_ctx); - if(stage == PluginHfStageComplete) { - ret = NfcCommandStop; - } else if(stage == PluginHfStageFail) { - ctx->api->notify_worker_exit(ctx->host_ctx); - ret = NfcCommandStop; - } + ret = plugin_hf_run_conversation(ctx); } else if(stage == PluginHfStageComplete) { ret = NfcCommandStop; } else if(stage == PluginHfStageFail) { @@ -532,7 +574,10 @@ static NfcCommand plugin_hf_poller_callback_iso14443_4a(NfcGenericEvent event, v } static NfcCommand plugin_hf_poller_callback_mfc(NfcGenericEvent event, void* context) { - PluginHfContext* ctx = plugin_hf_require_ctx(context); + PluginHfContext* ctx = plugin_hf_get_ctx(context); + if(!ctx) { + return NfcCommandStop; + } NfcCommand ret = NfcCommandContinue; MfClassicPollerEvent* mfc_event = event.event_data; if(event.protocol != NfcProtocolMfClassic || !mfc_event) { @@ -546,12 +591,11 @@ static NfcCommand plugin_hf_poller_callback_mfc(NfcGenericEvent event, void* con if(mfc_event->type == MfClassicPollerEventTypeSuccess) { HF_DIAG_D("MFC success stage=%d", stage); if(stage == PluginHfStageCardDetect) { - ctx->api->notify_card_detected(ctx->host_ctx); - if(!ctx->api->sam_can_accept_card(ctx->host_ctx)) { - return NfcCommandContinue; + if(!ctx->poller) { + FURI_LOG_E(TAG, "MFC detect without poller"); + ctx->api->set_stage(ctx->host_ctx, PluginHfStageFail); + return NfcCommandStop; } - - furi_check(ctx->poller); const MfClassicData* mfc_data = nfc_poller_get_data(ctx->poller); if(!mfc_data || !mfc_data->iso14443_3a_data) { FURI_LOG_E(TAG, "MFC data unavailable"); @@ -565,25 +609,20 @@ static NfcCommand plugin_hf_poller_callback_mfc(NfcGenericEvent event, void* con ctx->api->set_stage(ctx->host_ctx, PluginHfStageFail); return NfcCommandStop; } - ctx->api->send_card_detected( - ctx->host_ctx, - iso14443_3a_get_sak(mfc_data->iso14443_3a_data), - uid, - uid_len, - NULL, - 0); - FURI_LOG_D(TAG, "MFC cardDetected delivered uid_len=%u", uid_len); + if(!ctx->api->begin_card_session( + ctx->host_ctx, + iso14443_3a_get_sak(mfc_data->iso14443_3a_data), + uid, + uid_len, + NULL, + 0)) { + ctx->api->set_stage(ctx->host_ctx, PluginHfStageFail); + return NfcCommandStop; + } ctx->api->set_stage(ctx->host_ctx, PluginHfStageConversation); } else if(stage == PluginHfStageConversation) { FURI_LOG_D(TAG, "MFC enter conversation"); - ctx->api->run_conversation(ctx->host_ctx); - stage = ctx->api->get_stage(ctx->host_ctx); - if(stage == PluginHfStageComplete) { - ret = NfcCommandStop; - } else if(stage == PluginHfStageFail) { - ctx->api->notify_worker_exit(ctx->host_ctx); - ret = NfcCommandStop; - } + ret = plugin_hf_run_conversation(ctx); } else if(stage == PluginHfStageComplete) { ret = NfcCommandStop; } else if(stage == PluginHfStageFail) { @@ -599,7 +638,10 @@ static NfcCommand plugin_hf_poller_callback_mfc(NfcGenericEvent event, void* con } static NfcCommand plugin_hf_poller_callback_picopass(PicopassPollerEvent event, void* context) { - PluginHfContext* ctx = plugin_hf_require_ctx(context); + PluginHfContext* ctx = plugin_hf_get_ctx(context); + if(!ctx) { + return NfcCommandStop; + } NfcCommand ret = NfcCommandContinue; PluginHfStage stage = ctx->api->get_stage(ctx->host_ctx); @@ -609,26 +651,21 @@ static NfcCommand plugin_hf_poller_callback_picopass(PicopassPollerEvent event, } else if(event.type == PicopassPollerEventTypeSuccess) { HF_DIAG_D("Picopass success stage=%d", stage); if(stage == PluginHfStageCardDetect) { - ctx->api->notify_card_detected(ctx->host_ctx); - if(!ctx->api->sam_can_accept_card(ctx->host_ctx)) { - return NfcCommandContinue; - } uint8_t* csn = ctx->api->picopass_get_csn(ctx->host_ctx); - furi_check(csn); - ctx->api->send_card_detected( - ctx->host_ctx, 0, csn, sizeof(PicopassSerialNum), NULL, 0); - FURI_LOG_D(TAG, "Picopass cardDetected delivered"); + if(!csn) { + FURI_LOG_E(TAG, "Picopass CSN unavailable"); + ctx->api->set_stage(ctx->host_ctx, PluginHfStageFail); + return NfcCommandStop; + } + if(!ctx->api->begin_card_session( + ctx->host_ctx, 0, csn, sizeof(PicopassSerialNum), NULL, 0)) { + ctx->api->set_stage(ctx->host_ctx, PluginHfStageFail); + return NfcCommandStop; + } ctx->api->set_stage(ctx->host_ctx, PluginHfStageConversation); } else if(stage == PluginHfStageConversation) { FURI_LOG_D(TAG, "Picopass enter conversation"); - ctx->api->run_conversation(ctx->host_ctx); - stage = ctx->api->get_stage(ctx->host_ctx); - if(stage == PluginHfStageComplete) { - ret = NfcCommandStop; - } else if(stage == PluginHfStageFail) { - ctx->api->notify_worker_exit(ctx->host_ctx); - ret = NfcCommandStop; - } + ret = plugin_hf_run_conversation(ctx); } else if(stage == PluginHfStageComplete) { ret = NfcCommandStop; } else if(stage == PluginHfStageFail) { @@ -674,7 +711,11 @@ static void* plugin_hf_alloc(const PluginHfHostApi* api, void* host_ctx) { } static void plugin_hf_free(void* plugin_ctx) { - PluginHfContext* ctx = plugin_hf_require_ctx(plugin_ctx); + PluginHfContext* ctx = plugin_hf_get_ctx(plugin_ctx); + if(!ctx) { + free(plugin_ctx); + return; + } plugin_hf_cleanup_pollers(ctx); free(ctx); } @@ -683,9 +724,11 @@ static size_t plugin_hf_detect_supported_types( void* plugin_ctx, SeaderCredentialType* detected_types, size_t detected_capacity) { - PluginHfContext* ctx = plugin_hf_require_ctx(plugin_ctx); - furi_check(detected_types); - furi_check(detected_capacity > 0U); + PluginHfContext* ctx = plugin_hf_get_ctx(plugin_ctx); + if(!ctx || !detected_types || detected_capacity == 0U) { + FURI_LOG_W(TAG, "HF detect called with invalid state"); + return 0U; + } size_t detected_type_count = 0; HF_DIAG_D("Detect supported HF types"); NfcPoller* poller_detect = nfc_poller_alloc(ctx->nfc, NfcProtocolIso14443_4a); @@ -718,7 +761,10 @@ static size_t plugin_hf_detect_supported_types( } static bool plugin_hf_start_read_for_type(void* plugin_ctx, SeaderCredentialType type) { - PluginHfContext* ctx = plugin_hf_require_ctx(plugin_ctx); + PluginHfContext* ctx = plugin_hf_get_ctx(plugin_ctx); + if(!ctx) { + return false; + } NfcPoller* poller_detect = NULL; plugin_hf_cleanup_pollers(ctx); @@ -779,14 +825,20 @@ static bool plugin_hf_start_read_for_type(void* plugin_ctx, SeaderCredentialType } static void plugin_hf_stop(void* plugin_ctx) { - PluginHfContext* ctx = plugin_hf_require_ctx(plugin_ctx); + PluginHfContext* ctx = plugin_hf_get_ctx(plugin_ctx); + if(!ctx) { + return; + } plugin_hf_cleanup_pollers(ctx); ctx->active_type = SeaderCredentialTypeNone; } static bool plugin_hf_handle_action(void* plugin_ctx, const PluginHfAction* action) { - PluginHfContext* ctx = plugin_hf_require_ctx(plugin_ctx); - furi_check(action); + PluginHfContext* ctx = plugin_hf_get_ctx(plugin_ctx); + if(!ctx || !action) { + FURI_LOG_W(TAG, "HF action called with invalid state"); + return false; + } HF_DIAG_D("Handle action type=%d len=%u", action->type, action->len); if(action->type == PluginHfActionTypePicopassTx) { diff --git a/hf_read_lifecycle.c b/hf_read_lifecycle.c new file mode 100644 index 0000000..3d79a7c --- /dev/null +++ b/hf_read_lifecycle.c @@ -0,0 +1,50 @@ +#include "hf_read_lifecycle.h" + +SeaderHfCardSessionDecision + seader_hf_read_on_card_detect(SeaderHfReadState state, bool sam_can_accept_card) { + if(state != SeaderHfReadStateDetecting) { + return SeaderHfCardSessionDecisionAbort; + } + + if(!sam_can_accept_card) { + return SeaderHfCardSessionDecisionAbort; + } + + return SeaderHfCardSessionDecisionStartConversation; +} + +bool seader_hf_read_is_waiting_for_progress(SeaderHfReadState state) { + return state == SeaderHfReadStateConversationStarting || + state == SeaderHfReadStateConversationActive || state == SeaderHfReadStateFinishing; +} + +bool seader_hf_read_should_timeout( + SeaderHfReadState state, + uint32_t elapsed_ms, + uint32_t timeout_ms) { + if(!seader_hf_read_is_waiting_for_progress(state)) { + return false; + } + + return elapsed_ms >= timeout_ms; +} + +const char* seader_hf_read_failure_reason_text(SeaderHfReadFailureReason reason) { + switch(reason) { + case SeaderHfReadFailureReasonUnavailable: + return "HF unavailable"; + case SeaderHfReadFailureReasonSamBusy: + return "SAM not idle"; + case SeaderHfReadFailureReasonSamTimeout: + return "SAM timeout"; + case SeaderHfReadFailureReasonBoardMissing: + return "Reader lost"; + case SeaderHfReadFailureReasonProtocolError: + return "Protocol error"; + case SeaderHfReadFailureReasonInternalState: + return "Read state error"; + case SeaderHfReadFailureReasonNone: + default: + return "Read failed"; + } +} diff --git a/hf_read_lifecycle.h b/hf_read_lifecycle.h new file mode 100644 index 0000000..5352633 --- /dev/null +++ b/hf_read_lifecycle.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +typedef enum { + SeaderHfReadStateIdle = 0, + SeaderHfReadStateDetecting, + SeaderHfReadStateConversationStarting, + SeaderHfReadStateConversationActive, + SeaderHfReadStateFinishing, + SeaderHfReadStateTerminalSuccess, + SeaderHfReadStateTerminalFail, +} SeaderHfReadState; + +typedef enum { + SeaderHfReadFailureReasonNone = 0, + SeaderHfReadFailureReasonUnavailable, + SeaderHfReadFailureReasonSamBusy, + SeaderHfReadFailureReasonSamTimeout, + SeaderHfReadFailureReasonBoardMissing, + SeaderHfReadFailureReasonProtocolError, + SeaderHfReadFailureReasonInternalState, +} SeaderHfReadFailureReason; + +typedef enum { + SeaderHfCardSessionDecisionIgnore = 0, + SeaderHfCardSessionDecisionStartConversation, + SeaderHfCardSessionDecisionAbort, +} SeaderHfCardSessionDecision; + +SeaderHfCardSessionDecision + seader_hf_read_on_card_detect(SeaderHfReadState state, bool sam_can_accept_card); +bool seader_hf_read_is_waiting_for_progress(SeaderHfReadState state); +bool seader_hf_read_should_timeout( + SeaderHfReadState state, + uint32_t elapsed_ms, + uint32_t timeout_ms); +const char* seader_hf_read_failure_reason_text(SeaderHfReadFailureReason reason); diff --git a/lib/host_tests/test_hf_read_lifecycle.c b/lib/host_tests/test_hf_read_lifecycle.c new file mode 100644 index 0000000..5434815 --- /dev/null +++ b/lib/host_tests/test_hf_read_lifecycle.c @@ -0,0 +1,97 @@ +#include + +#include "munit.h" + +#include "hf_read_lifecycle.h" + +static MunitResult test_card_detect_starts_only_from_detecting_when_sam_idle( + const MunitParameter params[], + void* fixture) { + (void)params; + (void)fixture; + + munit_assert_int( + seader_hf_read_on_card_detect(SeaderHfReadStateDetecting, true), + ==, + SeaderHfCardSessionDecisionStartConversation); + munit_assert_int( + seader_hf_read_on_card_detect(SeaderHfReadStateIdle, true), + ==, + SeaderHfCardSessionDecisionAbort); + munit_assert_int( + seader_hf_read_on_card_detect(SeaderHfReadStateDetecting, false), + ==, + SeaderHfCardSessionDecisionAbort); + return MUNIT_OK; +} + +static MunitResult test_waiting_states_and_timeout_policy( + const MunitParameter params[], + void* fixture) { + (void)params; + (void)fixture; + + munit_assert_true(seader_hf_read_is_waiting_for_progress(SeaderHfReadStateConversationStarting)); + munit_assert_true(seader_hf_read_is_waiting_for_progress(SeaderHfReadStateConversationActive)); + munit_assert_true(seader_hf_read_is_waiting_for_progress(SeaderHfReadStateFinishing)); + munit_assert_false(seader_hf_read_is_waiting_for_progress(SeaderHfReadStateDetecting)); + + munit_assert_false( + seader_hf_read_should_timeout(SeaderHfReadStateConversationActive, 2999U, 3000U)); + munit_assert_true( + seader_hf_read_should_timeout(SeaderHfReadStateConversationActive, 3000U, 3000U)); + munit_assert_false(seader_hf_read_should_timeout(SeaderHfReadStateIdle, 99999U, 3000U)); + return MUNIT_OK; +} + +static MunitResult test_failure_reason_texts_are_stable( + const MunitParameter params[], + void* fixture) { + (void)params; + (void)fixture; + + munit_assert_string_equal( + seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonUnavailable), + "HF unavailable"); + munit_assert_string_equal( + seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonSamBusy), "SAM not idle"); + munit_assert_string_equal( + seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonInternalState), + "Read state error"); + munit_assert_string_equal( + seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonSamTimeout), "SAM timeout"); + munit_assert_string_equal( + seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonBoardMissing), "Reader lost"); + return MUNIT_OK; +} + +static MunitResult test_error_texts_fit_read_error_storage( + const MunitParameter params[], + void* fixture) { + (void)params; + (void)fixture; + + static const char* protected_read_timeout = + "Protected read timed out.\nNo supported data\nor wrong key."; + + munit_assert_size(strlen(protected_read_timeout), <, 96U); + munit_assert_size( + strlen(seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonInternalState)), <, 96U); + return MUNIT_OK; +} + +static MunitTest test_hf_read_lifecycle_cases[] = { + {(char*)"/card-detect-gating", test_card_detect_starts_only_from_detecting_when_sam_idle, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/timeout-policy", test_waiting_states_and_timeout_policy, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/failure-text", test_failure_reason_texts_are_stable, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/failure-text-fits", test_error_texts_fit_read_error_storage, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {NULL, NULL, NULL, NULL, 0, NULL}, +}; + +MunitSuite test_hf_read_lifecycle_suite = { + "", + test_hf_read_lifecycle_cases, + NULL, + 1, + MUNIT_SUITE_OPTION_NONE, +}; diff --git a/lib/host_tests/test_runtime_policy.c b/lib/host_tests/test_runtime_policy.c index 0ecd088..ddc0a36 100644 --- a/lib/host_tests/test_runtime_policy.c +++ b/lib/host_tests/test_runtime_policy.c @@ -120,13 +120,102 @@ static MunitResult test_begin_hf_teardown_sets_state( return MUNIT_OK; } +static MunitResult test_fail_hf_startup_clears_runtime_and_sets_failure( + const MunitParameter params[], + void* fixture) { + (void)params; + (void)fixture; + + SeaderHfReadState read_state = SeaderHfReadStateDetecting; + SeaderHfReadFailureReason failure_reason = SeaderHfReadFailureReasonNone; + uint32_t last_progress_tick = 1234U; + SeaderHfSessionState hf_state = SeaderHfSessionStateLoaded; + SeaderModeRuntime mode_runtime = SeaderModeRuntimeHF; + + seader_runtime_fail_hf_startup( + &read_state, &failure_reason, &last_progress_tick, &hf_state, &mode_runtime); + + munit_assert_int(read_state, ==, SeaderHfReadStateTerminalFail); + munit_assert_int(failure_reason, ==, SeaderHfReadFailureReasonUnavailable); + munit_assert_uint32(last_progress_tick, ==, 0U); + munit_assert_int(hf_state, ==, SeaderHfSessionStateUnloaded); + munit_assert_int(mode_runtime, ==, SeaderModeRuntimeNone); + return MUNIT_OK; +} + +static MunitResult test_begin_board_auto_recover_sets_pending_and_target( + const MunitParameter params[], + void* fixture) { + (void)params; + (void)fixture; + + bool pending = false; + bool resume_read = false; + SeaderCredentialType preserved_read_type = SeaderCredentialTypeNone; + + munit_assert_true(seader_runtime_begin_board_auto_recover( + true, + true, + SeaderCredentialTypeMifareClassic, + &pending, + &resume_read, + &preserved_read_type)); + munit_assert_true(pending); + munit_assert_true(resume_read); + munit_assert_int(preserved_read_type, ==, SeaderCredentialTypeMifareClassic); + + seader_runtime_finish_board_auto_recover(&pending, &resume_read, &preserved_read_type); + munit_assert_false(pending); + munit_assert_false(resume_read); + munit_assert_int(preserved_read_type, ==, SeaderCredentialTypeNone); + return MUNIT_OK; +} + +static MunitResult test_begin_board_auto_recover_rejects_invalid_or_duplicate_state( + const MunitParameter params[], + void* fixture) { + (void)params; + (void)fixture; + + bool pending = false; + bool resume_read = false; + SeaderCredentialType preserved_read_type = SeaderCredentialTypeNone; + + munit_assert_false( + seader_runtime_begin_board_auto_recover( + false, + true, + SeaderCredentialType14A, + &pending, + &resume_read, + &preserved_read_type)); + munit_assert_false(pending); + munit_assert_false(resume_read); + munit_assert_int(preserved_read_type, ==, SeaderCredentialTypeNone); + + pending = true; + munit_assert_false( + seader_runtime_begin_board_auto_recover( + true, + false, + SeaderCredentialType14A, + &pending, + &resume_read, + &preserved_read_type)); + munit_assert_true(pending); + return MUNIT_OK; +} + static MunitTest test_runtime_policy_cases[] = { {(char*)"/reset-sam-metadata", test_reset_cached_sam_metadata_clears_all_fields, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, {(char*)"/begin-uhf-probe", test_begin_uhf_probe_sets_runtime_and_initializes_probe, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, {(char*)"/begin-uhf-probe-invalid", test_begin_uhf_probe_rejects_invalid_states, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, {(char*)"/finish-uhf-probe", test_finish_uhf_probe_restores_none, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, {(char*)"/begin-hf-teardown", test_begin_hf_teardown_sets_state, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/fail-hf-startup", test_fail_hf_startup_clears_runtime_and_sets_failure, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, {(char*)"/finalize-hf-release", test_finalize_hf_release_sets_terminal_state, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/begin-board-auto-recover", test_begin_board_auto_recover_sets_pending_and_target, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/begin-board-auto-recover-invalid", test_begin_board_auto_recover_rejects_invalid_or_duplicate_state, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, {NULL, NULL, NULL, NULL, 0, NULL}, }; diff --git a/runtime_policy.c b/runtime_policy.c index 5b8e44f..9369763 100644 --- a/runtime_policy.c +++ b/runtime_policy.c @@ -76,3 +76,64 @@ void seader_runtime_finalize_hf_release( *mode_runtime = SeaderModeRuntimeNone; } } + +void seader_runtime_fail_hf_startup( + SeaderHfReadState* hf_read_state, + SeaderHfReadFailureReason* failure_reason, + uint32_t* last_progress_tick, + SeaderHfSessionState* hf_session_state, + SeaderModeRuntime* mode_runtime) { + if(hf_read_state) { + *hf_read_state = SeaderHfReadStateTerminalFail; + } + + if(failure_reason) { + *failure_reason = SeaderHfReadFailureReasonUnavailable; + } + + if(last_progress_tick) { + *last_progress_tick = 0U; + } + + if(hf_session_state) { + *hf_session_state = SeaderHfSessionStateUnloaded; + } + + if(mode_runtime && *mode_runtime == SeaderModeRuntimeHF) { + *mode_runtime = SeaderModeRuntimeNone; + } +} + +bool seader_runtime_begin_board_auto_recover( + bool sam_present, + bool hf_runtime_active, + SeaderCredentialType selected_read_type, + bool* pending, + bool* resume_read, + SeaderCredentialType* preserved_read_type) { + if(!sam_present || !pending || !resume_read || !preserved_read_type || *pending) { + return false; + } + + *pending = true; + *resume_read = hf_runtime_active; + *preserved_read_type = hf_runtime_active ? selected_read_type : SeaderCredentialTypeNone; + return true; +} + +void seader_runtime_finish_board_auto_recover( + bool* pending, + bool* resume_read, + SeaderCredentialType* preserved_read_type) { + if(pending) { + *pending = false; + } + + if(resume_read) { + *resume_read = false; + } + + if(preserved_read_type) { + *preserved_read_type = SeaderCredentialTypeNone; + } +} diff --git a/runtime_policy.h b/runtime_policy.h index b0534f5..5eedc55 100644 --- a/runtime_policy.h +++ b/runtime_policy.h @@ -5,6 +5,7 @@ #include #include "seader.h" +#include "seader_credential_type.h" #include "uhf_snmp_probe.h" void seader_runtime_reset_cached_sam_metadata( @@ -26,3 +27,20 @@ void seader_runtime_begin_hf_teardown(SeaderHfSessionState* hf_session_state); void seader_runtime_finalize_hf_release( SeaderHfSessionState* hf_session_state, SeaderModeRuntime* mode_runtime); +void seader_runtime_fail_hf_startup( + SeaderHfReadState* hf_read_state, + SeaderHfReadFailureReason* failure_reason, + uint32_t* last_progress_tick, + SeaderHfSessionState* hf_session_state, + SeaderModeRuntime* mode_runtime); +bool seader_runtime_begin_board_auto_recover( + bool sam_present, + bool hf_runtime_active, + SeaderCredentialType selected_read_type, + bool* pending, + bool* resume_read, + SeaderCredentialType* preserved_read_type); +void seader_runtime_finish_board_auto_recover( + bool* pending, + bool* resume_read, + SeaderCredentialType* preserved_read_type); diff --git a/seader_worker.c b/seader_worker.c index bdc64bf..19cb994 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -1,5 +1,7 @@ #include "seader_worker_i.h" #include "seader_hf_read_plan.h" +#include "hf_read_lifecycle.h" +#include "runtime_policy.h" #include "trace_log.h" #include @@ -7,9 +9,11 @@ #define TAG "SeaderWorker" -#define APDU_HEADER_LEN 5 -#define ASN1_PREFIX 6 -#define SEADER_HEX_LOG_MAX_BYTES 32U +#define APDU_HEADER_LEN 5 +#define ASN1_PREFIX 6 +#define SEADER_HEX_LOG_MAX_BYTES 32U +#define SEADER_HF_CONVERSATION_TIMEOUT_MS 3000U +#define SEADER_WORKER_STACK_SIZE 4096U // #define ASN1_DEBUG true #define RFAL_PICOPASS_TXRX_FLAGS \ @@ -28,6 +32,30 @@ static void seader_worker_release_hf_session(Seader* seader) { seader_hf_plugin_release(seader); } +static void seader_worker_fail_hf_startup(Seader* seader, const char* detail) { + if(!seader || !seader->worker) { + return; + } + + SeaderWorker* seader_worker = seader->worker; + seader_hf_plugin_release(seader); + seader_runtime_fail_hf_startup( + &seader->hf_read_state, + &seader->hf_read_failure_reason, + &seader->hf_read_last_progress_tick, + &seader->hf_session_state, + &seader->mode_runtime); + strlcpy( + seader->read_error, + detail ? detail : seader_hf_read_failure_reason_text(seader->hf_read_failure_reason), + sizeof(seader->read_error)); + seader_sam_force_idle_for_recovery(seader); + seader_worker->stage = SeaderPollerEventTypeFail; + if(seader_worker->callback) { + seader_worker->callback(SeaderWorkerEventFail, seader_worker->context); + } +} + typedef struct { volatile bool done; volatile bool detected; @@ -192,52 +220,6 @@ static size_t __attribute__((unused)) seader_worker_detect_supported_types( return detected_type_count; } -static bool __attribute__((unused)) -seader_worker_start_read_for_type(Seader* seader, SeaderCredentialType type) { - NfcPoller* poller_detect = NULL; - - if(type == SeaderCredentialType14A) { - poller_detect = nfc_poller_alloc(seader->nfc, NfcProtocolIso14443_4a); - if(!nfc_poller_detect(poller_detect)) { - nfc_poller_free(poller_detect); - return false; - } - FURI_LOG_I(TAG, "Detected ISO14443-4A card"); - nfc_poller_free(poller_detect); - seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolIso14443_4a); - seader->worker->stage = SeaderPollerEventTypeCardDetect; - seader->credential->type = SeaderCredentialType14A; - nfc_poller_start(seader->poller, seader_worker_poller_callback_iso14443_4a, seader); - return true; - } else if(type == SeaderCredentialTypeMifareClassic) { - poller_detect = nfc_poller_alloc(seader->nfc, NfcProtocolMfClassic); - if(!nfc_poller_detect(poller_detect)) { - nfc_poller_free(poller_detect); - return false; - } - FURI_LOG_I(TAG, "Detected Mifare Classic card"); - nfc_poller_free(poller_detect); - seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolMfClassic); - seader->worker->stage = SeaderPollerEventTypeCardDetect; - seader->credential->type = SeaderCredentialTypeMifareClassic; - nfc_poller_start(seader->poller, seader_worker_poller_callback_mfc, seader); - return true; - } else if(type == SeaderCredentialTypePicopass) { - if(!seader_worker_detect_picopass(seader->nfc)) { - return false; - } - FURI_LOG_I(TAG, "Detected Picopass card"); - seader->picopass_poller = picopass_poller_alloc(seader->nfc); - seader->worker->stage = SeaderPollerEventTypeCardDetect; - seader->credential->type = SeaderCredentialTypePicopass; - picopass_poller_start( - seader->picopass_poller, seader_worker_poller_callback_picopass, seader); - return true; - } - - return false; -} - /***************************** Seader Worker API *******************************/ SeaderWorker* seader_worker_alloc() { @@ -247,8 +229,8 @@ SeaderWorker* seader_worker_alloc() { } // Worker thread attributes - seader_worker->thread = - furi_thread_alloc_ex("SeaderWorker", 5120, seader_worker_task, seader_worker); + seader_worker->thread = furi_thread_alloc_ex( + "SeaderWorker", SEADER_WORKER_STACK_SIZE, seader_worker_task, seader_worker); seader_worker->messages = furi_message_queue_alloc(2, sizeof(uint8_t)); seader_worker->apdu_slots = calloc(SEADER_WORKER_APDU_SLOT_COUNT, sizeof(SeaderAPDU)); @@ -560,6 +542,12 @@ int32_t seader_worker_task(void* context) { FURI_LOG_D(TAG, "Reading mode started"); seader_worker_reading(seader); } + if(seader && seader->is_debug_enabled) { + FURI_LOG_D( + TAG, + "Worker thread stack watermark free=%lu", + (unsigned long)furi_thread_get_stack_space(furi_thread_get_current_id())); + } seader_worker_change_state(seader_worker, SeaderWorkerStateReady); return 0; @@ -569,15 +557,34 @@ void seader_worker_reading(Seader* seader) { SeaderWorker* seader_worker = seader->worker; FURI_LOG_I(TAG, "Reading loop started"); - furi_check(seader_hf_plugin_acquire(seader)); - furi_check(seader->plugin_hf); - furi_check(seader->hf_plugin_ctx); + if(!seader_hf_plugin_acquire(seader) || !seader->plugin_hf || !seader->hf_plugin_ctx) { + FURI_LOG_E( + TAG, + "HF plugin unavailable acquire=%d plugin=%p ctx=%p", + seader->plugin_hf != NULL && seader->hf_plugin_ctx != NULL, + (void*)seader->plugin_hf, + seader->hf_plugin_ctx); + seader_worker_fail_hf_startup(seader, "HF unavailable"); + return; + } while(seader_worker->state == SeaderWorkerStateReading) { bool detected = false; SeaderPollerEventType result_stage = SeaderPollerEventTypeFail; SeaderCredentialType type_to_read = seader_hf_mode_get_selected_read_type(seader); SeaderHfReadPlan read_plan = {0}; + if(!seader_sam_can_accept_card(seader) || seader->hf_read_state != SeaderHfReadStateIdle) { + FURI_LOG_W( + TAG, + "Recover stale HF read state=%d sam=%d intent=%d", + seader->hf_read_state, + seader->sam_state, + seader->sam_intent); + seader_sam_force_idle_for_recovery(seader); + seader->hf_read_state = SeaderHfReadStateIdle; + seader->hf_read_failure_reason = SeaderHfReadFailureReasonNone; + seader->hf_read_last_progress_tick = 0U; + } FURI_LOG_D(TAG, "HF loop selected type=%d stage=%d", type_to_read, seader_worker->stage); if(type_to_read == SeaderCredentialTypeNone) { @@ -600,6 +607,9 @@ void seader_worker_reading(Seader* seader) { break; } else if(read_plan.decision == SeaderHfReadDecisionStartRead) { FURI_LOG_I(TAG, "HF start read for type=%d", read_plan.type_to_read); + seader->hf_read_state = SeaderHfReadStateDetecting; + seader->hf_read_failure_reason = SeaderHfReadFailureReasonNone; + seader->hf_read_last_progress_tick = furi_get_tick(); detected = seader->plugin_hf->start_read_for_type( seader->hf_plugin_ctx, read_plan.type_to_read); if(detected) { @@ -655,6 +665,8 @@ void seader_worker_run_hf_conversation(Seader* seader) { FuriStatus status = furi_message_queue_get(seader_worker->messages, &slot_index, 100); if(status == FuriStatusOk) { + seader->hf_read_state = SeaderHfReadStateConversationActive; + seader->hf_read_last_progress_tick = furi_get_tick(); furi_assert(slot_index < SEADER_WORKER_APDU_SLOT_COUNT); SeaderAPDU* seaderApdu = &seader_worker->apdu_slots[slot_index]; FURI_LOG_D(TAG, "Dequeue SAM message [%d bytes]", seaderApdu->len); @@ -669,250 +681,33 @@ void seader_worker_run_hf_conversation(Seader* seader) { } seader_worker_release_apdu_slot(seader_worker, slot_index); } else if(status == FuriStatusErrorTimeout) { - // No message yet, keep looping to stay in callback - // This is "properly idling" while waiting for SAM + const uint32_t elapsed = furi_get_tick() - seader->hf_read_last_progress_tick; + if(seader_hf_read_should_timeout( + seader->hf_read_state, elapsed, SEADER_HF_CONVERSATION_TIMEOUT_MS)) { + FURI_LOG_W(TAG, "HF conversation timeout after %lu ms", elapsed); + seader->hf_read_state = SeaderHfReadStateTerminalFail; + seader->hf_read_failure_reason = SeaderHfReadFailureReasonSamTimeout; + strlcpy( + seader->read_error, + seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonSamTimeout), + sizeof(seader->read_error)); + seader_sam_force_idle_for_recovery(seader); + seader_worker->stage = SeaderPollerEventTypeFail; + view_dispatcher_send_custom_event( + seader->view_dispatcher, SeaderCustomEventWorkerExit); + } } else { FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status); + seader->hf_read_state = SeaderHfReadStateTerminalFail; + seader->hf_read_failure_reason = SeaderHfReadFailureReasonProtocolError; + strlcpy( + seader->read_error, + seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonProtocolError), + sizeof(seader->read_error)); + seader_sam_force_idle_for_recovery(seader); seader_worker->stage = SeaderPollerEventTypeFail; view_dispatcher_send_custom_event( seader->view_dispatcher, SeaderCustomEventWorkerExit); } } } - -NfcCommand seader_worker_poller_callback_iso14443_4a(NfcGenericEvent event, void* context) { - if(event.protocol != NfcProtocolIso14443_4a || !event.event_data) { - FURI_LOG_W(TAG, "Ignore invalid host 14A callback"); - return NfcCommandStop; - } - furi_check(context); - NfcCommand ret = NfcCommandContinue; - - Seader* seader = context; - SeaderWorker* seader_worker = seader->worker; - - const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data; - if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { - if(seader_worker->stage == SeaderPollerEventTypeCardDetect) { - FURI_LOG_D(TAG, "14a stage CardDetect -> Conversation"); - seader_trace(TAG, "14a CardDetect->Conversation"); - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventPollerDetect); - - if(!seader_sam_can_accept_card(seader)) { - seader_trace( - TAG, - "14a defer detect sam_state=%d intent=%d", - seader->sam_state, - seader->sam_intent); - return NfcCommandContinue; - } - - nfc_device_set_data( - seader->nfc_device, NfcProtocolIso14443_4a, nfc_poller_get_data(seader->poller)); - - size_t uid_len; - const uint8_t* uid = nfc_device_get_uid(seader->nfc_device, &uid_len); - - const Iso14443_4aData* iso14443_4a_data = - nfc_device_get_data(seader->nfc_device, NfcProtocolIso14443_4a); - const Iso14443_3aData* iso14443_3a_data = iso14443_4a_get_base_data(iso14443_4a_data); - - uint32_t t1_tk_size = 0; - if(iso14443_4a_data->ats_data.t1_tk != NULL) { - t1_tk_size = simple_array_get_count(iso14443_4a_data->ats_data.t1_tk); - if(t1_tk_size > 0xFF) { - t1_tk_size = 0; - } - } - - uint8_t ats_len = 0; - uint8_t ats[SEADER_MAX_ATR_SIZE] = {0}; - - if(iso14443_4a_data->ats_data.tl > 1) { - if(sizeof(ats) < 4U + t1_tk_size) { - FURI_LOG_E(TAG, "Host ATS buffer too small: %u", (unsigned)(4U + t1_tk_size)); - seader_worker->stage = SeaderPollerEventTypeFail; - return NfcCommandStop; - } - ats[ats_len++] = iso14443_4a_data->ats_data.t0; - if(iso14443_4a_data->ats_data.t0 & ISO14443_4A_ATS_T0_TA1) { - ats[ats_len++] = iso14443_4a_data->ats_data.ta_1; - } - if(iso14443_4a_data->ats_data.t0 & ISO14443_4A_ATS_T0_TB1) { - ats[ats_len++] = iso14443_4a_data->ats_data.tb_1; - } - if(iso14443_4a_data->ats_data.t0 & ISO14443_4A_ATS_T0_TC1) { - ats[ats_len++] = iso14443_4a_data->ats_data.tc_1; - } - - if(t1_tk_size != 0) { - memcpy( - ats + ats_len, - simple_array_cget_data(iso14443_4a_data->ats_data.t1_tk), - t1_tk_size); - ats_len += t1_tk_size; - } - } - - uint8_t sak = iso14443_3a_get_sak(iso14443_3a_data); - - seader_worker_card_detect( - seader, sak, (uint8_t*)iso14443_3a_data->atqa, uid, uid_len, ats, ats_len); - seader_trace(TAG, "14a card_detect sent uid_len=%d sak=%d", uid_len, sak); - - if(seader_worker->state == SeaderWorkerStateReading) { - seader_worker->stage = SeaderPollerEventTypeConversation; - return NfcCommandContinue; - } - - // nfc_set_fdt_poll_fc(event.instance, SEADER_POLLER_MAX_FWT); - furi_thread_set_current_priority(FuriThreadPriorityLowest); - seader_worker->stage = SeaderPollerEventTypeConversation; - } else if(seader_worker->stage == SeaderPollerEventTypeConversation) { - seader_trace(TAG, "14a ready in Conversation"); - seader_worker_run_hf_conversation(seader); - } else if(seader_worker->stage == SeaderPollerEventTypeComplete) { - seader_trace(TAG, "14a ready in Complete"); - ret = NfcCommandStop; - } else if(seader_worker->stage == SeaderPollerEventTypeFail) { - seader_trace(TAG, "14a ready in Fail"); - ret = NfcCommandStop; - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventWorkerExit); - FURI_LOG_W(TAG, "SeaderPollerEventTypeFail"); - } - } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) { - Iso14443_4aPollerEventData* data = iso14443_4a_event->data; - Iso14443_4aError error = data->error; - FURI_LOG_W(TAG, "Iso14443_4aError %i", error); - seader_trace(TAG, "14a error=%d stage=%d", error, seader_worker->stage); - // I was hoping to catch MFC here, but it seems to be treated the same (None) as no card being present. - switch(error) { - case Iso14443_4aErrorNone: - break; - case Iso14443_4aErrorNotPresent: - break; - case Iso14443_4aErrorProtocol: - ret = NfcCommandStop; - break; - case Iso14443_4aErrorTimeout: - break; - } - } - - return ret; -} - -NfcCommand seader_worker_poller_callback_mfc(NfcGenericEvent event, void* context) { - if(event.protocol != NfcProtocolMfClassic || !event.event_data) { - FURI_LOG_W(TAG, "Ignore invalid host MFC callback"); - return NfcCommandStop; - } - furi_check(context); - NfcCommand ret = NfcCommandContinue; - - Seader* seader = context; - SeaderWorker* seader_worker = seader->worker; - - MfClassicPollerEvent* mfc_event = event.event_data; - if(mfc_event->type == MfClassicPollerEventTypeSuccess) { - if(seader_worker->stage == SeaderPollerEventTypeCardDetect) { - FURI_LOG_D(TAG, "MFC stage CardDetect -> Conversation"); - seader_trace(TAG, "mfc CardDetect->Conversation"); - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventPollerDetect); - - if(!seader_sam_can_accept_card(seader)) { - seader_trace( - TAG, - "mfc defer detect sam_state=%d intent=%d", - seader->sam_state, - seader->sam_intent); - return NfcCommandContinue; - } - - const MfClassicData* mfc_data = nfc_poller_get_data(seader->poller); - uint8_t sak = iso14443_3a_get_sak(mfc_data->iso14443_3a_data); - size_t uid_len = 0; - const uint8_t* uid = mf_classic_get_uid(mfc_data, &uid_len); - seader_worker_card_detect(seader, sak, NULL, uid, uid_len, NULL, 0); - - if(seader_worker->state == SeaderWorkerStateReading) { - seader_worker->stage = SeaderPollerEventTypeConversation; - return NfcCommandContinue; - } - - furi_thread_set_current_priority(FuriThreadPriorityLowest); - seader_worker->stage = SeaderPollerEventTypeConversation; - } else if(seader_worker->stage == SeaderPollerEventTypeConversation) { - seader_worker_run_hf_conversation(seader); - } else if(seader_worker->stage == SeaderPollerEventTypeComplete) { - ret = NfcCommandStop; - } else if(seader_worker->stage == SeaderPollerEventTypeFail) { - seader_trace(TAG, "mfc ready in Fail"); - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventWorkerExit); - ret = NfcCommandStop; - } - } else if(mfc_event->type == MfClassicPollerEventTypeFail) { - seader_trace(TAG, "mfc poller event fail"); - view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit); - ret = NfcCommandStop; - } - - return ret; -} - -NfcCommand seader_worker_poller_callback_picopass(PicopassPollerEvent event, void* context) { - furi_check(context); - NfcCommand ret = NfcCommandContinue; - - Seader* seader = context; - SeaderWorker* seader_worker = seader->worker; - // I know this is is passing the same thing that is on seader all the way down, but I prefer the symmetry between the 15a and iso15 stuff - PicopassPoller* instance = seader->picopass_poller; - if(event.type == PicopassPollerEventTypeCardDetected) { - seader_worker->stage = SeaderPollerEventTypeCardDetect; - } else if(event.type == PicopassPollerEventTypeSuccess) { - if(seader_worker->stage == SeaderPollerEventTypeCardDetect) { - FURI_LOG_D(TAG, "Picopass stage CardDetect -> Conversation"); - seader_trace(TAG, "picopass CardDetect->Conversation"); - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventPollerDetect); - if(!seader_sam_can_accept_card(seader)) { - seader_trace( - TAG, - "picopass defer detect sam_state=%d intent=%d", - seader->sam_state, - seader->sam_intent); - return NfcCommandContinue; - } - uint8_t* csn = picopass_poller_get_csn(instance); - seader_worker_card_detect(seader, 0, NULL, csn, sizeof(PicopassSerialNum), NULL, 0); - - if(seader_worker->state == SeaderWorkerStateReading) { - seader_worker->stage = SeaderPollerEventTypeConversation; - return NfcCommandContinue; - } - - furi_thread_set_current_priority(FuriThreadPriorityLowest); - seader_worker->stage = SeaderPollerEventTypeConversation; - } else if(seader_worker->stage == SeaderPollerEventTypeConversation) { - seader_worker_run_hf_conversation(seader); - } else if(seader_worker->stage == SeaderPollerEventTypeComplete) { - ret = NfcCommandStop; - } else if(seader_worker->stage == SeaderPollerEventTypeFail) { - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventWorkerExit); - ret = NfcCommandStop; - } - } else if(event.type == PicopassPollerEventTypeFail) { - ret = NfcCommandStop; - FURI_LOG_W(TAG, "PicopassPollerEventTypeFail"); - } else { - FURI_LOG_D(TAG, "picopass event type %x", event.type); - } - - return ret; -}