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,358 @@
|
||||
#include "iso15693_3.h"
|
||||
#include "iso15693_3_device_defs.h"
|
||||
|
||||
#include <nfc/nfc_common.h>
|
||||
|
||||
#define ISO15693_3_PROTOCOL_NAME "ISO15693-3"
|
||||
#define ISO15693_3_PROTOCOL_NAME_LEGACY "ISO15693"
|
||||
#define ISO15693_3_DEVICE_NAME "ISO15693-3 (Unknown)"
|
||||
|
||||
#define ISO15693_3_LOCK_DSFID_LEGACY (1U << 0)
|
||||
#define ISO15693_3_LOCK_AFI_LEGACY (1U << 1)
|
||||
|
||||
#define ISO15693_3_DSFID_KEY "DSFID"
|
||||
#define ISO15693_3_AFI_KEY "AFI"
|
||||
#define ISO15693_3_IC_REF_KEY "IC Reference"
|
||||
#define ISO15693_3_BLOCK_COUNT_KEY "Block Count"
|
||||
#define ISO15693_3_BLOCK_SIZE_KEY "Block Size"
|
||||
#define ISO15693_3_DATA_CONTENT_KEY "Data Content"
|
||||
#define ISO15693_3_LOCK_DSFID_KEY "Lock DSFID"
|
||||
#define ISO15693_3_LOCK_AFI_KEY "Lock AFI"
|
||||
#define ISO15693_3_SECURITY_STATUS_KEY "Security Status"
|
||||
|
||||
const NfcDeviceBase nfc_device_iso15693_3 = {
|
||||
.protocol_name = ISO15693_3_PROTOCOL_NAME,
|
||||
.alloc = (NfcDeviceAlloc)iso15693_3_alloc,
|
||||
.free = (NfcDeviceFree)iso15693_3_free,
|
||||
.reset = (NfcDeviceReset)iso15693_3_reset,
|
||||
.copy = (NfcDeviceCopy)iso15693_3_copy,
|
||||
.verify = (NfcDeviceVerify)iso15693_3_verify,
|
||||
.load = (NfcDeviceLoad)iso15693_3_load,
|
||||
.save = (NfcDeviceSave)iso15693_3_save,
|
||||
.is_equal = (NfcDeviceEqual)iso15693_3_is_equal,
|
||||
.get_name = (NfcDeviceGetName)iso15693_3_get_device_name,
|
||||
.get_uid = (NfcDeviceGetUid)iso15693_3_get_uid,
|
||||
.set_uid = (NfcDeviceSetUid)iso15693_3_set_uid,
|
||||
.get_base_data = (NfcDeviceGetBaseData)iso15693_3_get_base_data,
|
||||
};
|
||||
|
||||
Iso15693_3Data* iso15693_3_alloc() {
|
||||
Iso15693_3Data* data = malloc(sizeof(Iso15693_3Data));
|
||||
|
||||
data->block_data = simple_array_alloc(&simple_array_config_uint8_t);
|
||||
data->block_security = simple_array_alloc(&simple_array_config_uint8_t);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void iso15693_3_free(Iso15693_3Data* data) {
|
||||
furi_assert(data);
|
||||
|
||||
simple_array_free(data->block_data);
|
||||
simple_array_free(data->block_security);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void iso15693_3_reset(Iso15693_3Data* data) {
|
||||
furi_assert(data);
|
||||
|
||||
memset(data->uid, 0, ISO15693_3_UID_SIZE);
|
||||
memset(&data->system_info, 0, sizeof(Iso15693_3SystemInfo));
|
||||
memset(&data->settings, 0, sizeof(Iso15693_3Settings));
|
||||
|
||||
simple_array_reset(data->block_data);
|
||||
simple_array_reset(data->block_security);
|
||||
}
|
||||
|
||||
void iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
|
||||
memcpy(data->uid, other->uid, ISO15693_3_UID_SIZE);
|
||||
|
||||
data->system_info = other->system_info;
|
||||
data->settings = other->settings;
|
||||
|
||||
simple_array_copy(data->block_data, other->block_data);
|
||||
simple_array_copy(data->block_security, other->block_security);
|
||||
}
|
||||
|
||||
bool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type) {
|
||||
UNUSED(data);
|
||||
return furi_string_equal(device_type, ISO15693_3_PROTOCOL_NAME_LEGACY);
|
||||
}
|
||||
|
||||
static inline bool iso15693_3_load_security_legacy(Iso15693_3Data* data, FlipperFormat* ff) {
|
||||
bool loaded = false;
|
||||
uint8_t* legacy_data = NULL;
|
||||
|
||||
do {
|
||||
uint32_t value_count;
|
||||
if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count))
|
||||
break;
|
||||
if(simple_array_get_count(data->block_security) + 1 != value_count) break;
|
||||
|
||||
legacy_data = malloc(value_count);
|
||||
if(!flipper_format_read_hex(ff, ISO15693_3_SECURITY_STATUS_KEY, legacy_data, value_count))
|
||||
break;
|
||||
|
||||
// First legacy data byte is lock bits
|
||||
data->settings.lock_bits.dsfid = legacy_data[0] & ISO15693_3_LOCK_DSFID_LEGACY;
|
||||
data->settings.lock_bits.afi = legacy_data[0] & ISO15693_3_LOCK_AFI_LEGACY;
|
||||
|
||||
// The rest are block security
|
||||
memcpy(
|
||||
&legacy_data[1],
|
||||
simple_array_get_data(data->block_security),
|
||||
simple_array_get_count(data->block_security));
|
||||
|
||||
loaded = true;
|
||||
} while(false);
|
||||
|
||||
if(legacy_data) free(legacy_data);
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
static inline bool iso15693_3_load_security(Iso15693_3Data* data, FlipperFormat* ff) {
|
||||
bool loaded = false;
|
||||
|
||||
do {
|
||||
uint32_t value_count;
|
||||
if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count))
|
||||
break;
|
||||
if(simple_array_get_count(data->block_security) != value_count) break;
|
||||
if(!flipper_format_read_hex(
|
||||
ff,
|
||||
ISO15693_3_SECURITY_STATUS_KEY,
|
||||
simple_array_get_data(data->block_security),
|
||||
simple_array_get_count(data->block_security)))
|
||||
break;
|
||||
|
||||
loaded = true;
|
||||
} while(false);
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
UNUSED(version);
|
||||
|
||||
bool loaded = false;
|
||||
|
||||
do {
|
||||
if(flipper_format_key_exist(ff, ISO15693_3_DSFID_KEY)) {
|
||||
if(!flipper_format_read_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1))
|
||||
break;
|
||||
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_DSFID;
|
||||
}
|
||||
|
||||
if(flipper_format_key_exist(ff, ISO15693_3_AFI_KEY)) {
|
||||
if(!flipper_format_read_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break;
|
||||
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_AFI;
|
||||
}
|
||||
|
||||
if(flipper_format_key_exist(ff, ISO15693_3_IC_REF_KEY)) {
|
||||
if(!flipper_format_read_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1))
|
||||
break;
|
||||
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_IC_REF;
|
||||
}
|
||||
|
||||
const bool has_lock_bits = flipper_format_key_exist(ff, ISO15693_3_LOCK_DSFID_KEY) &&
|
||||
flipper_format_key_exist(ff, ISO15693_3_LOCK_AFI_KEY);
|
||||
if(has_lock_bits) {
|
||||
Iso15693_3LockBits* lock_bits = &data->settings.lock_bits;
|
||||
if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_DSFID_KEY, &lock_bits->dsfid, 1))
|
||||
break;
|
||||
if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_AFI_KEY, &lock_bits->afi, 1)) break;
|
||||
}
|
||||
|
||||
if(flipper_format_key_exist(ff, ISO15693_3_BLOCK_COUNT_KEY) &&
|
||||
flipper_format_key_exist(ff, ISO15693_3_BLOCK_SIZE_KEY)) {
|
||||
uint32_t block_count;
|
||||
if(!flipper_format_read_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1)) break;
|
||||
|
||||
data->system_info.block_count = block_count;
|
||||
data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_MEMORY;
|
||||
|
||||
if(!flipper_format_read_hex(
|
||||
ff, ISO15693_3_BLOCK_SIZE_KEY, &(data->system_info.block_size), 1))
|
||||
break;
|
||||
|
||||
simple_array_init(
|
||||
data->block_data, data->system_info.block_size * data->system_info.block_count);
|
||||
|
||||
if(!flipper_format_read_hex(
|
||||
ff,
|
||||
ISO15693_3_DATA_CONTENT_KEY,
|
||||
simple_array_get_data(data->block_data),
|
||||
simple_array_get_count(data->block_data)))
|
||||
break;
|
||||
|
||||
if(flipper_format_key_exist(ff, ISO15693_3_SECURITY_STATUS_KEY)) {
|
||||
simple_array_init(data->block_security, data->system_info.block_count);
|
||||
|
||||
const bool security_loaded = has_lock_bits ?
|
||||
iso15693_3_load_security(data, ff) :
|
||||
iso15693_3_load_security_legacy(data, ff);
|
||||
if(!security_loaded) break;
|
||||
}
|
||||
}
|
||||
|
||||
loaded = true;
|
||||
} while(false);
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
|
||||
bool saved = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_write_comment_cstr(ff, ISO15693_3_PROTOCOL_NAME " specific data"))
|
||||
break;
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) {
|
||||
if(!flipper_format_write_comment_cstr(ff, "Data Storage Format Identifier")) break;
|
||||
if(!flipper_format_write_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1))
|
||||
break;
|
||||
}
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) {
|
||||
if(!flipper_format_write_comment_cstr(ff, "Application Family Identifier")) break;
|
||||
if(!flipper_format_write_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break;
|
||||
}
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {
|
||||
if(!flipper_format_write_comment_cstr(ff, "IC Reference - Vendor specific meaning"))
|
||||
break;
|
||||
if(!flipper_format_write_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1))
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_comment_cstr(ff, "Lock Bits")) break;
|
||||
if(!flipper_format_write_bool(
|
||||
ff, ISO15693_3_LOCK_DSFID_KEY, &data->settings.lock_bits.dsfid, 1))
|
||||
break;
|
||||
if(!flipper_format_write_bool(
|
||||
ff, ISO15693_3_LOCK_AFI_KEY, &data->settings.lock_bits.afi, 1))
|
||||
break;
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {
|
||||
const uint32_t block_count = data->system_info.block_count;
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
ff, "Number of memory blocks, valid range = 1..256"))
|
||||
break;
|
||||
if(!flipper_format_write_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1))
|
||||
break;
|
||||
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
ff, "Size of a single memory block, valid range = 01...20 (hex)"))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
ff, ISO15693_3_BLOCK_SIZE_KEY, &data->system_info.block_size, 1))
|
||||
break;
|
||||
|
||||
if(!flipper_format_write_hex(
|
||||
ff,
|
||||
ISO15693_3_DATA_CONTENT_KEY,
|
||||
simple_array_cget_data(data->block_data),
|
||||
simple_array_get_count(data->block_data)))
|
||||
break;
|
||||
|
||||
if(!flipper_format_write_comment_cstr(
|
||||
ff, "Block Security Status: 01 = locked, 00 = not locked"))
|
||||
break;
|
||||
if(!flipper_format_write_hex(
|
||||
ff,
|
||||
ISO15693_3_SECURITY_STATUS_KEY,
|
||||
simple_array_cget_data(data->block_security),
|
||||
simple_array_get_count(data->block_security)))
|
||||
break;
|
||||
}
|
||||
saved = true;
|
||||
} while(false);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
|
||||
return memcmp(data->uid, other->uid, ISO15693_3_UID_SIZE) == 0 &&
|
||||
memcmp(&data->settings, &other->settings, sizeof(Iso15693_3Settings)) == 0 &&
|
||||
memcmp(&data->system_info, &other->system_info, sizeof(Iso15693_3SystemInfo)) == 0 &&
|
||||
simple_array_is_equal(data->block_data, other->block_data) &&
|
||||
simple_array_is_equal(data->block_security, other->block_security);
|
||||
}
|
||||
|
||||
const char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceNameType name_type) {
|
||||
UNUSED(data);
|
||||
UNUSED(name_type);
|
||||
|
||||
return ISO15693_3_DEVICE_NAME;
|
||||
}
|
||||
|
||||
const uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len) {
|
||||
furi_assert(data);
|
||||
|
||||
if(uid_len) *uid_len = ISO15693_3_UID_SIZE;
|
||||
return data->uid;
|
||||
}
|
||||
|
||||
bool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
furi_assert(uid);
|
||||
|
||||
bool uid_valid = uid_len == ISO15693_3_UID_SIZE;
|
||||
|
||||
if(uid_valid) {
|
||||
memcpy(data->uid, uid, uid_len);
|
||||
// All ISO15693-3 cards must have this as first UID byte
|
||||
data->uid[0] = 0xe0;
|
||||
}
|
||||
|
||||
return uid_valid;
|
||||
}
|
||||
|
||||
Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data) {
|
||||
UNUSED(data);
|
||||
furi_crash("No base data");
|
||||
}
|
||||
|
||||
bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index) {
|
||||
furi_assert(data);
|
||||
furi_assert(block_index < data->system_info.block_count);
|
||||
|
||||
return *(const uint8_t*)simple_array_cget(data->block_security, block_index);
|
||||
}
|
||||
|
||||
uint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data) {
|
||||
furi_assert(data);
|
||||
|
||||
return data->uid[1];
|
||||
}
|
||||
|
||||
uint16_t iso15693_3_get_block_count(const Iso15693_3Data* data) {
|
||||
furi_assert(data);
|
||||
|
||||
return data->system_info.block_count;
|
||||
}
|
||||
|
||||
uint8_t iso15693_3_get_block_size(const Iso15693_3Data* data) {
|
||||
furi_assert(data);
|
||||
|
||||
return data->system_info.block_size;
|
||||
}
|
||||
|
||||
const uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index) {
|
||||
furi_assert(data);
|
||||
furi_assert(data->system_info.block_count > block_index);
|
||||
|
||||
return (const uint8_t*)simple_array_cget(
|
||||
data->block_data, block_index * data->system_info.block_size);
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_device_base.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <toolbox/simple_array.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ISO15693_3_UID_SIZE (8U)
|
||||
|
||||
#define ISO15693_3_GUARD_TIME_US (5000U)
|
||||
#define ISO15693_3_FDT_POLL_FC (4202U)
|
||||
#define ISO15693_3_FDT_LISTEN_FC (4320U)
|
||||
#define ISO15693_3_POLL_POLL_MIN_US (1500U)
|
||||
|
||||
#define ISO15693_3_REQ_FLAG_SUBCARRIER_1 (0U << 0)
|
||||
#define ISO15693_3_REQ_FLAG_SUBCARRIER_2 (1U << 0)
|
||||
#define ISO15693_3_REQ_FLAG_DATA_RATE_LO (0U << 1)
|
||||
#define ISO15693_3_REQ_FLAG_DATA_RATE_HI (1U << 1)
|
||||
#define ISO15693_3_REQ_FLAG_INVENTORY_T4 (0U << 2)
|
||||
#define ISO15693_3_REQ_FLAG_INVENTORY_T5 (1U << 2)
|
||||
#define ISO15693_3_REQ_FLAG_EXTENSION (1U << 3)
|
||||
|
||||
#define ISO15693_3_REQ_FLAG_T4_SELECTED (1U << 4)
|
||||
#define ISO15693_3_REQ_FLAG_T4_ADDRESSED (1U << 5)
|
||||
#define ISO15693_3_REQ_FLAG_T4_OPTION (1U << 6)
|
||||
|
||||
#define ISO15693_3_REQ_FLAG_T5_AFI_PRESENT (1U << 4)
|
||||
#define ISO15693_3_REQ_FLAG_T5_N_SLOTS_16 (0U << 5)
|
||||
#define ISO15693_3_REQ_FLAG_T5_N_SLOTS_1 (1U << 5)
|
||||
#define ISO15693_3_REQ_FLAG_T5_OPTION (1U << 6)
|
||||
|
||||
#define ISO15693_3_RESP_FLAG_NONE (0U)
|
||||
#define ISO15693_3_RESP_FLAG_ERROR (1U << 0)
|
||||
#define ISO15693_3_RESP_FLAG_EXTENSION (1U << 3)
|
||||
|
||||
#define ISO15693_3_RESP_ERROR_NOT_SUPPORTED (0x01U)
|
||||
#define ISO15693_3_RESP_ERROR_FORMAT (0x02U)
|
||||
#define ISO15693_3_RESP_ERROR_OPTION (0x03U)
|
||||
#define ISO15693_3_RESP_ERROR_UNKNOWN (0x0FU)
|
||||
#define ISO15693_3_RESP_ERROR_BLOCK_UNAVAILABLE (0x10U)
|
||||
#define ISO15693_3_RESP_ERROR_BLOCK_ALREADY_LOCKED (0x11U)
|
||||
#define ISO15693_3_RESP_ERROR_BLOCK_LOCKED (0x12U)
|
||||
#define ISO15693_3_RESP_ERROR_BLOCK_WRITE (0x13U)
|
||||
#define ISO15693_3_RESP_ERROR_BLOCK_LOCK (0x14U)
|
||||
#define ISO15693_3_RESP_ERROR_CUSTOM_START (0xA0U)
|
||||
#define ISO15693_3_RESP_ERROR_CUSTOM_END (0xDFU)
|
||||
|
||||
#define ISO15693_3_CMD_MANDATORY_START (0x01U)
|
||||
#define ISO15693_3_CMD_INVENTORY (0x01U)
|
||||
#define ISO15693_3_CMD_STAY_QUIET (0x02U)
|
||||
#define ISO15693_3_CMD_MANDATORY_RFU (0x03U)
|
||||
#define ISO15693_3_CMD_OPTIONAL_START (0x20U)
|
||||
#define ISO15693_3_CMD_READ_BLOCK (0x20U)
|
||||
#define ISO15693_3_CMD_WRITE_BLOCK (0x21U)
|
||||
#define ISO15693_3_CMD_LOCK_BLOCK (0x22U)
|
||||
#define ISO15693_3_CMD_READ_MULTI_BLOCKS (0x23U)
|
||||
#define ISO15693_3_CMD_WRITE_MULTI_BLOCKS (0x24U)
|
||||
#define ISO15693_3_CMD_SELECT (0x25U)
|
||||
#define ISO15693_3_CMD_RESET_TO_READY (0x26U)
|
||||
#define ISO15693_3_CMD_WRITE_AFI (0x27U)
|
||||
#define ISO15693_3_CMD_LOCK_AFI (0x28U)
|
||||
#define ISO15693_3_CMD_WRITE_DSFID (0x29U)
|
||||
#define ISO15693_3_CMD_LOCK_DSFID (0x2AU)
|
||||
#define ISO15693_3_CMD_GET_SYS_INFO (0x2BU)
|
||||
#define ISO15693_3_CMD_GET_BLOCKS_SECURITY (0x2CU)
|
||||
#define ISO15693_3_CMD_OPTIONAL_RFU (0x2DU)
|
||||
#define ISO15693_3_CMD_CUSTOM_START (0xA0U)
|
||||
|
||||
#define ISO15693_3_MANDATORY_COUNT (ISO15693_3_CMD_MANDATORY_RFU - ISO15693_3_CMD_MANDATORY_START)
|
||||
#define ISO15693_3_OPTIONAL_COUNT (ISO15693_3_CMD_OPTIONAL_RFU - ISO15693_3_CMD_OPTIONAL_START)
|
||||
|
||||
#define ISO15693_3_SYSINFO_FLAG_DSFID (1U << 0)
|
||||
#define ISO15693_3_SYSINFO_FLAG_AFI (1U << 1)
|
||||
#define ISO15693_3_SYSINFO_FLAG_MEMORY (1U << 2)
|
||||
#define ISO15693_3_SYSINFO_FLAG_IC_REF (1U << 3)
|
||||
|
||||
typedef enum {
|
||||
Iso15693_3ErrorNone,
|
||||
Iso15693_3ErrorNotPresent,
|
||||
Iso15693_3ErrorBufferEmpty,
|
||||
Iso15693_3ErrorBufferOverflow,
|
||||
Iso15693_3ErrorFieldOff,
|
||||
Iso15693_3ErrorWrongCrc,
|
||||
Iso15693_3ErrorTimeout,
|
||||
Iso15693_3ErrorFormat,
|
||||
Iso15693_3ErrorIgnore,
|
||||
Iso15693_3ErrorNotSupported,
|
||||
Iso15693_3ErrorUidMismatch,
|
||||
Iso15693_3ErrorFullyHandled,
|
||||
Iso15693_3ErrorUnexpectedResponse,
|
||||
Iso15693_3ErrorInternal,
|
||||
Iso15693_3ErrorCustom,
|
||||
Iso15693_3ErrorUnknown,
|
||||
} Iso15693_3Error;
|
||||
|
||||
typedef struct {
|
||||
uint8_t flags;
|
||||
uint8_t dsfid;
|
||||
uint8_t afi;
|
||||
uint8_t ic_ref;
|
||||
uint16_t block_count;
|
||||
uint8_t block_size;
|
||||
} Iso15693_3SystemInfo;
|
||||
|
||||
typedef struct {
|
||||
bool dsfid;
|
||||
bool afi;
|
||||
} Iso15693_3LockBits;
|
||||
|
||||
typedef struct {
|
||||
Iso15693_3LockBits lock_bits;
|
||||
} Iso15693_3Settings;
|
||||
|
||||
typedef struct {
|
||||
uint8_t uid[ISO15693_3_UID_SIZE];
|
||||
Iso15693_3SystemInfo system_info;
|
||||
Iso15693_3Settings settings;
|
||||
SimpleArray* block_data;
|
||||
SimpleArray* block_security;
|
||||
} Iso15693_3Data;
|
||||
|
||||
Iso15693_3Data* iso15693_3_alloc();
|
||||
|
||||
void iso15693_3_free(Iso15693_3Data* data);
|
||||
|
||||
void iso15693_3_reset(Iso15693_3Data* data);
|
||||
|
||||
void iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other);
|
||||
|
||||
bool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type);
|
||||
|
||||
bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version);
|
||||
|
||||
bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff);
|
||||
|
||||
bool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other);
|
||||
|
||||
const char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceNameType name_type);
|
||||
|
||||
const uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len);
|
||||
|
||||
bool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len);
|
||||
|
||||
Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data);
|
||||
|
||||
// Getters and tests
|
||||
|
||||
bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index);
|
||||
|
||||
uint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data);
|
||||
|
||||
uint16_t iso15693_3_get_block_count(const Iso15693_3Data* data);
|
||||
|
||||
uint8_t iso15693_3_get_block_size(const Iso15693_3Data* data);
|
||||
|
||||
const uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_device_base_i.h>
|
||||
|
||||
extern const NfcDeviceBase nfc_device_iso15693_3;
|
||||
@@ -0,0 +1,263 @@
|
||||
#include "iso15693_3_i.h"
|
||||
|
||||
bool iso15693_3_error_response_parse(Iso15693_3Error* error, const BitBuffer* buf) {
|
||||
furi_assert(error);
|
||||
|
||||
if(bit_buffer_get_size_bytes(buf) == 0) {
|
||||
// YEET!
|
||||
*error = Iso15693_3ErrorBufferEmpty;
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t flags;
|
||||
uint8_t error;
|
||||
} ErrorResponseLayout;
|
||||
|
||||
const ErrorResponseLayout* resp = (const ErrorResponseLayout*)bit_buffer_get_data(buf);
|
||||
|
||||
if((resp->flags & ISO15693_3_RESP_FLAG_ERROR) == 0) {
|
||||
// No error flag is set, the data does not contain an error frame
|
||||
return false;
|
||||
} else if(bit_buffer_get_size_bytes(buf) < sizeof(ErrorResponseLayout)) {
|
||||
// Error bit is set, but not enough data to determine the error
|
||||
*error = Iso15693_3ErrorUnexpectedResponse;
|
||||
return true;
|
||||
} else if(
|
||||
resp->error >= ISO15693_3_RESP_ERROR_CUSTOM_START &&
|
||||
resp->error <= ISO15693_3_RESP_ERROR_CUSTOM_END) {
|
||||
// Custom vendor-specific error, must be checked in the respective protocol implementation
|
||||
*error = Iso15693_3ErrorCustom;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch(resp->error) {
|
||||
case ISO15693_3_RESP_ERROR_NOT_SUPPORTED:
|
||||
case ISO15693_3_RESP_ERROR_OPTION:
|
||||
*error = Iso15693_3ErrorNotSupported;
|
||||
break;
|
||||
case ISO15693_3_RESP_ERROR_FORMAT:
|
||||
*error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
case ISO15693_3_RESP_ERROR_BLOCK_UNAVAILABLE:
|
||||
case ISO15693_3_RESP_ERROR_BLOCK_ALREADY_LOCKED:
|
||||
case ISO15693_3_RESP_ERROR_BLOCK_LOCKED:
|
||||
case ISO15693_3_RESP_ERROR_BLOCK_WRITE:
|
||||
case ISO15693_3_RESP_ERROR_BLOCK_LOCK:
|
||||
*error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
case ISO15693_3_RESP_ERROR_UNKNOWN:
|
||||
default:
|
||||
*error = Iso15693_3ErrorUnknown;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_inventory_response_parse(uint8_t* data, const BitBuffer* buf) {
|
||||
furi_assert(data);
|
||||
|
||||
Iso15693_3Error ret = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
if(iso15693_3_error_response_parse(&ret, buf)) break;
|
||||
|
||||
typedef struct {
|
||||
uint8_t flags;
|
||||
uint8_t dsfid;
|
||||
uint8_t uid[ISO15693_3_UID_SIZE];
|
||||
} InventoryResponseLayout;
|
||||
|
||||
if(bit_buffer_get_size_bytes(buf) != sizeof(InventoryResponseLayout)) {
|
||||
ret = Iso15693_3ErrorUnexpectedResponse;
|
||||
break;
|
||||
}
|
||||
|
||||
const InventoryResponseLayout* resp =
|
||||
(const InventoryResponseLayout*)bit_buffer_get_data(buf);
|
||||
// Reverse UID for backward compatibility
|
||||
for(uint32_t i = 0; i < ISO15693_3_UID_SIZE; ++i) {
|
||||
data[i] = resp->uid[ISO15693_3_UID_SIZE - i - 1];
|
||||
}
|
||||
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso15693_3Error
|
||||
iso15693_3_system_info_response_parse(Iso15693_3SystemInfo* data, const BitBuffer* buf) {
|
||||
furi_assert(data);
|
||||
|
||||
Iso15693_3Error ret = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
if(iso15693_3_error_response_parse(&ret, buf)) break;
|
||||
|
||||
typedef struct {
|
||||
uint8_t flags;
|
||||
uint8_t info_flags;
|
||||
uint8_t uid[ISO15693_3_UID_SIZE];
|
||||
uint8_t extra[];
|
||||
} SystemInfoResponseLayout;
|
||||
|
||||
if(bit_buffer_get_size_bytes(buf) < sizeof(SystemInfoResponseLayout)) {
|
||||
ret = Iso15693_3ErrorUnexpectedResponse;
|
||||
break;
|
||||
}
|
||||
|
||||
const SystemInfoResponseLayout* resp =
|
||||
(const SystemInfoResponseLayout*)bit_buffer_get_data(buf);
|
||||
|
||||
const uint8_t* extra = resp->extra;
|
||||
const size_t extra_size = (resp->info_flags & ISO15693_3_SYSINFO_FLAG_DSFID ? 1 : 0) +
|
||||
(resp->info_flags & ISO15693_3_SYSINFO_FLAG_AFI ? 1 : 0) +
|
||||
(resp->info_flags & ISO15693_3_SYSINFO_FLAG_MEMORY ? 2 : 0) +
|
||||
(resp->info_flags & ISO15693_3_SYSINFO_FLAG_IC_REF ? 1 : 0);
|
||||
|
||||
if(extra_size != bit_buffer_get_size_bytes(buf) - sizeof(SystemInfoResponseLayout)) {
|
||||
ret = Iso15693_3ErrorUnexpectedResponse;
|
||||
break;
|
||||
}
|
||||
|
||||
data->flags = resp->info_flags;
|
||||
|
||||
if(data->flags & ISO15693_3_SYSINFO_FLAG_DSFID) {
|
||||
data->dsfid = *extra++;
|
||||
}
|
||||
|
||||
if(data->flags & ISO15693_3_SYSINFO_FLAG_AFI) {
|
||||
data->afi = *extra++;
|
||||
}
|
||||
|
||||
if(data->flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {
|
||||
// Add 1 to get actual values
|
||||
data->block_count = *extra++ + 1;
|
||||
data->block_size = (*extra++ & 0x1F) + 1;
|
||||
}
|
||||
|
||||
if(data->flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {
|
||||
data->ic_ref = *extra;
|
||||
}
|
||||
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso15693_3Error
|
||||
iso15693_3_read_block_response_parse(uint8_t* data, uint8_t block_size, const BitBuffer* buf) {
|
||||
furi_assert(data);
|
||||
|
||||
Iso15693_3Error ret = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
if(iso15693_3_error_response_parse(&ret, buf)) break;
|
||||
|
||||
typedef struct {
|
||||
uint8_t flags;
|
||||
uint8_t block_data[];
|
||||
} ReadBlockResponseLayout;
|
||||
|
||||
const size_t buf_size = bit_buffer_get_size_bytes(buf);
|
||||
const size_t received_block_size = buf_size - sizeof(ReadBlockResponseLayout);
|
||||
|
||||
if(buf_size <= sizeof(ReadBlockResponseLayout) || received_block_size != block_size) {
|
||||
ret = Iso15693_3ErrorUnexpectedResponse;
|
||||
break;
|
||||
}
|
||||
|
||||
const ReadBlockResponseLayout* resp =
|
||||
(const ReadBlockResponseLayout*)bit_buffer_get_data(buf);
|
||||
memcpy(data, resp->block_data, received_block_size);
|
||||
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_get_block_security_response_parse(
|
||||
uint8_t* data,
|
||||
uint16_t block_count,
|
||||
const BitBuffer* buf) {
|
||||
furi_assert(data);
|
||||
furi_assert(block_count);
|
||||
Iso15693_3Error ret = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
if(iso15693_3_error_response_parse(&ret, buf)) break;
|
||||
|
||||
typedef struct {
|
||||
uint8_t flags;
|
||||
uint8_t block_security[];
|
||||
} GetBlockSecurityResponseLayout;
|
||||
|
||||
const size_t buf_size = bit_buffer_get_size_bytes(buf);
|
||||
const size_t received_block_count = buf_size - sizeof(GetBlockSecurityResponseLayout);
|
||||
|
||||
if(buf_size <= sizeof(GetBlockSecurityResponseLayout) ||
|
||||
received_block_count != block_count) {
|
||||
ret = Iso15693_3ErrorUnexpectedResponse;
|
||||
break;
|
||||
}
|
||||
|
||||
const GetBlockSecurityResponseLayout* resp =
|
||||
(const GetBlockSecurityResponseLayout*)bit_buffer_get_data(buf);
|
||||
|
||||
memcpy(data, resp->block_security, received_block_count);
|
||||
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf) {
|
||||
for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) {
|
||||
// Reverse the UID
|
||||
bit_buffer_append_byte(buf, data->uid[ISO15693_3_UID_SIZE - i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf) {
|
||||
furi_assert(block_num < data->system_info.block_count);
|
||||
|
||||
const uint32_t block_offset = block_num * data->system_info.block_size;
|
||||
const uint8_t* block_data = simple_array_cget(data->block_data, block_offset);
|
||||
|
||||
bit_buffer_append_bytes(buf, block_data, data->system_info.block_size);
|
||||
}
|
||||
|
||||
void iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_index, bool locked) {
|
||||
furi_assert(data);
|
||||
furi_assert(block_index < data->system_info.block_count);
|
||||
|
||||
*(uint8_t*)simple_array_get(data->block_security, block_index) = locked ? 1 : 0;
|
||||
}
|
||||
|
||||
void iso15693_3_set_block_data(
|
||||
Iso15693_3Data* data,
|
||||
uint8_t block_num,
|
||||
const uint8_t* block_data,
|
||||
size_t block_data_size) {
|
||||
furi_assert(block_num < data->system_info.block_count);
|
||||
furi_assert(block_data_size == data->system_info.block_size);
|
||||
|
||||
const uint32_t block_offset = block_num * data->system_info.block_size;
|
||||
uint8_t* block = simple_array_get(data->block_data, block_offset);
|
||||
|
||||
memcpy(block, block_data, block_data_size);
|
||||
}
|
||||
|
||||
void iso15693_3_append_block_security(
|
||||
const Iso15693_3Data* data,
|
||||
uint8_t block_num,
|
||||
BitBuffer* buf) {
|
||||
bit_buffer_append_byte(buf, *(uint8_t*)simple_array_cget(data->block_security, block_num));
|
||||
}
|
||||
|
||||
bool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid) {
|
||||
for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) {
|
||||
if(data->uid[i] != uid[ISO15693_3_UID_SIZE - i - 1]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "iso15693_3.h"
|
||||
|
||||
#include <toolbox/bit_buffer.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Check if the buffer contains an error frame and if it does, determine
|
||||
* the error type.
|
||||
* NOTE: No changes are done to the result if no error is present.
|
||||
*
|
||||
* @param [out] data Pointer to the resulting error value.
|
||||
* @param [in] buf Data buffer to be checked
|
||||
*
|
||||
* @return True if data contains an error frame or is empty, false otherwise
|
||||
*/
|
||||
bool iso15693_3_error_response_parse(Iso15693_3Error* error, const BitBuffer* buf);
|
||||
|
||||
Iso15693_3Error iso15693_3_inventory_response_parse(uint8_t* data, const BitBuffer* buf);
|
||||
|
||||
Iso15693_3Error
|
||||
iso15693_3_system_info_response_parse(Iso15693_3SystemInfo* data, const BitBuffer* buf);
|
||||
|
||||
Iso15693_3Error
|
||||
iso15693_3_read_block_response_parse(uint8_t* data, uint8_t block_size, const BitBuffer* buf);
|
||||
|
||||
Iso15693_3Error iso15693_3_get_block_security_response_parse(
|
||||
uint8_t* data,
|
||||
uint16_t block_count,
|
||||
const BitBuffer* buf);
|
||||
|
||||
void iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf);
|
||||
|
||||
void iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf);
|
||||
|
||||
void iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_index, bool locked);
|
||||
|
||||
void iso15693_3_set_block_data(
|
||||
Iso15693_3Data* data,
|
||||
uint8_t block_num,
|
||||
const uint8_t* block_data,
|
||||
size_t block_data_size);
|
||||
|
||||
void iso15693_3_append_block_security(
|
||||
const Iso15693_3Data* data,
|
||||
uint8_t block_num,
|
||||
BitBuffer* buf);
|
||||
|
||||
// NOTE: the uid parameter has reversed byte order with respect to data
|
||||
bool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "iso15693_3_listener_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#include <nfc/nfc.h>
|
||||
#include <nfc/protocols/nfc_listener_base.h>
|
||||
#include <nfc/helpers/iso13239_crc.h>
|
||||
|
||||
#define TAG "Iso15693_3Listener"
|
||||
|
||||
#define ISO15693_3_LISTENER_BUFFER_SIZE (64U)
|
||||
|
||||
Iso15693_3Listener* iso15693_3_listener_alloc(Nfc* nfc, Iso15693_3Data* data) {
|
||||
furi_assert(nfc);
|
||||
|
||||
Iso15693_3Listener* instance = malloc(sizeof(Iso15693_3Listener));
|
||||
instance->nfc = nfc;
|
||||
instance->data = data;
|
||||
|
||||
instance->tx_buffer = bit_buffer_alloc(ISO15693_3_LISTENER_BUFFER_SIZE);
|
||||
|
||||
instance->iso15693_3_event.data = &instance->iso15693_3_event_data;
|
||||
instance->generic_event.protocol = NfcProtocolIso15693_3;
|
||||
instance->generic_event.instance = instance;
|
||||
instance->generic_event.event_data = &instance->iso15693_3_event;
|
||||
|
||||
nfc_set_fdt_listen_fc(instance->nfc, ISO15693_3_FDT_LISTEN_FC);
|
||||
nfc_config(instance->nfc, NfcModeListener, NfcTechIso15693);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void iso15693_3_listener_free(Iso15693_3Listener* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
bit_buffer_free(instance->tx_buffer);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void iso15693_3_listener_set_callback(
|
||||
Iso15693_3Listener* instance,
|
||||
NfcGenericCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
const Iso15693_3Data* iso15693_3_listener_get_data(Iso15693_3Listener* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->data);
|
||||
|
||||
return instance->data;
|
||||
}
|
||||
|
||||
NfcCommand iso15693_3_listener_run(NfcGenericEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
furi_assert(event.protocol == NfcProtocolInvalid);
|
||||
furi_assert(event.event_data);
|
||||
|
||||
Iso15693_3Listener* instance = context;
|
||||
NfcEvent* nfc_event = event.event_data;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(nfc_event->type == NfcEventTypeRxEnd) {
|
||||
BitBuffer* rx_buffer = nfc_event->data.buffer;
|
||||
|
||||
if(iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) {
|
||||
iso13239_crc_trim(rx_buffer);
|
||||
|
||||
const Iso15693_3Error error = iso15693_3_listener_process_request(instance, rx_buffer);
|
||||
|
||||
if(error == Iso15693_3ErrorNotSupported) {
|
||||
if(instance->callback) {
|
||||
instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeCustomCommand;
|
||||
instance->iso15693_3_event.data->buffer = rx_buffer;
|
||||
command = instance->callback(instance->generic_event, instance->context);
|
||||
}
|
||||
|
||||
} else if(error == Iso15693_3ErrorUidMismatch) {
|
||||
iso15693_3_listener_process_uid_mismatch(instance, rx_buffer);
|
||||
}
|
||||
|
||||
} else if(bit_buffer_get_size(rx_buffer) == 0) {
|
||||
// Special case: Single EOF
|
||||
const Iso15693_3Error error = iso15693_3_listener_process_single_eof(instance);
|
||||
if(error == Iso15693_3ErrorUnexpectedResponse) {
|
||||
if(instance->callback) {
|
||||
instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeSingleEof;
|
||||
command = instance->callback(instance->generic_event, instance->context);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(
|
||||
TAG, "Wrong CRC, buffer size: %zu", bit_buffer_get_size(nfc_event->data.buffer));
|
||||
}
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
const NfcListenerBase nfc_listener_iso15693_3 = {
|
||||
.alloc = (NfcListenerAlloc)iso15693_3_listener_alloc,
|
||||
.free = (NfcListenerFree)iso15693_3_listener_free,
|
||||
.set_callback = (NfcListenerSetCallback)iso15693_3_listener_set_callback,
|
||||
.get_data = (NfcListenerGetData)iso15693_3_listener_get_data,
|
||||
.run = (NfcListenerRun)iso15693_3_listener_run,
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/nfc_listener.h>
|
||||
|
||||
#include "iso15693_3.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct Iso15693_3Listener Iso15693_3Listener;
|
||||
|
||||
typedef enum {
|
||||
Iso15693_3ListenerEventTypeFieldOff,
|
||||
Iso15693_3ListenerEventTypeCustomCommand,
|
||||
Iso15693_3ListenerEventTypeSingleEof,
|
||||
} Iso15693_3ListenerEventType;
|
||||
|
||||
typedef struct {
|
||||
BitBuffer* buffer;
|
||||
} Iso15693_3ListenerEventData;
|
||||
|
||||
typedef struct {
|
||||
Iso15693_3ListenerEventType type;
|
||||
Iso15693_3ListenerEventData* data;
|
||||
} Iso15693_3ListenerEvent;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_listener_base.h>
|
||||
|
||||
extern const NfcListenerBase nfc_listener_iso15693_3;
|
||||
@@ -0,0 +1,883 @@
|
||||
#include "iso15693_3_listener_i.h"
|
||||
|
||||
#include <nfc/helpers/iso13239_crc.h>
|
||||
|
||||
#define TAG "Iso15693_3Listener"
|
||||
|
||||
typedef Iso15693_3Error (*Iso15693_3RequestHandler)(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags);
|
||||
|
||||
typedef struct {
|
||||
Iso15693_3RequestHandler mandatory[ISO15693_3_MANDATORY_COUNT];
|
||||
Iso15693_3RequestHandler optional[ISO15693_3_OPTIONAL_COUNT];
|
||||
} Iso15693_3ListenerHandlerTable;
|
||||
|
||||
static Iso15693_3Error
|
||||
iso15693_3_listener_extension_handler(Iso15693_3Listener* instance, uint32_t command, ...) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
if(instance->extension_table == NULL) break;
|
||||
|
||||
Iso15693_3ExtensionHandler handler = NULL;
|
||||
|
||||
if(command < ISO15693_3_CMD_MANDATORY_RFU) {
|
||||
const Iso15693_3ExtensionHandler* mandatory = instance->extension_table->mandatory;
|
||||
handler = mandatory[command - ISO15693_3_CMD_MANDATORY_START];
|
||||
} else if(command >= ISO15693_3_CMD_OPTIONAL_START && command < ISO15693_3_CMD_OPTIONAL_RFU) {
|
||||
const Iso15693_3ExtensionHandler* optional = instance->extension_table->optional;
|
||||
handler = optional[command - ISO15693_3_CMD_OPTIONAL_START];
|
||||
}
|
||||
|
||||
if(handler == NULL) break;
|
||||
|
||||
va_list args;
|
||||
va_start(args, command);
|
||||
|
||||
error = handler(instance->extension_context, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
} while(false);
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_inventory_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
const bool afi_flag = flags & ISO15693_3_REQ_FLAG_T5_AFI_PRESENT;
|
||||
const size_t data_size_min = sizeof(uint8_t) * (afi_flag ? 2 : 1);
|
||||
|
||||
if(data_size < data_size_min) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
if(afi_flag) {
|
||||
const uint8_t afi = *data++;
|
||||
// When AFI flag is set, ignore non-matching requests
|
||||
if(afi != instance->data->system_info.afi) break;
|
||||
}
|
||||
|
||||
const uint8_t mask_len = *data++;
|
||||
const size_t data_size_required = data_size_min + mask_len;
|
||||
|
||||
if(data_size != data_size_required) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
if(mask_len != 0) {
|
||||
// TODO FL-3633: Take mask_len and mask_value into account (if present)
|
||||
}
|
||||
|
||||
error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_INVENTORY);
|
||||
if(error != Iso15693_3ErrorNone) break;
|
||||
|
||||
bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid); // DSFID
|
||||
iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_stay_quiet_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
UNUSED(data);
|
||||
UNUSED(data_size);
|
||||
UNUSED(flags);
|
||||
|
||||
instance->state = Iso15693_3ListenerStateQuiet;
|
||||
return Iso15693_3ErrorIgnore;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_read_block_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
typedef struct {
|
||||
uint8_t block_num;
|
||||
} Iso15693_3ReadBlockRequestLayout;
|
||||
|
||||
const Iso15693_3ReadBlockRequestLayout* request =
|
||||
(const Iso15693_3ReadBlockRequestLayout*)data;
|
||||
|
||||
if(data_size != sizeof(Iso15693_3ReadBlockRequestLayout)) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t block_index = request->block_num;
|
||||
const uint32_t block_count_max = instance->data->system_info.block_count;
|
||||
|
||||
if(block_index >= block_count_max) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
|
||||
error = iso15693_3_listener_extension_handler(
|
||||
instance, ISO15693_3_CMD_READ_BLOCK, block_index);
|
||||
if(error != Iso15693_3ErrorNone) break;
|
||||
|
||||
if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) {
|
||||
iso15693_3_append_block_security(
|
||||
instance->data, block_index, instance->tx_buffer); // Block security (optional)
|
||||
}
|
||||
|
||||
iso15693_3_append_block(instance->data, block_index, instance->tx_buffer); // Block data
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_write_block_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
typedef struct {
|
||||
uint8_t block_num;
|
||||
uint8_t block_data[];
|
||||
} Iso15693_3WriteBlockRequestLayout;
|
||||
|
||||
const Iso15693_3WriteBlockRequestLayout* request =
|
||||
(const Iso15693_3WriteBlockRequestLayout*)data;
|
||||
|
||||
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
|
||||
|
||||
if(data_size <= sizeof(Iso15693_3WriteBlockRequestLayout)) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t block_index = request->block_num;
|
||||
const uint32_t block_count_max = instance->data->system_info.block_count;
|
||||
const uint32_t block_size_max = instance->data->system_info.block_size;
|
||||
const size_t block_size_received = data_size - sizeof(Iso15693_3WriteBlockRequestLayout);
|
||||
|
||||
if(block_index >= block_count_max) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
} else if(block_size_received != block_size_max) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
} else if(iso15693_3_is_block_locked(instance->data, block_index)) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
|
||||
error = iso15693_3_listener_extension_handler(
|
||||
instance, ISO15693_3_CMD_WRITE_BLOCK, block_index, request->block_data);
|
||||
if(error != Iso15693_3ErrorNone) break;
|
||||
|
||||
iso15693_3_set_block_data(
|
||||
instance->data, block_index, request->block_data, block_size_received);
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_lock_block_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
typedef struct {
|
||||
uint8_t block_num;
|
||||
} Iso15693_3LockBlockRequestLayout;
|
||||
|
||||
const Iso15693_3LockBlockRequestLayout* request =
|
||||
(const Iso15693_3LockBlockRequestLayout*)data;
|
||||
|
||||
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
|
||||
|
||||
if(data_size != sizeof(Iso15693_3LockBlockRequestLayout)) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t block_index = request->block_num;
|
||||
const uint32_t block_count_max = instance->data->system_info.block_count;
|
||||
|
||||
if(block_index >= block_count_max) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
} else if(iso15693_3_is_block_locked(instance->data, block_index)) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
|
||||
error = iso15693_3_listener_extension_handler(
|
||||
instance, ISO15693_3_CMD_LOCK_BLOCK, block_index);
|
||||
if(error != Iso15693_3ErrorNone) break;
|
||||
|
||||
iso15693_3_set_block_locked(instance->data, block_index, true);
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_read_multi_blocks_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
typedef struct {
|
||||
uint8_t first_block_num;
|
||||
uint8_t block_count;
|
||||
} Iso15693_3ReadMultiBlocksRequestLayout;
|
||||
|
||||
const Iso15693_3ReadMultiBlocksRequestLayout* request =
|
||||
(const Iso15693_3ReadMultiBlocksRequestLayout*)data;
|
||||
|
||||
if(data_size != sizeof(Iso15693_3ReadMultiBlocksRequestLayout)) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t block_index_start = request->first_block_num;
|
||||
const uint32_t block_index_end = block_index_start + request->block_count;
|
||||
|
||||
const uint32_t block_count = request->block_count + 1;
|
||||
const uint32_t block_count_max = instance->data->system_info.block_count;
|
||||
const uint32_t block_count_available = block_count_max - block_index_start;
|
||||
|
||||
if(block_count > block_count_available) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
|
||||
error = iso15693_3_listener_extension_handler(
|
||||
instance,
|
||||
ISO15693_3_CMD_READ_MULTI_BLOCKS,
|
||||
(uint32_t)block_index_start,
|
||||
(uint32_t)block_index_end);
|
||||
if(error != Iso15693_3ErrorNone) break;
|
||||
|
||||
for(uint32_t i = block_index_start; i <= block_index_end; ++i) {
|
||||
if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) {
|
||||
iso15693_3_append_block_security(
|
||||
instance->data, i, instance->tx_buffer); // Block security (optional)
|
||||
}
|
||||
iso15693_3_append_block(instance->data, i, instance->tx_buffer); // Block data
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_write_multi_blocks_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
typedef struct {
|
||||
uint8_t first_block_num;
|
||||
uint8_t block_count;
|
||||
uint8_t block_data[];
|
||||
} Iso15693_3WriteMultiBlocksRequestLayout;
|
||||
|
||||
const Iso15693_3WriteMultiBlocksRequestLayout* request =
|
||||
(const Iso15693_3WriteMultiBlocksRequestLayout*)data;
|
||||
|
||||
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
|
||||
|
||||
if(data_size <= sizeof(Iso15693_3WriteMultiBlocksRequestLayout)) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t block_index_start = request->first_block_num;
|
||||
const uint32_t block_index_end = block_index_start + request->block_count;
|
||||
|
||||
const uint32_t block_count = request->block_count + 1;
|
||||
const uint32_t block_count_max = instance->data->system_info.block_count;
|
||||
const uint32_t block_count_available = block_count_max - block_index_start;
|
||||
|
||||
const size_t block_data_size = data_size - sizeof(Iso15693_3WriteMultiBlocksRequestLayout);
|
||||
const size_t block_size = block_data_size / block_count;
|
||||
const size_t block_size_max = instance->data->system_info.block_size;
|
||||
|
||||
if(block_count > block_count_available) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
} else if(block_size != block_size_max) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
|
||||
error = iso15693_3_listener_extension_handler(
|
||||
instance, ISO15693_3_CMD_WRITE_MULTI_BLOCKS, block_index_start, block_index_end);
|
||||
if(error != Iso15693_3ErrorNone) break;
|
||||
|
||||
for(uint32_t i = block_index_start; i <= block_index_end; ++i) {
|
||||
if(iso15693_3_is_block_locked(instance->data, i)) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(error != Iso15693_3ErrorNone) break;
|
||||
|
||||
for(uint32_t i = block_index_start; i < block_count + request->first_block_num; ++i) {
|
||||
const uint8_t* block_data = &request->block_data[block_size * i];
|
||||
iso15693_3_set_block_data(instance->data, i, block_data, block_size);
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_select_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
UNUSED(data);
|
||||
UNUSED(data_size);
|
||||
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
if(!(flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED)) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->state = Iso15693_3ListenerStateSelected;
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_reset_to_ready_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
UNUSED(data);
|
||||
UNUSED(data_size);
|
||||
UNUSED(flags);
|
||||
|
||||
instance->state = Iso15693_3ListenerStateReady;
|
||||
return Iso15693_3ErrorNone;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_write_afi_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
typedef struct {
|
||||
uint8_t afi;
|
||||
} Iso15693_3WriteAfiRequestLayout;
|
||||
|
||||
const Iso15693_3WriteAfiRequestLayout* request =
|
||||
(const Iso15693_3WriteAfiRequestLayout*)data;
|
||||
|
||||
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
|
||||
|
||||
if(data_size <= sizeof(Iso15693_3WriteAfiRequestLayout)) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
} else if(instance->data->settings.lock_bits.afi) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
|
||||
error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_WRITE_AFI);
|
||||
if(error != Iso15693_3ErrorNone) break;
|
||||
|
||||
instance->data->system_info.afi = request->afi;
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_lock_afi_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
UNUSED(data);
|
||||
UNUSED(data_size);
|
||||
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
|
||||
|
||||
Iso15693_3LockBits* lock_bits = &instance->data->settings.lock_bits;
|
||||
|
||||
if(lock_bits->afi) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
|
||||
error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_LOCK_AFI);
|
||||
if(error != Iso15693_3ErrorNone) break;
|
||||
|
||||
lock_bits->afi = true;
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_write_dsfid_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
typedef struct {
|
||||
uint8_t dsfid;
|
||||
} Iso15693_3WriteDsfidRequestLayout;
|
||||
|
||||
const Iso15693_3WriteDsfidRequestLayout* request =
|
||||
(const Iso15693_3WriteDsfidRequestLayout*)data;
|
||||
|
||||
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
|
||||
|
||||
if(data_size <= sizeof(Iso15693_3WriteDsfidRequestLayout)) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
} else if(instance->data->settings.lock_bits.dsfid) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
|
||||
error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_WRITE_DSFID);
|
||||
if(error != Iso15693_3ErrorNone) break;
|
||||
|
||||
instance->data->system_info.dsfid = request->dsfid;
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_lock_dsfid_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
UNUSED(data);
|
||||
UNUSED(data_size);
|
||||
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION;
|
||||
|
||||
Iso15693_3LockBits* lock_bits = &instance->data->settings.lock_bits;
|
||||
|
||||
if(lock_bits->dsfid) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
|
||||
error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_LOCK_DSFID);
|
||||
if(error != Iso15693_3ErrorNone) break;
|
||||
|
||||
lock_bits->dsfid = true;
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_get_system_info_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
UNUSED(data);
|
||||
UNUSED(data_size);
|
||||
UNUSED(flags);
|
||||
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
const uint8_t system_flags = instance->data->system_info.flags;
|
||||
bit_buffer_append_byte(instance->tx_buffer, system_flags); // System info flags
|
||||
|
||||
iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID
|
||||
|
||||
if(system_flags & ISO15693_3_SYSINFO_FLAG_DSFID) {
|
||||
bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid);
|
||||
}
|
||||
if(system_flags & ISO15693_3_SYSINFO_FLAG_AFI) {
|
||||
bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.afi);
|
||||
}
|
||||
if(system_flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {
|
||||
const uint8_t memory_info[2] = {
|
||||
instance->data->system_info.block_count - 1,
|
||||
instance->data->system_info.block_size - 1,
|
||||
};
|
||||
bit_buffer_append_bytes(instance->tx_buffer, memory_info, COUNT_OF(memory_info));
|
||||
}
|
||||
if(system_flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {
|
||||
bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.ic_ref);
|
||||
}
|
||||
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_get_multi_blocks_security_handler(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t flags) {
|
||||
UNUSED(flags);
|
||||
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
typedef struct {
|
||||
uint8_t first_block_num;
|
||||
uint8_t block_count;
|
||||
} Iso15693_3GetMultiBlocksSecurityRequestLayout;
|
||||
|
||||
const Iso15693_3GetMultiBlocksSecurityRequestLayout* request =
|
||||
(const Iso15693_3GetMultiBlocksSecurityRequestLayout*)data;
|
||||
|
||||
if(data_size < sizeof(Iso15693_3GetMultiBlocksSecurityRequestLayout)) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t block_index_start = request->first_block_num;
|
||||
const uint32_t block_index_end = block_index_start + request->block_count;
|
||||
|
||||
const uint32_t block_count_max = instance->data->system_info.block_count;
|
||||
|
||||
if(block_index_end >= block_count_max) {
|
||||
error = Iso15693_3ErrorInternal;
|
||||
break;
|
||||
}
|
||||
|
||||
for(uint32_t i = block_index_start; i <= block_index_end; ++i) {
|
||||
bit_buffer_append_byte(
|
||||
instance->tx_buffer, iso15693_3_is_block_locked(instance->data, i) ? 1 : 0);
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
const Iso15693_3ListenerHandlerTable iso15693_3_handler_table = {
|
||||
.mandatory =
|
||||
{
|
||||
iso15693_3_listener_inventory_handler,
|
||||
iso15693_3_listener_stay_quiet_handler,
|
||||
},
|
||||
.optional =
|
||||
{
|
||||
iso15693_3_listener_read_block_handler,
|
||||
iso15693_3_listener_write_block_handler,
|
||||
iso15693_3_listener_lock_block_handler,
|
||||
iso15693_3_listener_read_multi_blocks_handler,
|
||||
iso15693_3_listener_write_multi_blocks_handler,
|
||||
iso15693_3_listener_select_handler,
|
||||
iso15693_3_listener_reset_to_ready_handler,
|
||||
iso15693_3_listener_write_afi_handler,
|
||||
iso15693_3_listener_lock_afi_handler,
|
||||
iso15693_3_listener_write_dsfid_handler,
|
||||
iso15693_3_listener_lock_dsfid_handler,
|
||||
iso15693_3_listener_get_system_info_handler,
|
||||
iso15693_3_listener_get_multi_blocks_security_handler,
|
||||
},
|
||||
};
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_handle_standard_request(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t command,
|
||||
uint8_t flags) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
Iso15693_3RequestHandler handler = NULL;
|
||||
|
||||
if(command < ISO15693_3_CMD_MANDATORY_RFU) {
|
||||
handler = iso15693_3_handler_table.mandatory[command - ISO15693_3_CMD_MANDATORY_START];
|
||||
} else if(command >= ISO15693_3_CMD_OPTIONAL_START && command < ISO15693_3_CMD_OPTIONAL_RFU) {
|
||||
handler = iso15693_3_handler_table.optional[command - ISO15693_3_CMD_OPTIONAL_START];
|
||||
}
|
||||
|
||||
if(handler == NULL) {
|
||||
error = Iso15693_3ErrorNotSupported;
|
||||
break;
|
||||
}
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_NONE);
|
||||
|
||||
error = handler(instance, data, data_size, flags);
|
||||
|
||||
// The request was fully handled in the protocol extension, no further action necessary
|
||||
if(error == Iso15693_3ErrorFullyHandled) {
|
||||
error = Iso15693_3ErrorNone;
|
||||
}
|
||||
|
||||
// Several commands may not require an answer
|
||||
if(error == Iso15693_3ErrorFormat || error == Iso15693_3ErrorIgnore) break;
|
||||
|
||||
if(error != Iso15693_3ErrorNone) {
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_ERROR);
|
||||
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_ERROR_UNKNOWN);
|
||||
}
|
||||
|
||||
Iso15693_3ListenerSessionState* session_state = &instance->session_state;
|
||||
|
||||
if(!session_state->wait_for_eof) {
|
||||
error = iso15693_3_listener_send_frame(instance, instance->tx_buffer);
|
||||
}
|
||||
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static inline Iso15693_3Error iso15693_3_listener_handle_custom_request(
|
||||
Iso15693_3Listener* instance,
|
||||
const uint8_t* data,
|
||||
size_t data_size) {
|
||||
Iso15693_3Error error;
|
||||
|
||||
do {
|
||||
typedef struct {
|
||||
uint8_t manufacturer;
|
||||
uint8_t extra[];
|
||||
} Iso15693_3CustomRequestLayout;
|
||||
|
||||
if(data_size < sizeof(Iso15693_3CustomRequestLayout)) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
const Iso15693_3CustomRequestLayout* request = (const Iso15693_3CustomRequestLayout*)data;
|
||||
|
||||
if(request->manufacturer != iso15693_3_get_manufacturer_id(instance->data)) {
|
||||
error = Iso15693_3ErrorIgnore;
|
||||
break;
|
||||
}
|
||||
|
||||
// This error code will trigger the CustomCommand listener event
|
||||
error = Iso15693_3ErrorNotSupported;
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_listener_set_extension_handler_table(
|
||||
Iso15693_3Listener* instance,
|
||||
const Iso15693_3ExtensionHandlerTable* table,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(context);
|
||||
|
||||
instance->extension_table = table;
|
||||
instance->extension_context = context;
|
||||
return Iso15693_3ErrorNone;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance) {
|
||||
furi_assert(instance);
|
||||
instance->state = Iso15693_3ListenerStateReady;
|
||||
return Iso15693_3ErrorNone;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_listener_process_nfc_error(NfcError error) {
|
||||
Iso15693_3Error ret = Iso15693_3ErrorNone;
|
||||
|
||||
if(error == NfcErrorNone) {
|
||||
ret = Iso15693_3ErrorNone;
|
||||
} else if(error == NfcErrorTimeout) {
|
||||
ret = Iso15693_3ErrorTimeout;
|
||||
} else {
|
||||
ret = Iso15693_3ErrorFieldOff;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso15693_3Error
|
||||
iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer) {
|
||||
furi_assert(instance);
|
||||
furi_assert(tx_buffer);
|
||||
|
||||
bit_buffer_copy(instance->tx_buffer, tx_buffer);
|
||||
iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer);
|
||||
|
||||
NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
|
||||
return iso15693_3_listener_process_nfc_error(error);
|
||||
}
|
||||
|
||||
Iso15693_3Error
|
||||
iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
typedef struct {
|
||||
uint8_t flags;
|
||||
uint8_t command;
|
||||
uint8_t data[];
|
||||
} Iso15693_3RequestLayout;
|
||||
|
||||
const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer);
|
||||
const size_t buf_size_min = sizeof(Iso15693_3RequestLayout);
|
||||
|
||||
if(buf_size < buf_size_min) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
}
|
||||
|
||||
const Iso15693_3RequestLayout* request =
|
||||
(const Iso15693_3RequestLayout*)bit_buffer_get_data(rx_buffer);
|
||||
|
||||
Iso15693_3ListenerSessionState* session_state = &instance->session_state;
|
||||
|
||||
if((request->flags & ISO15693_3_REQ_FLAG_INVENTORY_T5) == 0) {
|
||||
session_state->selected = request->flags & ISO15693_3_REQ_FLAG_T4_SELECTED;
|
||||
session_state->addressed = request->flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED;
|
||||
|
||||
if(session_state->selected && session_state->addressed) {
|
||||
// A request mode can be either addressed or selected, but not both
|
||||
error = Iso15693_3ErrorUnknown;
|
||||
break;
|
||||
} else if(instance->state == Iso15693_3ListenerStateQuiet) {
|
||||
// If the card is quiet, ignore non-addressed commands
|
||||
if(session_state->addressed) {
|
||||
error = Iso15693_3ErrorIgnore;
|
||||
break;
|
||||
}
|
||||
} else if(instance->state != Iso15693_3ListenerStateSelected) {
|
||||
// If the card is not selected, ignore selected commands
|
||||
if(session_state->selected) {
|
||||
error = Iso15693_3ErrorIgnore;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If the card is quiet, ignore inventory commands
|
||||
if(instance->state == Iso15693_3ListenerStateQuiet) {
|
||||
error = Iso15693_3ErrorIgnore;
|
||||
break;
|
||||
}
|
||||
|
||||
session_state->selected = false;
|
||||
session_state->addressed = false;
|
||||
}
|
||||
|
||||
if(request->command >= ISO15693_3_CMD_CUSTOM_START) {
|
||||
// Custom commands are properly handled in the protocol-specific top-level poller
|
||||
error = iso15693_3_listener_handle_custom_request(
|
||||
instance, request->data, buf_size - buf_size_min);
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t* data;
|
||||
size_t data_size;
|
||||
|
||||
if(session_state->addressed) {
|
||||
// In addressed mode, UID must be included in each command
|
||||
const size_t buf_size_min_addr = buf_size_min + ISO15693_3_UID_SIZE;
|
||||
|
||||
if(buf_size < buf_size_min_addr) {
|
||||
error = Iso15693_3ErrorFormat;
|
||||
break;
|
||||
} else if(!iso15693_3_is_equal_uid(instance->data, request->data)) {
|
||||
error = Iso15693_3ErrorUidMismatch;
|
||||
break;
|
||||
}
|
||||
|
||||
data = &request->data[ISO15693_3_UID_SIZE];
|
||||
data_size = buf_size - buf_size_min_addr;
|
||||
|
||||
} else {
|
||||
data = request->data;
|
||||
data_size = buf_size - buf_size_min;
|
||||
}
|
||||
|
||||
error = iso15693_3_listener_handle_standard_request(
|
||||
instance, data, data_size, request->command, request->flags);
|
||||
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_listener_process_single_eof(Iso15693_3Listener* instance) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
if(!instance->session_state.wait_for_eof) {
|
||||
error = Iso15693_3ErrorUnexpectedResponse;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->session_state.wait_for_eof = false;
|
||||
|
||||
error = iso15693_3_listener_send_frame(instance, instance->tx_buffer);
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_listener_process_uid_mismatch(
|
||||
Iso15693_3Listener* instance,
|
||||
const BitBuffer* rx_buffer) {
|
||||
Iso15693_3Error error = Iso15693_3ErrorNone;
|
||||
|
||||
// No checks, assuming they have been made beforehand
|
||||
typedef struct {
|
||||
uint8_t flags;
|
||||
uint8_t command;
|
||||
} Iso15693_3RequestLayout;
|
||||
|
||||
const Iso15693_3RequestLayout* request =
|
||||
(const Iso15693_3RequestLayout*)bit_buffer_get_data(rx_buffer);
|
||||
|
||||
if(request->command == ISO15693_3_CMD_SELECT) {
|
||||
if(instance->state == Iso15693_3ListenerStateSelected) {
|
||||
error = iso15693_3_listener_ready(instance);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_generic_event.h>
|
||||
|
||||
#include "iso15693_3_listener.h"
|
||||
|
||||
#include "iso15693_3_i.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
Iso15693_3ListenerStateReady,
|
||||
Iso15693_3ListenerStateSelected,
|
||||
Iso15693_3ListenerStateQuiet,
|
||||
} Iso15693_3ListenerState;
|
||||
|
||||
typedef struct {
|
||||
bool selected;
|
||||
bool addressed;
|
||||
bool wait_for_eof;
|
||||
} Iso15693_3ListenerSessionState;
|
||||
|
||||
typedef Iso15693_3Error (*Iso15693_3ExtensionHandler)(void* context, va_list args);
|
||||
|
||||
typedef struct {
|
||||
Iso15693_3ExtensionHandler mandatory[ISO15693_3_MANDATORY_COUNT];
|
||||
Iso15693_3ExtensionHandler optional[ISO15693_3_OPTIONAL_COUNT];
|
||||
} Iso15693_3ExtensionHandlerTable;
|
||||
|
||||
struct Iso15693_3Listener {
|
||||
Nfc* nfc;
|
||||
Iso15693_3Data* data;
|
||||
Iso15693_3ListenerState state;
|
||||
Iso15693_3ListenerSessionState session_state;
|
||||
BitBuffer* tx_buffer;
|
||||
|
||||
NfcGenericEvent generic_event;
|
||||
Iso15693_3ListenerEvent iso15693_3_event;
|
||||
Iso15693_3ListenerEventData iso15693_3_event_data;
|
||||
NfcGenericCallback callback;
|
||||
void* context;
|
||||
|
||||
const Iso15693_3ExtensionHandlerTable* extension_table;
|
||||
void* extension_context;
|
||||
};
|
||||
|
||||
Iso15693_3Error iso15693_3_listener_set_extension_handler_table(
|
||||
Iso15693_3Listener* instance,
|
||||
const Iso15693_3ExtensionHandlerTable* table,
|
||||
void* context);
|
||||
|
||||
Iso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance);
|
||||
|
||||
Iso15693_3Error
|
||||
iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer);
|
||||
|
||||
Iso15693_3Error
|
||||
iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer);
|
||||
|
||||
Iso15693_3Error iso15693_3_listener_process_single_eof(Iso15693_3Listener* instance);
|
||||
|
||||
Iso15693_3Error iso15693_3_listener_process_uid_mismatch(
|
||||
Iso15693_3Listener* instance,
|
||||
const BitBuffer* rx_buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
#include "iso15693_3_poller_i.h"
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "ISO15693_3Poller"
|
||||
|
||||
const Iso15693_3Data* iso15693_3_poller_get_data(Iso15693_3Poller* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->data);
|
||||
|
||||
return instance->data;
|
||||
}
|
||||
|
||||
static Iso15693_3Poller* iso15693_3_poller_alloc(Nfc* nfc) {
|
||||
furi_assert(nfc);
|
||||
|
||||
Iso15693_3Poller* instance = malloc(sizeof(Iso15693_3Poller));
|
||||
instance->nfc = nfc;
|
||||
instance->tx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE);
|
||||
instance->rx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE);
|
||||
|
||||
nfc_config(instance->nfc, NfcModePoller, NfcTechIso15693);
|
||||
nfc_set_guard_time_us(instance->nfc, ISO15693_3_GUARD_TIME_US);
|
||||
nfc_set_fdt_poll_fc(instance->nfc, ISO15693_3_FDT_POLL_FC);
|
||||
nfc_set_fdt_poll_poll_us(instance->nfc, ISO15693_3_POLL_POLL_MIN_US);
|
||||
instance->data = iso15693_3_alloc();
|
||||
|
||||
instance->iso15693_3_event.data = &instance->iso15693_3_event_data;
|
||||
instance->general_event.protocol = NfcProtocolIso15693_3;
|
||||
instance->general_event.event_data = &instance->iso15693_3_event;
|
||||
instance->general_event.instance = instance;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void iso15693_3_poller_free(Iso15693_3Poller* 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);
|
||||
iso15693_3_free(instance->data);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static void iso15693_3_poller_set_callback(
|
||||
Iso15693_3Poller* instance,
|
||||
NfcGenericCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
static NfcCommand iso15693_3_poller_run(NfcGenericEvent event, void* context) {
|
||||
furi_assert(context);
|
||||
furi_assert(event.protocol == NfcProtocolInvalid);
|
||||
furi_assert(event.event_data);
|
||||
|
||||
Iso15693_3Poller* instance = context;
|
||||
NfcEvent* nfc_event = event.event_data;
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(nfc_event->type == NfcEventTypePollerReady) {
|
||||
if(instance->state != Iso15693_3PollerStateActivated) {
|
||||
Iso15693_3Error error = iso15693_3_poller_async_activate(instance, instance->data);
|
||||
if(error == Iso15693_3ErrorNone) {
|
||||
instance->iso15693_3_event.type = Iso15693_3PollerEventTypeReady;
|
||||
instance->iso15693_3_event_data.error = error;
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
} else {
|
||||
instance->iso15693_3_event.type = Iso15693_3PollerEventTypeError;
|
||||
instance->iso15693_3_event_data.error = error;
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
// Add delay to switch context
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
} else {
|
||||
instance->iso15693_3_event.type = Iso15693_3PollerEventTypeReady;
|
||||
instance->iso15693_3_event_data.error = Iso15693_3ErrorNone;
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
}
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static bool iso15693_3_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;
|
||||
Iso15693_3Poller* instance = context;
|
||||
NfcEvent* nfc_event = event.event_data;
|
||||
furi_assert(instance->state == Iso15693_3PollerStateIdle);
|
||||
|
||||
if(nfc_event->type == NfcEventTypePollerReady) {
|
||||
uint8_t uid[ISO15693_3_UID_SIZE];
|
||||
Iso15693_3Error error = iso15693_3_poller_async_inventory(instance, uid);
|
||||
protocol_detected = (error == Iso15693_3ErrorNone);
|
||||
}
|
||||
|
||||
return protocol_detected;
|
||||
}
|
||||
|
||||
const NfcPollerBase nfc_poller_iso15693_3 = {
|
||||
.alloc = (NfcPollerAlloc)iso15693_3_poller_alloc,
|
||||
.free = (NfcPollerFree)iso15693_3_poller_free,
|
||||
.set_callback = (NfcPollerSetCallback)iso15693_3_poller_set_callback,
|
||||
.run = (NfcPollerRun)iso15693_3_poller_run,
|
||||
.detect = (NfcPollerDetect)iso15693_3_poller_detect,
|
||||
.get_data = (NfcPollerGetData)iso15693_3_poller_get_data,
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "iso15693_3.h"
|
||||
|
||||
#include <nfc/nfc_poller.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct Iso15693_3Poller Iso15693_3Poller;
|
||||
|
||||
typedef enum {
|
||||
Iso15693_3PollerEventTypeError,
|
||||
Iso15693_3PollerEventTypeReady,
|
||||
} Iso15693_3PollerEventType;
|
||||
|
||||
typedef struct {
|
||||
Iso15693_3Error error;
|
||||
} Iso15693_3PollerEventData;
|
||||
|
||||
typedef struct {
|
||||
Iso15693_3PollerEventType type;
|
||||
Iso15693_3PollerEventData* data;
|
||||
} Iso15693_3PollerEvent;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
extern const NfcPollerBase nfc_poller_iso15693_3;
|
||||
@@ -0,0 +1,303 @@
|
||||
#include "iso15693_3_poller_i.h"
|
||||
|
||||
#include <nfc/helpers/iso13239_crc.h>
|
||||
|
||||
#define TAG "Iso15693_3Poller"
|
||||
|
||||
#define BITS_IN_BYTE (8)
|
||||
|
||||
#define ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY (32U)
|
||||
|
||||
static Iso15693_3Error iso15693_3_poller_process_nfc_error(NfcError error) {
|
||||
switch(error) {
|
||||
case NfcErrorNone:
|
||||
return Iso15693_3ErrorNone;
|
||||
case NfcErrorTimeout:
|
||||
return Iso15693_3ErrorTimeout;
|
||||
default:
|
||||
return Iso15693_3ErrorNotPresent;
|
||||
}
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_poller_filter_error(Iso15693_3Error error) {
|
||||
switch(error) {
|
||||
/* If a particular optional command is not supported, the card might
|
||||
* respond with a "Not supported" error or not respond at all.
|
||||
* Therefore, treat these errors as non-critical ones. */
|
||||
case Iso15693_3ErrorNotSupported:
|
||||
case Iso15693_3ErrorTimeout:
|
||||
return Iso15693_3ErrorNone;
|
||||
default:
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_poller_prepare_trx(Iso15693_3Poller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(instance->state == Iso15693_3PollerStateIdle) {
|
||||
return iso15693_3_poller_async_activate(instance, NULL);
|
||||
}
|
||||
|
||||
return Iso15693_3ErrorNone;
|
||||
}
|
||||
|
||||
static Iso15693_3Error iso15693_3_poller_frame_exchange(
|
||||
Iso15693_3Poller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
furi_assert(instance);
|
||||
|
||||
Iso15693_3Error ret = Iso15693_3ErrorNone;
|
||||
|
||||
do {
|
||||
if(bit_buffer_get_size_bytes(tx_buffer) >
|
||||
bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO13239_CRC_SIZE) {
|
||||
ret = Iso15693_3ErrorBufferOverflow;
|
||||
break;
|
||||
}
|
||||
|
||||
bit_buffer_copy(instance->tx_buffer, tx_buffer);
|
||||
iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer);
|
||||
|
||||
NfcError error =
|
||||
nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt);
|
||||
if(error != NfcErrorNone) {
|
||||
ret = iso15693_3_poller_process_nfc_error(error);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!iso13239_crc_check(Iso13239CrcTypeDefault, instance->rx_buffer)) {
|
||||
ret = Iso15693_3ErrorWrongCrc;
|
||||
break;
|
||||
}
|
||||
|
||||
iso13239_crc_trim(instance->rx_buffer);
|
||||
bit_buffer_copy(rx_buffer, instance->rx_buffer);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso15693_3Error
|
||||
iso15693_3_poller_async_activate(Iso15693_3Poller* instance, Iso15693_3Data* data) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
|
||||
iso15693_3_reset(data);
|
||||
|
||||
Iso15693_3Error ret;
|
||||
|
||||
do {
|
||||
instance->state = Iso15693_3PollerStateColResInProgress;
|
||||
|
||||
// Inventory: Mandatory command
|
||||
ret = iso15693_3_poller_async_inventory(instance, data->uid);
|
||||
if(ret != Iso15693_3ErrorNone) {
|
||||
instance->state = Iso15693_3PollerStateColResFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->state = Iso15693_3PollerStateActivated;
|
||||
|
||||
// Get system info: Optional command
|
||||
Iso15693_3SystemInfo* system_info = &data->system_info;
|
||||
ret = iso15693_3_poller_async_get_system_info(instance, system_info);
|
||||
if(ret != Iso15693_3ErrorNone) {
|
||||
ret = iso15693_3_poller_filter_error(ret);
|
||||
break;
|
||||
}
|
||||
|
||||
// Read blocks: Optional command
|
||||
simple_array_init(data->block_data, system_info->block_count * system_info->block_size);
|
||||
ret = iso15693_3_poller_async_read_blocks(
|
||||
instance,
|
||||
simple_array_get_data(data->block_data),
|
||||
system_info->block_count,
|
||||
system_info->block_size);
|
||||
if(ret != Iso15693_3ErrorNone) {
|
||||
ret = iso15693_3_poller_filter_error(ret);
|
||||
break;
|
||||
}
|
||||
|
||||
// Get block security status: Optional command
|
||||
simple_array_init(data->block_security, system_info->block_count);
|
||||
|
||||
ret = iso15693_3_poller_async_get_blocks_security(
|
||||
instance, simple_array_get_data(data->block_security), system_info->block_count);
|
||||
if(ret != Iso15693_3ErrorNone) {
|
||||
ret = iso15693_3_poller_filter_error(ret);
|
||||
break;
|
||||
}
|
||||
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_async_inventory(Iso15693_3Poller* instance, uint8_t* uid) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->nfc);
|
||||
furi_assert(uid);
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
// Send INVENTORY
|
||||
bit_buffer_append_byte(
|
||||
instance->tx_buffer,
|
||||
ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI |
|
||||
ISO15693_3_REQ_FLAG_INVENTORY_T5 | ISO15693_3_REQ_FLAG_T5_N_SLOTS_1);
|
||||
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_INVENTORY);
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0x00);
|
||||
|
||||
Iso15693_3Error ret;
|
||||
|
||||
do {
|
||||
ret = iso15693_3_poller_frame_exchange(
|
||||
instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);
|
||||
if(ret != Iso15693_3ErrorNone) break;
|
||||
|
||||
ret = iso15693_3_inventory_response_parse(uid, instance->rx_buffer);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_async_get_system_info(
|
||||
Iso15693_3Poller* instance,
|
||||
Iso15693_3SystemInfo* data) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
// Send GET SYSTEM INFO
|
||||
bit_buffer_append_byte(
|
||||
instance->tx_buffer, ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI);
|
||||
|
||||
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_GET_SYS_INFO);
|
||||
|
||||
Iso15693_3Error ret;
|
||||
|
||||
do {
|
||||
ret = iso15693_3_poller_frame_exchange(
|
||||
instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);
|
||||
if(ret != Iso15693_3ErrorNone) break;
|
||||
|
||||
ret = iso15693_3_system_info_response_parse(data, instance->rx_buffer);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_async_read_block(
|
||||
Iso15693_3Poller* instance,
|
||||
uint8_t* data,
|
||||
uint8_t block_number,
|
||||
uint8_t block_size) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
bit_buffer_append_byte(
|
||||
instance->tx_buffer, ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI);
|
||||
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_READ_BLOCK);
|
||||
bit_buffer_append_byte(instance->tx_buffer, block_number);
|
||||
|
||||
Iso15693_3Error ret;
|
||||
|
||||
do {
|
||||
ret = iso15693_3_poller_send_frame(
|
||||
instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);
|
||||
if(ret != Iso15693_3ErrorNone) break;
|
||||
|
||||
ret = iso15693_3_read_block_response_parse(data, block_size, instance->rx_buffer);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_async_read_blocks(
|
||||
Iso15693_3Poller* instance,
|
||||
uint8_t* data,
|
||||
uint16_t block_count,
|
||||
uint8_t block_size) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
furi_assert(block_count);
|
||||
furi_assert(block_size);
|
||||
|
||||
Iso15693_3Error ret = Iso15693_3ErrorNone;
|
||||
|
||||
for(uint32_t i = 0; i < block_count; ++i) {
|
||||
ret = iso15693_3_poller_async_read_block(instance, &data[block_size * i], i, block_size);
|
||||
if(ret != Iso15693_3ErrorNone) break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_async_get_blocks_security(
|
||||
Iso15693_3Poller* instance,
|
||||
uint8_t* data,
|
||||
uint16_t block_count) {
|
||||
furi_assert(instance);
|
||||
furi_assert(data);
|
||||
|
||||
// Limit the number of blocks to 32 in a single query
|
||||
const uint32_t num_queries = block_count / ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY +
|
||||
(block_count % ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY ? 1 : 0);
|
||||
|
||||
Iso15693_3Error ret = Iso15693_3ErrorNone;
|
||||
|
||||
for(uint32_t i = 0; i < num_queries; ++i) {
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
bit_buffer_append_byte(
|
||||
instance->tx_buffer,
|
||||
ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI);
|
||||
|
||||
bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_GET_BLOCKS_SECURITY);
|
||||
|
||||
const uint8_t start_block_num = i * ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY;
|
||||
bit_buffer_append_byte(instance->tx_buffer, start_block_num);
|
||||
|
||||
const uint8_t block_count_per_query =
|
||||
MIN(block_count - start_block_num, (uint16_t)ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY);
|
||||
// Block count byte must be 1 less than the desired count
|
||||
bit_buffer_append_byte(instance->tx_buffer, block_count_per_query - 1);
|
||||
|
||||
ret = iso15693_3_poller_send_frame(
|
||||
instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC);
|
||||
if(ret != Iso15693_3ErrorNone) break;
|
||||
|
||||
ret = iso15693_3_get_block_security_response_parse(
|
||||
&data[start_block_num], block_count_per_query, instance->rx_buffer);
|
||||
if(ret != Iso15693_3ErrorNone) break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_send_frame(
|
||||
Iso15693_3Poller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt) {
|
||||
Iso15693_3Error ret;
|
||||
|
||||
do {
|
||||
ret = iso15693_3_poller_prepare_trx(instance);
|
||||
if(ret != Iso15693_3ErrorNone) break;
|
||||
|
||||
ret = iso15693_3_poller_frame_exchange(instance, tx_buffer, rx_buffer, fwt);
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include "iso15693_3_poller.h"
|
||||
|
||||
#include "iso15693_3_i.h"
|
||||
|
||||
#include <toolbox/bit_buffer.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ISO15693_3_POLLER_MAX_BUFFER_SIZE (64U)
|
||||
|
||||
typedef enum {
|
||||
Iso15693_3PollerStateIdle,
|
||||
Iso15693_3PollerStateColResInProgress,
|
||||
Iso15693_3PollerStateColResFailed,
|
||||
Iso15693_3PollerStateActivated,
|
||||
} Iso15693_3PollerState;
|
||||
|
||||
struct Iso15693_3Poller {
|
||||
Nfc* nfc;
|
||||
Iso15693_3PollerState state;
|
||||
Iso15693_3Data* data;
|
||||
BitBuffer* tx_buffer;
|
||||
BitBuffer* rx_buffer;
|
||||
|
||||
NfcGenericEvent general_event;
|
||||
Iso15693_3PollerEvent iso15693_3_event;
|
||||
Iso15693_3PollerEventData iso15693_3_event_data;
|
||||
NfcGenericCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
const Iso15693_3Data* iso15693_3_poller_get_data(Iso15693_3Poller* instance);
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_async_activate(Iso15693_3Poller* instance, Iso15693_3Data* data);
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_async_inventory(Iso15693_3Poller* instance, uint8_t* uid);
|
||||
|
||||
Iso15693_3Error
|
||||
iso15693_3_poller_async_get_system_info(Iso15693_3Poller* instance, Iso15693_3SystemInfo* data);
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_async_read_block(
|
||||
Iso15693_3Poller* instance,
|
||||
uint8_t* data,
|
||||
uint8_t block_number,
|
||||
uint8_t block_size);
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_async_read_blocks(
|
||||
Iso15693_3Poller* instance,
|
||||
uint8_t* data,
|
||||
uint16_t block_count,
|
||||
uint8_t block_size);
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_async_get_blocks_security(
|
||||
Iso15693_3Poller* instance,
|
||||
uint8_t* data,
|
||||
uint16_t block_count);
|
||||
|
||||
Iso15693_3Error iso15693_3_poller_send_frame(
|
||||
Iso15693_3Poller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer,
|
||||
uint32_t fwt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user