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:
gornekich
2023-10-24 07:08:09 +04:00
committed by GitHub
parent 35c903494c
commit d92b0a82cc
514 changed files with 41488 additions and 68125 deletions
+300
View File
@@ -0,0 +1,300 @@
#include "iso14443_4a_i.h"
#include <furi.h>
#define ISO14443_4A_PROTOCOL_NAME "ISO14443-4A"
#define ISO14443_4A_DEVICE_NAME "ISO14443-4A (Unknown)"
#define ISO14443_4A_T0_KEY "T0"
#define ISO14443_4A_TA1_KEY "TA(1)"
#define ISO14443_4A_TB1_KEY "TB(1)"
#define ISO14443_4A_TC1_KEY "TC(1)"
#define ISO14443_4A_T1_TK_KEY "T1...Tk"
#define ISO14443_4A_FDT_DEFAULT_FC ISO14443_3A_FDT_POLL_FC
typedef enum {
Iso14443_4aInterfaceByteTA1,
Iso14443_4aInterfaceByteTB1,
Iso14443_4aInterfaceByteTC1,
} Iso14443_4aInterfaceByte;
const NfcDeviceBase nfc_device_iso14443_4a = {
.protocol_name = ISO14443_4A_PROTOCOL_NAME,
.alloc = (NfcDeviceAlloc)iso14443_4a_alloc,
.free = (NfcDeviceFree)iso14443_4a_free,
.reset = (NfcDeviceReset)iso14443_4a_reset,
.copy = (NfcDeviceCopy)iso14443_4a_copy,
.verify = (NfcDeviceVerify)iso14443_4a_verify,
.load = (NfcDeviceLoad)iso14443_4a_load,
.save = (NfcDeviceSave)iso14443_4a_save,
.is_equal = (NfcDeviceEqual)iso14443_4a_is_equal,
.get_name = (NfcDeviceGetName)iso14443_4a_get_device_name,
.get_uid = (NfcDeviceGetUid)iso14443_4a_get_uid,
.set_uid = (NfcDeviceSetUid)iso14443_4a_set_uid,
.get_base_data = (NfcDeviceGetBaseData)iso14443_4a_get_base_data,
};
Iso14443_4aData* iso14443_4a_alloc() {
Iso14443_4aData* data = malloc(sizeof(Iso14443_4aData));
data->iso14443_3a_data = iso14443_3a_alloc();
data->ats_data.t1_tk = simple_array_alloc(&simple_array_config_uint8_t);
return data;
}
void iso14443_4a_free(Iso14443_4aData* data) {
furi_assert(data);
simple_array_free(data->ats_data.t1_tk);
iso14443_3a_free(data->iso14443_3a_data);
free(data);
}
void iso14443_4a_reset(Iso14443_4aData* data) {
furi_assert(data);
iso14443_3a_reset(data->iso14443_3a_data);
data->ats_data.tl = 1;
data->ats_data.t0 = 0;
data->ats_data.ta_1 = 0;
data->ats_data.tb_1 = 0;
data->ats_data.tc_1 = 0;
simple_array_reset(data->ats_data.t1_tk);
}
void iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other) {
furi_assert(data);
furi_assert(other);
iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data);
data->ats_data.tl = other->ats_data.tl;
data->ats_data.t0 = other->ats_data.t0;
data->ats_data.ta_1 = other->ats_data.ta_1;
data->ats_data.tb_1 = other->ats_data.tb_1;
data->ats_data.tc_1 = other->ats_data.tc_1;
simple_array_copy(data->ats_data.t1_tk, other->ats_data.t1_tk);
}
bool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type) {
UNUSED(data);
UNUSED(device_type);
// Empty, unified file format only
return false;
}
bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version) {
furi_assert(data);
bool parsed = false;
do {
if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break;
Iso14443_4aAtsData* ats_data = &data->ats_data;
ats_data->tl = 1;
if(flipper_format_key_exist(ff, ISO14443_4A_T0_KEY)) {
if(!flipper_format_read_hex(ff, ISO14443_4A_T0_KEY, &ats_data->t0, 1)) break;
++ats_data->tl;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TA1) {
if(!flipper_format_key_exist(ff, ISO14443_4A_TA1_KEY)) break;
if(!flipper_format_read_hex(ff, ISO14443_4A_TA1_KEY, &ats_data->ta_1, 1)) break;
++ats_data->tl;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TB1) {
if(!flipper_format_key_exist(ff, ISO14443_4A_TB1_KEY)) break;
if(!flipper_format_read_hex(ff, ISO14443_4A_TB1_KEY, &ats_data->tb_1, 1)) break;
++ats_data->tl;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TC1) {
if(!flipper_format_key_exist(ff, ISO14443_4A_TC1_KEY)) break;
if(!flipper_format_read_hex(ff, ISO14443_4A_TC1_KEY, &ats_data->tc_1, 1)) break;
++ats_data->tl;
}
if(flipper_format_key_exist(ff, ISO14443_4A_T1_TK_KEY)) {
uint32_t t1_tk_size;
if(!flipper_format_get_value_count(ff, ISO14443_4A_T1_TK_KEY, &t1_tk_size)) break;
if(t1_tk_size > 0) {
simple_array_init(ats_data->t1_tk, t1_tk_size);
if(!flipper_format_read_hex(
ff,
ISO14443_4A_T1_TK_KEY,
simple_array_get_data(ats_data->t1_tk),
t1_tk_size))
break;
ats_data->tl += t1_tk_size;
}
}
parsed = true;
} while(false);
return parsed;
}
bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff) {
furi_assert(data);
bool saved = false;
do {
if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break;
if(!flipper_format_write_comment_cstr(ff, ISO14443_4A_PROTOCOL_NAME " specific data"))
break;
const Iso14443_4aAtsData* ats_data = &data->ats_data;
if(ats_data->tl > 1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_T0_KEY, &ats_data->t0, 1)) break;
if(ats_data->t0 & ISO14443_4A_ATS_T0_TA1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_TA1_KEY, &ats_data->ta_1, 1)) break;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TB1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_TB1_KEY, &ats_data->tb_1, 1)) break;
}
if(ats_data->t0 & ISO14443_4A_ATS_T0_TC1) {
if(!flipper_format_write_hex(ff, ISO14443_4A_TC1_KEY, &ats_data->tc_1, 1)) break;
}
const uint32_t t1_tk_size = simple_array_get_count(ats_data->t1_tk);
if(t1_tk_size > 0) {
if(!flipper_format_write_hex(
ff,
ISO14443_4A_T1_TK_KEY,
simple_array_cget_data(ats_data->t1_tk),
t1_tk_size))
break;
}
}
saved = true;
} while(false);
return saved;
}
bool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other) {
return iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data);
}
const char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type) {
UNUSED(data);
UNUSED(name_type);
return ISO14443_4A_DEVICE_NAME;
}
const uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len) {
return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len);
}
bool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len) {
furi_assert(data);
return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len);
}
Iso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data) {
furi_assert(data);
return data->iso14443_3a_data;
}
uint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data) {
furi_assert(data);
const uint8_t fsci = data->ats_data.t0 & 0x0F;
if(fsci < 5) {
return fsci * 8 + 16;
} else if(fsci == 5) {
return 64;
} else if(fsci == 6) {
return 96;
} else if(fsci < 13) {
return 128U << (fsci - 7);
} else {
return 0;
}
}
uint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data) {
furi_assert(data);
uint32_t fwt_fc_max = ISO14443_4A_FDT_DEFAULT_FC;
do {
if(!(data->ats_data.tl > 1)) break;
if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TB1)) break;
const uint8_t fwi = data->ats_data.tb_1 >> 4;
if(fwi == 0x0F) break;
fwt_fc_max = 4096UL << fwi;
} while(false);
return fwt_fc_max;
}
const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count) {
furi_assert(data);
furi_assert(count);
*count = simple_array_get_count(data->ats_data.t1_tk);
return simple_array_cget_data(data->ats_data.t1_tk);
}
bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate) {
furi_assert(data);
if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TA1))
return bit_rate == Iso14443_4aBitRateBoth106Kbit;
const uint8_t ta_1 = data->ats_data.ta_1;
switch(bit_rate) {
case Iso14443_4aBitRateBoth106Kbit:
return ta_1 == ISO14443_4A_ATS_TA1_BOTH_SAME_COMPULSORY;
case Iso14443_4aBitRatePiccToPcd212Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_212KBIT;
case Iso14443_4aBitRatePiccToPcd424Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_424KBIT;
case Iso14443_4aBitRatePiccToPcd848Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_848KBIT;
case Iso14443_4aBitRatePcdToPicc212Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_212KBIT;
case Iso14443_4aBitRatePcdToPicc424Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_424KBIT;
case Iso14443_4aBitRatePcdToPicc848Kbit:
return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_848KBIT;
default:
return false;
}
}
bool iso14443_4a_supports_frame_option(const Iso14443_4aData* data, Iso14443_4aFrameOption option) {
furi_assert(data);
const Iso14443_4aAtsData* ats_data = &data->ats_data;
if(!(ats_data->t0 & ISO14443_4A_ATS_T0_TC1)) return false;
switch(option) {
case Iso14443_4aFrameOptionNad:
return ats_data->tc_1 & ISO14443_4A_ATS_TC1_NAD;
case Iso14443_4aFrameOptionCid:
return ats_data->tc_1 & ISO14443_4A_ATS_TC1_CID;
default:
return false;
}
}
@@ -0,0 +1,73 @@
#pragma once
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_4aErrorNone,
Iso14443_4aErrorNotPresent,
Iso14443_4aErrorProtocol,
Iso14443_4aErrorTimeout,
} Iso14443_4aError;
typedef enum {
Iso14443_4aBitRateBoth106Kbit,
Iso14443_4aBitRatePiccToPcd212Kbit,
Iso14443_4aBitRatePiccToPcd424Kbit,
Iso14443_4aBitRatePiccToPcd848Kbit,
Iso14443_4aBitRatePcdToPicc212Kbit,
Iso14443_4aBitRatePcdToPicc424Kbit,
Iso14443_4aBitRatePcdToPicc848Kbit,
} Iso14443_4aBitRate;
typedef enum {
Iso14443_4aFrameOptionNad,
Iso14443_4aFrameOptionCid,
} Iso14443_4aFrameOption;
typedef struct Iso14443_4aData Iso14443_4aData;
// Virtual methods
Iso14443_4aData* iso14443_4a_alloc();
void iso14443_4a_free(Iso14443_4aData* data);
void iso14443_4a_reset(Iso14443_4aData* data);
void iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other);
bool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type);
bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version);
bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff);
bool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other);
const char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type);
const uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len);
bool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len);
Iso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data);
// Getters & Tests
uint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data);
uint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data);
const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count);
bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate);
bool iso14443_4a_supports_frame_option(const Iso14443_4aData* data, Iso14443_4aFrameOption option);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_device_base_i.h>
extern const NfcDeviceBase nfc_device_iso14443_4a;
@@ -0,0 +1,71 @@
#include "iso14443_4a_i.h"
bool iso14443_4a_ats_parse(Iso14443_4aAtsData* data, const BitBuffer* buf) {
bool can_parse = false;
do {
const size_t buf_size = bit_buffer_get_size_bytes(buf);
if(buf_size == 0) break;
size_t current_index = 0;
const uint8_t tl = bit_buffer_get_byte(buf, current_index++);
if(tl != buf_size) break;
data->tl = tl;
if(tl > 1) {
const uint8_t t0 = bit_buffer_get_byte(buf, current_index++);
const bool has_ta_1 = t0 & ISO14443_4A_ATS_T0_TA1;
const bool has_tb_1 = t0 & ISO14443_4A_ATS_T0_TB1;
const bool has_tc_1 = t0 & ISO14443_4A_ATS_T0_TC1;
const uint8_t buf_size_min =
2 + (has_ta_1 ? 1 : 0) + (has_tb_1 ? 1 : 0) + (has_tc_1 ? 1 : 0);
if(buf_size < buf_size_min) break;
data->t0 = t0;
if(has_ta_1) {
data->ta_1 = bit_buffer_get_byte(buf, current_index++);
}
if(has_tb_1) {
data->tb_1 = bit_buffer_get_byte(buf, current_index++);
}
if(has_tc_1) {
data->tc_1 = bit_buffer_get_byte(buf, current_index++);
}
const uint8_t t1_tk_size = buf_size - buf_size_min;
if(t1_tk_size > 0) {
simple_array_init(data->t1_tk, t1_tk_size);
bit_buffer_write_bytes_mid(
buf, simple_array_get_data(data->t1_tk), current_index, t1_tk_size);
}
}
can_parse = true;
} while(false);
return can_parse;
}
Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error) {
switch(error) {
case Iso14443_3aErrorNone:
return Iso14443_4aErrorNone;
case Iso14443_3aErrorNotPresent:
return Iso14443_4aErrorNotPresent;
case Iso14443_3aErrorColResFailed:
case Iso14443_3aErrorCommunication:
case Iso14443_3aErrorWrongCrc:
return Iso14443_4aErrorProtocol;
case Iso14443_3aErrorTimeout:
return Iso14443_4aErrorTimeout;
default:
return Iso14443_4aErrorProtocol;
}
}
@@ -0,0 +1,42 @@
#pragma once
#include "iso14443_4a.h"
#include <lib/toolbox/simple_array.h>
#define ISO14443_4A_CMD_READ_ATS (0xE0)
// ATS bit definitions
#define ISO14443_4A_ATS_T0_TA1 (1U << 4)
#define ISO14443_4A_ATS_T0_TB1 (1U << 5)
#define ISO14443_4A_ATS_T0_TC1 (1U << 6)
#define ISO14443_4A_ATS_TA1_BOTH_106KBIT (0U << 0)
#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_212KBIT (1U << 0)
#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_424KBIT (1U << 1)
#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_848KBIT (1U << 2)
#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_212KBIT (1U << 4)
#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_424KBIT (1U << 5)
#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_848KBIT (1U << 6)
#define ISO14443_4A_ATS_TA1_BOTH_SAME_COMPULSORY (1U << 7)
#define ISO14443_4A_ATS_TC1_NAD (1U << 0)
#define ISO14443_4A_ATS_TC1_CID (1U << 1)
typedef struct {
uint8_t tl;
uint8_t t0;
uint8_t ta_1;
uint8_t tb_1;
uint8_t tc_1;
SimpleArray* t1_tk;
} Iso14443_4aAtsData;
struct Iso14443_4aData {
Iso14443_3aData* iso14443_3a_data;
Iso14443_4aAtsData ats_data;
};
bool iso14443_4a_ats_parse(Iso14443_4aAtsData* data, const BitBuffer* buf);
Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error);
@@ -0,0 +1,99 @@
#include "iso14443_4a_listener_i.h"
#include <furi.h>
#include <nfc/protocols/nfc_listener_base.h>
#define TAG "Iso14443_4aListener"
#define ISO14443_4A_LISTENER_BUF_SIZE (256U)
static Iso14443_4aListener*
iso14443_4a_listener_alloc(Iso14443_3aListener* iso14443_3a_listener, Iso14443_4aData* data) {
furi_assert(iso14443_3a_listener);
Iso14443_4aListener* instance = malloc(sizeof(Iso14443_4aListener));
instance->iso14443_3a_listener = iso14443_3a_listener;
instance->data = data;
instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_LISTENER_BUF_SIZE);
instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data;
instance->generic_event.protocol = NfcProtocolIso14443_4a;
instance->generic_event.instance = instance;
instance->generic_event.event_data = &instance->iso14443_4a_event;
return instance;
}
static void iso14443_4a_listener_free(Iso14443_4aListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
furi_assert(instance->tx_buffer);
bit_buffer_free(instance->tx_buffer);
free(instance);
}
static void iso14443_4a_listener_set_callback(
Iso14443_4aListener* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
static const Iso14443_4aData* iso14443_4a_listener_get_data(Iso14443_4aListener* instance) {
furi_assert(instance);
furi_assert(instance->data);
return instance->data;
}
static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol == NfcProtocolIso14443_3a);
furi_assert(event.event_data);
Iso14443_4aListener* instance = context;
Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data;
BitBuffer* rx_buffer = iso14443_3a_event->data->buffer;
NfcCommand command = NfcCommandContinue;
if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) {
if(instance->state == Iso14443_4aListenerStateIdle) {
if(bit_buffer_get_size_bytes(rx_buffer) == 2 &&
bit_buffer_get_byte(rx_buffer, 0) == ISO14443_4A_CMD_READ_ATS) {
if(iso14443_4a_listener_send_ats(instance, &instance->data->ats_data) !=
Iso14443_4aErrorNone) {
command = NfcCommandContinue;
} else {
instance->state = Iso14443_4aListenerStateActive;
}
}
} else {
instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData;
instance->iso14443_4a_event.data->buffer = rx_buffer;
if(instance->callback) {
command = instance->callback(instance->generic_event, instance->context);
}
}
} else if(
iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted ||
iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff) {
instance->state = Iso14443_4aListenerStateIdle;
command = NfcCommandContinue;
}
return command;
}
const NfcListenerBase nfc_listener_iso14443_4a = {
.alloc = (NfcListenerAlloc)iso14443_4a_listener_alloc,
.free = (NfcListenerFree)iso14443_4a_listener_free,
.set_callback = (NfcListenerSetCallback)iso14443_4a_listener_set_callback,
.get_data = (NfcListenerGetData)iso14443_4a_listener_get_data,
.run = (NfcListenerRun)iso14443_4a_listener_run,
};
@@ -0,0 +1,29 @@
#pragma once
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h>
#include "iso14443_4a.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_4aListener Iso14443_4aListener;
typedef enum {
Iso14443_4aListenerEventTypeHalted,
Iso14443_4aListenerEventTypeReceivedData,
} Iso14443_4aListenerEventType;
typedef struct {
BitBuffer* buffer;
} Iso14443_4aListenerEventData;
typedef struct {
Iso14443_4aListenerEventType type;
Iso14443_4aListenerEventData* data;
} Iso14443_4aListenerEvent;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_listener_base.h>
extern const NfcListenerBase nfc_listener_iso14443_4a;
@@ -0,0 +1,32 @@
#include "iso14443_4a_listener_i.h"
#include <nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h>
Iso14443_4aError
iso14443_4a_listener_send_ats(Iso14443_4aListener* instance, const Iso14443_4aAtsData* data) {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, data->tl);
if(data->tl > 1) {
bit_buffer_append_byte(instance->tx_buffer, data->t0);
if(data->t0 & ISO14443_4A_ATS_T0_TA1) {
bit_buffer_append_byte(instance->tx_buffer, data->ta_1);
}
if(data->t0 & ISO14443_4A_ATS_T0_TB1) {
bit_buffer_append_byte(instance->tx_buffer, data->tb_1);
}
if(data->t0 & ISO14443_4A_ATS_T0_TC1) {
bit_buffer_append_byte(instance->tx_buffer, data->tc_1);
}
const uint32_t t1_tk_size = simple_array_get_count(data->t1_tk);
if(t1_tk_size != 0) {
bit_buffer_append_bytes(
instance->tx_buffer, simple_array_cget_data(data->t1_tk), t1_tk_size);
}
}
const Iso14443_3aError error = iso14443_3a_listener_send_standard_frame(
instance->iso14443_3a_listener, instance->tx_buffer);
return iso14443_4a_process_error(error);
}
@@ -0,0 +1,36 @@
#pragma once
#include <nfc/protocols/nfc_generic_event.h>
#include "iso14443_4a_listener.h"
#include "iso14443_4a_i.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Iso14443_4aListenerStateIdle,
Iso14443_4aListenerStateActive,
} Iso14443_4aListenerState;
struct Iso14443_4aListener {
Iso14443_3aListener* iso14443_3a_listener;
Iso14443_4aData* data;
Iso14443_4aListenerState state;
BitBuffer* tx_buffer;
NfcGenericEvent generic_event;
Iso14443_4aListenerEvent iso14443_4a_event;
Iso14443_4aListenerEventData iso14443_4a_event_data;
NfcGenericCallback callback;
void* context;
};
Iso14443_4aError
iso14443_4a_listener_send_ats(Iso14443_4aListener* instance, const Iso14443_4aAtsData* data);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,154 @@
#include "iso14443_4a_poller_i.h"
#include <nfc/protocols/nfc_poller_base.h>
#include <furi.h>
#define TAG "Iso14443_4aPoller"
#define ISO14443_4A_POLLER_BUF_SIZE (256U)
typedef NfcCommand (*Iso14443_4aPollerStateHandler)(Iso14443_4aPoller* instance);
const Iso14443_4aData* iso14443_4a_poller_get_data(Iso14443_4aPoller* instance) {
furi_assert(instance);
return instance->data;
}
static Iso14443_4aPoller* iso14443_4a_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) {
Iso14443_4aPoller* instance = malloc(sizeof(Iso14443_4aPoller));
instance->iso14443_3a_poller = iso14443_3a_poller;
instance->data = iso14443_4a_alloc();
instance->iso14443_4_layer = iso14443_4_layer_alloc();
instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE);
instance->rx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE);
instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data;
instance->general_event.protocol = NfcProtocolIso14443_4a;
instance->general_event.event_data = &instance->iso14443_4a_event;
instance->general_event.instance = instance;
return instance;
}
static void iso14443_4a_poller_free(Iso14443_4aPoller* instance) {
furi_assert(instance);
iso14443_4a_free(instance->data);
iso14443_4_layer_free(instance->iso14443_4_layer);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
free(instance);
}
static NfcCommand iso14443_4a_poller_handler_idle(Iso14443_4aPoller* instance) {
iso14443_3a_copy(
instance->data->iso14443_3a_data,
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));
iso14443_4_layer_reset(instance->iso14443_4_layer);
instance->poller_state = Iso14443_4aPollerStateReadAts;
return NfcCommandContinue;
}
static NfcCommand iso14443_4a_poller_handler_read_ats(Iso14443_4aPoller* instance) {
Iso14443_4aError error =
iso14443_4a_poller_async_read_ats(instance, &instance->data->ats_data);
if(error == Iso14443_4aErrorNone) {
FURI_LOG_D(TAG, "Read ATS success");
instance->poller_state = Iso14443_4aPollerStateReady;
} else {
FURI_LOG_D(TAG, "Failed to read ATS");
instance->poller_state = Iso14443_4aPollerStateError;
}
return NfcCommandContinue;
}
static NfcCommand iso14443_4a_poller_handler_error(Iso14443_4aPoller* instance) {
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->iso14443_4a_event_data.error = instance->error;
NfcCommand command = instance->callback(instance->general_event, instance->context);
instance->poller_state = Iso14443_4aPollerStateIdle;
return command;
}
static NfcCommand iso14443_4a_poller_handler_ready(Iso14443_4aPoller* instance) {
instance->iso14443_4a_event.type = Iso14443_4aPollerEventTypeReady;
NfcCommand command = instance->callback(instance->general_event, instance->context);
return command;
}
static const Iso14443_4aPollerStateHandler
iso14443_4a_poller_state_handler[Iso14443_4aPollerStateNum] = {
[Iso14443_4aPollerStateIdle] = iso14443_4a_poller_handler_idle,
[Iso14443_4aPollerStateReadAts] = iso14443_4a_poller_handler_read_ats,
[Iso14443_4aPollerStateError] = iso14443_4a_poller_handler_error,
[Iso14443_4aPollerStateReady] = iso14443_4a_poller_handler_ready,
};
static void iso14443_4a_poller_set_callback(
Iso14443_4aPoller* instance,
NfcGenericCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static NfcCommand iso14443_4a_poller_run(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_3a);
Iso14443_4aPoller* instance = context;
furi_assert(instance);
furi_assert(instance->callback);
Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
furi_assert(iso14443_3a_event);
NfcCommand command = NfcCommandContinue;
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
command = iso14443_4a_poller_state_handler[instance->poller_state](instance);
} else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) {
instance->iso14443_4a_event.type = Iso14443_4aPollerEventTypeError;
command = instance->callback(instance->general_event, instance->context);
}
return command;
}
static bool iso14443_4a_poller_detect(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolIso14443_3a);
const Iso14443_4aPoller* instance = context;
furi_assert(instance);
const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data;
furi_assert(iso14443_3a_event);
iso14443_3a_copy(
instance->data->iso14443_3a_data,
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));
bool protocol_detected = false;
if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) {
protocol_detected = iso14443_3a_supports_iso14443_4(instance->data->iso14443_3a_data);
}
return protocol_detected;
}
const NfcPollerBase nfc_poller_iso14443_4a = {
.alloc = (NfcPollerAlloc)iso14443_4a_poller_alloc,
.free = (NfcPollerFree)iso14443_4a_poller_free,
.set_callback = (NfcPollerSetCallback)iso14443_4a_poller_set_callback,
.run = (NfcPollerRun)iso14443_4a_poller_run,
.detect = (NfcPollerDetect)iso14443_4a_poller_detect,
.get_data = (NfcPollerGetData)iso14443_4a_poller_get_data,
};
@@ -0,0 +1,29 @@
#pragma once
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
#include "iso14443_4a.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Iso14443_4aPoller Iso14443_4aPoller;
typedef enum {
Iso14443_4aPollerEventTypeError,
Iso14443_4aPollerEventTypeReady,
} Iso14443_4aPollerEventType;
typedef struct {
Iso14443_4aError error;
} Iso14443_4aPollerEventData;
typedef struct {
Iso14443_4aPollerEventType type;
Iso14443_4aPollerEventData* data;
} Iso14443_4aPollerEvent;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,5 @@
#pragma once
#include <nfc/protocols/nfc_poller_base.h>
extern const NfcPollerBase nfc_poller_iso14443_4a;
@@ -0,0 +1,83 @@
#include "iso14443_4a_poller_i.h"
#include <furi.h>
#include "iso14443_4a_i.h"
#define TAG "Iso14443_4aPoller"
#define ISO14443_4A_FSDI_256 (0x8U)
Iso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance) {
furi_assert(instance);
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->poller_state = Iso14443_4aPollerStateIdle;
return Iso14443_4aErrorNone;
}
Iso14443_4aError
iso14443_4a_poller_async_read_ats(Iso14443_4aPoller* instance, Iso14443_4aAtsData* data) {
furi_assert(instance);
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, ISO14443_4A_CMD_READ_ATS);
bit_buffer_append_byte(instance->tx_buffer, ISO14443_4A_FSDI_256 << 4);
Iso14443_4aError error = Iso14443_4aErrorNone;
do {
const Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame(
instance->iso14443_3a_poller,
instance->tx_buffer,
instance->rx_buffer,
ISO14443_4A_POLLER_ATS_FWT_FC);
if(iso14443_3a_error != Iso14443_3aErrorNone) {
FURI_LOG_E(TAG, "ATS request failed");
error = iso14443_4a_process_error(iso14443_3a_error);
break;
} else if(!iso14443_4a_ats_parse(data, instance->rx_buffer)) {
FURI_LOG_E(TAG, "Failed to parse ATS response");
error = Iso14443_4aErrorProtocol;
break;
}
} while(false);
return error;
}
Iso14443_4aError iso14443_4a_poller_send_block(
Iso14443_4aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) {
furi_assert(instance);
bit_buffer_reset(instance->tx_buffer);
iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer);
Iso14443_4aError error = Iso14443_4aErrorNone;
do {
Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame(
instance->iso14443_3a_poller,
instance->tx_buffer,
instance->rx_buffer,
iso14443_4a_get_fwt_fc_max(instance->data));
if(iso14443_3a_error != Iso14443_3aErrorNone) {
error = iso14443_4a_process_error(iso14443_3a_error);
break;
} else if(!iso14443_4_layer_decode_block(
instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) {
error = Iso14443_4aErrorProtocol;
break;
}
} while(false);
return error;
}
@@ -0,0 +1,62 @@
#pragma once
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>
#include <nfc/helpers/iso14443_4_layer.h>
#include "iso14443_4a_poller.h"
#include "iso14443_4a_i.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ISO14443_4A_POLLER_ATS_FWT_FC (40000)
typedef enum {
Iso14443_4aPollerStateIdle,
Iso14443_4aPollerStateReadAts,
Iso14443_4aPollerStateError,
Iso14443_4aPollerStateReady,
Iso14443_4aPollerStateNum,
} Iso14443_4aPollerState;
typedef enum {
Iso14443_4aPollerSessionStateIdle,
Iso14443_4aPollerSessionStateActive,
Iso14443_4aPollerSessionStateStopRequest,
} Iso14443_4aPollerSessionState;
struct Iso14443_4aPoller {
Iso14443_3aPoller* iso14443_3a_poller;
Iso14443_4aPollerState poller_state;
Iso14443_4aPollerSessionState session_state;
Iso14443_4aError error;
Iso14443_4aData* data;
Iso14443_4Layer* iso14443_4_layer;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
Iso14443_4aPollerEventData iso14443_4a_event_data;
Iso14443_4aPollerEvent iso14443_4a_event;
NfcGenericEvent general_event;
NfcGenericCallback callback;
void* context;
};
Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error);
const Iso14443_4aData* iso14443_4a_poller_get_data(Iso14443_4aPoller* instance);
Iso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance);
Iso14443_4aError
iso14443_4a_poller_async_read_ats(Iso14443_4aPoller* instance, Iso14443_4aAtsData* data);
Iso14443_4aError iso14443_4a_poller_send_block(
Iso14443_4aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer);
#ifdef __cplusplus
}
#endif