Merge pull request #31 from cindersocket/feat-unify-hf
Some checks failed
FAP: Build and lint / ufbt: Build for release branch (push) Failing after 4m48s

Unify HF reading and PACS parsing
This commit is contained in:
Eric Betts
2026-03-08 17:04:11 -07:00
committed by GitHub
12 changed files with 320 additions and 321 deletions

159
sam_api.c
View File

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

View File

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

View File

@@ -2,28 +2,31 @@
#include "seader_scene_read_common.h"
#include <dolphin/dolphin.h>
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);
}

View File

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

View File

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

View File

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

View File

@@ -1,59 +0,0 @@
#include "../seader_i.h"
#include "seader_scene_read_common.h"
#include <dolphin/dolphin.h>
#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);
}

View File

@@ -1,54 +0,0 @@
#include "../seader_i.h"
#include "seader_scene_read_common.h"
#include <dolphin/dolphin.h>
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);
}

View File

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

View File

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

View File

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

View File

@@ -28,8 +28,6 @@ struct SeaderWorker {
Storage* storage;
uint8_t sam_version[2];
FuriMessageQueue* messages;
FuriMutex* mq_mutex;
SeaderUartBridge* uart;
SeaderWorkerCallback callback;
void* context;