From c1e0d02afc8131e782aa3f2e5c1bbd6f43890790 Mon Sep 17 00:00:00 2001 From: Augusto Zanellato Date: Fri, 1 Dec 2023 10:42:00 +0100 Subject: [PATCH] ST25TB poller refining + write support (#3239) * nfc: st25tb: rework async poller * nfc: st25tb: introduce sync poller * nfc: st25tb: add write support * nfc: st25tb: rewrite poller to use better states * nfc: st25tb: move to mode request state after success * nfc: st25tb: minor bug fixes * type wasn't properly set on ready event * sending NfcCustomEventPollerFailure on St25tbPollerEventTypeFailure caused poller to being freed and ultimately resulted in a thread crash Co-authored-by: Aleksandr Kutuzov --- .../helpers/protocol_support/st25tb/st25tb.c | 4 +- lib/nfc/SConscript | 1 + lib/nfc/protocols/st25tb/st25tb.c | 20 ++ lib/nfc/protocols/st25tb/st25tb.h | 5 +- lib/nfc/protocols/st25tb/st25tb_poller.c | 154 +++++++++++-- lib/nfc/protocols/st25tb/st25tb_poller.h | 40 +++- lib/nfc/protocols/st25tb/st25tb_poller_i.c | 194 ++++++++-------- lib/nfc/protocols/st25tb/st25tb_poller_i.h | 33 ++- lib/nfc/protocols/st25tb/st25tb_poller_sync.c | 211 ++++++++++++++++++ lib/nfc/protocols/st25tb/st25tb_poller_sync.h | 20 ++ targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 11 +- 12 files changed, 566 insertions(+), 129 deletions(-) create mode 100644 lib/nfc/protocols/st25tb/st25tb_poller_sync.c create mode 100644 lib/nfc/protocols/st25tb/st25tb_poller_sync.h diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c index 32b2f477..e22af48b 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c @@ -29,7 +29,9 @@ static NfcCommand nfc_scene_read_poller_callback_st25tb(NfcGenericEvent event, v NfcApp* instance = context; const St25tbPollerEvent* st25tb_event = event.event_data; - if(st25tb_event->type == St25tbPollerEventTypeReady) { + if(st25tb_event->type == St25tbPollerEventTypeRequestMode) { + st25tb_event->data->mode_request.mode = St25tbPollerModeRead; + } else if(st25tb_event->type == St25tbPollerEventTypeSuccess) { nfc_device_set_data( instance->nfc_device, NfcProtocolSt25tb, nfc_poller_get_data(instance->poller)); view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index 21f2fb49..d2cfbe2f 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -42,6 +42,7 @@ env.Append( File("protocols/iso14443_3a/iso14443_3a_poller_sync.h"), File("protocols/mf_ultralight/mf_ultralight_poller_sync.h"), File("protocols/mf_classic/mf_classic_poller_sync.h"), + File("protocols/st25tb/st25tb_poller_sync.h"), # Misc File("helpers/nfc_util.h"), File("helpers/iso14443_crc.h"), diff --git a/lib/nfc/protocols/st25tb/st25tb.c b/lib/nfc/protocols/st25tb/st25tb.c index d3fac7ee..785cf831 100644 --- a/lib/nfc/protocols/st25tb/st25tb.c +++ b/lib/nfc/protocols/st25tb/st25tb.c @@ -232,3 +232,23 @@ St25tbData* st25tb_get_base_data(const St25tbData* data) { UNUSED(data); furi_crash("No base data"); } + +St25tbType st25tb_get_type_from_uid(const uint8_t* uid) { + switch(uid[2] >> 2) { + case 0x0: + case 0x3: + return St25tbTypeX4k; + case 0x4: + return St25tbTypeX512; + case 0x6: + return St25tbType512Ac; + case 0x7: + return St25tbType04k; + case 0xc: + return St25tbType512At; + case 0xf: + return St25tbType02k; + default: + furi_crash("unsupported st25tb type"); + } +} diff --git a/lib/nfc/protocols/st25tb/st25tb.h b/lib/nfc/protocols/st25tb/st25tb.h index 1edb296c..ed02dc2b 100644 --- a/lib/nfc/protocols/st25tb/st25tb.h +++ b/lib/nfc/protocols/st25tb/st25tb.h @@ -1,6 +1,5 @@ #pragma once -#include #include #ifdef __cplusplus @@ -27,6 +26,7 @@ typedef enum { St25tbErrorFieldOff, St25tbErrorWrongCrc, St25tbErrorTimeout, + St25tbErrorWriteFailed, } St25tbError; typedef enum { @@ -44,7 +44,6 @@ typedef struct { St25tbType type; uint32_t blocks[ST25TB_MAX_BLOCKS]; uint32_t system_otp_block; - uint8_t chip_id; } St25tbData; extern const NfcDeviceBase nfc_device_st25tb; @@ -75,6 +74,8 @@ bool st25tb_set_uid(St25tbData* data, const uint8_t* uid, size_t uid_len); St25tbData* st25tb_get_base_data(const St25tbData* data); +St25tbType st25tb_get_type_from_uid(const uint8_t* uid); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/st25tb/st25tb_poller.c b/lib/nfc/protocols/st25tb/st25tb_poller.c index 2bc5dd94..fd6dc4f0 100644 --- a/lib/nfc/protocols/st25tb/st25tb_poller.c +++ b/lib/nfc/protocols/st25tb/st25tb_poller.c @@ -1,13 +1,12 @@ -#include "protocols/nfc_protocol.h" -#include "protocols/st25tb/st25tb.h" +#include "st25tb_poller.h" #include "st25tb_poller_i.h" #include -#include - #define TAG "ST25TBPoller" +typedef NfcCommand (*St25tbPollerStateHandler)(St25tbPoller* instance); + const St25tbData* st25tb_poller_get_data(St25tbPoller* instance) { furi_assert(instance); furi_assert(instance->data); @@ -20,6 +19,7 @@ static St25tbPoller* st25tb_poller_alloc(Nfc* nfc) { St25tbPoller* instance = malloc(sizeof(St25tbPoller)); instance->nfc = nfc; + instance->state = St25tbPollerStateSelect; instance->tx_buffer = bit_buffer_alloc(ST25TB_POLLER_MAX_BUFFER_SIZE); instance->rx_buffer = bit_buffer_alloc(ST25TB_POLLER_MAX_BUFFER_SIZE); @@ -60,6 +60,128 @@ static void instance->context = context; } +static NfcCommand st25tb_poller_select_handler(St25tbPoller* instance) { + NfcCommand command = NfcCommandContinue; + + do { + St25tbError error = st25tb_poller_select(instance, NULL); + if(error != St25tbErrorNone) { + instance->state = St25tbPollerStateFailure; + instance->st25tb_event_data.error = error; + break; + } + + instance->st25tb_event.type = St25tbPollerEventTypeReady; + instance->st25tb_event.data->ready.type = instance->data->type; + command = instance->callback(instance->general_event, instance->context); + instance->state = St25tbPollerStateRequestMode; + } while(false); + + return command; +} + +static NfcCommand st25tb_poller_request_mode_handler(St25tbPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->st25tb_event.type = St25tbPollerEventTypeRequestMode; + command = instance->callback(instance->general_event, instance->context); + + St25tbPollerEventDataModeRequest* mode_request_data = + &instance->st25tb_event_data.mode_request; + + furi_assert(mode_request_data->mode < St25tbPollerModeNum); + + if(mode_request_data->mode == St25tbPollerModeRead) { + instance->state = St25tbPollerStateRead; + instance->poller_ctx.read.current_block = 0; + } else { + instance->state = St25tbPollerStateWrite; + instance->poller_ctx.write.block_number = + mode_request_data->params.write_params.block_number; + instance->poller_ctx.write.block_data = mode_request_data->params.write_params.block_data; + } + + return command; +} + +static NfcCommand st25tb_poller_read_handler(St25tbPoller* instance) { + St25tbError error = St25tbErrorNone; + + do { + uint8_t total_blocks = st25tb_get_block_count(instance->data->type); + uint8_t* current_block = &instance->poller_ctx.read.current_block; + if(*current_block == total_blocks) { + error = st25tb_poller_read_block( + instance, &instance->data->system_otp_block, ST25TB_SYSTEM_OTP_BLOCK); + if(error != St25tbErrorNone) { + FURI_LOG_E(TAG, "Failed to read OTP block"); + instance->state = St25tbPollerStateFailure; + instance->st25tb_event_data.error = error; + break; + } else { + instance->state = St25tbPollerStateSuccess; + break; + } + } else { + error = st25tb_poller_read_block( + instance, &instance->data->blocks[*current_block], *current_block); + if(error != St25tbErrorNone) { + FURI_LOG_E(TAG, "Failed to read block %d", *current_block); + instance->state = St25tbPollerStateFailure; + instance->st25tb_event_data.error = error; + break; + } + + *current_block += 1; + } + } while(false); + + return NfcCommandContinue; +} + +static NfcCommand st25tb_poller_write_handler(St25tbPoller* instance) { + St25tbPollerWriteContext* write_ctx = &instance->poller_ctx.write; + St25tbError error = + st25tb_poller_write_block(instance, write_ctx->block_data, write_ctx->block_number); + + if(error == St25tbErrorNone) { + instance->state = St25tbPollerStateSuccess; + } else { + instance->state = St25tbPollerStateFailure; + instance->st25tb_event_data.error = error; + } + + return NfcCommandContinue; +} + +NfcCommand st25tb_poller_success_handler(St25tbPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->st25tb_event.type = St25tbPollerEventTypeSuccess; + command = instance->callback(instance->general_event, instance->context); + furi_delay_ms(100); + instance->state = St25tbPollerStateRequestMode; + + return command; +} + +NfcCommand st25tb_poller_failure_handler(St25tbPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->st25tb_event.type = St25tbPollerEventTypeFailure; + command = instance->callback(instance->general_event, instance->context); + furi_delay_ms(100); + instance->state = St25tbPollerStateSelect; + + return command; +} + +static St25tbPollerStateHandler st25tb_poller_state_handlers[St25tbPollerStateNum] = { + [St25tbPollerStateSelect] = st25tb_poller_select_handler, + [St25tbPollerStateRequestMode] = st25tb_poller_request_mode_handler, + [St25tbPollerStateRead] = st25tb_poller_read_handler, + [St25tbPollerStateWrite] = st25tb_poller_write_handler, + [St25tbPollerStateSuccess] = st25tb_poller_success_handler, + [St25tbPollerStateFailure] = st25tb_poller_failure_handler, +}; + static NfcCommand st25tb_poller_run(NfcGenericEvent event, void* context) { furi_assert(context); furi_assert(event.protocol == NfcProtocolInvalid); @@ -69,26 +191,10 @@ static NfcCommand st25tb_poller_run(NfcGenericEvent event, void* context) { NfcEvent* nfc_event = event.event_data; NfcCommand command = NfcCommandContinue; - if(nfc_event->type == NfcEventTypePollerReady) { - if(instance->state != St25tbPollerStateActivated) { - St25tbError error = st25tb_poller_activate(instance, instance->data); + furi_assert(instance->state < St25tbPollerStateNum); - if(error == St25tbErrorNone) { - instance->st25tb_event.type = St25tbPollerEventTypeReady; - instance->st25tb_event_data.error = error; - command = instance->callback(instance->general_event, instance->context); - } else { - instance->st25tb_event.type = St25tbPollerEventTypeError; - instance->st25tb_event_data.error = error; - command = instance->callback(instance->general_event, instance->context); - // Add delay to switch context - furi_delay_ms(100); - } - } else { - instance->st25tb_event.type = St25tbPollerEventTypeReady; - instance->st25tb_event_data.error = St25tbErrorNone; - command = instance->callback(instance->general_event, instance->context); - } + if(nfc_event->type == NfcEventTypePollerReady) { + command = st25tb_poller_state_handlers[instance->state](instance); } return command; @@ -103,7 +209,7 @@ static bool st25tb_poller_detect(NfcGenericEvent event, void* context) { bool protocol_detected = false; St25tbPoller* instance = context; NfcEvent* nfc_event = event.event_data; - furi_assert(instance->state == St25tbPollerStateIdle); + furi_assert(instance->state == St25tbPollerStateSelect); if(nfc_event->type == NfcEventTypePollerReady) { St25tbError error = st25tb_poller_initiate(instance, NULL); diff --git a/lib/nfc/protocols/st25tb/st25tb_poller.h b/lib/nfc/protocols/st25tb/st25tb_poller.h index d3b85e30..87687b7e 100644 --- a/lib/nfc/protocols/st25tb/st25tb_poller.h +++ b/lib/nfc/protocols/st25tb/st25tb_poller.h @@ -3,8 +3,6 @@ #include "st25tb.h" #include -#include - #ifdef __cplusplus extern "C" { #endif @@ -12,11 +10,40 @@ extern "C" { typedef struct St25tbPoller St25tbPoller; typedef enum { - St25tbPollerEventTypeError, St25tbPollerEventTypeReady, + St25tbPollerEventTypeRequestMode, + St25tbPollerEventTypeFailure, + St25tbPollerEventTypeSuccess, } St25tbPollerEventType; typedef struct { + St25tbType type; +} St25tbPollerReadyData; + +typedef enum { + St25tbPollerModeRead, + St25tbPollerModeWrite, + + St25tbPollerModeNum, +} St25tbPollerMode; + +typedef struct { + uint8_t block_number; + uint32_t block_data; +} St25tbPollerEventDataModeRequestWriteParams; + +typedef union { + St25tbPollerEventDataModeRequestWriteParams write_params; +} St25tbPollerEventDataModeRequestParams; + +typedef struct { + St25tbPollerMode mode; + St25tbPollerEventDataModeRequestParams params; +} St25tbPollerEventDataModeRequest; + +typedef union { + St25tbPollerReadyData ready; + St25tbPollerEventDataModeRequest mode_request; St25tbError error; } St25tbPollerEventData; @@ -31,15 +58,18 @@ St25tbError st25tb_poller_send_frame( BitBuffer* rx_buffer, uint32_t fwt); -St25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id); +St25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id_ptr); -St25tbError st25tb_poller_activate(St25tbPoller* instance, St25tbData* data); +St25tbError st25tb_poller_select(St25tbPoller* instance, uint8_t* chip_id_ptr); St25tbError st25tb_poller_get_uid(St25tbPoller* instance, uint8_t* uid); St25tbError st25tb_poller_read_block(St25tbPoller* instance, uint32_t* block, uint8_t block_number); +St25tbError + st25tb_poller_write_block(St25tbPoller* instance, uint32_t block, uint8_t block_number); + St25tbError st25tb_poller_halt(St25tbPoller* instance); #ifdef __cplusplus diff --git a/lib/nfc/protocols/st25tb/st25tb_poller_i.c b/lib/nfc/protocols/st25tb/st25tb_poller_i.c index 76c9a8b1..adb8626a 100644 --- a/lib/nfc/protocols/st25tb/st25tb_poller_i.c +++ b/lib/nfc/protocols/st25tb/st25tb_poller_i.c @@ -1,8 +1,5 @@ #include "st25tb_poller_i.h" -#include "bit_buffer.h" -#include "core/core_defines.h" -#include "protocols/st25tb/st25tb.h" #include #define TAG "ST25TBPoller" @@ -18,17 +15,7 @@ static St25tbError st25tb_poller_process_error(NfcError error) { } } -static St25tbError st25tb_poller_prepare_trx(St25tbPoller* instance) { - furi_assert(instance); - - if(instance->state == St25tbPollerStateIdle) { - return st25tb_poller_activate(instance, NULL); - } - - return St25tbErrorNone; -} - -static St25tbError st25tb_poller_frame_exchange( +St25tbError st25tb_poller_send_frame( St25tbPoller* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, @@ -48,7 +35,7 @@ static St25tbError st25tb_poller_frame_exchange( NfcError error = nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); if(error != NfcErrorNone) { - FURI_LOG_D(TAG, "error during trx: %d", error); + FURI_LOG_T(TAG, "error during trx: %d", error); ret = st25tb_poller_process_error(error); break; } @@ -65,32 +52,11 @@ static St25tbError st25tb_poller_frame_exchange( return ret; } -St25tbType st25tb_get_type_from_uid(const uint8_t uid[ST25TB_UID_SIZE]) { - switch(uid[2] >> 2) { - case 0x0: - case 0x3: - return St25tbTypeX4k; - case 0x4: - return St25tbTypeX512; - case 0x6: - return St25tbType512Ac; - case 0x7: - return St25tbType04k; - case 0xc: - return St25tbType512At; - case 0xf: - return St25tbType02k; - default: - furi_crash("unsupported st25tb type"); - } -} - -St25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id) { +St25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id_ptr) { // Send Initiate() furi_assert(instance); furi_assert(instance->nfc); - instance->state = St25tbPollerStateInitiateInProgress; bit_buffer_reset(instance->tx_buffer); bit_buffer_reset(instance->rx_buffer); bit_buffer_append_byte(instance->tx_buffer, 0x06); @@ -98,77 +64,90 @@ St25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id) { St25tbError ret; do { - ret = st25tb_poller_frame_exchange( + ret = st25tb_poller_send_frame( instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); if(ret != St25tbErrorNone) { break; } if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1) { - FURI_LOG_D(TAG, "Unexpected Initiate response size"); + FURI_LOG_E(TAG, "Unexpected Initiate response size"); ret = St25tbErrorCommunication; break; } - if(chip_id) { - *chip_id = bit_buffer_get_byte(instance->rx_buffer, 0); + uint8_t chip_id = bit_buffer_get_byte(instance->rx_buffer, 0); + FURI_LOG_D(TAG, "Got chip_id=0x%02X", chip_id); + if(chip_id_ptr) { + *chip_id_ptr = bit_buffer_get_byte(instance->rx_buffer, 0); } } while(false); return ret; } -St25tbError st25tb_poller_activate(St25tbPoller* instance, St25tbData* data) { +St25tbError st25tb_poller_select(St25tbPoller* instance, uint8_t* chip_id_ptr) { furi_assert(instance); furi_assert(instance->nfc); - st25tb_reset(data); - St25tbError ret; do { - ret = st25tb_poller_initiate(instance, &data->chip_id); - if(ret != St25tbErrorNone) { - break; - } + uint8_t chip_id; - instance->state = St25tbPollerStateActivationInProgress; + if(chip_id_ptr != NULL) { + chip_id = *chip_id_ptr; + } else { + ret = st25tb_poller_initiate(instance, &chip_id); + if(ret != St25tbErrorNone) { + break; + } + } bit_buffer_reset(instance->tx_buffer); bit_buffer_reset(instance->rx_buffer); // Send Select(Chip_ID), let's just assume that collisions won't ever happen :D bit_buffer_append_byte(instance->tx_buffer, 0x0E); - bit_buffer_append_byte(instance->tx_buffer, data->chip_id); + bit_buffer_append_byte(instance->tx_buffer, chip_id); - ret = st25tb_poller_frame_exchange( + ret = st25tb_poller_send_frame( instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); if(ret != St25tbErrorNone) { - instance->state = St25tbPollerStateActivationFailed; break; } if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1) { - FURI_LOG_D(TAG, "Unexpected Select response size"); - instance->state = St25tbPollerStateActivationFailed; + FURI_LOG_E(TAG, "Unexpected Select response size"); ret = St25tbErrorCommunication; break; } - if(bit_buffer_get_byte(instance->rx_buffer, 0) != data->chip_id) { - FURI_LOG_D(TAG, "ChipID mismatch"); - instance->state = St25tbPollerStateActivationFailed; + if(bit_buffer_get_byte(instance->rx_buffer, 0) != chip_id) { + FURI_LOG_E(TAG, "ChipID mismatch"); ret = St25tbErrorColResFailed; break; } - instance->state = St25tbPollerStateActivated; - ret = st25tb_poller_get_uid(instance, data->uid); + ret = st25tb_poller_get_uid(instance, instance->data->uid); if(ret != St25tbErrorNone) { - instance->state = St25tbPollerStateActivationFailed; break; } - data->type = st25tb_get_type_from_uid(data->uid); + instance->data->type = st25tb_get_type_from_uid(instance->data->uid); + } while(false); + + return ret; +} + +St25tbError st25tb_poller_read(St25tbPoller* instance, St25tbData* data) { + furi_assert(instance); + furi_assert(instance->nfc); + + St25tbError ret; + + memcpy(data, instance->data, sizeof(St25tbData)); + + do { bool read_blocks = true; for(uint8_t i = 0; i < st25tb_get_block_count(data->type); i++) { ret = st25tb_poller_read_block(instance, &data->blocks[i], i); @@ -181,6 +160,9 @@ St25tbError st25tb_poller_activate(St25tbPoller* instance, St25tbData* data) { break; } ret = st25tb_poller_read_block(instance, &data->system_otp_block, ST25TB_SYSTEM_OTP_BLOCK); + if(ret != St25tbErrorNone) { + break; + } } while(false); return ret; @@ -198,15 +180,14 @@ St25tbError st25tb_poller_get_uid(St25tbPoller* instance, uint8_t* uid) { bit_buffer_append_byte(instance->tx_buffer, 0x0B); - ret = st25tb_poller_frame_exchange( + ret = st25tb_poller_send_frame( instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); if(ret != St25tbErrorNone) { break; } if(bit_buffer_get_size_bytes(instance->rx_buffer) != ST25TB_UID_SIZE) { - FURI_LOG_D(TAG, "Unexpected Get_UID() response size"); - instance->state = St25tbPollerStateActivationFailed; + FURI_LOG_E(TAG, "Unexpected Get_UID() response size"); ret = St25tbErrorCommunication; break; } @@ -215,6 +196,17 @@ St25tbError st25tb_poller_get_uid(St25tbPoller* instance, uint8_t* uid) { FURI_SWAP(uid[1], uid[6]); FURI_SWAP(uid[2], uid[5]); FURI_SWAP(uid[3], uid[4]); + FURI_LOG_I( + TAG, + "Got tag with uid: %02X %02X %02X %02X %02X %02X %02X %02X", + uid[0], + uid[1], + uid[2], + uid[3], + uid[4], + uid[5], + uid[6], + uid[7]); } while(false); return ret; } @@ -227,7 +219,7 @@ St25tbError furi_assert( (block_number <= st25tb_get_block_count(instance->data->type)) || block_number == ST25TB_SYSTEM_OTP_BLOCK); - FURI_LOG_D(TAG, "reading block %d", block_number); + FURI_LOG_T(TAG, "reading block %d", block_number); bit_buffer_reset(instance->tx_buffer); bit_buffer_reset(instance->rx_buffer); @@ -236,19 +228,64 @@ St25tbError bit_buffer_append_byte(instance->tx_buffer, block_number); St25tbError ret; do { - ret = st25tb_poller_frame_exchange( + ret = st25tb_poller_send_frame( instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); if(ret != St25tbErrorNone) { break; } if(bit_buffer_get_size_bytes(instance->rx_buffer) != ST25TB_BLOCK_SIZE) { - FURI_LOG_D(TAG, "Unexpected Read_block(Addr) response size"); + FURI_LOG_E(TAG, "Unexpected Read_block(Addr) response size"); ret = St25tbErrorCommunication; break; } bit_buffer_write_bytes(instance->rx_buffer, block, ST25TB_BLOCK_SIZE); - FURI_LOG_D(TAG, "read result: %08lX", *block); + FURI_LOG_D(TAG, "Read_block(%d) result: %08lX", block_number, *block); + } while(false); + + return ret; +} + +St25tbError + st25tb_poller_write_block(St25tbPoller* instance, uint32_t block, uint8_t block_number) { + furi_assert(instance); + furi_assert(instance->nfc); + furi_assert( + (block_number <= st25tb_get_block_count(instance->data->type)) || + block_number == ST25TB_SYSTEM_OTP_BLOCK); + FURI_LOG_T(TAG, "writing block %d", block_number); + bit_buffer_reset(instance->tx_buffer); + + // Send Write_block(Addr, Data) + bit_buffer_append_byte(instance->tx_buffer, 0x09); + bit_buffer_append_byte(instance->tx_buffer, block_number); + bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&block, ST25TB_BLOCK_SIZE); + St25tbError ret; + do { + ret = st25tb_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); + if(ret != St25tbErrorTimeout) { // tag doesn't ack writes so timeout are expected. + break; + } + + furi_delay_ms(7); // 7ms is the max programming time as per datasheet + + uint32_t block_check; + ret = st25tb_poller_read_block(instance, &block_check, block_number); + if(ret != St25tbErrorNone) { + FURI_LOG_E(TAG, "write verification failed: read error"); + break; + } + if(block_check != block) { + FURI_LOG_E( + TAG, + "write verification failed: wrote %08lX but read back %08lX", + block, + block_check); + ret = St25tbErrorWriteFailed; + break; + } + FURI_LOG_D(TAG, "wrote %08lX to block %d", block, block_number); } while(false); return ret; @@ -266,30 +303,13 @@ St25tbError st25tb_poller_halt(St25tbPoller* instance) { St25tbError ret; do { - ret = st25tb_poller_frame_exchange( + ret = st25tb_poller_send_frame( instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); if(ret != St25tbErrorTimeout) { break; } - instance->state = St25tbPollerStateIdle; - } while(false); - - return ret; -} - -St25tbError st25tb_poller_send_frame( - St25tbPoller* instance, - const BitBuffer* tx_buffer, - BitBuffer* rx_buffer, - uint32_t fwt) { - St25tbError ret; - - do { - ret = st25tb_poller_prepare_trx(instance); - if(ret != St25tbErrorNone) break; - - ret = st25tb_poller_frame_exchange(instance, tx_buffer, rx_buffer, fwt); + instance->state = St25tbPollerStateSelect; } while(false); return ret; diff --git a/lib/nfc/protocols/st25tb/st25tb_poller_i.h b/lib/nfc/protocols/st25tb/st25tb_poller_i.h index 27218d7b..e16feb78 100644 --- a/lib/nfc/protocols/st25tb/st25tb_poller_i.h +++ b/lib/nfc/protocols/st25tb/st25tb_poller_i.h @@ -1,8 +1,9 @@ #pragma once -#include "protocols/st25tb/st25tb.h" #include "st25tb_poller.h" +#include + #ifdef __cplusplus extern "C" { #endif @@ -10,14 +11,30 @@ extern "C" { #define ST25TB_POLLER_MAX_BUFFER_SIZE (16U) typedef enum { - St25tbPollerStateIdle, - St25tbPollerStateInitiateInProgress, - St25tbPollerStateInitiateFailed, - St25tbPollerStateActivationInProgress, - St25tbPollerStateActivationFailed, - St25tbPollerStateActivated, + St25tbPollerStateSelect, + St25tbPollerStateRequestMode, + St25tbPollerStateRead, + St25tbPollerStateWrite, + St25tbPollerStateSuccess, + St25tbPollerStateFailure, + + St25tbPollerStateNum, } St25tbPollerState; +typedef struct { + uint8_t current_block; +} St25tbPollerReadContext; + +typedef struct { + uint8_t block_number; + uint32_t block_data; +} St25tbPollerWriteContext; + +typedef union { + St25tbPollerReadContext read; + St25tbPollerWriteContext write; +} St25tbPollerContext; + struct St25tbPoller { Nfc* nfc; St25tbPollerState state; @@ -25,6 +42,8 @@ struct St25tbPoller { BitBuffer* tx_buffer; BitBuffer* rx_buffer; + St25tbPollerContext poller_ctx; + NfcGenericEvent general_event; St25tbPollerEvent st25tb_event; St25tbPollerEventData st25tb_event_data; diff --git a/lib/nfc/protocols/st25tb/st25tb_poller_sync.c b/lib/nfc/protocols/st25tb/st25tb_poller_sync.c new file mode 100644 index 00000000..3cd0b379 --- /dev/null +++ b/lib/nfc/protocols/st25tb/st25tb_poller_sync.c @@ -0,0 +1,211 @@ +#include "st25tb_poller_sync.h" +#include "st25tb_poller_i.h" + +#define ST25TB_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0) + +typedef enum { + St25tbPollerCmdTypeDetectType, + St25tbPollerCmdTypeReadBlock, + St25tbPollerCmdTypeWriteBlock, + + St25tbPollerCmdTypeNum, +} St25tbPollerCmdType; + +typedef struct { + St25tbType* type; +} St25tbPollerCmdDetectTypeData; + +typedef struct { + St25tbData* data; +} St25tbPollerCmdReadData; + +typedef struct { + uint8_t block_num; + uint32_t* block; +} St25tbPollerCmdReadBlockData; + +typedef struct { + uint8_t block_num; + uint32_t block; +} St25tbPollerCmdWriteBlockData; + +typedef union { + St25tbPollerCmdDetectTypeData detect_type; + St25tbPollerCmdReadData read; + St25tbPollerCmdReadBlockData read_block; + St25tbPollerCmdWriteBlockData write_block; +} St25tbPollerCmdData; + +typedef struct { + FuriThreadId thread_id; + St25tbError error; + St25tbPollerCmdType cmd_type; + St25tbPollerCmdData cmd_data; +} St25tbPollerSyncContext; + +typedef St25tbError (*St25tbPollerCmdHandler)(St25tbPoller* poller, St25tbPollerCmdData* data); + +static St25tbError st25tb_poller_detect_handler(St25tbPoller* poller, St25tbPollerCmdData* data) { + uint8_t uid[ST25TB_UID_SIZE]; + St25tbError error = st25tb_poller_get_uid(poller, uid); + if(error == St25tbErrorNone) { + *data->detect_type.type = st25tb_get_type_from_uid(uid); + } + return error; +} + +static St25tbError + st25tb_poller_read_block_handler(St25tbPoller* poller, St25tbPollerCmdData* data) { + return st25tb_poller_read_block(poller, data->read_block.block, data->read_block.block_num); +} + +static St25tbError + st25tb_poller_write_block_handler(St25tbPoller* poller, St25tbPollerCmdData* data) { + return st25tb_poller_write_block(poller, data->write_block.block, data->write_block.block_num); +} + +static St25tbPollerCmdHandler st25tb_poller_cmd_handlers[St25tbPollerCmdTypeNum] = { + [St25tbPollerCmdTypeDetectType] = st25tb_poller_detect_handler, + [St25tbPollerCmdTypeReadBlock] = st25tb_poller_read_block_handler, + [St25tbPollerCmdTypeWriteBlock] = st25tb_poller_write_block_handler, +}; + +static NfcCommand st25tb_poller_cmd_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolSt25tb); + + St25tbPollerSyncContext* poller_context = context; + St25tbPoller* st25tb_poller = event.instance; + St25tbPollerEvent* st25tb_event = event.event_data; + + if(st25tb_event->type == St25tbPollerEventTypeReady) { + poller_context->error = st25tb_poller_cmd_handlers[poller_context->cmd_type]( + st25tb_poller, &poller_context->cmd_data); + } else { + poller_context->error = st25tb_event->data->error; + } + + furi_thread_flags_set(poller_context->thread_id, ST25TB_POLLER_FLAG_COMMAND_COMPLETE); + + return NfcCommandStop; +} + +static St25tbError st25tb_poller_cmd_execute(Nfc* nfc, St25tbPollerSyncContext* poller_ctx) { + furi_assert(nfc); + furi_assert(poller_ctx->cmd_type < St25tbPollerCmdTypeNum); + poller_ctx->thread_id = furi_thread_get_current_id(); + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolSt25tb); + nfc_poller_start(poller, st25tb_poller_cmd_callback, poller_ctx); + furi_thread_flags_wait(ST25TB_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(ST25TB_POLLER_FLAG_COMMAND_COMPLETE); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + return poller_ctx->error; +} + +St25tbError st25tb_poller_sync_read_block(Nfc* nfc, uint8_t block_num, uint32_t* block) { + furi_assert(block); + St25tbPollerSyncContext poller_context = { + .cmd_type = St25tbPollerCmdTypeReadBlock, + .cmd_data = + { + .read_block = + { + .block = block, + .block_num = block_num, + }, + }, + }; + return st25tb_poller_cmd_execute(nfc, &poller_context); +} + +St25tbError st25tb_poller_sync_write_block(Nfc* nfc, uint8_t block_num, uint32_t block) { + St25tbPollerSyncContext poller_context = { + .cmd_type = St25tbPollerCmdTypeWriteBlock, + .cmd_data = + { + .write_block = + { + .block = block, + .block_num = block_num, + }, + }, + }; + return st25tb_poller_cmd_execute(nfc, &poller_context); +} + +St25tbError st25tb_poller_sync_detect_type(Nfc* nfc, St25tbType* type) { + furi_assert(type); + St25tbPollerSyncContext poller_context = { + .cmd_type = St25tbPollerCmdTypeDetectType, + .cmd_data = + { + .detect_type = + { + .type = type, + }, + }, + }; + return st25tb_poller_cmd_execute(nfc, &poller_context); +} + +static NfcCommand nfc_scene_read_poller_callback_st25tb(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolSt25tb); + + St25tbPollerSyncContext* poller_context = context; + St25tbPollerEvent* st25tb_event = event.event_data; + + NfcCommand command = NfcCommandContinue; + if(st25tb_event->type == St25tbPollerEventTypeRequestMode) { + st25tb_event->data->mode_request.mode = St25tbPollerModeRead; + } else if( + st25tb_event->type == St25tbPollerEventTypeSuccess || + st25tb_event->type == St25tbPollerEventTypeFailure) { + if(st25tb_event->type == St25tbPollerEventTypeSuccess) { + memcpy( + poller_context->cmd_data.read.data, + st25tb_poller_get_data(event.instance), + sizeof(St25tbData)); + } else { + poller_context->error = st25tb_event->data->error; + } + command = NfcCommandStop; + furi_thread_flags_set(poller_context->thread_id, ST25TB_POLLER_FLAG_COMMAND_COMPLETE); + } + + return command; +} + +St25tbError st25tb_poller_sync_read(Nfc* nfc, St25tbData* data) { + furi_assert(nfc); + furi_assert(data); + + St25tbPollerSyncContext poller_context = { + .thread_id = furi_thread_get_current_id(), + .cmd_data = + { + .read = + { + .data = data, + }, + }, + }; + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolSt25tb); + nfc_poller_start(poller, nfc_scene_read_poller_callback_st25tb, &poller_context); + furi_thread_flags_wait(ST25TB_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(ST25TB_POLLER_FLAG_COMMAND_COMPLETE); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + return poller_context.error; +} \ No newline at end of file diff --git a/lib/nfc/protocols/st25tb/st25tb_poller_sync.h b/lib/nfc/protocols/st25tb/st25tb_poller_sync.h new file mode 100644 index 00000000..ecd994b3 --- /dev/null +++ b/lib/nfc/protocols/st25tb/st25tb_poller_sync.h @@ -0,0 +1,20 @@ +#pragma once + +#include "st25tb.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +St25tbError st25tb_poller_sync_read_block(Nfc* nfc, uint8_t block_num, uint32_t* block); + +St25tbError st25tb_poller_sync_write_block(Nfc* nfc, uint8_t block_num, uint32_t block); + +St25tbError st25tb_poller_sync_detect_type(Nfc* nfc, St25tbType* type); + +St25tbError st25tb_poller_sync_read(Nfc* nfc, St25tbData* data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index cd89b554..e735e2c6 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,48.0,, +Version,+,49.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 45c98ae1..e25254dd 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,48.0,, +Version,+,49.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -145,6 +145,7 @@ Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h,, Header,+,lib/nfc/protocols/slix/slix.h,, Header,+,lib/nfc/protocols/st25tb/st25tb.h,, Header,+,lib/nfc/protocols/st25tb/st25tb_poller.h,, +Header,+,lib/nfc/protocols/st25tb/st25tb_poller_sync.h,, Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_slave.h,, @@ -2810,15 +2811,21 @@ Function,+,st25tb_free,void,St25tbData* Function,+,st25tb_get_base_data,St25tbData*,const St25tbData* Function,+,st25tb_get_block_count,uint8_t,St25tbType Function,+,st25tb_get_device_name,const char*,"const St25tbData*, NfcDeviceNameType" +Function,+,st25tb_get_type_from_uid,St25tbType,const uint8_t* Function,+,st25tb_get_uid,const uint8_t*,"const St25tbData*, size_t*" Function,+,st25tb_is_equal,_Bool,"const St25tbData*, const St25tbData*" Function,+,st25tb_load,_Bool,"St25tbData*, FlipperFormat*, uint32_t" -Function,+,st25tb_poller_activate,St25tbError,"St25tbPoller*, St25tbData*" Function,+,st25tb_poller_get_uid,St25tbError,"St25tbPoller*, uint8_t*" Function,+,st25tb_poller_halt,St25tbError,St25tbPoller* Function,+,st25tb_poller_initiate,St25tbError,"St25tbPoller*, uint8_t*" Function,+,st25tb_poller_read_block,St25tbError,"St25tbPoller*, uint32_t*, uint8_t" +Function,+,st25tb_poller_select,St25tbError,"St25tbPoller*, uint8_t*" Function,+,st25tb_poller_send_frame,St25tbError,"St25tbPoller*, const BitBuffer*, BitBuffer*, uint32_t" +Function,+,st25tb_poller_sync_detect_type,St25tbError,"Nfc*, St25tbType*" +Function,+,st25tb_poller_sync_read,St25tbError,"Nfc*, St25tbData*" +Function,+,st25tb_poller_sync_read_block,St25tbError,"Nfc*, uint8_t, uint32_t*" +Function,+,st25tb_poller_sync_write_block,St25tbError,"Nfc*, uint8_t, uint32_t" +Function,+,st25tb_poller_write_block,St25tbError,"St25tbPoller*, uint32_t, uint8_t" Function,+,st25tb_reset,void,St25tbData* Function,+,st25tb_save,_Bool,"const St25tbData*, FlipperFormat*" Function,+,st25tb_set_uid,_Bool,"St25tbData*, const uint8_t*, size_t"