NFC refactoring (#3050)
"A long time ago in a galaxy far, far away...." we started NFC subsystem refactoring. Starring: - @gornekich - NFC refactoring project lead, architect, senior developer - @gsurkov - architect, senior developer - @RebornedBrain - senior developer Supporting roles: - @skotopes, @DrZlo13, @hedger - general architecture advisors, code review - @Astrrra, @doomwastaken, @Hellitron, @ImagineVagon333 - quality assurance Special thanks: @bettse, @pcunning, @nxv, @noproto, @AloneLiberty and everyone else who has been helping us all this time and contributing valuable knowledges, ideas and source code.
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
#include "st25tb.h"
|
||||
|
||||
#include "core/string.h"
|
||||
#include "flipper_format.h"
|
||||
#include <furi.h>
|
||||
|
||||
#include <nfc/nfc_common.h>
|
||||
#include <nfc/helpers/iso14443_crc.h>
|
||||
|
||||
#define ST25TB_PROTOCOL_NAME "ST25TB"
|
||||
#define ST25TB_TYPE_KEY "ST25TB Type"
|
||||
#define ST25TB_BLOCK_KEY "Block %d"
|
||||
#define ST25TB_SYSTEM_BLOCK_KEY "System OTP Block"
|
||||
|
||||
typedef struct {
|
||||
uint8_t blocks_total;
|
||||
bool has_otp;
|
||||
const char* full_name;
|
||||
const char* type_name;
|
||||
} St25tbFeatures;
|
||||
|
||||
static const St25tbFeatures st25tb_features[St25tbTypeNum] = {
|
||||
[St25tbType512At] =
|
||||
{
|
||||
.blocks_total = 16,
|
||||
.has_otp = false,
|
||||
.full_name = "ST25TB512-AT/SRI512",
|
||||
.type_name = "512AT",
|
||||
},
|
||||
[St25tbType512Ac] =
|
||||
{
|
||||
.blocks_total = 16,
|
||||
.has_otp = true,
|
||||
.full_name = "ST25TB512-AC/SRT512",
|
||||
.type_name = "512AC",
|
||||
},
|
||||
[St25tbTypeX512] =
|
||||
{
|
||||
.blocks_total = 16,
|
||||
.has_otp = true,
|
||||
.full_name = "SRIX512",
|
||||
.type_name = "X512",
|
||||
},
|
||||
[St25tbType02k] =
|
||||
{
|
||||
.blocks_total = 64,
|
||||
.has_otp = true,
|
||||
.full_name = "ST25TB02K/SRI2K",
|
||||
.type_name = "2K",
|
||||
},
|
||||
[St25tbType04k] =
|
||||
{
|
||||
.blocks_total = 128,
|
||||
.has_otp = true,
|
||||
.full_name = "ST25TB04K/SRI4K",
|
||||
.type_name = "4K",
|
||||
},
|
||||
[St25tbTypeX4k] =
|
||||
{
|
||||
.blocks_total = 128,
|
||||
.has_otp = true,
|
||||
.full_name = "SRIX4K",
|
||||
.type_name = "X4K",
|
||||
},
|
||||
};
|
||||
|
||||
const NfcDeviceBase nfc_device_st25tb = {
|
||||
.protocol_name = ST25TB_PROTOCOL_NAME,
|
||||
.alloc = (NfcDeviceAlloc)st25tb_alloc,
|
||||
.free = (NfcDeviceFree)st25tb_free,
|
||||
.reset = (NfcDeviceReset)st25tb_reset,
|
||||
.copy = (NfcDeviceCopy)st25tb_copy,
|
||||
.verify = (NfcDeviceVerify)st25tb_verify,
|
||||
.load = (NfcDeviceLoad)st25tb_load,
|
||||
.save = (NfcDeviceSave)st25tb_save,
|
||||
.is_equal = (NfcDeviceEqual)st25tb_is_equal,
|
||||
.get_name = (NfcDeviceGetName)st25tb_get_device_name,
|
||||
.get_uid = (NfcDeviceGetUid)st25tb_get_uid,
|
||||
.set_uid = (NfcDeviceSetUid)st25tb_set_uid,
|
||||
.get_base_data = (NfcDeviceGetBaseData)st25tb_get_base_data,
|
||||
};
|
||||
|
||||
St25tbData* st25tb_alloc() {
|
||||
St25tbData* data = malloc(sizeof(St25tbData));
|
||||
return data;
|
||||
}
|
||||
|
||||
void st25tb_free(St25tbData* data) {
|
||||
furi_assert(data);
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
void st25tb_reset(St25tbData* data) {
|
||||
memset(data, 0, sizeof(St25tbData));
|
||||
}
|
||||
|
||||
void st25tb_copy(St25tbData* data, const St25tbData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
|
||||
*data = *other;
|
||||
}
|
||||
|
||||
bool st25tb_verify(St25tbData* data, const FuriString* device_type) {
|
||||
UNUSED(data);
|
||||
return furi_string_equal_str(device_type, ST25TB_PROTOCOL_NAME);
|
||||
}
|
||||
|
||||
bool st25tb_load(St25tbData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
|
||||
bool parsed = false;
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
|
||||
do {
|
||||
if(version < NFC_UNIFIED_FORMAT_VERSION) break;
|
||||
if(!flipper_format_read_string(ff, ST25TB_TYPE_KEY, temp_str)) break;
|
||||
|
||||
bool type_parsed = false;
|
||||
for(size_t i = 0; i < St25tbTypeNum; i++) {
|
||||
if(furi_string_equal_str(temp_str, st25tb_features[i].type_name)) {
|
||||
data->type = i;
|
||||
type_parsed = true;
|
||||
}
|
||||
}
|
||||
if(!type_parsed) break;
|
||||
|
||||
bool blocks_parsed = true;
|
||||
for(uint8_t block = 0; block < st25tb_features[data->type].blocks_total; block++) {
|
||||
furi_string_printf(temp_str, ST25TB_BLOCK_KEY, block);
|
||||
if(!flipper_format_read_hex(
|
||||
ff, furi_string_get_cstr(temp_str), (uint8_t*)&data->blocks[block], 4)) {
|
||||
blocks_parsed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!blocks_parsed) break;
|
||||
|
||||
if(!flipper_format_read_hex(
|
||||
ff, ST25TB_SYSTEM_BLOCK_KEY, (uint8_t*)&data->system_otp_block, 4))
|
||||
break;
|
||||
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
bool st25tb_save(const St25tbData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
bool saved = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_write_comment_cstr(ff, ST25TB_PROTOCOL_NAME " specific data")) break;
|
||||
if(!flipper_format_write_string_cstr(
|
||||
ff, ST25TB_TYPE_KEY, st25tb_features[data->type].type_name))
|
||||
break;
|
||||
|
||||
bool blocks_saved = true;
|
||||
for(uint8_t block = 0; block < st25tb_features[data->type].blocks_total; block++) {
|
||||
furi_string_printf(temp_str, ST25TB_BLOCK_KEY, block);
|
||||
if(!flipper_format_write_hex(
|
||||
ff, furi_string_get_cstr(temp_str), (uint8_t*)&data->blocks[block], 4)) {
|
||||
blocks_saved = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!blocks_saved) break;
|
||||
|
||||
if(!flipper_format_write_hex(
|
||||
ff, ST25TB_SYSTEM_BLOCK_KEY, (uint8_t*)&data->system_otp_block, 4))
|
||||
break;
|
||||
|
||||
saved = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool st25tb_is_equal(const St25tbData* data, const St25tbData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
|
||||
return memcmp(data, other, sizeof(St25tbData)) == 0;
|
||||
}
|
||||
|
||||
uint8_t st25tb_get_block_count(St25tbType type) {
|
||||
return st25tb_features[type].blocks_total;
|
||||
}
|
||||
|
||||
const char* st25tb_get_device_name(const St25tbData* data, NfcDeviceNameType name_type) {
|
||||
furi_assert(data);
|
||||
furi_assert(data->type < St25tbTypeNum);
|
||||
|
||||
if(name_type == NfcDeviceNameTypeFull) {
|
||||
return st25tb_features[data->type].full_name;
|
||||
} else {
|
||||
return st25tb_features[data->type].type_name;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t* st25tb_get_uid(const St25tbData* data, size_t* uid_len) {
|
||||
furi_assert(data);
|
||||
|
||||
if(uid_len) {
|
||||
*uid_len = ST25TB_UID_SIZE;
|
||||
}
|
||||
|
||||
return data->uid;
|
||||
}
|
||||
|
||||
bool st25tb_set_uid(St25tbData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
|
||||
const bool uid_valid = uid_len == ST25TB_UID_SIZE;
|
||||
|
||||
if(uid_valid) {
|
||||
memcpy(data->uid, uid, uid_len);
|
||||
}
|
||||
|
||||
return uid_valid;
|
||||
}
|
||||
|
||||
St25tbData* st25tb_get_base_data(const St25tbData* data) {
|
||||
UNUSED(data);
|
||||
furi_crash("No base data");
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <toolbox/bit_buffer.h>
|
||||
#include <nfc/protocols/nfc_device_base_i.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ST25TB_UID_SIZE (8U)
|
||||
|
||||
//#define ST25TB_FDT_FC (4205U)
|
||||
#define ST25TB_FDT_FC (8494U)
|
||||
#define ST25TB_GUARD_TIME_US (5000U)
|
||||
#define ST25TB_POLL_POLL_MIN_US (1280U)
|
||||
|
||||
#define ST25TB_MAX_BLOCKS (128U)
|
||||
#define ST25TB_SYSTEM_OTP_BLOCK (0xFFU)
|
||||
#define ST25TB_BLOCK_SIZE (4U)
|
||||
|
||||
typedef enum {
|
||||
St25tbErrorNone,
|
||||
St25tbErrorNotPresent,
|
||||
St25tbErrorColResFailed,
|
||||
St25tbErrorBufferOverflow,
|
||||
St25tbErrorCommunication,
|
||||
St25tbErrorFieldOff,
|
||||
St25tbErrorWrongCrc,
|
||||
St25tbErrorTimeout,
|
||||
} St25tbError;
|
||||
|
||||
typedef enum {
|
||||
St25tbType512At,
|
||||
St25tbType512Ac,
|
||||
St25tbTypeX512,
|
||||
St25tbType02k,
|
||||
St25tbType04k,
|
||||
St25tbTypeX4k,
|
||||
St25tbTypeNum,
|
||||
} St25tbType;
|
||||
|
||||
typedef struct {
|
||||
uint8_t uid[ST25TB_UID_SIZE];
|
||||
St25tbType type;
|
||||
uint32_t blocks[ST25TB_MAX_BLOCKS];
|
||||
uint32_t system_otp_block;
|
||||
uint8_t chip_id;
|
||||
} St25tbData;
|
||||
|
||||
extern const NfcDeviceBase nfc_device_st25tb;
|
||||
|
||||
St25tbData* st25tb_alloc();
|
||||
|
||||
void st25tb_free(St25tbData* data);
|
||||
|
||||
void st25tb_reset(St25tbData* data);
|
||||
|
||||
void st25tb_copy(St25tbData* data, const St25tbData* other);
|
||||
|
||||
bool st25tb_verify(St25tbData* data, const FuriString* device_type);
|
||||
|
||||
bool st25tb_load(St25tbData* data, FlipperFormat* ff, uint32_t version);
|
||||
|
||||
bool st25tb_save(const St25tbData* data, FlipperFormat* ff);
|
||||
|
||||
bool st25tb_is_equal(const St25tbData* data, const St25tbData* other);
|
||||
|
||||
uint8_t st25tb_get_block_count(St25tbType type);
|
||||
|
||||
const char* st25tb_get_device_name(const St25tbData* data, NfcDeviceNameType name_type);
|
||||
|
||||
const uint8_t* st25tb_get_uid(const St25tbData* data, size_t* uid_len);
|
||||
|
||||
bool st25tb_set_uid(St25tbData* data, const uint8_t* uid, size_t uid_len);
|
||||
|
||||
St25tbData* st25tb_get_base_data(const St25tbData* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "protocols/nfc_protocol.h"
|
||||
#include "protocols/st25tb/st25tb.h"
|
||||
#include "st25tb_poller_i.h"
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "ST25TBPoller"
|
||||
|
||||
const St25tbData* st25tb_poller_get_data(St25tbPoller* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->data);
|
||||
|
||||
return instance->data;
|
||||
}
|
||||
|
||||
static St25tbPoller* st25tb_poller_alloc(Nfc* nfc) {
|
||||
furi_assert(nfc);
|
||||
|
||||
St25tbPoller* instance = malloc(sizeof(St25tbPoller));
|
||||
instance->nfc = nfc;
|
||||
instance->tx_buffer = bit_buffer_alloc(ST25TB_POLLER_MAX_BUFFER_SIZE);
|
||||
instance->rx_buffer = bit_buffer_alloc(ST25TB_POLLER_MAX_BUFFER_SIZE);
|
||||
|
||||
// RF configuration is the same as 14b
|
||||
nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443b);
|
||||
nfc_set_guard_time_us(instance->nfc, ST25TB_GUARD_TIME_US);
|
||||
nfc_set_fdt_poll_fc(instance->nfc, ST25TB_FDT_FC);
|
||||
nfc_set_fdt_poll_poll_us(instance->nfc, ST25TB_POLL_POLL_MIN_US);
|
||||
instance->data = st25tb_alloc();
|
||||
|
||||
instance->st25tb_event.data = &instance->st25tb_event_data;
|
||||
instance->general_event.protocol = NfcProtocolSt25tb;
|
||||
instance->general_event.event_data = &instance->st25tb_event;
|
||||
instance->general_event.instance = instance;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void st25tb_poller_free(St25tbPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
furi_assert(instance->tx_buffer);
|
||||
furi_assert(instance->rx_buffer);
|
||||
furi_assert(instance->data);
|
||||
|
||||
bit_buffer_free(instance->tx_buffer);
|
||||
bit_buffer_free(instance->rx_buffer);
|
||||
st25tb_free(instance->data);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static void
|
||||
st25tb_poller_set_callback(St25tbPoller* instance, NfcGenericCallback callback, void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
static NfcCommand st25tb_poller_run(NfcGenericEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
furi_assert(event.protocol == NfcProtocolInvalid);
|
||||
furi_assert(event.event_data);
|
||||
|
||||
St25tbPoller* instance = context;
|
||||
NfcEvent* nfc_event = event.event_data;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(nfc_event->type == NfcEventTypePollerReady) {
|
||||
if(instance->state != St25tbPollerStateActivated) {
|
||||
St25tbError error = st25tb_poller_async_activate(instance, instance->data);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static bool st25tb_poller_detect(NfcGenericEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
furi_assert(event.event_data);
|
||||
furi_assert(event.instance);
|
||||
furi_assert(event.protocol == NfcProtocolInvalid);
|
||||
|
||||
bool protocol_detected = false;
|
||||
St25tbPoller* instance = context;
|
||||
NfcEvent* nfc_event = event.event_data;
|
||||
furi_assert(instance->state == St25tbPollerStateIdle);
|
||||
|
||||
if(nfc_event->type == NfcEventTypePollerReady) {
|
||||
St25tbError error = st25tb_poller_async_initiate(instance, NULL);
|
||||
protocol_detected = (error == St25tbErrorNone);
|
||||
}
|
||||
|
||||
return protocol_detected;
|
||||
}
|
||||
|
||||
const NfcPollerBase nfc_poller_st25tb = {
|
||||
.alloc = (NfcPollerAlloc)st25tb_poller_alloc,
|
||||
.free = (NfcPollerFree)st25tb_poller_free,
|
||||
.set_callback = (NfcPollerSetCallback)st25tb_poller_set_callback,
|
||||
.run = (NfcPollerRun)st25tb_poller_run,
|
||||
.detect = (NfcPollerDetect)st25tb_poller_detect,
|
||||
.get_data = (NfcPollerGetData)st25tb_poller_get_data,
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "st25tb.h"
|
||||
#include <lib/nfc/nfc.h>
|
||||
|
||||
#include <nfc/nfc_poller.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct St25tbPoller St25tbPoller;
|
||||
|
||||
typedef enum {
|
||||
St25tbPollerEventTypeError,
|
||||
St25tbPollerEventTypeReady,
|
||||
} St25tbPollerEventType;
|
||||
|
||||
typedef struct {
|
||||
St25tbError error;
|
||||
} St25tbPollerEventData;
|
||||
|
||||
typedef struct {
|
||||
St25tbPollerEventType type;
|
||||
St25tbPollerEventData* data;
|
||||
} St25tbPollerEvent;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const NfcPollerBase nfc_poller_st25tb;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,297 @@
|
||||
#include "st25tb_poller_i.h"
|
||||
|
||||
#include "bit_buffer.h"
|
||||
#include "core/core_defines.h"
|
||||
#include "protocols/st25tb/st25tb.h"
|
||||
#include <nfc/helpers/iso14443_crc.h>
|
||||
|
||||
#define TAG "ST25TBPoller"
|
||||
|
||||
static St25tbError st25tb_poller_process_error(NfcError error) {
|
||||
switch(error) {
|
||||
case NfcErrorNone:
|
||||
return St25tbErrorNone;
|
||||
case NfcErrorTimeout:
|
||||
return St25tbErrorTimeout;
|
||||
default:
|
||||
return St25tbErrorNotPresent;
|
||||
}
|
||||
}
|
||||
|
||||
static St25tbError st25tb_poller_prepare_trx(St25tbPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(instance->state == St25tbPollerStateIdle) {
|
||||
return st25tb_poller_async_activate(instance, NULL);
|
||||
}
|
||||
|
||||
return St25tbErrorNone;
|
||||
}
|
||||
|
||||
static St25tbError st25tb_poller_frame_exchange(
|
||||
St25tbPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
furi_assert(instance);
|
||||
|
||||
const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer);
|
||||
furi_assert(
|
||||
tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO14443_CRC_SIZE);
|
||||
|
||||
bit_buffer_copy(instance->tx_buffer, tx_buffer);
|
||||
iso14443_crc_append(Iso14443CrcTypeB, instance->tx_buffer);
|
||||
|
||||
St25tbError ret = St25tbErrorNone;
|
||||
|
||||
do {
|
||||
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);
|
||||
ret = st25tb_poller_process_error(error);
|
||||
break;
|
||||
}
|
||||
|
||||
bit_buffer_copy(rx_buffer, instance->rx_buffer);
|
||||
if(!iso14443_crc_check(Iso14443CrcTypeB, instance->rx_buffer)) {
|
||||
ret = St25tbErrorWrongCrc;
|
||||
break;
|
||||
}
|
||||
|
||||
iso14443_crc_trim(rx_buffer);
|
||||
} while(false);
|
||||
|
||||
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_async_initiate(St25tbPoller* instance, uint8_t* chip_id) {
|
||||
// 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);
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0x00);
|
||||
|
||||
St25tbError ret;
|
||||
do {
|
||||
ret = st25tb_poller_frame_exchange(
|
||||
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");
|
||||
ret = St25tbErrorCommunication;
|
||||
break;
|
||||
}
|
||||
if(chip_id) {
|
||||
*chip_id = bit_buffer_get_byte(instance->rx_buffer, 0);
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
St25tbError st25tb_poller_async_activate(St25tbPoller* instance, St25tbData* data) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
|
||||
st25tb_reset(data);
|
||||
|
||||
St25tbError ret;
|
||||
|
||||
do {
|
||||
ret = st25tb_poller_async_initiate(instance, &data->chip_id);
|
||||
if(ret != St25tbErrorNone) {
|
||||
break;
|
||||
}
|
||||
|
||||
instance->state = St25tbPollerStateActivationInProgress;
|
||||
|
||||
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);
|
||||
|
||||
ret = st25tb_poller_frame_exchange(
|
||||
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;
|
||||
ret = St25tbErrorCommunication;
|
||||
break;
|
||||
}
|
||||
|
||||
if(bit_buffer_get_byte(instance->rx_buffer, 0) != data->chip_id) {
|
||||
FURI_LOG_D(TAG, "ChipID mismatch");
|
||||
instance->state = St25tbPollerStateActivationFailed;
|
||||
ret = St25tbErrorColResFailed;
|
||||
break;
|
||||
}
|
||||
instance->state = St25tbPollerStateActivated;
|
||||
|
||||
ret = st25tb_poller_async_get_uid(instance, data->uid);
|
||||
if(ret != St25tbErrorNone) {
|
||||
instance->state = St25tbPollerStateActivationFailed;
|
||||
break;
|
||||
}
|
||||
data->type = st25tb_get_type_from_uid(data->uid);
|
||||
|
||||
bool read_blocks = true;
|
||||
for(uint8_t i = 0; i < st25tb_get_block_count(data->type); i++) {
|
||||
ret = st25tb_poller_async_read_block(instance, &data->blocks[i], i);
|
||||
if(ret != St25tbErrorNone) {
|
||||
read_blocks = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!read_blocks) {
|
||||
break;
|
||||
}
|
||||
ret = st25tb_poller_async_read_block(
|
||||
instance, &data->system_otp_block, ST25TB_SYSTEM_OTP_BLOCK);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
St25tbError st25tb_poller_async_get_uid(St25tbPoller* instance, uint8_t uid[ST25TB_UID_SIZE]) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
|
||||
St25tbError ret;
|
||||
|
||||
do {
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0x0B);
|
||||
|
||||
ret = st25tb_poller_frame_exchange(
|
||||
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;
|
||||
ret = St25tbErrorCommunication;
|
||||
break;
|
||||
}
|
||||
bit_buffer_write_bytes(instance->rx_buffer, uid, ST25TB_UID_SIZE);
|
||||
FURI_SWAP(uid[0], uid[7]);
|
||||
FURI_SWAP(uid[1], uid[6]);
|
||||
FURI_SWAP(uid[2], uid[5]);
|
||||
FURI_SWAP(uid[3], uid[4]);
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
St25tbError
|
||||
st25tb_poller_async_read_block(St25tbPoller* instance, uint32_t* block, uint8_t block_number) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
furi_assert(block);
|
||||
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);
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
// Send Read_block(Addr)
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0x08);
|
||||
bit_buffer_append_byte(instance->tx_buffer, block_number);
|
||||
St25tbError ret;
|
||||
do {
|
||||
ret = st25tb_poller_frame_exchange(
|
||||
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");
|
||||
ret = St25tbErrorCommunication;
|
||||
break;
|
||||
}
|
||||
bit_buffer_write_bytes(instance->rx_buffer, block, ST25TB_BLOCK_SIZE);
|
||||
FURI_LOG_D(TAG, "read result: %08lX", *block);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
St25tbError st25tb_poller_halt(St25tbPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
// Send Completion()
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0x0F);
|
||||
|
||||
St25tbError ret;
|
||||
|
||||
do {
|
||||
ret = st25tb_poller_frame_exchange(
|
||||
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);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "protocols/st25tb/st25tb.h"
|
||||
#include "st25tb_poller.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ST25TB_POLLER_MAX_BUFFER_SIZE (16U)
|
||||
|
||||
typedef enum {
|
||||
St25tbPollerStateIdle,
|
||||
St25tbPollerStateInitiateInProgress,
|
||||
St25tbPollerStateInitiateFailed,
|
||||
St25tbPollerStateActivationInProgress,
|
||||
St25tbPollerStateActivationFailed,
|
||||
St25tbPollerStateActivated,
|
||||
} St25tbPollerState;
|
||||
|
||||
struct St25tbPoller {
|
||||
Nfc* nfc;
|
||||
St25tbPollerState state;
|
||||
St25tbData* data;
|
||||
BitBuffer* tx_buffer;
|
||||
BitBuffer* rx_buffer;
|
||||
|
||||
NfcGenericEvent general_event;
|
||||
St25tbPollerEvent st25tb_event;
|
||||
St25tbPollerEventData st25tb_event_data;
|
||||
NfcGenericCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
const St25tbData* st25tb_poller_get_data(St25tbPoller* instance);
|
||||
|
||||
St25tbError st25tb_poller_async_initiate(St25tbPoller* instance, uint8_t* chip_id);
|
||||
|
||||
St25tbError st25tb_poller_async_activate(St25tbPoller* instance, St25tbData* data);
|
||||
|
||||
St25tbError st25tb_poller_async_get_uid(St25tbPoller* instance, uint8_t uid[ST25TB_UID_SIZE]);
|
||||
|
||||
St25tbError
|
||||
st25tb_poller_async_read_block(St25tbPoller* instance, uint32_t* block, uint8_t block_number);
|
||||
|
||||
St25tbError st25tb_poller_halt(St25tbPoller* instance);
|
||||
|
||||
St25tbError st25tb_poller_send_frame(
|
||||
St25tbPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user