mirror of
https://github.com/bettse/seader.git
synced 2026-10-22 17:32:48 +00:00
Improve SAM read state handling and failure recovery
This commit is contained in:
1 parent
29861150a2
commit
4ffc656a23
8 files changed
+230
-9
No files matched your search
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
Reference in new issue
Block a user