mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-30 08:20:21 +00:00
917410a0a8
* fbt: reworking targets & assets handling WIP * fbt: dist fixes * fbt: moved SD card resources to owning apps * unit_tests: moved resources to app folder * github: updated unit_tests paths * github: packaging fixes * unit_tests: fixes * fbt: assets: internal cleanup * fbt: reworked assets handling * github: unit_tests: reintroducing fixes * minor cleanup * fbt: naming changes to reflect private nature of scons tools * fbt: resources: fixed dist archive paths * docs: updated paths * docs: updated more paths * docs: included "resources" parameter in app manifest docs; updated assets readme * updated gitignore for assets * github: updated action versions * unit_tests: restored timeout; scripts: assets: logging changes * gh: don't upload desktop animations for unit test run Co-authored-by: あく <alleteam@gmail.com>
463 lines
15 KiB
C
463 lines
15 KiB
C
#include "furi_hal_nfc_i.h"
|
|
#include "furi_hal_nfc_tech_i.h"
|
|
|
|
#include <digital_signal/presets/nfc/iso15693_signal.h>
|
|
#include <signal_reader/parsers/iso15693/iso15693_parser.h>
|
|
|
|
#include <furi_hal_resources.h>
|
|
|
|
#define FURI_HAL_NFC_ISO15693_MAX_FRAME_SIZE (1024U)
|
|
#define FURI_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE (64)
|
|
|
|
#define FURI_HAL_NFC_ISO15693_RESP_SOF_SIZE (5)
|
|
#define FURI_HAL_NFC_ISO15693_RESP_EOF_SIZE (5)
|
|
#define FURI_HAL_NFC_ISO15693_RESP_SOF_MASK (0x1FU)
|
|
#define FURI_HAL_NFC_ISO15693_RESP_SOF_PATTERN (0x17U)
|
|
#define FURI_HAL_NFC_ISO15693_RESP_EOF_PATTERN (0x1DU)
|
|
|
|
#define FURI_HAL_NFC_ISO15693_RESP_PATTERN_MASK (0x03U)
|
|
#define FURI_HAL_NFC_ISO15693_RESP_PATTERN_0 (0x01U)
|
|
#define FURI_HAL_NFC_ISO15693_RESP_PATTERN_1 (0x02U)
|
|
|
|
// Derived experimentally
|
|
#define FURI_HAL_NFC_ISO15693_POLLER_FWT_COMP_FC (-1300)
|
|
#define FURI_HAL_NFC_ISO15693_LISTENER_FDT_COMP_FC (2850)
|
|
|
|
#define BITS_IN_BYTE (8U)
|
|
|
|
#define TAG "FuriHalIso15693"
|
|
|
|
typedef struct {
|
|
Iso15693Signal* signal;
|
|
Iso15693Parser* parser;
|
|
} FuriHalNfcIso15693Listener;
|
|
|
|
typedef struct {
|
|
// 4 bits per data bit on transmit
|
|
uint8_t fifo_buf[FURI_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE * 4];
|
|
size_t fifo_buf_bits;
|
|
uint8_t frame_buf[FURI_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE * 2];
|
|
size_t frame_buf_bits;
|
|
} FuriHalNfcIso15693Poller;
|
|
|
|
static FuriHalNfcIso15693Listener* furi_hal_nfc_iso15693_listener = NULL;
|
|
static FuriHalNfcIso15693Poller* furi_hal_nfc_iso15693_poller = NULL;
|
|
|
|
static FuriHalNfcIso15693Listener* furi_hal_nfc_iso15693_listener_alloc() {
|
|
FuriHalNfcIso15693Listener* instance = malloc(sizeof(FuriHalNfcIso15693Listener));
|
|
|
|
instance->signal = iso15693_signal_alloc(&gpio_spi_r_mosi);
|
|
instance->parser =
|
|
iso15693_parser_alloc(&gpio_nfc_irq_rfid_pull, FURI_HAL_NFC_ISO15693_MAX_FRAME_SIZE);
|
|
|
|
return instance;
|
|
}
|
|
|
|
static void furi_hal_nfc_iso15693_listener_free(FuriHalNfcIso15693Listener* instance) {
|
|
furi_assert(instance);
|
|
|
|
iso15693_signal_free(instance->signal);
|
|
iso15693_parser_free(instance->parser);
|
|
|
|
free(instance);
|
|
}
|
|
|
|
static FuriHalNfcIso15693Poller* furi_hal_nfc_iso15693_poller_alloc() {
|
|
FuriHalNfcIso15693Poller* instance = malloc(sizeof(FuriHalNfcIso15693Poller));
|
|
|
|
return instance;
|
|
}
|
|
|
|
static void furi_hal_nfc_iso15693_poller_free(FuriHalNfcIso15693Poller* instance) {
|
|
furi_assert(instance);
|
|
|
|
free(instance);
|
|
}
|
|
|
|
static FuriHalNfcError furi_hal_nfc_iso15693_common_init(FuriHalSpiBusHandle* handle) {
|
|
// Common NFC-V settings, 26.48 kbps
|
|
|
|
// 1st stage zero = 12 kHz, 3rd stage zero = 80 kHz, low-pass = 600 kHz
|
|
st25r3916_write_reg(
|
|
handle,
|
|
ST25R3916_REG_RX_CONF1,
|
|
ST25R3916_REG_RX_CONF1_z12k | ST25R3916_REG_RX_CONF1_h80 |
|
|
ST25R3916_REG_RX_CONF1_lp_600khz);
|
|
|
|
// Enable AGC
|
|
// AGC Ratio 6
|
|
// AGC algorithm with RESET (recommended for ISO15693)
|
|
// AGC operation during complete receive period
|
|
// Squelch automatic activation on TX end
|
|
st25r3916_write_reg(
|
|
handle,
|
|
ST25R3916_REG_RX_CONF2,
|
|
ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m |
|
|
ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn);
|
|
|
|
// HF operation, full gain on AM and PM channels
|
|
st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00);
|
|
// No gain reduction on AM and PM channels
|
|
st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00);
|
|
|
|
// Collision detection level 53%
|
|
// AM & PM summation before digitizing on
|
|
st25r3916_write_reg(
|
|
handle,
|
|
ST25R3916_REG_CORR_CONF1,
|
|
ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s1 |
|
|
ST25R3916_REG_CORR_CONF1_corr_s4);
|
|
// 424 kHz subcarrier stream mode on
|
|
st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, ST25R3916_REG_CORR_CONF2_corr_s8);
|
|
return FuriHalNfcErrorNone;
|
|
}
|
|
|
|
static FuriHalNfcError furi_hal_nfc_iso15693_poller_init(FuriHalSpiBusHandle* handle) {
|
|
furi_assert(furi_hal_nfc_iso15693_poller == NULL);
|
|
|
|
furi_hal_nfc_iso15693_poller = furi_hal_nfc_iso15693_poller_alloc();
|
|
|
|
// Enable Subcarrier Stream mode, OOK modulation
|
|
st25r3916_change_reg_bits(
|
|
handle,
|
|
ST25R3916_REG_MODE,
|
|
ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am,
|
|
ST25R3916_REG_MODE_om_subcarrier_stream | ST25R3916_REG_MODE_tr_am_ook);
|
|
|
|
// Subcarrier 424 kHz mode
|
|
// 8 sub-carrier pulses in report period
|
|
st25r3916_write_reg(
|
|
handle,
|
|
ST25R3916_REG_STREAM_MODE,
|
|
ST25R3916_REG_STREAM_MODE_scf_sc424 | ST25R3916_REG_STREAM_MODE_stx_106 |
|
|
ST25R3916_REG_STREAM_MODE_scp_8pulses);
|
|
|
|
// Use regulator AM, resistive AM disabled
|
|
st25r3916_clear_reg_bits(
|
|
handle,
|
|
ST25R3916_REG_AUX_MOD,
|
|
ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am);
|
|
|
|
return furi_hal_nfc_iso15693_common_init(handle);
|
|
}
|
|
|
|
static FuriHalNfcError furi_hal_nfc_iso15693_poller_deinit(FuriHalSpiBusHandle* handle) {
|
|
UNUSED(handle);
|
|
furi_assert(furi_hal_nfc_iso15693_poller);
|
|
|
|
furi_hal_nfc_iso15693_poller_free(furi_hal_nfc_iso15693_poller);
|
|
furi_hal_nfc_iso15693_poller = NULL;
|
|
|
|
return FuriHalNfcErrorNone;
|
|
}
|
|
|
|
static void iso15693_3_poller_encode_frame(
|
|
const uint8_t* tx_data,
|
|
size_t tx_bits,
|
|
uint8_t* frame_buf,
|
|
size_t frame_buf_size,
|
|
size_t* frame_buf_bits) {
|
|
static const uint8_t bit_patterns_1_out_of_4[] = {0x02, 0x08, 0x20, 0x80};
|
|
size_t frame_buf_size_calc = (tx_bits / 2) + 2;
|
|
furi_assert(frame_buf_size >= frame_buf_size_calc);
|
|
|
|
// Add SOF 1 out of 4
|
|
frame_buf[0] = 0x21;
|
|
|
|
size_t byte_pos = 1;
|
|
for(size_t i = 0; i < tx_bits / BITS_IN_BYTE; ++i) {
|
|
for(size_t j = 0; j < BITS_IN_BYTE; j += (BITS_IN_BYTE) / 4) {
|
|
const uint8_t bit_pair = (tx_data[i] >> j) & 0x03;
|
|
frame_buf[byte_pos++] = bit_patterns_1_out_of_4[bit_pair];
|
|
}
|
|
}
|
|
// Add EOF
|
|
frame_buf[byte_pos++] = 0x04;
|
|
*frame_buf_bits = byte_pos * BITS_IN_BYTE;
|
|
}
|
|
|
|
static FuriHalNfcError iso15693_3_poller_decode_frame(
|
|
const uint8_t* buf,
|
|
size_t buf_bits,
|
|
uint8_t* buf_decoded,
|
|
size_t buf_decoded_size,
|
|
size_t* buf_decoded_bits) {
|
|
FuriHalNfcError ret = FuriHalNfcErrorDataFormat;
|
|
size_t bit_pos = 0;
|
|
memset(buf_decoded, 0, buf_decoded_size);
|
|
|
|
do {
|
|
if(buf_bits == 0) break;
|
|
// Check SOF
|
|
if((buf[0] & FURI_HAL_NFC_ISO15693_RESP_SOF_MASK) !=
|
|
FURI_HAL_NFC_ISO15693_RESP_SOF_PATTERN)
|
|
break;
|
|
|
|
if(buf_bits == BITS_IN_BYTE) {
|
|
ret = FuriHalNfcErrorIncompleteFrame;
|
|
break;
|
|
}
|
|
|
|
// 2 response bits = 1 data bit
|
|
for(uint32_t i = FURI_HAL_NFC_ISO15693_RESP_SOF_SIZE;
|
|
i < buf_bits - FURI_HAL_NFC_ISO15693_RESP_SOF_SIZE;
|
|
i += BITS_IN_BYTE / 4) {
|
|
const size_t byte_index = i / BITS_IN_BYTE;
|
|
const size_t bit_offset = i % BITS_IN_BYTE;
|
|
const uint8_t resp_byte = (buf[byte_index] >> bit_offset) |
|
|
(buf[byte_index + 1] << (BITS_IN_BYTE - bit_offset));
|
|
|
|
// Check EOF
|
|
if(resp_byte == FURI_HAL_NFC_ISO15693_RESP_EOF_PATTERN) {
|
|
ret = FuriHalNfcErrorNone;
|
|
break;
|
|
}
|
|
|
|
const uint8_t bit_pattern = resp_byte & FURI_HAL_NFC_ISO15693_RESP_PATTERN_MASK;
|
|
|
|
if(bit_pattern == FURI_HAL_NFC_ISO15693_RESP_PATTERN_0) {
|
|
bit_pos++;
|
|
} else if(bit_pattern == FURI_HAL_NFC_ISO15693_RESP_PATTERN_1) {
|
|
buf_decoded[bit_pos / BITS_IN_BYTE] |= 1 << (bit_pos % BITS_IN_BYTE);
|
|
bit_pos++;
|
|
} else {
|
|
break;
|
|
}
|
|
if(bit_pos / BITS_IN_BYTE > buf_decoded_size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
} while(false);
|
|
|
|
if(ret == FuriHalNfcErrorNone) {
|
|
*buf_decoded_bits = bit_pos;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static FuriHalNfcError furi_hal_nfc_iso15693_poller_tx(
|
|
FuriHalSpiBusHandle* handle,
|
|
const uint8_t* tx_data,
|
|
size_t tx_bits) {
|
|
FuriHalNfcIso15693Poller* instance = furi_hal_nfc_iso15693_poller;
|
|
iso15693_3_poller_encode_frame(
|
|
tx_data,
|
|
tx_bits,
|
|
instance->frame_buf,
|
|
sizeof(instance->frame_buf),
|
|
&instance->frame_buf_bits);
|
|
return furi_hal_nfc_poller_tx_common(handle, instance->frame_buf, instance->frame_buf_bits);
|
|
}
|
|
|
|
static FuriHalNfcError furi_hal_nfc_iso15693_poller_rx(
|
|
FuriHalSpiBusHandle* handle,
|
|
uint8_t* rx_data,
|
|
size_t rx_data_size,
|
|
size_t* rx_bits) {
|
|
FuriHalNfcError error = FuriHalNfcErrorNone;
|
|
FuriHalNfcIso15693Poller* instance = furi_hal_nfc_iso15693_poller;
|
|
|
|
do {
|
|
error = furi_hal_nfc_common_fifo_rx(
|
|
handle, instance->fifo_buf, sizeof(instance->fifo_buf), &instance->fifo_buf_bits);
|
|
if(error != FuriHalNfcErrorNone) break;
|
|
|
|
error = iso15693_3_poller_decode_frame(
|
|
instance->fifo_buf,
|
|
instance->fifo_buf_bits,
|
|
instance->frame_buf,
|
|
sizeof(instance->frame_buf),
|
|
&instance->frame_buf_bits);
|
|
if(error != FuriHalNfcErrorNone) break;
|
|
|
|
if(rx_data_size < instance->frame_buf_bits / BITS_IN_BYTE) {
|
|
error = FuriHalNfcErrorBufferOverflow;
|
|
break;
|
|
}
|
|
|
|
memcpy(rx_data, instance->frame_buf, instance->frame_buf_bits / BITS_IN_BYTE);
|
|
*rx_bits = instance->frame_buf_bits;
|
|
} while(false);
|
|
|
|
return error;
|
|
}
|
|
|
|
static void furi_hal_nfc_iso15693_listener_transparent_mode_enter(FuriHalSpiBusHandle* handle) {
|
|
st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSPARENT_MODE);
|
|
|
|
furi_hal_spi_bus_handle_deinit(handle);
|
|
furi_hal_nfc_deinit_gpio_isr();
|
|
}
|
|
|
|
static void furi_hal_nfc_iso15693_listener_transparent_mode_exit(FuriHalSpiBusHandle* handle) {
|
|
// Configure gpio back to SPI and exit transparent mode
|
|
furi_hal_nfc_init_gpio_isr();
|
|
furi_hal_spi_bus_handle_init(handle);
|
|
|
|
st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA);
|
|
}
|
|
|
|
static FuriHalNfcError furi_hal_nfc_iso15693_listener_init(FuriHalSpiBusHandle* handle) {
|
|
furi_assert(furi_hal_nfc_iso15693_listener == NULL);
|
|
|
|
furi_hal_nfc_iso15693_listener = furi_hal_nfc_iso15693_listener_alloc();
|
|
|
|
// Set default operation mode
|
|
st25r3916_change_reg_bits(
|
|
handle,
|
|
ST25R3916_REG_MODE,
|
|
ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am,
|
|
ST25R3916_REG_MODE_om_targ_nfca | ST25R3916_REG_MODE_tr_am_ook);
|
|
|
|
st25r3916_change_reg_bits(
|
|
handle,
|
|
ST25R3916_REG_OP_CONTROL,
|
|
ST25R3916_REG_OP_CONTROL_rx_en,
|
|
ST25R3916_REG_OP_CONTROL_rx_en);
|
|
|
|
// Enable passive target mode
|
|
st25r3916_change_reg_bits(
|
|
handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ, ST25R3916_REG_MODE_targ_targ);
|
|
|
|
FuriHalNfcError error = furi_hal_nfc_iso15693_common_init(handle);
|
|
|
|
furi_hal_nfc_iso15693_listener_transparent_mode_enter(handle);
|
|
|
|
return error;
|
|
}
|
|
|
|
static FuriHalNfcError furi_hal_nfc_iso15693_listener_deinit(FuriHalSpiBusHandle* handle) {
|
|
furi_assert(furi_hal_nfc_iso15693_listener);
|
|
|
|
furi_hal_nfc_iso15693_listener_transparent_mode_exit(handle);
|
|
|
|
furi_hal_nfc_iso15693_listener_free(furi_hal_nfc_iso15693_listener);
|
|
furi_hal_nfc_iso15693_listener = NULL;
|
|
|
|
return FuriHalNfcErrorNone;
|
|
}
|
|
|
|
static FuriHalNfcError
|
|
furi_hal_nfc_iso15693_listener_tx_transparent(const uint8_t* data, size_t data_size) {
|
|
iso15693_signal_tx(
|
|
furi_hal_nfc_iso15693_listener->signal, Iso15693SignalDataRateHi, data, data_size);
|
|
|
|
return FuriHalNfcErrorNone;
|
|
}
|
|
|
|
static void furi_hal_nfc_iso15693_parser_callback(Iso15693ParserEvent event, void* context) {
|
|
furi_assert(context);
|
|
|
|
if(event == Iso15693ParserEventDataReceived) {
|
|
FuriThreadId thread_id = context;
|
|
furi_thread_flags_set(thread_id, FuriHalNfcEventInternalTypeTransparentDataReceived);
|
|
}
|
|
}
|
|
|
|
static FuriHalNfcEvent furi_hal_nfc_iso15693_wait_event(uint32_t timeout_ms) {
|
|
FuriHalNfcEvent event = 0;
|
|
|
|
FuriThreadId thread_id = furi_thread_get_current_id();
|
|
iso15693_parser_start(
|
|
furi_hal_nfc_iso15693_listener->parser, furi_hal_nfc_iso15693_parser_callback, thread_id);
|
|
|
|
while(true) {
|
|
uint32_t flag = furi_thread_flags_wait(
|
|
FuriHalNfcEventInternalTypeAbort | FuriHalNfcEventInternalTypeTransparentDataReceived,
|
|
FuriFlagWaitAny,
|
|
timeout_ms);
|
|
furi_thread_flags_clear(flag);
|
|
|
|
if(flag & FuriHalNfcEventInternalTypeAbort) {
|
|
event = FuriHalNfcEventAbortRequest;
|
|
break;
|
|
}
|
|
if(flag & FuriHalNfcEventInternalTypeTransparentDataReceived) {
|
|
if(iso15693_parser_run(furi_hal_nfc_iso15693_listener->parser)) {
|
|
event = FuriHalNfcEventRxEnd;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
iso15693_parser_stop(furi_hal_nfc_iso15693_listener->parser);
|
|
|
|
return event;
|
|
}
|
|
|
|
static FuriHalNfcError furi_hal_nfc_iso15693_listener_tx(
|
|
FuriHalSpiBusHandle* handle,
|
|
const uint8_t* tx_data,
|
|
size_t tx_bits) {
|
|
UNUSED(handle);
|
|
furi_assert(furi_hal_nfc_iso15693_listener);
|
|
|
|
FuriHalNfcError error = FuriHalNfcErrorNone;
|
|
|
|
error = furi_hal_nfc_iso15693_listener_tx_transparent(tx_data, tx_bits / BITS_IN_BYTE);
|
|
|
|
return error;
|
|
}
|
|
|
|
FuriHalNfcError furi_hal_nfc_iso15693_listener_tx_sof() {
|
|
iso15693_signal_tx_sof(furi_hal_nfc_iso15693_listener->signal, Iso15693SignalDataRateHi);
|
|
|
|
return FuriHalNfcErrorNone;
|
|
}
|
|
|
|
static FuriHalNfcError furi_hal_nfc_iso15693_listener_rx(
|
|
FuriHalSpiBusHandle* handle,
|
|
uint8_t* rx_data,
|
|
size_t rx_data_size,
|
|
size_t* rx_bits) {
|
|
furi_assert(furi_hal_nfc_iso15693_listener);
|
|
UNUSED(handle);
|
|
|
|
if(rx_data_size <
|
|
iso15693_parser_get_data_size_bytes(furi_hal_nfc_iso15693_listener->parser)) {
|
|
return FuriHalNfcErrorBufferOverflow;
|
|
}
|
|
|
|
iso15693_parser_get_data(
|
|
furi_hal_nfc_iso15693_listener->parser, rx_data, rx_data_size, rx_bits);
|
|
|
|
return FuriHalNfcErrorNone;
|
|
}
|
|
|
|
FuriHalNfcError furi_hal_nfc_iso15693_listener_sleep(FuriHalSpiBusHandle* handle) {
|
|
UNUSED(handle);
|
|
|
|
return FuriHalNfcErrorNone;
|
|
}
|
|
|
|
const FuriHalNfcTechBase furi_hal_nfc_iso15693 = {
|
|
.poller =
|
|
{
|
|
.compensation =
|
|
{
|
|
.fdt = FURI_HAL_NFC_POLLER_FDT_COMP_FC,
|
|
.fwt = FURI_HAL_NFC_ISO15693_POLLER_FWT_COMP_FC,
|
|
},
|
|
.init = furi_hal_nfc_iso15693_poller_init,
|
|
.deinit = furi_hal_nfc_iso15693_poller_deinit,
|
|
.wait_event = furi_hal_nfc_wait_event_common,
|
|
.tx = furi_hal_nfc_iso15693_poller_tx,
|
|
.rx = furi_hal_nfc_iso15693_poller_rx,
|
|
},
|
|
|
|
.listener =
|
|
{
|
|
.compensation =
|
|
{
|
|
.fdt = FURI_HAL_NFC_ISO15693_LISTENER_FDT_COMP_FC,
|
|
},
|
|
.init = furi_hal_nfc_iso15693_listener_init,
|
|
.deinit = furi_hal_nfc_iso15693_listener_deinit,
|
|
.wait_event = furi_hal_nfc_iso15693_wait_event,
|
|
.tx = furi_hal_nfc_iso15693_listener_tx,
|
|
.rx = furi_hal_nfc_iso15693_listener_rx,
|
|
.sleep = furi_hal_nfc_iso15693_listener_sleep,
|
|
.idle = furi_hal_nfc_iso15693_listener_sleep,
|
|
},
|
|
};
|