Stabilize HF plugin lifecycle and SAM cleanup

This commit is contained in:
CinderSocket
2026-03-24 13:37:57 -07:00
parent 54e121f71a
commit 7f551fc6ec
12 changed files with 336 additions and 111 deletions
+15 -5
View File
@@ -1717,8 +1717,6 @@ NfcCommand seader_worker_card_detect(
FURI_LOG_E(TAG, "Failed to encode CSN");
return NfcCommandStop;
}
OCTET_STRING_t sak_string = {.buf = &sak, .size = 1};
OCTET_STRING_t ats_string = {.buf = ats, .size = ats_len};
uint8_t protocol_bytes[] = {0x00, 0x00};
// this won't hold true for Seos cards, but then we won't see the SIO from Seos cards anyway
@@ -1742,9 +1740,16 @@ NfcCommand seader_worker_card_detect(
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_CardDetails, &cardDetails);
return NfcCommandStop;
}
cardDetails.sak = &sak_string;
cardDetails.sak = calloc(1, sizeof(*cardDetails.sak));
cardDetails.atsOrAtqbOrAtr = calloc(1, sizeof(*cardDetails.atsOrAtqbOrAtr));
if(!cardDetails.sak || !cardDetails.atsOrAtqbOrAtr ||
OCTET_STRING_fromBuf(cardDetails.sak, (const char*)&sak, 1) != 0 ||
OCTET_STRING_fromBuf(cardDetails.atsOrAtqbOrAtr, (const char*)ats, ats_len) != 0) {
FURI_LOG_E(TAG, "Failed to encode 14A optional card details");
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_CardDetails, &cardDetails);
return NfcCommandStop;
}
// TODO: Update asn1 to change atqa to ats
cardDetails.atsOrAtqbOrAtr = &ats_string;
} else if(uid_len == 8) { // picopass
protocol_bytes[1] = FrameProtocol_iclass;
if(OCTET_STRING_fromBuf(&cardDetails.protocol, (const char*)protocol_bytes, sizeof(protocol_bytes)) != 0) {
@@ -1759,7 +1764,12 @@ NfcCommand seader_worker_card_detect(
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_CardDetails, &cardDetails);
return NfcCommandStop;
}
cardDetails.sak = &sak_string;
cardDetails.sak = calloc(1, sizeof(*cardDetails.sak));
if(!cardDetails.sak || OCTET_STRING_fromBuf(cardDetails.sak, (const char*)&sak, 1) != 0) {
FURI_LOG_E(TAG, "Failed to encode MFC SAK");
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_CardDetails, &cardDetails);
return NfcCommandStop;
}
}
seader_sam_set_state(
+1 -2
View File
@@ -88,8 +88,7 @@ bool seader_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
seader->scene_manager, SeaderSceneSamPresent);
consumed = scene_manager_previous_scene(seader->scene_manager);
}
return consumed;
+11 -9
View File
@@ -39,6 +39,11 @@ bool seader_scene_read_on_event(void* context, SceneManagerEvent event) {
if(event.event == SeaderCustomEventWorkerExit) {
scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
consumed = true;
} else if(event.event == SeaderWorkerEventFail) {
scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
consumed = true;
} else if(event.event == SeaderWorkerEventHfTeardownComplete) {
consumed = seader_hf_finish_teardown_action(seader);
} else if(event.event == SeaderCustomEventPollerDetect) {
Popup* popup = seader->popup;
popup_set_header(popup, "DON'T\nMOVE", 68, 30, AlignLeft, AlignTop);
@@ -51,13 +56,9 @@ bool seader_scene_read_on_event(void* context, SceneManagerEvent event) {
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
seader_hf_mode_set_selected_read_type(seader, SeaderCredentialTypeNone);
seader_hf_mode_clear_detected_types(seader);
seader_hf_mode_deactivate(seader);
seader_hf_plugin_release(seader);
scene_manager_search_and_switch_to_previous_scene(
seader->scene_manager, SeaderSceneSamPresent);
consumed = true;
seader_scene_read_cleanup(seader);
seader->hf_teardown_skip_read_cleanup = true;
consumed = seader_hf_request_teardown(seader, SeaderHfTeardownActionSamPresent);
}
return consumed;
@@ -65,8 +66,9 @@ bool seader_scene_read_on_event(void* context, SceneManagerEvent event) {
void seader_scene_read_on_exit(void* context) {
Seader* seader = context;
if(seader->worker) {
seader_worker_stop(seader->worker);
if(seader->hf_teardown_skip_read_cleanup) {
seader->hf_teardown_skip_read_cleanup = false;
return;
}
seader_scene_read_cleanup(seader);
seader_worker_release(seader);
+9 -8
View File
@@ -20,7 +20,6 @@ void seader_scene_read_card_success_on_enter(void* context) {
SeaderCredential* credential = seader->credential;
PluginWiegand* plugin = seader_wiegand_plugin_acquire(seader) ? seader->plugin_wiegand : NULL;
Widget* widget = seader->widget;
seader_hf_mode_deactivate(seader);
// Use reusable strings instead of allocating new ones
FuriString* type_str = seader->temp_string1;
@@ -134,23 +133,25 @@ bool seader_scene_read_card_success_on_event(void* context, SceneManagerEvent ev
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(seader->scene_manager);
consumed = seader_hf_request_teardown(seader, SeaderHfTeardownActionRestartRead);
} else if(event.event == GuiButtonTypeRight) {
if(seader->credential->bit_length > 0) {
scene_manager_next_scene(seader->scene_manager, SeaderSceneCardMenu);
} else {
scene_manager_search_and_switch_to_previous_scene(
seader->scene_manager, SeaderSceneSamPresent);
consumed =
seader_hf_request_teardown(seader, SeaderHfTeardownActionSamPresent);
}
if(seader->credential->bit_length > 0) {
consumed = true;
}
consumed = true;
} else if(event.event == GuiButtonTypeCenter) {
scene_manager_next_scene(seader->scene_manager, SeaderSceneFormats);
consumed = true;
} else if(event.event == SeaderWorkerEventHfTeardownComplete) {
consumed = seader_hf_finish_teardown_action(seader);
}
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_search_and_switch_to_previous_scene(
seader->scene_manager, SeaderSceneSamPresent);
consumed = true;
consumed = seader_hf_request_teardown(seader, SeaderHfTeardownActionSamPresent);
}
return consumed;
}
+4 -8
View File
@@ -44,7 +44,9 @@ bool seader_scene_read_card_type_on_event(void* context, SceneManagerEvent event
if(event.type == SceneManagerEventTypeCustom) {
const SeaderCredentialType type = event.event;
if(type == SeaderCredentialType14A || type == SeaderCredentialTypeMifareClassic ||
if(event.event == SeaderWorkerEventHfTeardownComplete) {
consumed = seader_hf_finish_teardown_action(seader);
} else if(type == SeaderCredentialType14A || type == SeaderCredentialTypeMifareClassic ||
type == SeaderCredentialTypePicopass) {
seader_hf_mode_set_selected_read_type(seader, type);
seader_hf_mode_clear_detected_types(seader);
@@ -52,13 +54,7 @@ bool seader_scene_read_card_type_on_event(void* context, SceneManagerEvent event
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
seader_hf_mode_set_selected_read_type(seader, SeaderCredentialTypeNone);
seader_hf_mode_clear_detected_types(seader);
seader_hf_mode_deactivate(seader);
seader_hf_plugin_release(seader);
scene_manager_search_and_switch_to_previous_scene(
seader->scene_manager, SeaderSceneSamPresent);
consumed = true;
consumed = seader_hf_request_teardown(seader, SeaderHfTeardownActionSamPresent);
}
return consumed;
+4
View File
@@ -42,6 +42,10 @@ bool seader_scene_read_config_card_on_event(void* context, SceneManagerEvent eve
if(event.event == SeaderCustomEventWorkerExit || event.event == SeaderWorkerEventSuccess) {
scene_manager_next_scene(seader->scene_manager, SeaderSceneReadConfigCardSuccess);
consumed = true;
} else if(event.event == SeaderWorkerEventFail) {
scene_manager_search_and_switch_to_previous_scene(
seader->scene_manager, SeaderSceneSamPresent);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_search_and_switch_to_previous_scene(
+27 -28
View File
@@ -9,14 +9,9 @@ enum SubmenuIndex {
static uint8_t fwChecks = 3;
void seader_scene_sam_present_submenu_callback(void* context, uint32_t index) {
Seader* seader = context;
view_dispatcher_send_custom_event(seader->view_dispatcher, index);
}
void seader_scene_sam_present_on_update(void* context) {
Seader* seader = context;
void seader_scene_sam_present_submenu_callback(void* context, uint32_t index);
static void seader_scene_sam_present_rebuild_menu(Seader* seader, uint32_t selected_item) {
Submenu* submenu = seader->submenu;
submenu_reset(submenu);
@@ -42,29 +37,32 @@ void seader_scene_sam_present_on_update(void* context) {
seader_scene_sam_present_submenu_callback,
seader);
}
submenu_add_item(
submenu,
seader->sam_key_label,
SubmenuIndexSamInfo,
seader_scene_sam_present_submenu_callback,
seader);
if(seader->sam_version[0] != 0 && seader->sam_version[1] != 0) {
submenu_add_item(
submenu,
seader->sam_key_label,
SubmenuIndexSamInfo,
seader_scene_sam_present_submenu_callback,
seader);
fwChecks = 0;
} else {
submenu_add_item(
submenu,
seader->sam_key_label,
SubmenuIndexSamInfo,
seader_scene_sam_present_submenu_callback,
seader);
}
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(seader->scene_manager, SeaderSceneSamPresent));
submenu_set_selected_item(submenu, selected_item);
view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewMenu);
}
void seader_scene_sam_present_submenu_callback(void* context, uint32_t index) {
Seader* seader = context;
view_dispatcher_send_custom_event(seader->view_dispatcher, index);
}
void seader_scene_sam_present_on_update(void* context) {
Seader* seader = context;
seader_scene_sam_present_rebuild_menu(
seader, scene_manager_get_scene_state(seader->scene_manager, SeaderSceneSamPresent));
}
void seader_scene_sam_present_on_enter(void* context) {
seader_scene_sam_present_on_update(context);
}
@@ -102,18 +100,19 @@ bool seader_scene_sam_present_on_event(void* context, SceneManagerEvent event) {
seader->scene_manager, SeaderSceneSamPresent, event.event);
scene_manager_next_scene(seader->scene_manager, SeaderSceneAPDURunner);
consumed = true;
} else if(event.event == SeaderWorkerEventHfTeardownComplete) {
consumed = seader_hf_finish_teardown_action(seader);
} else if(event.event == SeaderCustomEventSamStatusUpdated) {
seader_scene_sam_present_on_update(context);
seader_scene_sam_present_rebuild_menu(
seader, submenu_get_selected_item(seader->submenu));
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_stop(seader->scene_manager);
view_dispatcher_stop(seader->view_dispatcher);
consumed = true;
consumed = seader_hf_request_teardown(seader, SeaderHfTeardownActionStopApp);
} else if(event.type == SceneManagerEventTypeTick) {
if(fwChecks > 0 && seader->sam_version[0] != 0 && seader->sam_version[1] != 0) {
fwChecks--;
seader_scene_sam_present_on_update(context);
seader_scene_sam_present_rebuild_menu(seader, submenu_get_selected_item(seader->submenu));
}
}
+191 -45
View File
@@ -7,10 +7,13 @@
#define SEADER_HF_PLUGIN_PATH APP_ASSETS_PATH("plugins/plugin_hf.fal")
typedef struct {
bool done;
bool detected;
volatile bool done;
volatile bool detected;
} SeaderHfPicopassDetectContext;
static void seader_hf_worker_event_callback(uint32_t event, void* context);
static void seader_hf_teardown_blocking(Seader* seader);
static NfcCommand seader_hf_picopass_detect_callback(PicopassPollerEvent event, void* context) {
SeaderHfPicopassDetectContext* detect_context = context;
@@ -215,13 +218,18 @@ static bool seader_hf_plugin_picopass_detect(void* host_ctx) {
PicopassPoller* poller = picopass_poller_alloc(seader->nfc);
SeaderHfPicopassDetectContext detect_context = {0};
if(!poller) {
FURI_LOG_W(TAG, "Failed to allocate Picopass detect poller");
return false;
}
picopass_poller_start(poller, seader_hf_picopass_detect_callback, &detect_context);
for(uint8_t i = 0; i < 10 && !detect_context.done; i++) {
furi_delay_ms(10);
}
detected = detect_context.detected;
picopass_poller_stop(poller);
detected = detect_context.detected;
picopass_poller_free(poller);
return detected;
@@ -343,6 +351,15 @@ static const PluginHfHostApi seader_hf_plugin_host_api = {
.set_read_error = seader_hf_plugin_set_read_error,
};
static void seader_hf_worker_event_callback(uint32_t event, void* context) {
Seader* seader = context;
if(!seader || !seader->view_dispatcher) {
return;
}
view_dispatcher_send_custom_event(seader->view_dispatcher, event);
}
bool seader_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
Seader* seader = context;
@@ -384,8 +401,8 @@ Seader* seader_alloc() {
seader_uhf_status_label_format(
false, false, false, false, seader->uhf_status_label, sizeof(seader->uhf_status_label));
seader_uhf_snmp_probe_init(&seader->snmp_probe);
seader->nfc = NULL;
seader->nfc_device = NULL;
seader->nfc = nfc_alloc();
seader->nfc_device = seader->nfc ? nfc_device_alloc() : NULL;
seader->scratch.offset = 0U;
seader->scratch.high_water = 0U;
seader->hf_mode = NULL;
@@ -405,8 +422,13 @@ Seader* seader_alloc() {
seader->credential = seader_credential_alloc();
seader->nfc = NULL;
seader->nfc_device = NULL;
if(!seader->nfc || !seader->nfc_device) {
FURI_LOG_W(
TAG,
"HF host NFC objects unavailable at startup nfc=%p device=%p",
seader->nfc,
seader->nfc_device);
}
// Open GUI record
seader->gui = furi_record_open(RECORD_GUI);
@@ -458,6 +480,9 @@ Seader* seader_alloc() {
seader->hf_plugin_manager = NULL;
seader->plugin_hf = NULL;
seader->hf_plugin_ctx = NULL;
seader->hf_session_state = SeaderHfSessionStateUnloaded;
seader->hf_teardown_action = SeaderHfTeardownActionNone;
seader->hf_teardown_skip_read_cleanup = false;
return seader;
}
@@ -469,21 +494,31 @@ void seader_free(Seader* seader) {
furi_hal_power_disable_otg();
}
seader_uart_free(seader->uart);
seader->uart = NULL;
seader_hf_teardown_blocking(seader);
seader_hf_mode_deactivate(seader);
seader_worker_release(seader);
if(seader->worker) {
seader_worker_free(seader->worker);
seader->worker = NULL;
}
seader_credential_free(seader->credential);
seader->credential = NULL;
seader_wiegand_plugin_release(seader);
if(seader->nfc_device) {
nfc_device_free(seader->nfc_device);
seader->nfc_device = NULL;
}
if(seader->nfc) {
nfc_free(seader->nfc);
seader->nfc = NULL;
}
if(seader->nfc_device) {
nfc_device_free(seader->nfc_device);
seader->nfc_device = NULL;
}
seader_uart_free(seader->uart);
seader->uart = NULL;
seader_credential_free(seader->credential);
seader->credential = NULL;
// Submenu
view_dispatcher_remove_view(seader->view_dispatcher, SeaderViewMenu);
@@ -516,13 +551,6 @@ void seader_free(Seader* seader) {
furi_string_free(seader->temp_string3);
furi_string_free(seader->temp_string4);
seader_hf_mode_deactivate(seader);
seader_worker_release(seader);
if(seader->worker) {
seader_worker_free(seader->worker);
seader->worker = NULL;
}
// View Dispatcher
view_dispatcher_free(seader->view_dispatcher);
@@ -537,9 +565,6 @@ void seader_free(Seader* seader) {
furi_record_close(RECORD_NOTIFICATION);
seader->notifications = NULL;
seader_wiegand_plugin_release(seader);
seader_hf_plugin_release(seader);
free(seader);
}
@@ -651,26 +676,36 @@ void seader_wiegand_plugin_release(Seader* seader) {
bool seader_hf_plugin_acquire(Seader* seader) {
furi_assert(seader);
if(seader->hf_session_state == SeaderHfSessionStateTearingDown) {
FURI_LOG_W(TAG, "Reject HF plugin acquire during teardown");
return false;
}
if(seader->plugin_hf && seader->hf_plugin_ctx) {
if(seader->hf_session_state == SeaderHfSessionStateUnloaded) {
seader->hf_session_state = SeaderHfSessionStateLoaded;
}
return true;
}
if(!seader->nfc) {
seader->nfc = nfc_alloc();
if(!seader->nfc) {
FURI_LOG_E(TAG, "Failed to allocate host NFC");
return false;
}
if(seader->hf_plugin_manager || seader->plugin_hf || seader->hf_plugin_ctx) {
FURI_LOG_W(
TAG,
"Normalize partial HF session manager=%p plugin=%p ctx=%p state=%d",
(void*)seader->hf_plugin_manager,
(void*)seader->plugin_hf,
seader->hf_plugin_ctx,
seader->hf_session_state);
seader_hf_plugin_release(seader);
}
if(!seader->nfc_device) {
seader->nfc_device = nfc_device_alloc();
if(!seader->nfc_device) {
FURI_LOG_E(TAG, "Failed to allocate host NFC device");
nfc_free(seader->nfc);
seader->nfc = NULL;
return false;
}
if(!seader->nfc || !seader->nfc_device) {
FURI_LOG_E(
TAG,
"Host NFC objects unavailable nfc=%p device=%p",
seader->nfc,
seader->nfc_device);
return false;
}
if(!seader->hf_plugin_manager) {
@@ -688,6 +723,9 @@ bool seader_hf_plugin_acquire(Seader* seader) {
FURI_LOG_E(TAG, "Failed to load HF plugin");
plugin_manager_free(seader->hf_plugin_manager);
seader->hf_plugin_manager = NULL;
seader->plugin_hf = NULL;
seader->hf_plugin_ctx = NULL;
seader->hf_session_state = SeaderHfSessionStateUnloaded;
return false;
}
@@ -697,6 +735,8 @@ bool seader_hf_plugin_acquire(Seader* seader) {
FURI_LOG_E(TAG, "Failed to resolve HF plugin entry point");
plugin_manager_free(seader->hf_plugin_manager);
seader->hf_plugin_manager = NULL;
seader->hf_plugin_ctx = NULL;
seader->hf_session_state = SeaderHfSessionStateUnloaded;
return false;
}
@@ -706,13 +746,44 @@ bool seader_hf_plugin_acquire(Seader* seader) {
plugin_manager_free(seader->hf_plugin_manager);
seader->hf_plugin_manager = NULL;
seader->plugin_hf = NULL;
seader->hf_session_state = SeaderHfSessionStateUnloaded;
return false;
}
seader->hf_session_state = SeaderHfSessionStateLoaded;
FURI_LOG_I(TAG, "HF plugin loaded: %s", seader->plugin_hf->name);
return true;
}
static bool seader_hf_has_runtime(const Seader* seader) {
return seader &&
(seader->hf_plugin_manager || seader->plugin_hf || seader->hf_plugin_ctx ||
seader->poller || seader->picopass_poller);
}
static void seader_hf_teardown_blocking(Seader* seader) {
if(!seader || !seader_hf_has_runtime(seader)) {
return;
}
if(!seader_worker_acquire(seader) || !seader->worker || !seader->uart) {
FURI_LOG_W(TAG, "HF blocking teardown fallback");
seader_hf_plugin_release(seader);
return;
}
seader->hf_session_state = SeaderHfSessionStateTearingDown;
seader_worker_stop(seader->worker);
FURI_LOG_I(TAG, "HF teardown blocking");
seader_worker_start(
seader->worker,
SeaderWorkerStateHfTeardown,
seader->uart,
NULL,
seader);
seader_worker_join(seader->worker);
}
void seader_hf_plugin_release(Seader* seader) {
furi_assert(seader);
@@ -727,23 +798,98 @@ void seader_hf_plugin_release(Seader* seader) {
seader->hf_plugin_ctx = NULL;
seader->plugin_hf = NULL;
if(seader->poller) {
FURI_LOG_I(TAG, "Stopping host NFC poller");
nfc_poller_stop(seader->poller);
nfc_poller_free(seader->poller);
seader->poller = NULL;
}
if(seader->picopass_poller) {
FURI_LOG_I(TAG, "Stopping host Picopass poller");
picopass_poller_stop(seader->picopass_poller);
picopass_poller_free(seader->picopass_poller);
seader->picopass_poller = NULL;
}
if(seader->hf_plugin_manager) {
FURI_LOG_I(TAG, "Unloading HF plugin");
plugin_manager_free(seader->hf_plugin_manager);
seader->hf_plugin_manager = NULL;
}
if(seader->nfc_device) {
nfc_device_free(seader->nfc_device);
seader->nfc_device = NULL;
seader->hf_session_state = SeaderHfSessionStateUnloaded;
}
bool seader_hf_finish_teardown_action(Seader* seader) {
if(!seader) {
return false;
}
if(seader->nfc) {
nfc_free(seader->nfc);
seader->nfc = NULL;
FURI_LOG_I(TAG, "HF teardown complete action=%d", seader->hf_teardown_action);
seader_show_loading_popup(seader, false);
seader->hf_teardown_skip_read_cleanup = false;
seader_hf_mode_set_selected_read_type(seader, SeaderCredentialTypeNone);
seader_hf_mode_clear_detected_types(seader);
seader_hf_mode_deactivate(seader);
const SeaderHfTeardownAction action = seader->hf_teardown_action;
seader->hf_teardown_action = SeaderHfTeardownActionNone;
switch(action) {
case SeaderHfTeardownActionSamPresent:
return scene_manager_search_and_switch_to_another_scene(
seader->scene_manager, SeaderSceneSamPresent);
case SeaderHfTeardownActionRestartRead:
scene_manager_next_scene(seader->scene_manager, SeaderSceneRead);
return true;
case SeaderHfTeardownActionStopApp:
scene_manager_stop(seader->scene_manager);
view_dispatcher_stop(seader->view_dispatcher);
return true;
case SeaderHfTeardownActionNone:
default:
return false;
}
}
bool seader_hf_request_teardown(Seader* seader, SeaderHfTeardownAction action) {
furi_assert(seader);
FURI_LOG_I(
TAG,
"HF teardown requested action=%d state=%d worker_state=%d",
action,
seader->hf_session_state,
seader->worker ? seader_worker_get_state(seader->worker) : -1);
seader->hf_teardown_action = action;
if(!seader_hf_has_runtime(seader)) {
seader->hf_session_state = SeaderHfSessionStateUnloaded;
return seader_hf_finish_teardown_action(seader);
}
if(!seader_worker_acquire(seader)) {
return seader_hf_finish_teardown_action(seader);
}
if(seader->hf_session_state == SeaderHfSessionStateTearingDown ||
(seader->worker &&
seader_worker_get_state(seader->worker) == SeaderWorkerStateHfTeardown)) {
return true;
}
seader->hf_session_state = SeaderHfSessionStateTearingDown;
seader_worker_stop(seader->worker);
seader_worker_start(
seader->worker,
SeaderWorkerStateHfTeardown,
seader->uart,
seader_hf_worker_event_callback,
seader);
return true;
}
bool seader_worker_acquire(Seader* seader) {
furi_assert(seader);
+16
View File
@@ -6,6 +6,20 @@
typedef struct Seader Seader;
typedef struct SeaderPollerContainer SeaderPollerContainer;
typedef enum {
SeaderHfSessionStateUnloaded,
SeaderHfSessionStateLoaded,
SeaderHfSessionStateActive,
SeaderHfSessionStateTearingDown,
} SeaderHfSessionState;
typedef enum {
SeaderHfTeardownActionNone,
SeaderHfTeardownActionSamPresent,
SeaderHfTeardownActionRestartRead,
SeaderHfTeardownActionStopApp,
} SeaderHfTeardownAction;
bool seader_worker_acquire(Seader* seader);
void seader_worker_release(Seader* seader);
void seader_scratch_reset(Seader* seader);
@@ -14,3 +28,5 @@ bool seader_wiegand_plugin_acquire(Seader* seader);
void seader_wiegand_plugin_release(Seader* seader);
bool seader_hf_plugin_acquire(Seader* seader);
void seader_hf_plugin_release(Seader* seader);
bool seader_hf_request_teardown(Seader* seader, SeaderHfTeardownAction action);
bool seader_hf_finish_teardown_action(Seader* seader);
+3
View File
@@ -187,6 +187,9 @@ struct Seader {
PluginManager* hf_plugin_manager;
PluginHf* plugin_hf;
void* hf_plugin_ctx;
SeaderHfSessionState hf_session_state;
SeaderHfTeardownAction hf_teardown_action;
bool hf_teardown_skip_read_cleanup;
APDULog* apdu_log;
SeaderAPDURunnerContext apdu_runner_ctx;
+52 -6
View File
@@ -18,11 +18,33 @@
void seader_send_card_detected(SeaderUartBridge* seader_uart, CardDetails_t* cardDetails);
void seader_worker_reading(Seader* seader);
static void seader_worker_release_hf_session(Seader* seader) {
if(!seader) {
return;
}
seader_hf_plugin_release(seader);
if(seader->worker) {
seader_worker_reset_poller_session(seader->worker);
}
}
typedef struct {
bool done;
bool detected;
volatile bool done;
volatile bool detected;
} SeaderPicopassDetectContext;
static void seader_worker_clear_active_card(Seader* seader, const char* reason) {
if(!seader) {
return;
}
if(seader_sam_has_active_card(seader)) {
FURI_LOG_I(TAG, "Clear active SAM card (%s)", reason ? reason : "worker");
seader_send_no_card_detected(seader);
}
}
static NfcCommand
seader_worker_picopass_detect_callback(PicopassPollerEvent event, void* context) {
SeaderPicopassDetectContext* detect_context = context;
@@ -45,14 +67,19 @@ static bool seader_worker_detect_picopass(Nfc* nfc) {
PicopassPoller* poller = picopass_poller_alloc(nfc);
SeaderPicopassDetectContext detect_context = {0};
if(!poller) {
FURI_LOG_W(TAG, "Failed to allocate Picopass detect poller");
return false;
}
picopass_poller_start(poller, seader_worker_picopass_detect_callback, &detect_context);
for(uint8_t i = 0; i < 10 && !detect_context.done; i++) {
furi_delay_ms(10);
}
detected = detect_context.detected;
picopass_poller_stop(poller);
detected = detect_context.detected;
picopass_poller_free(poller);
return detected;
@@ -213,6 +240,15 @@ void seader_worker_stop(SeaderWorker* seader_worker) {
furi_thread_join(seader_worker->thread);
}
void seader_worker_join(SeaderWorker* seader_worker) {
furi_assert(seader_worker);
if(furi_thread_get_state(seader_worker->thread) == FuriThreadStateStopped) {
return;
}
furi_thread_join(seader_worker->thread);
}
void seader_worker_change_state(SeaderWorker* seader_worker, SeaderWorkerState state) {
seader_worker->state = state;
}
@@ -427,6 +463,12 @@ 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 == SeaderWorkerStateHfTeardown) {
FURI_LOG_I(TAG, "HF teardown started");
seader_worker_release_hf_session(seader);
if(seader_worker->callback) {
seader_worker->callback(SeaderWorkerEventHfTeardownComplete, seader_worker->context);
}
} else if(seader_worker->state == SeaderWorkerStateReading) {
FURI_LOG_D(TAG, "Reading mode started");
seader_worker_reading(seader);
@@ -442,6 +484,7 @@ void seader_worker_reading(Seader* seader) {
if(!seader_hf_plugin_acquire(seader) || !seader->plugin_hf || !seader->hf_plugin_ctx) {
FURI_LOG_E(TAG, "HF plugin unavailable");
strlcpy(seader->read_error, "HF plugin unavailable", sizeof(seader->read_error));
if(seader_worker->callback) {
seader_worker->callback(SeaderWorkerEventFail, seader_worker->context);
}
@@ -475,6 +518,9 @@ void seader_worker_reading(Seader* seader) {
if(type_to_read != SeaderCredentialTypeNone) {
FURI_LOG_I(TAG, "HF start read for type=%d", type_to_read);
detected = seader->plugin_hf->start_read_for_type(seader->hf_plugin_ctx, type_to_read);
if(detected) {
seader->hf_session_state = SeaderHfSessionStateActive;
}
FURI_LOG_I(TAG, "HF start read result=%d", detected);
}
@@ -488,8 +534,9 @@ void seader_worker_reading(Seader* seader) {
furi_delay_ms(10);
}
result_stage = seader_worker->stage;
seader->plugin_hf->stop(seader->hf_plugin_ctx);
seader_worker_clear_active_card(
seader,
result_stage == SeaderPollerEventTypeComplete ? "read-complete" : "read-abort");
if(result_stage == SeaderPollerEventTypeComplete) {
// Notify UI of success
@@ -504,7 +551,6 @@ void seader_worker_reading(Seader* seader) {
furi_delay_ms(50);
}
}
seader->plugin_hf->stop(seader->hf_plugin_ctx);
FURI_LOG_I(TAG, "Reading loop stopped");
}
+3
View File
@@ -24,6 +24,7 @@ typedef enum {
SeaderWorkerStateVirtualCredential,
SeaderWorkerStateAPDURunner,
SeaderWorkerStateReading,
SeaderWorkerStateHfTeardown,
// Transition
SeaderWorkerStateStop,
} SeaderWorkerState;
@@ -44,6 +45,7 @@ typedef enum {
SeaderWorkerEventAPDURunnerUpdate,
SeaderWorkerEventAPDURunnerSuccess,
SeaderWorkerEventAPDURunnerError,
SeaderWorkerEventHfTeardownComplete,
} SeaderWorkerEvent;
typedef enum {
@@ -71,6 +73,7 @@ void seader_worker_start(
void* context);
void seader_worker_stop(SeaderWorker* seader_worker);
void seader_worker_join(SeaderWorker* seader_worker);
bool seader_worker_process_sam_message(Seader* seader, uint8_t* apdu, uint32_t len);
void seader_worker_send_version(Seader* seader);
void seader_worker_cancel_poller_session(SeaderWorker* seader_worker);