Improve SAM read state handling and failure recovery

This commit is contained in:
CinderSocket committed 2026-03-07 00:27:56 -08:00
1 parent 29861150a2
commit 4ffc656a23
8 files changed
+230 -9

No files matched your search

+152 -1
View File
@@ -51,6 +51,105 @@ static void
}
}
#ifdef SEADER_ENABLE_TRACE_LOG
static void seader_trace_mfc_packed_frame(const char* prefix, const uint8_t* buffer, size_t len) {
if(!buffer || len == 0) {
seader_trace(TAG, "%s <empty>", prefix);
return;
}
if(len < 2) {
seader_trace_hex(TAG, prefix, buffer, len);
return;
}
uint8_t packed[SEADER_POLLER_MAX_BUFFER_SIZE] = {0};
if(len > sizeof(packed)) {
seader_trace_hex(TAG, prefix, buffer, len);
return;
}
memcpy(packed, buffer, len);
uint8_t parity = 0;
size_t decoded_len = len - 1;
uint8_t decoded[SEADER_POLLER_MAX_BUFFER_SIZE] = {0};
char parity_bits[SEADER_POLLER_MAX_BUFFER_SIZE + 1] = {0};
for(size_t i = 0; i < len; i++) {
bit_lib_reverse_bits(packed + i, 0, 8);
}
for(size_t i = 0; i < decoded_len; i++) {
bool val = bit_lib_get_bit(packed + i + 1, i);
bit_lib_set_bit(&parity, i, val);
}
for(size_t i = 0; i < decoded_len; i++) {
packed[i] = (packed[i] << i) | (packed[i + 1] >> (8 - i));
bit_lib_reverse_bits(packed + i, 0, 8);
decoded[i] = packed[i];
parity_bits[i] = bit_lib_get_bit(&parity, i) ? '1' : '0';
}
parity_bits[decoded_len] = '\0';
seader_trace_hex(TAG, prefix, buffer, len);
seader_trace_hex(TAG, "mfc tx decoded", decoded, decoded_len);
seader_trace(TAG, "mfc tx parity bits=%s", parity_bits);
}
static void seader_trace_mfc_bitbuffer(
const char* prefix,
BitBuffer* buffer,
bool include_parity) {
if(!buffer) {
seader_trace(TAG, "%s <null>", prefix);
return;
}
size_t len = bit_buffer_get_size_bytes(buffer);
uint8_t bytes[SEADER_POLLER_MAX_BUFFER_SIZE] = {0};
char parity_bits[SEADER_POLLER_MAX_BUFFER_SIZE + 1] = {0};
if(len > sizeof(bytes)) len = sizeof(bytes);
for(size_t i = 0; i < len; i++) {
bytes[i] = bit_buffer_get_byte(buffer, i);
if(include_parity) {
const uint8_t* parity = bit_buffer_get_parity(buffer);
parity_bits[i] = bit_lib_get_bit(parity, i) ? '1' : '0';
}
}
if(include_parity) {
parity_bits[len] = '\0';
}
seader_trace_hex(TAG, prefix, bytes, len);
if(include_parity) {
seader_trace(TAG, "%s parity=%s", prefix, parity_bits);
}
}
#else
static void seader_trace_mfc_packed_frame(const char* prefix, const uint8_t* buffer, size_t len) {
(void)prefix;
(void)buffer;
(void)len;
}
static void seader_trace_mfc_bitbuffer(
const char* prefix,
BitBuffer* buffer,
bool include_parity) {
(void)prefix;
(void)buffer;
(void)include_parity;
}
#endif
uint8_t updateBlock2[] = {RFAL_PICOPASS_CMD_UPDATE, 0x02};
uint8_t select_seos_app[] =
@@ -96,6 +195,13 @@ bool seader_sam_can_accept_card(const Seader* seader) {
return seader->sam_state == SeaderSamStateIdle;
}
bool seader_sam_has_active_card(const Seader* seader) {
return
seader->sam_state == SeaderSamStateDetectPending ||
seader->sam_state == SeaderSamStateConversation ||
seader->sam_state == SeaderSamStateFinishing;
}
PicopassError seader_worker_fake_epurse_update(BitBuffer* tx_buffer, BitBuffer* rx_buffer) {
const uint8_t* buffer = bit_buffer_get_data(tx_buffer);
uint8_t fake_response[8];
@@ -732,7 +838,9 @@ static void seader_abort_active_read(Seader* seader) {
seader->sam_state,
seader->sam_intent);
seader_worker->stage = SeaderPollerEventTypeFail;
seader_sam_set_state(seader, SeaderSamStateIdle, SeaderSamIntentNone, SamCommand_PR_NOTHING);
if(!seader_sam_has_active_card(seader) && seader->sam_state != SeaderSamStateClearPending) {
seader_sam_set_state(seader, SeaderSamStateIdle, SeaderSamIntentNone, SamCommand_PR_NOTHING);
}
view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit);
}
@@ -1050,15 +1158,38 @@ void seader_mfc_transmit(
BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE);
do {
seader_trace(
TAG,
"mfc tx format=%02x%02x%02x len=%u",
format[0],
format[1],
format[2],
(unsigned)len);
if(
(format[0] == 0x00 && format[1] == 0x00 && format[2] == 0x40) ||
(format[0] == 0x00 && format[1] == 0x00 && format[2] == 0x24) ||
(format[0] == 0x00 && format[1] == 0x00 && format[2] == 0x44)) {
seader_trace_mfc_packed_frame("mfc tx raw", buffer, len);
} else {
seader_trace_hex(TAG, "mfc tx raw", buffer, len);
}
if(format[0] == 0x00 && format[1] == 0xC0 && format[2] == 0x00) {
bit_buffer_append_bytes(tx_buffer, buffer, len);
MfClassicError error =
mf_classic_poller_send_frame(mfc_poller, tx_buffer, rx_buffer, MF_CLASSIC_FWT_FC);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "mf_classic_poller_send_frame error %d", error);
seader_trace(TAG, "mfc send_frame error=%d", error);
seader_worker->stage = SeaderPollerEventTypeFail;
break;
}
seader_trace_hex(
TAG,
"mfc rx raw",
bit_buffer_get_data(rx_buffer),
bit_buffer_get_size_bytes(rx_buffer));
} else if(
(format[0] == 0x00 && format[1] == 0x00 && format[2] == 0x40) ||
(format[0] == 0x00 && format[1] == 0x00 && format[2] == 0x24) ||
@@ -1090,6 +1221,7 @@ void seader_mfc_transmit(
bit_buffer_set_byte_with_parity(
tx_buffer, i, buffer[i], bit_lib_get_bit(&tx_parity, i));
}
seader_trace_mfc_bitbuffer("mfc tx bitbuffer", tx_buffer, true);
// Log the BitBuffer contents efficiently
size_t tx_size = bit_buffer_get_size_bytes(tx_buffer);
@@ -1099,6 +1231,7 @@ void seader_mfc_transmit(
tx_data[i] = bit_buffer_get_byte(tx_buffer, i);
}
seader_log_hex_data(TAG, "NFC Send without parity", tx_data, tx_size);
seader_trace_hex(TAG, "mfc tx no parity", tx_data, tx_size);
free(tx_data);
}
@@ -1106,12 +1239,22 @@ void seader_mfc_transmit(
mfc_poller, tx_buffer, rx_buffer, MF_CLASSIC_FWT_FC);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "mf_classic_poller_send_encrypted_frame error %d", error);
seader_trace(TAG, "mfc send_custom_parity error=%d", error);
if(
error == MfClassicErrorTimeout &&
seader->credential->type == SeaderCredentialTypeMifareClassic) {
snprintf(
seader->read_error,
sizeof(seader->read_error),
"Protected read timed out.\nNo supported data\nor wrong key.");
}
seader_worker->stage = SeaderPollerEventTypeFail;
break;
}
size_t length = bit_buffer_get_size_bytes(rx_buffer);
const uint8_t* rx_parity = bit_buffer_get_parity(rx_buffer);
seader_trace_mfc_bitbuffer("mfc rx bitbuffer", rx_buffer, true);
// Log the BitBuffer contents efficiently
uint8_t* rx_data = malloc(length);
@@ -1120,6 +1263,7 @@ void seader_mfc_transmit(
rx_data[i] = bit_buffer_get_byte(rx_buffer, i);
}
seader_log_hex_data(TAG, "NFC Response without parity", rx_data, length);
seader_trace_hex(TAG, "mfc rx no parity", rx_data, length);
free(rx_data);
}
@@ -1166,11 +1310,18 @@ void seader_mfc_transmit(
rx_data_parity[i] = bit_buffer_get_byte(rx_buffer, i);
}
seader_log_hex_data(TAG, "NFC Response with parity", rx_data_parity, length);
seader_trace_hex(TAG, "mfc rx parity", rx_data_parity, length);
free(rx_data_parity);
}
} else {
FURI_LOG_W(TAG, "UNHANDLED FORMAT");
seader_trace(
TAG,
"mfc unhandled format=%02x%02x%02x",
format[0],
format[1],
format[2]);
}
seader_send_nfc_rx(
+1
View File
@@ -28,6 +28,7 @@ NfcCommand seader_worker_card_detect(
void seader_send_no_card_detected(Seader* seader);
bool seader_sam_can_accept_card(const Seader* seader);
bool seader_sam_has_active_card(const Seader* seader);
bool seader_process_success_response_i(
Seader* seader,
+15 -4
View File
@@ -54,6 +54,7 @@ void seader_scene_read_card_success_on_enter(void* context) {
}
} else {
furi_string_set(type_str, "Read error");
furi_string_set(bitlength_str, seader->read_error[0] ? seader->read_error : "Read failed");
SeaderWorker* seader_worker = seader->worker;
SeaderUartBridge* seader_uart = seader_worker->uart;
@@ -64,10 +65,15 @@ void seader_scene_read_card_success_on_enter(void* context) {
widget_add_button_element(
widget, GuiButtonTypeLeft, "Retry", seader_scene_read_card_success_widget_callback, seader);
widget_add_button_element(
widget, GuiButtonTypeRight, "More", seader_scene_read_card_success_widget_callback, seader);
if(credential->bit_length > 0) {
widget_add_button_element(
widget, GuiButtonTypeRight, "More", seader_scene_read_card_success_widget_callback, seader);
} else {
widget_add_button_element(
widget, GuiButtonTypeRight, "Back", seader_scene_read_card_success_widget_callback, seader);
}
if(plugin) {
if(plugin && credential->bit_length > 0) {
size_t format_count = plugin->count(credential->bit_length, credential->credential);
if(format_count > 0) {
widget_add_button_element(
@@ -127,7 +133,12 @@ bool seader_scene_read_card_success_on_event(void* context, SceneManagerEvent ev
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(seader->scene_manager);
} else if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(seader->scene_manager, SeaderSceneCardMenu);
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 = true;
} else if(event.event == GuiButtonTypeCenter) {
scene_manager_next_scene(seader->scene_manager, SeaderSceneFormats);
+2 -3
View File
@@ -11,6 +11,7 @@ void seader_scene_read_prepare(Seader* seader) {
if(seader->sam_state == SeaderSamStateIdle) {
seader->samCommand = SamCommand_PR_NOTHING;
}
memset(seader->read_error, 0, sizeof(seader->read_error));
seader_worker_reset_poller_session(seader->worker);
}
@@ -21,9 +22,7 @@ void seader_scene_read_cleanup(Seader* seader) {
"SceneRead", "cleanup sam=%d state=%d intent=%d", seader->samCommand, seader->sam_state, seader->sam_intent);
seader_worker_cancel_poller_session(seader->worker);
if(
seader->sam_state == SeaderSamStateDetectPending ||
seader->sam_state == SeaderSamStateConversation) {
if(seader_sam_has_active_card(seader)) {
seader_send_no_card_detected(seader);
}
+1
View File
@@ -133,6 +133,7 @@ struct Seader {
size_t ATR_len;
char text_store[SEADER_TEXT_STORE_SIZE + 1];
char read_error[SEADER_TEXT_STORE_SIZE + 1];
FuriString* text_box_store;
// Reusable strings to optimize scene string allocations
+5
View File
@@ -496,9 +496,14 @@ NfcCommand seader_worker_poller_callback_mfc(NfcGenericEvent event, void* contex
} 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;
}
+27
View File
@@ -1,5 +1,7 @@
#include "trace_log.h"
#ifdef SEADER_ENABLE_TRACE_LOG
#include <storage/storage.h>
#include <toolbox/stream/buffered_file_stream.h>
@@ -33,3 +35,28 @@ void seader_trace(const char* tag, const char* fmt, ...) {
snprintf(line, sizeof(line), "[%s] %s\n", tag, message);
seader_trace_write(FSOM_OPEN_ALWAYS, line);
}
void seader_trace_hex(const char* tag, const char* prefix, const uint8_t* data, size_t len) {
if(!data || len == 0) {
seader_trace(tag, "%s <empty>", prefix);
return;
}
/* Keep trace lines bounded; enough to diagnose short RF exchanges. */
const size_t max_bytes = 32;
size_t trace_len = len > max_bytes ? max_bytes : len;
char hex[(max_bytes * 2) + 1];
for(size_t i = 0; i < trace_len; i++) {
snprintf(hex + (i * 2), sizeof(hex) - (i * 2), "%02x", data[i]);
}
hex[trace_len * 2] = '\0';
if(trace_len < len) {
seader_trace(tag, "%s len=%u data=%s...", prefix, (unsigned)len, hex);
} else {
seader_trace(tag, "%s len=%u data=%s", prefix, (unsigned)len, hex);
}
}
#endif
+27 -1
View File
@@ -1,8 +1,34 @@
#pragma once
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#ifdef SEADER_ENABLE_TRACE_LOG
/* Define SEADER_ENABLE_TRACE_LOG in the app build to enable SD-card tracing. */
#define SEADER_TRACE_FILE_NAME APP_DATA_PATH("trace.log")
void seader_trace_reset(void);
void seader_trace(const char* tag, const char* fmt, ...);
void seader_trace_hex(const char* tag, const char* prefix, const uint8_t* data, size_t len);
#else
static inline void seader_trace_reset(void) {
}
static inline void seader_trace(const char* tag, const char* fmt, ...) {
(void)tag;
(void)fmt;
}
static inline void
seader_trace_hex(const char* tag, const char* prefix, const uint8_t* data, size_t len) {
(void)tag;
(void)prefix;
(void)data;
(void)len;
}
#endif