diff --git a/sam_api.c b/sam_api.c index a2b2afc..b90bc67 100644 --- a/sam_api.c +++ b/sam_api.c @@ -587,81 +587,86 @@ void seader_send_no_card_detected(Seader* seader) { ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_CardDetails, &cardDetails); } -bool seader_unpack_pacs(Seader* seader, uint8_t* buf, size_t size) { - SeaderCredential* seader_credential = seader->credential; - PAC_t pac = {0}; - PAC_t* pac_p = &pac; - bool rtn = false; - - asn_dec_rval_t rval = asn_decode(0, ATS_DER, &asn_DEF_PAC, (void**)&pac_p, buf, size); - - if(rval.code == RC_OK) { -#ifdef ASN1_DEBUG - char pacDebug[384] = {0}; - (&asn_DEF_PAC) - ->op->print_struct(&asn_DEF_PAC, &pac, 1, seader_print_struct_callback, pacDebug); - if(strlen(pacDebug) > 0) { - FURI_LOG_D(TAG, "Received pac: %s", pacDebug); - } -#endif - - if(seader_credential->sio[0] == 0x30) { - seader_log_hex_data(TAG, "SIO", seader_credential->sio, seader_credential->sio_len); - -#ifdef ASN1_DEBUG - SIO_t sio = {0}; - SIO_t* sio_p = &sio; - rval = asn_decode( - 0, - ATS_DER, - &asn_DEF_SIO, - (void**)&sio_p, - seader_credential->sio, - seader_credential->sio_len); - - if(rval.code == RC_OK) { - FURI_LOG_D(TAG, "Decoded SIO"); - char sioDebug[384] = {0}; - (&asn_DEF_SIO) - ->op->print_struct( - &asn_DEF_SIO, &sio, 1, seader_print_struct_callback, sioDebug); - if(strlen(sioDebug) > 0) { - FURI_LOG_D(TAG, "SIO: %s", sioDebug); - } - } else { - FURI_LOG_W(TAG, "Failed to decode SIO %d consumed", rval.consumed); - } - - ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_SIO, &sio); -#endif - } - - if(pac.size <= sizeof(seader_credential->credential)) { - // TODO: make credential into a 12 byte array - seader_credential->bit_length = pac.size * 8 - pac.bits_unused; - memcpy(&seader_credential->credential, pac.buf, pac.size); - seader_credential->credential = __builtin_bswap64(seader_credential->credential); - seader_credential->credential = seader_credential->credential >> - (64 - seader_credential->bit_length); - - FURI_LOG_D( - TAG, - "credential (%d) %016llx", - seader_credential->bit_length, - seader_credential->credential); - - rtn = true; - } else { - // PACS too big (probably bad data) - seader_abort_active_read(seader); - } - } else { - FURI_LOG_W(TAG, "Failed to decode PAC %d consumed, size %d", rval.consumed, size); - seader_abort_active_read(seader); +static bool seader_store_pacs_bits( + SeaderCredential* credential, + const uint8_t* payload, + size_t payload_size, + uint8_t unused_bits) { + if(!credential || !payload || payload_size == 0 || + payload_size > sizeof(credential->credential) || unused_bits > 7) { + return false; } - ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_PAC, &pac); - return rtn; + const uint8_t bit_length = payload_size * 8 - unused_bits; + if(bit_length == 0) { + return false; + } + + uint64_t credential_val = 0; + memcpy(&credential_val, payload, payload_size); + credential_val = __builtin_bswap64(credential_val); + + credential->bit_length = bit_length; + credential->credential = credential_val >> (64 - bit_length); + return true; +} + +static bool seader_unpack_pacs2_bits(Seader* seader, const OCTET_STRING_t* pacs_bits) { + SeaderCredential* seader_credential = seader->credential; + if(!pacs_bits || !pacs_bits->buf || pacs_bits->size < 2) { + FURI_LOG_W(TAG, "Malformed pacs2 bits"); + return false; + } + + seader_log_hex_data(TAG, "PACS2 bits", pacs_bits->buf, pacs_bits->size); + + if(seader_credential->sio[0] == 0x30) { + seader_log_hex_data(TAG, "SIO", seader_credential->sio, seader_credential->sio_len); +#ifdef ASN1_DEBUG + asn_dec_rval_t rval; + SIO_t sio = {0}; + SIO_t* sio_p = &sio; + rval = asn_decode( + 0, + ATS_DER, + &asn_DEF_SIO, + (void**)&sio_p, + seader_credential->sio, + seader_credential->sio_len); + + if(rval.code == RC_OK) { + FURI_LOG_D(TAG, "Decoded SIO"); + char sioDebug[384] = {0}; + (&asn_DEF_SIO) + ->op->print_struct(&asn_DEF_SIO, &sio, 1, seader_print_struct_callback, sioDebug); + if(strlen(sioDebug) > 0) { + FURI_LOG_D(TAG, "SIO: %s", sioDebug); + } + } else { + FURI_LOG_W(TAG, "Failed to decode SIO %d consumed", rval.consumed); + } + + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_SIO, &sio); +#endif + } + + const uint8_t unused_bits = pacs_bits->buf[0]; + const uint8_t* payload = pacs_bits->buf + 1; + const size_t payload_size = pacs_bits->size - 1; + FURI_LOG_D(TAG, "PACS2 unused_bits=%u payload_size=%zu", unused_bits, payload_size); + + if(!seader_store_pacs_bits(seader_credential, payload, payload_size, unused_bits)) { + FURI_LOG_W(TAG, "Failed to store PACS2 bits"); + return false; + } + + FURI_LOG_D( + TAG, + "credential (%d) %016llx", + seader_credential->bit_length, + seader_credential->credential); + + return true; } // 800201298106683d052026b6820101 @@ -817,7 +822,6 @@ static void seader_abort_active_read(Seader* seader) { } bool seader_parse_sam_response2(Seader* seader, SamResponse2_t* samResponse) { - uint8_t buffer[10]; switch(samResponse->present) { case SamResponse2_PR_pacs: FURI_LOG_I(TAG, "samResponse2 SamResponse2_PR_pacs"); @@ -835,14 +839,13 @@ bool seader_parse_sam_response2(Seader* seader, SamResponse2_t* samResponse) { Pacs2_t pacs2 = samResponse->choice.pacs; OCTET_STRING_t* pacs = pacs2.bits; - buffer[0] = 0x03; - buffer[1] = pacs->size & 0xFF; - memcpy(buffer + 2, pacs->buf, pacs->size); - if(seader_unpack_pacs(seader, buffer, pacs->size + 2)) { + if(seader_unpack_pacs2_bits(seader, pacs)) { view_dispatcher_send_custom_event( seader->view_dispatcher, SeaderCustomEventPollerSuccess); seader_sam_set_state( seader, SeaderSamStateIdle, SeaderSamIntentNone, SamCommand_PR_NOTHING); + } else { + seader_abort_active_read(seader); } break; case SamResponse2_PR_NOTHING: diff --git a/scenes/seader_scene_config.h b/scenes/seader_scene_config.h index 27309dc..09e9323 100644 --- a/scenes/seader_scene_config.h +++ b/scenes/seader_scene_config.h @@ -2,8 +2,7 @@ ADD_SCENE(seader, start, Start) ADD_SCENE(seader, sam_present, SamPresent) ADD_SCENE(seader, sam_missing, SamMissing) ADD_SCENE(seader, sam_wrong, SamWrong) -ADD_SCENE(seader, read_picopass, ReadPicopass) -ADD_SCENE(seader, read_14a, Read14a) +ADD_SCENE(seader, read, Read) ADD_SCENE(seader, read_config_card, ReadConfigCard) ADD_SCENE(seader, read_card_success, ReadCardSuccess) ADD_SCENE(seader, read_config_card_success, ReadConfigCardSuccess) @@ -18,5 +17,4 @@ ADD_SCENE(seader, credential_info, CredentialInfo) ADD_SCENE(seader, sam_info, SamInfo) ADD_SCENE(seader, virtual_credential, VirtualCredential) ADD_SCENE(seader, formats, Formats) -ADD_SCENE(seader, read_mfc, ReadMfc) ADD_SCENE(seader, apdu_runner, APDURunner) diff --git a/scenes/seader_scene_read_14a.c b/scenes/seader_scene_read.c similarity index 70% rename from scenes/seader_scene_read_14a.c rename to scenes/seader_scene_read.c index 5b8acde..47c7ecf 100644 --- a/scenes/seader_scene_read_14a.c +++ b/scenes/seader_scene_read.c @@ -2,28 +2,31 @@ #include "seader_scene_read_common.h" #include -void seader_scene_read_14a_on_enter(void* context) { +void seader_scene_read_on_enter(void* context) { Seader* seader = context; dolphin_deed(DolphinDeedNfcRead); // Setup view Popup* popup = seader->popup; - popup_set_header(popup, "Detecting\n14a\ncard", 68, 30, AlignLeft, AlignTop); + popup_set_header(popup, "Detecting\nHF card...", 68, 30, AlignLeft, AlignTop); popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); // Start worker view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); seader_scene_read_prepare(seader); - seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolIso14443_4a); seader_credential_clear(seader->credential); - seader->credential->type = SeaderCredentialType14A; - nfc_poller_start(seader->poller, seader_worker_poller_callback_iso14443_4a, seader); + seader_worker_start( + seader->worker, + SeaderWorkerStateReading, + seader->uart, + seader_sam_check_worker_callback, + seader); seader_blink_start(seader); } -bool seader_scene_read_14a_on_event(void* context, SceneManagerEvent event) { +bool seader_scene_read_on_event(void* context, SceneManagerEvent event) { Seader* seader = context; bool consumed = false; @@ -35,7 +38,9 @@ bool seader_scene_read_14a_on_event(void* context, SceneManagerEvent event) { Popup* popup = seader->popup; popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop); consumed = true; - } else if(event.event == SeaderCustomEventPollerSuccess) { + } else if( + event.event == SeaderCustomEventPollerSuccess || + event.event == SeaderWorkerEventSuccess) { scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); consumed = true; } @@ -48,7 +53,8 @@ bool seader_scene_read_14a_on_event(void* context, SceneManagerEvent event) { return consumed; } -void seader_scene_read_14a_on_exit(void* context) { +void seader_scene_read_on_exit(void* context) { Seader* seader = context; + seader_worker_stop(seader->worker); seader_scene_read_cleanup(seader); } diff --git a/scenes/seader_scene_read_card_success.c b/scenes/seader_scene_read_card_success.c index d615e4a..8897dbb 100644 --- a/scenes/seader_scene_read_card_success.c +++ b/scenes/seader_scene_read_card_success.c @@ -83,6 +83,11 @@ void seader_scene_read_card_success_on_enter(void* context) { if(plugin && credential->bit_length > 0) { size_t format_count = plugin->count(credential->bit_length, credential->credential); + FURI_LOG_D( + TAG, + "Plugin present, bit_length=%d, format_count=%zu", + credential->bit_length, + format_count); if(format_count > 0) { widget_add_button_element( seader->widget, @@ -91,6 +96,8 @@ void seader_scene_read_card_success_on_enter(void* context) { seader_scene_read_card_success_widget_callback, seader); } + } else { + FURI_LOG_D(TAG, "Plugin=%p, bit_length=%d", plugin, credential->bit_length); } widget_add_string_element( diff --git a/scenes/seader_scene_read_common.c b/scenes/seader_scene_read_common.c index 11ad5c9..0341d3a 100644 --- a/scenes/seader_scene_read_common.c +++ b/scenes/seader_scene_read_common.c @@ -39,18 +39,6 @@ void seader_scene_read_cleanup(Seader* seader) { seader_send_no_card_detected(seader); } - if(seader->poller) { - nfc_poller_stop(seader->poller); - nfc_poller_free(seader->poller); - seader->poller = NULL; - } - - if(seader->picopass_poller) { - picopass_poller_stop(seader->picopass_poller); - picopass_poller_free(seader->picopass_poller); - seader->picopass_poller = NULL; - } - popup_reset(seader->popup); seader_worker_reset_poller_session(seader->worker); if(seader->sam_state == SeaderSamStateIdle) { diff --git a/scenes/seader_scene_read_config_card.c b/scenes/seader_scene_read_config_card.c index 9f8914a..0fbf2d2 100644 --- a/scenes/seader_scene_read_config_card.c +++ b/scenes/seader_scene_read_config_card.c @@ -20,10 +20,15 @@ void seader_scene_read_config_card_on_enter(void* context) { view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); seader_scene_read_prepare(seader); - seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolIso14443_4a); seader_credential_clear(seader->credential); seader->credential->type = SeaderCredentialTypeConfig; - nfc_poller_start(seader->poller, seader_worker_poller_callback_iso14443_4a, seader); + + seader_worker_start( + seader->worker, + SeaderWorkerStateReading, + seader->uart, + seader_sam_check_worker_callback, + seader); seader_blink_start(seader); } @@ -33,7 +38,7 @@ bool seader_scene_read_config_card_on_event(void* context, SceneManagerEvent eve bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SeaderCustomEventWorkerExit) { + if(event.event == SeaderCustomEventWorkerExit || event.event == SeaderWorkerEventSuccess) { scene_manager_next_scene(seader->scene_manager, SeaderSceneReadConfigCardSuccess); consumed = true; } else if(event.event == SeaderCustomEventPollerSuccess) { @@ -51,5 +56,6 @@ bool seader_scene_read_config_card_on_event(void* context, SceneManagerEvent eve void seader_scene_read_config_card_on_exit(void* context) { Seader* seader = context; + seader_worker_stop(seader->worker); seader_scene_read_cleanup(seader); } diff --git a/scenes/seader_scene_read_mfc.c b/scenes/seader_scene_read_mfc.c deleted file mode 100644 index c8d5188..0000000 --- a/scenes/seader_scene_read_mfc.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "../seader_i.h" -#include "seader_scene_read_common.h" -#include - -#define TAG "SceneReadNfc" - -void seader_scene_read_mfc_on_enter(void* context) { - Seader* seader = context; - dolphin_deed(DolphinDeedNfcRead); - - // Setup view - Popup* popup = seader->popup; - popup_set_header(popup, "Detecting\nMFC\ncard", 68, 30, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); - - // Start worker - view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); - - seader_scene_read_prepare(seader); - seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolMfClassic); - - seader_credential_clear(seader->credential); - seader->credential->type = SeaderCredentialTypeMifareClassic; - - FURI_LOG_W(TAG, "Start poller"); - nfc_poller_start(seader->poller, seader_worker_poller_callback_mfc, seader); - - seader_blink_start(seader); -} - -bool seader_scene_read_mfc_on_event(void* context, SceneManagerEvent event) { - Seader* seader = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SeaderCustomEventWorkerExit) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); - consumed = true; - } else if(event.event == SeaderCustomEventPollerDetect) { - Popup* popup = seader->popup; - popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop); - consumed = true; - } else if(event.event == SeaderCustomEventPollerSuccess) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_search_and_switch_to_previous_scene( - seader->scene_manager, SeaderSceneSamPresent); - consumed = true; - } - - return consumed; -} - -void seader_scene_read_mfc_on_exit(void* context) { - Seader* seader = context; - seader_scene_read_cleanup(seader); -} diff --git a/scenes/seader_scene_read_picopass.c b/scenes/seader_scene_read_picopass.c deleted file mode 100644 index 519de66..0000000 --- a/scenes/seader_scene_read_picopass.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "../seader_i.h" -#include "seader_scene_read_common.h" -#include - -void seader_scene_read_picopass_on_enter(void* context) { - Seader* seader = context; - dolphin_deed(DolphinDeedNfcRead); - - // Setup view - Popup* popup = seader->popup; - popup_set_header(popup, "Detecting\npicopass\ncard", 68, 30, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); - - // Start worker - view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); - - seader_scene_read_prepare(seader); - seader_credential_clear(seader->credential); - seader->credential->type = SeaderCredentialTypePicopass; - seader->picopass_poller = picopass_poller_alloc(seader->nfc); - picopass_poller_start(seader->picopass_poller, seader_worker_poller_callback_picopass, seader); - - seader_blink_start(seader); -} - -bool seader_scene_read_picopass_on_event(void* context, SceneManagerEvent event) { - Seader* seader = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SeaderCustomEventWorkerExit) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); - consumed = true; - } else if(event.event == SeaderCustomEventPollerDetect) { - Popup* popup = seader->popup; - popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop); - consumed = true; - } else if(event.event == SeaderCustomEventPollerSuccess) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); - consumed = true; - } - - } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_search_and_switch_to_previous_scene( - seader->scene_manager, SeaderSceneSamPresent); - consumed = true; - } - return consumed; -} - -void seader_scene_read_picopass_on_exit(void* context) { - Seader* seader = context; - seader_scene_read_cleanup(seader); -} diff --git a/scenes/seader_scene_sam_present.c b/scenes/seader_scene_sam_present.c index 34e11b1..c216c75 100644 --- a/scenes/seader_scene_sam_present.c +++ b/scenes/seader_scene_sam_present.c @@ -1,13 +1,11 @@ #include "../seader_i.h" enum SubmenuIndex { - SubmenuIndexReadPicopass, - SubmenuIndexRead14a, - SubmenuIndexReadMfc, - SubmenuIndexReadConfigCard, + SubmenuIndexRead, SubmenuIndexSaved, SubmenuIndexAPDURunner, SubmenuIndexSamInfo, SubmenuIndexFwVersion, + SubmenuIndexReadConfigCard, }; static uint8_t fwChecks = 3; @@ -25,23 +23,7 @@ void seader_scene_sam_present_on_update(void* context) { submenu_reset(submenu); submenu_add_item( - submenu, - "Read Picopass", - SubmenuIndexReadPicopass, - seader_scene_sam_present_submenu_callback, - seader); - submenu_add_item( - submenu, - "Read 14443A", - SubmenuIndexRead14a, - seader_scene_sam_present_submenu_callback, - seader); - submenu_add_item( - submenu, - "Read MFC", - SubmenuIndexReadMfc, - seader_scene_sam_present_submenu_callback, - seader); + submenu, "Read HF", SubmenuIndexRead, seader_scene_sam_present_submenu_callback, seader); submenu_add_item( submenu, "Saved", SubmenuIndexSaved, seader_scene_sam_present_submenu_callback, seader); @@ -95,14 +77,9 @@ bool seader_scene_sam_present_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state(seader->scene_manager, SeaderSceneSamPresent, event.event); - if(event.event == SubmenuIndexReadPicopass) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadPicopass); + if(event.event == SubmenuIndexRead) { + scene_manager_next_scene(seader->scene_manager, SeaderSceneRead); consumed = true; - } else if(event.event == SubmenuIndexRead14a) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneRead14a); - consumed = true; - } else if(event.event == SubmenuIndexReadMfc) { - scene_manager_next_scene(seader->scene_manager, SeaderSceneReadMfc); } else if(event.event == SubmenuIndexReadConfigCard) { scene_manager_set_scene_state( seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexReadConfigCard); diff --git a/seader.c b/seader.c index fcdad58..3758792 100644 --- a/seader.c +++ b/seader.c @@ -49,11 +49,8 @@ Seader* seader_alloc() { seader->credential = seader_credential_alloc(); - seader->nfc = nfc_alloc(); - - // Nfc device - seader->nfc_device = nfc_device_alloc(); - nfc_device_set_loading_callback(seader->nfc_device, seader_show_loading_popup, seader); + seader->nfc = NULL; + seader->nfc_device = NULL; // Open GUI record seader->gui = furi_record_open(RECORD_GUI); @@ -104,6 +101,7 @@ Seader* seader_alloc() { plugin_manager_alloc(PLUGIN_APP_ID, PLUGIN_API_VERSION, firmware_api_interface); seader->plugin_wiegand = NULL; + FURI_LOG_I(TAG, "Loading plugins from %s", APP_ASSETS_PATH("plugins")); if(plugin_manager_load_all(seader->plugin_manager, APP_ASSETS_PATH("plugins")) != PluginManagerErrorNone) { FURI_LOG_E(TAG, "Failed to load all libs"); @@ -113,8 +111,9 @@ Seader* seader_alloc() { for(uint32_t i = 0; i < plugin_count; i++) { const PluginWiegand* plugin = plugin_manager_get_ep(seader->plugin_manager, i); - FURI_LOG_I(TAG, "plugin name: %s", plugin->name); + FURI_LOG_I(TAG, "plugin index %lu, name: %s", i, plugin->name); if(strcmp(plugin->name, "Plugin Wiegand") == 0) { + FURI_LOG_I(TAG, "Wiegand plugin found and assigned"); // Have to cast to drop "const" qualifier seader->plugin_wiegand = (PluginWiegand*)plugin; } @@ -137,10 +136,15 @@ void seader_free(Seader* seader) { seader_credential_free(seader->credential); seader->credential = NULL; - nfc_free(seader->nfc); + if(seader->nfc) { + nfc_free(seader->nfc); + seader->nfc = NULL; + } - // Nfc device - nfc_device_free(seader->nfc_device); + if(seader->nfc_device) { + nfc_device_free(seader->nfc_device); + seader->nfc_device = NULL; + } // Submenu view_dispatcher_remove_view(seader->view_dispatcher, SeaderViewMenu); diff --git a/seader_worker.c b/seader_worker.c index 7231d3f..bb3e15a 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -16,6 +16,8 @@ // Forward declaration void seader_send_card_detected(SeaderUartBridge* seader_uart, CardDetails_t* cardDetails); +void seader_worker_reading(Seader* seader); +void seader_worker_poller_conversation(Seader* seader, SeaderPollerContainer* spc); /***************************** Seader Worker API *******************************/ @@ -26,7 +28,6 @@ SeaderWorker* seader_worker_alloc() { seader_worker->thread = furi_thread_alloc_ex("SeaderWorker", 8192, seader_worker_task, seader_worker); seader_worker->messages = furi_message_queue_alloc(3, sizeof(SeaderAPDU)); - seader_worker->mq_mutex = furi_mutex_alloc(FuriMutexTypeNormal); seader_worker->callback = NULL; seader_worker->context = NULL; @@ -43,7 +44,6 @@ void seader_worker_free(SeaderWorker* seader_worker) { furi_thread_free(seader_worker->thread); furi_message_queue_free(seader_worker->messages); - furi_mutex_free(seader_worker->mq_mutex); furi_record_close(RECORD_STORAGE); @@ -63,22 +63,25 @@ void seader_worker_start( furi_assert(seader_worker); furi_assert(uart); + if(furi_thread_get_state(seader_worker->thread) != FuriThreadStateStopped) { + seader_worker_stop(seader_worker); + } + seader_worker->stage = SeaderPollerEventTypeCardDetect; seader_worker->callback = callback; seader_worker->context = context; seader_worker->uart = uart; - seader_worker_change_state(seader_worker, state); + seader_worker->state = state; furi_thread_start(seader_worker->thread); } void seader_worker_stop(SeaderWorker* seader_worker) { furi_assert(seader_worker); - if(seader_worker->state == SeaderWorkerStateBroken || - seader_worker->state == SeaderWorkerStateReady) { + if(furi_thread_get_state(seader_worker->thread) == FuriThreadStateStopped) { return; } - seader_worker_change_state(seader_worker, SeaderWorkerStateStop); + seader_worker->state = SeaderWorkerStateStop; furi_thread_join(seader_worker->thread); } @@ -114,10 +117,7 @@ void seader_worker_reset_poller_session(SeaderWorker* seader_worker) { seader_worker->stage, furi_message_queue_get_count(seader_worker->messages)); - if(furi_mutex_acquire(seader_worker->mq_mutex, FuriWaitForever) == FuriStatusOk) { - furi_message_queue_reset(seader_worker->messages); - furi_mutex_release(seader_worker->mq_mutex); - } + furi_message_queue_reset(seader_worker->messages); seader_worker->stage = SeaderPollerEventTypeCardDetect; } @@ -163,10 +163,7 @@ bool seader_process_success_response(Seader* seader, uint8_t* apdu, size_t len) seaderApdu.len = len; memcpy(seaderApdu.buf, apdu, len); - if(furi_mutex_acquire(seader_worker->mq_mutex, FuriWaitForever) == FuriStatusOk) { - furi_message_queue_put(seader_worker->messages, &seaderApdu, FuriWaitForever); - furi_mutex_release(seader_worker->mq_mutex); - } + furi_message_queue_put(seader_worker->messages, &seaderApdu, FuriWaitForever); } } return true; @@ -239,33 +236,31 @@ void seader_worker_virtual_credential(Seader* seader) { uint8_t dead_loops = 20; while(running) { - if(furi_mutex_acquire(seader_worker->mq_mutex, 0) == FuriStatusOk) { - uint32_t count = furi_message_queue_get_count(seader_worker->messages); - if(count > 0) { - FURI_LOG_I(TAG, "Dequeue SAM message [%ld messages]", count); + uint32_t count = furi_message_queue_get_count(seader_worker->messages); + if(count > 0) { + FURI_LOG_I(TAG, "Dequeue SAM message [%ld messages]", count); - SeaderAPDU seaderApdu = {}; - FuriStatus status = - furi_message_queue_get(seader_worker->messages, &seaderApdu, FuriWaitForever); - if(status != FuriStatusOk) { - FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status); - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventWorkerExit); - } - if(seader_process_success_response_i( - seader, seaderApdu.buf, seaderApdu.len, true, NULL)) { - // no-op - } else { - FURI_LOG_I(TAG, "Response false"); - running = false; - } + SeaderAPDU seaderApdu = {}; + FuriStatus status = + furi_message_queue_get(seader_worker->messages, &seaderApdu, FuriWaitForever); + if(status != FuriStatusOk) { + FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status); + view_dispatcher_send_custom_event( + seader->view_dispatcher, SeaderCustomEventWorkerExit); + } + if(seader_process_success_response_i( + seader, seaderApdu.buf, seaderApdu.len, true, NULL)) { + // no-op + } else { + FURI_LOG_I(TAG, "Response false"); + running = false; } - furi_mutex_release(seader_worker->mq_mutex); } else { dead_loops--; running = (dead_loops > 0); FURI_LOG_D( TAG, "Dead loops: %d -> Running: %s", dead_loops, running ? "true" : "false"); + if(running) furi_delay_ms(10); // Don't tight loop if empty } running = (seader_worker->stage != SeaderPollerEventTypeComplete); } @@ -292,50 +287,163 @@ int32_t seader_worker_task(void* context) { FURI_LOG_D(TAG, "APDU Runner"); seader_apdu_runner_init(seader); return 0; + } else if(seader_worker->state == SeaderWorkerStateReading) { + FURI_LOG_D(TAG, "Reading mode started"); + seader_worker_reading(seader); } seader_worker_change_state(seader_worker, SeaderWorkerStateReady); return 0; } -void seader_worker_poller_conversation(Seader* seader, SeaderPollerContainer* spc) { +void seader_worker_reading(Seader* seader) { SeaderWorker* seader_worker = seader->worker; - seader_trace( - TAG, - "conversation stage=%d queued=%ld", - seader_worker->stage, - furi_message_queue_get_count(seader_worker->messages)); + FURI_LOG_I(TAG, "Reading loop started"); - if(furi_mutex_acquire(seader_worker->mq_mutex, 0) == FuriStatusOk) { - furi_thread_set_current_priority(FuriThreadPriorityHighest); - uint32_t count = furi_message_queue_get_count(seader_worker->messages); - if(count > 0) { - FURI_LOG_I(TAG, "Dequeue SAM message [%ld messages]", count); - seader_trace(TAG, "dequeue count=%ld", count); + seader->nfc = nfc_alloc(); + seader->nfc_device = nfc_device_alloc(); + nfc_device_set_loading_callback(seader->nfc_device, seader_show_loading_popup, seader); - SeaderAPDU seaderApdu = {}; - FuriStatus status = - furi_message_queue_get(seader_worker->messages, &seaderApdu, FuriWaitForever); - if(status != FuriStatusOk) { - FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status); - seader_worker->stage = SeaderPollerEventTypeComplete; - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventWorkerExit); - } + while(seader_worker->state == SeaderWorkerStateReading) { + bool detected = false; + SeaderPollerEventType result_stage = SeaderPollerEventTypeFail; - if(seader_process_success_response_i( - seader, seaderApdu.buf, seaderApdu.len, true, spc)) { - // no-op + // 1. HF Discovery (ISO14443-4A) + NfcPoller* poller_detect = nfc_poller_alloc(seader->nfc, NfcProtocolIso14443_4a); + if(nfc_poller_detect(poller_detect)) { + FURI_LOG_I(TAG, "Detected ISO14443-4A card"); + nfc_poller_free(poller_detect); + poller_detect = NULL; + + 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); + detected = true; + } else { + nfc_poller_free(poller_detect); + poller_detect = NULL; + } + + // 2. HF Discovery (Mifare Classic) + if(!detected) { + poller_detect = nfc_poller_alloc(seader->nfc, NfcProtocolMfClassic); + if(nfc_poller_detect(poller_detect)) { + FURI_LOG_I(TAG, "Detected Mifare Classic card"); + nfc_poller_free(poller_detect); + poller_detect = NULL; + + 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); + detected = true; } else { - FURI_LOG_I(TAG, "Response false"); - view_dispatcher_send_custom_event( - seader->view_dispatcher, SeaderCustomEventWorkerExit); - seader_worker->stage = SeaderPollerEventTypeComplete; + nfc_poller_free(poller_detect); + poller_detect = NULL; } } - furi_mutex_release(seader_worker->mq_mutex); - } else { - furi_thread_set_current_priority(FuriThreadPriorityLowest); + + // 3. HF Discovery (Picopass/iCLASS) + if(!detected) { + 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); + + // Wait up to 100ms for picopass detection + for(int i = 0; i < 10; i++) { + if(seader_worker->stage != SeaderPollerEventTypeCardDetect) { + detected = true; + break; + } + furi_delay_ms(10); + } + if(!detected) { + picopass_poller_stop(seader->picopass_poller); + picopass_poller_free(seader->picopass_poller); + seader->picopass_poller = NULL; + } + } + + if(detected) { + // Wait for conversation to finish + while(seader_worker->stage != SeaderPollerEventTypeComplete && + seader_worker->stage != SeaderPollerEventTypeFail && + seader_worker->state == SeaderWorkerStateReading) { + // The conversation is handled by the poller callback thread. + // We just wait here for it to finish. + furi_delay_ms(10); + } + result_stage = seader_worker->stage; + + // Cleanup poller + if(seader->poller) { + nfc_poller_stop(seader->poller); + nfc_poller_free(seader->poller); + seader->poller = NULL; + } + if(seader->picopass_poller) { + picopass_poller_stop(seader->picopass_poller); + picopass_poller_free(seader->picopass_poller); + seader->picopass_poller = NULL; + } + + if(result_stage == SeaderPollerEventTypeComplete) { + // Notify UI of success + if(seader_worker->callback) { + seader_worker->callback(SeaderWorkerEventSuccess, seader_worker->context); + } + break; + } + } + + if(seader_worker->state == SeaderWorkerStateReading) { + furi_delay_ms(50); + } + } + + nfc_free(seader->nfc); + seader->nfc = NULL; + nfc_device_free(seader->nfc_device); + seader->nfc_device = NULL; + + FURI_LOG_I(TAG, "Reading loop stopped"); +} + +void seader_worker_poller_conversation(Seader* seader, SeaderPollerContainer* spc) { + SeaderWorker* seader_worker = seader->worker; + + furi_thread_set_current_priority(FuriThreadPriorityHighest); + + while(seader_worker->stage == SeaderPollerEventTypeConversation && + seader_worker->state == SeaderWorkerStateReading) { + SeaderAPDU seaderApdu = {}; + // Short wait for SAM message + FuriStatus status = furi_message_queue_get(seader_worker->messages, &seaderApdu, 100); + + if(status == FuriStatusOk) { + FURI_LOG_D(TAG, "Dequeue SAM message [%d bytes]", seaderApdu.len); + if(seader_process_success_response_i( + seader, seaderApdu.buf, seaderApdu.len, true, spc)) { + // message was processed, loop again to see if SAM has more to say + } else { + FURI_LOG_I(TAG, "Response false, ending conversation"); + seader_worker->stage = SeaderPollerEventTypeComplete; + view_dispatcher_send_custom_event( + seader->view_dispatcher, SeaderCustomEventWorkerExit); + } + } else if(status == FuriStatusErrorTimeout) { + // No message yet, keep looping to stay in callback + // This is "properly idling" while waiting for SAM + } else { + FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status); + seader_worker->stage = SeaderPollerEventTypeFail; + view_dispatcher_send_custom_event( + seader->view_dispatcher, SeaderCustomEventWorkerExit); + } } } @@ -416,6 +524,11 @@ NfcCommand seader_worker_poller_callback_iso14443_4a(NfcGenericEvent event, void free(ats); + 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; @@ -485,6 +598,12 @@ NfcCommand seader_worker_poller_callback_mfc(NfcGenericEvent event, void* contex 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) { @@ -534,6 +653,12 @@ NfcCommand seader_worker_poller_callback_picopass(PicopassPollerEvent event, voi } 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) { diff --git a/seader_worker_i.h b/seader_worker_i.h index 4716f05..62df0c2 100644 --- a/seader_worker_i.h +++ b/seader_worker_i.h @@ -28,8 +28,6 @@ struct SeaderWorker { Storage* storage; uint8_t sam_version[2]; FuriMessageQueue* messages; - FuriMutex* mq_mutex; - SeaderUartBridge* uart; SeaderWorkerCallback callback; void* context;