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.
2023-10-24 06:08:09 +03:00
|
|
|
#ifndef FW_CFG_unit_tests
|
|
|
|
|
|
|
|
#include "nfc.h"
|
|
|
|
|
|
|
|
#include <furi_hal_nfc.h>
|
|
|
|
#include <furi/furi.h>
|
|
|
|
|
|
|
|
#define TAG "Nfc"
|
|
|
|
|
|
|
|
#define NFC_MAX_BUFFER_SIZE (256)
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
NfcStateIdle,
|
|
|
|
NfcStateRunning,
|
|
|
|
} NfcState;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
NfcPollerStateStart,
|
|
|
|
NfcPollerStateReady,
|
|
|
|
NfcPollerStateReset,
|
|
|
|
NfcPollerStateStop,
|
|
|
|
|
|
|
|
NfcPollerStateNum,
|
|
|
|
} NfcPollerState;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
NfcCommStateIdle,
|
|
|
|
NfcCommStateWaitBlockTxTimer,
|
|
|
|
NfcCommStateReadyTx,
|
|
|
|
NfcCommStateWaitTxEnd,
|
|
|
|
NfcCommStateWaitRxStart,
|
|
|
|
NfcCommStateWaitRxEnd,
|
|
|
|
NfcCommStateFailed,
|
|
|
|
} NfcCommState;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
NfcConfigurationStateIdle,
|
|
|
|
NfcConfigurationStateDone,
|
|
|
|
} NfcConfigurationState;
|
|
|
|
|
|
|
|
struct Nfc {
|
|
|
|
NfcState state;
|
|
|
|
NfcPollerState poller_state;
|
|
|
|
NfcCommState comm_state;
|
|
|
|
NfcConfigurationState config_state;
|
|
|
|
NfcMode mode;
|
|
|
|
|
|
|
|
uint32_t fdt_listen_fc;
|
|
|
|
uint32_t mask_rx_time_fc;
|
|
|
|
uint32_t fdt_poll_fc;
|
|
|
|
uint32_t fdt_poll_poll_us;
|
|
|
|
uint32_t guard_time_us;
|
|
|
|
NfcEventCallback callback;
|
|
|
|
void* context;
|
|
|
|
|
|
|
|
uint8_t tx_buffer[NFC_MAX_BUFFER_SIZE];
|
|
|
|
size_t tx_bits;
|
|
|
|
uint8_t rx_buffer[NFC_MAX_BUFFER_SIZE];
|
|
|
|
size_t rx_bits;
|
|
|
|
|
|
|
|
FuriThread* worker_thread;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef bool (*NfcWorkerPollerStateHandler)(Nfc* instance);
|
|
|
|
|
|
|
|
static const FuriHalNfcTech nfc_tech_table[NfcModeNum][NfcTechNum] = {
|
|
|
|
[NfcModePoller] =
|
|
|
|
{
|
|
|
|
[NfcTechIso14443a] = FuriHalNfcTechIso14443a,
|
|
|
|
[NfcTechIso14443b] = FuriHalNfcTechIso14443b,
|
|
|
|
[NfcTechIso15693] = FuriHalNfcTechIso15693,
|
|
|
|
[NfcTechFelica] = FuriHalNfcTechFelica,
|
|
|
|
},
|
|
|
|
[NfcModeListener] =
|
|
|
|
{
|
|
|
|
[NfcTechIso14443a] = FuriHalNfcTechIso14443a,
|
|
|
|
[NfcTechIso14443b] = FuriHalNfcTechInvalid,
|
|
|
|
[NfcTechIso15693] = FuriHalNfcTechIso15693,
|
2023-11-26 11:10:33 +03:00
|
|
|
[NfcTechFelica] = FuriHalNfcTechFelica,
|
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.
2023-10-24 06:08:09 +03:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static NfcError nfc_process_hal_error(FuriHalNfcError error) {
|
|
|
|
NfcError ret = NfcErrorNone;
|
|
|
|
|
|
|
|
switch(error) {
|
|
|
|
case FuriHalNfcErrorNone:
|
|
|
|
ret = NfcErrorNone;
|
|
|
|
break;
|
|
|
|
case FuriHalNfcErrorIncompleteFrame:
|
|
|
|
ret = NfcErrorIncompleteFrame;
|
|
|
|
break;
|
|
|
|
case FuriHalNfcErrorDataFormat:
|
|
|
|
ret = NfcErrorDataFormat;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ret = NfcErrorInternal;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t nfc_worker_listener(void* context) {
|
|
|
|
furi_assert(context);
|
|
|
|
|
|
|
|
Nfc* instance = context;
|
|
|
|
furi_assert(instance->callback);
|
|
|
|
furi_assert(instance->config_state == NfcConfigurationStateDone);
|
|
|
|
|
|
|
|
instance->state = NfcStateRunning;
|
|
|
|
|
|
|
|
furi_hal_nfc_event_start();
|
|
|
|
|
|
|
|
NfcEventData event_data = {};
|
|
|
|
event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE);
|
|
|
|
NfcEvent nfc_event = {.data = event_data};
|
|
|
|
NfcCommand command = NfcCommandContinue;
|
|
|
|
|
|
|
|
while(true) {
|
|
|
|
FuriHalNfcEvent event = furi_hal_nfc_listener_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
|
|
|
|
if(event & FuriHalNfcEventAbortRequest) {
|
|
|
|
nfc_event.type = NfcEventTypeUserAbort;
|
|
|
|
instance->callback(nfc_event, instance->context);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(event & FuriHalNfcEventFieldOn) {
|
|
|
|
nfc_event.type = NfcEventTypeFieldOn;
|
|
|
|
instance->callback(nfc_event, instance->context);
|
|
|
|
}
|
|
|
|
if(event & FuriHalNfcEventFieldOff) {
|
|
|
|
nfc_event.type = NfcEventTypeFieldOff;
|
|
|
|
instance->callback(nfc_event, instance->context);
|
|
|
|
furi_hal_nfc_listener_idle();
|
|
|
|
}
|
|
|
|
if(event & FuriHalNfcEventListenerActive) {
|
|
|
|
nfc_event.type = NfcEventTypeListenerActivated;
|
|
|
|
instance->callback(nfc_event, instance->context);
|
|
|
|
}
|
|
|
|
if(event & FuriHalNfcEventRxEnd) {
|
|
|
|
furi_hal_nfc_timer_block_tx_start(instance->fdt_listen_fc);
|
|
|
|
|
|
|
|
nfc_event.type = NfcEventTypeRxEnd;
|
|
|
|
furi_hal_nfc_listener_rx(
|
|
|
|
instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);
|
|
|
|
bit_buffer_copy_bits(event_data.buffer, instance->rx_buffer, instance->rx_bits);
|
|
|
|
command = instance->callback(nfc_event, instance->context);
|
|
|
|
if(command == NfcCommandStop) {
|
|
|
|
break;
|
|
|
|
} else if(command == NfcCommandReset) {
|
|
|
|
furi_hal_nfc_listener_enable_rx();
|
|
|
|
} else if(command == NfcCommandSleep) {
|
2023-12-29 06:24:20 +03:00
|
|
|
furi_hal_nfc_listener_idle();
|
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.
2023-10-24 06:08:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
furi_hal_nfc_reset_mode();
|
|
|
|
instance->config_state = NfcConfigurationStateIdle;
|
|
|
|
|
|
|
|
bit_buffer_free(event_data.buffer);
|
|
|
|
furi_hal_nfc_low_power_mode_start();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool nfc_worker_poller_start_handler(Nfc* instance) {
|
|
|
|
furi_hal_nfc_poller_field_on();
|
|
|
|
if(instance->guard_time_us) {
|
|
|
|
furi_hal_nfc_timer_block_tx_start_us(instance->guard_time_us);
|
|
|
|
FuriHalNfcEvent event = furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
|
|
|
|
furi_assert(event & FuriHalNfcEventTimerBlockTxExpired);
|
|
|
|
}
|
|
|
|
instance->poller_state = NfcPollerStateReady;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool nfc_worker_poller_ready_handler(Nfc* instance) {
|
|
|
|
NfcCommand command = NfcCommandContinue;
|
|
|
|
|
|
|
|
NfcEvent event = {.type = NfcEventTypePollerReady};
|
|
|
|
command = instance->callback(event, instance->context);
|
|
|
|
if(command == NfcCommandReset) {
|
|
|
|
instance->poller_state = NfcPollerStateReset;
|
|
|
|
} else if(command == NfcCommandStop) {
|
|
|
|
instance->poller_state = NfcPollerStateStop;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool nfc_worker_poller_reset_handler(Nfc* instance) {
|
|
|
|
furi_hal_nfc_low_power_mode_start();
|
|
|
|
furi_delay_ms(100);
|
|
|
|
furi_hal_nfc_low_power_mode_stop();
|
|
|
|
instance->poller_state = NfcPollerStateStart;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool nfc_worker_poller_stop_handler(Nfc* instance) {
|
|
|
|
furi_hal_nfc_reset_mode();
|
|
|
|
instance->config_state = NfcConfigurationStateIdle;
|
|
|
|
|
|
|
|
furi_hal_nfc_low_power_mode_start();
|
|
|
|
// Wait after field is off some time to reset tag power
|
|
|
|
furi_delay_ms(10);
|
|
|
|
instance->poller_state = NfcPollerStateStart;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const NfcWorkerPollerStateHandler nfc_worker_poller_state_handlers[NfcPollerStateNum] = {
|
|
|
|
[NfcPollerStateStart] = nfc_worker_poller_start_handler,
|
|
|
|
[NfcPollerStateReady] = nfc_worker_poller_ready_handler,
|
|
|
|
[NfcPollerStateReset] = nfc_worker_poller_reset_handler,
|
|
|
|
[NfcPollerStateStop] = nfc_worker_poller_stop_handler,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int32_t nfc_worker_poller(void* context) {
|
|
|
|
furi_assert(context);
|
|
|
|
|
|
|
|
Nfc* instance = context;
|
|
|
|
furi_assert(instance->callback);
|
|
|
|
instance->state = NfcStateRunning;
|
|
|
|
instance->poller_state = NfcPollerStateStart;
|
|
|
|
|
|
|
|
furi_hal_nfc_event_start();
|
|
|
|
|
|
|
|
bool exit = false;
|
|
|
|
while(!exit) {
|
|
|
|
exit = nfc_worker_poller_state_handlers[instance->poller_state](instance);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Nfc* nfc_alloc() {
|
|
|
|
furi_check(furi_hal_nfc_acquire() == FuriHalNfcErrorNone);
|
|
|
|
|
|
|
|
Nfc* instance = malloc(sizeof(Nfc));
|
|
|
|
instance->state = NfcStateIdle;
|
|
|
|
instance->comm_state = NfcCommStateIdle;
|
|
|
|
instance->config_state = NfcConfigurationStateIdle;
|
|
|
|
|
|
|
|
instance->worker_thread = furi_thread_alloc();
|
|
|
|
furi_thread_set_name(instance->worker_thread, "NfcWorker");
|
|
|
|
furi_thread_set_context(instance->worker_thread, instance);
|
|
|
|
furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHighest);
|
|
|
|
furi_thread_set_stack_size(instance->worker_thread, 8 * 1024);
|
|
|
|
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_free(Nfc* instance) {
|
|
|
|
furi_assert(instance);
|
|
|
|
furi_assert(instance->state == NfcStateIdle);
|
|
|
|
|
|
|
|
furi_thread_free(instance->worker_thread);
|
|
|
|
free(instance);
|
|
|
|
|
|
|
|
furi_hal_nfc_release();
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) {
|
|
|
|
furi_assert(instance);
|
|
|
|
furi_assert(mode < NfcModeNum);
|
|
|
|
furi_assert(tech < NfcTechNum);
|
|
|
|
furi_assert(instance->config_state == NfcConfigurationStateIdle);
|
|
|
|
|
|
|
|
FuriHalNfcTech hal_tech = nfc_tech_table[mode][tech];
|
|
|
|
if(hal_tech == FuriHalNfcTechInvalid) {
|
|
|
|
furi_crash("Unsupported mode for given tech");
|
|
|
|
}
|
|
|
|
FuriHalNfcMode hal_mode = (mode == NfcModePoller) ? FuriHalNfcModePoller :
|
|
|
|
FuriHalNfcModeListener;
|
|
|
|
furi_hal_nfc_low_power_mode_stop();
|
|
|
|
furi_hal_nfc_set_mode(hal_mode, hal_tech);
|
|
|
|
|
|
|
|
instance->mode = mode;
|
|
|
|
instance->config_state = NfcConfigurationStateDone;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) {
|
|
|
|
furi_assert(instance);
|
|
|
|
instance->fdt_poll_fc = fdt_poll_fc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) {
|
|
|
|
furi_assert(instance);
|
|
|
|
instance->fdt_listen_fc = fdt_listen_fc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) {
|
|
|
|
furi_assert(instance);
|
|
|
|
instance->fdt_poll_poll_us = fdt_poll_poll_us;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) {
|
|
|
|
furi_assert(instance);
|
|
|
|
instance->guard_time_us = guard_time_us;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) {
|
|
|
|
furi_assert(instance);
|
|
|
|
instance->mask_rx_time_fc = mask_rx_time_fc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) {
|
|
|
|
furi_assert(instance);
|
|
|
|
furi_assert(instance->worker_thread);
|
|
|
|
furi_assert(callback);
|
|
|
|
furi_assert(instance->config_state == NfcConfigurationStateDone);
|
|
|
|
|
|
|
|
instance->callback = callback;
|
|
|
|
instance->context = context;
|
|
|
|
if(instance->mode == NfcModePoller) {
|
|
|
|
furi_thread_set_callback(instance->worker_thread, nfc_worker_poller);
|
|
|
|
} else {
|
|
|
|
furi_thread_set_callback(instance->worker_thread, nfc_worker_listener);
|
|
|
|
}
|
|
|
|
instance->comm_state = NfcCommStateIdle;
|
|
|
|
furi_thread_start(instance->worker_thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nfc_stop(Nfc* instance) {
|
|
|
|
furi_assert(instance);
|
|
|
|
furi_assert(instance->state == NfcStateRunning);
|
|
|
|
|
|
|
|
if(instance->mode == NfcModeListener) {
|
|
|
|
furi_hal_nfc_abort();
|
|
|
|
}
|
|
|
|
furi_thread_join(instance->worker_thread);
|
|
|
|
|
|
|
|
instance->state = NfcStateIdle;
|
|
|
|
}
|
|
|
|
|
|
|
|
NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) {
|
|
|
|
furi_assert(instance);
|
|
|
|
furi_assert(tx_buffer);
|
|
|
|
|
|
|
|
NfcError ret = NfcErrorNone;
|
|
|
|
|
|
|
|
while(furi_hal_nfc_timer_block_tx_is_running()) {
|
|
|
|
}
|
|
|
|
|
|
|
|
FuriHalNfcError error =
|
|
|
|
furi_hal_nfc_listener_tx(bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer));
|
|
|
|
if(error != FuriHalNfcErrorNone) {
|
|
|
|
FURI_LOG_D(TAG, "Failed in listener TX");
|
|
|
|
ret = nfc_process_hal_error(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static NfcError nfc_poller_trx_state_machine(Nfc* instance, uint32_t fwt_fc) {
|
|
|
|
FuriHalNfcEvent event = 0;
|
|
|
|
NfcError error = NfcErrorNone;
|
|
|
|
|
|
|
|
while(true) {
|
|
|
|
event = furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
|
|
|
|
if(event & FuriHalNfcEventTimerBlockTxExpired) {
|
|
|
|
if(instance->comm_state == NfcCommStateWaitBlockTxTimer) {
|
|
|
|
instance->comm_state = NfcCommStateReadyTx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(event & FuriHalNfcEventTxEnd) {
|
|
|
|
if(instance->comm_state == NfcCommStateWaitTxEnd) {
|
|
|
|
if(fwt_fc) {
|
|
|
|
furi_hal_nfc_timer_fwt_start(fwt_fc);
|
|
|
|
}
|
|
|
|
furi_hal_nfc_timer_block_tx_start_us(instance->fdt_poll_poll_us);
|
|
|
|
instance->comm_state = NfcCommStateWaitRxStart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(event & FuriHalNfcEventRxStart) {
|
|
|
|
if(instance->comm_state == NfcCommStateWaitRxStart) {
|
|
|
|
furi_hal_nfc_timer_block_tx_stop();
|
|
|
|
furi_hal_nfc_timer_fwt_stop();
|
|
|
|
instance->comm_state = NfcCommStateWaitRxEnd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(event & FuriHalNfcEventRxEnd) {
|
|
|
|
furi_hal_nfc_timer_block_tx_start(instance->fdt_poll_fc);
|
|
|
|
furi_hal_nfc_timer_fwt_stop();
|
|
|
|
instance->comm_state = NfcCommStateWaitBlockTxTimer;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(event & FuriHalNfcEventTimerFwtExpired) {
|
|
|
|
if(instance->comm_state == NfcCommStateWaitRxStart) {
|
|
|
|
error = NfcErrorTimeout;
|
|
|
|
FURI_LOG_D(TAG, "FWT Timeout");
|
|
|
|
if(furi_hal_nfc_timer_block_tx_is_running()) {
|
|
|
|
instance->comm_state = NfcCommStateWaitBlockTxTimer;
|
|
|
|
} else {
|
|
|
|
instance->comm_state = NfcCommStateReadyTx;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
NfcError nfc_iso14443a_poller_trx_custom_parity(
|
|
|
|
Nfc* instance,
|
|
|
|
const BitBuffer* tx_buffer,
|
|
|
|
BitBuffer* rx_buffer,
|
|
|
|
uint32_t fwt) {
|
|
|
|
furi_assert(instance);
|
|
|
|
furi_assert(tx_buffer);
|
|
|
|
furi_assert(rx_buffer);
|
|
|
|
|
|
|
|
furi_assert(instance->poller_state == NfcPollerStateReady);
|
|
|
|
|
|
|
|
NfcError ret = NfcErrorNone;
|
|
|
|
FuriHalNfcError error = FuriHalNfcErrorNone;
|
|
|
|
do {
|
|
|
|
furi_hal_nfc_trx_reset();
|
|
|
|
while(furi_hal_nfc_timer_block_tx_is_running()) {
|
|
|
|
FuriHalNfcEvent event =
|
|
|
|
furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
|
|
|
|
if(event & FuriHalNfcEventTimerBlockTxExpired) break;
|
|
|
|
}
|
|
|
|
bit_buffer_write_bytes_with_parity(
|
|
|
|
tx_buffer, instance->tx_buffer, sizeof(instance->tx_buffer), &instance->tx_bits);
|
|
|
|
error =
|
|
|
|
furi_hal_nfc_iso14443a_poller_tx_custom_parity(instance->tx_buffer, instance->tx_bits);
|
|
|
|
if(error != FuriHalNfcErrorNone) {
|
|
|
|
FURI_LOG_D(TAG, "Failed in poller TX");
|
|
|
|
ret = nfc_process_hal_error(error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
instance->comm_state = NfcCommStateWaitTxEnd;
|
|
|
|
ret = nfc_poller_trx_state_machine(instance, fwt);
|
|
|
|
if(ret != NfcErrorNone) {
|
|
|
|
FURI_LOG_T(TAG, "Failed TRX state machine");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = furi_hal_nfc_poller_rx(
|
|
|
|
instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);
|
|
|
|
if(error != FuriHalNfcErrorNone) {
|
|
|
|
FURI_LOG_D(TAG, "Failed in poller RX");
|
|
|
|
ret = nfc_process_hal_error(error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(instance->rx_bits >= 9) {
|
|
|
|
if((instance->rx_bits % 9) != 0) {
|
|
|
|
ret = NfcErrorDataFormat;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bit_buffer_copy_bytes_with_parity(rx_buffer, instance->rx_buffer, instance->rx_bits);
|
|
|
|
} while(false);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
NfcError
|
|
|
|
nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) {
|
|
|
|
furi_assert(instance);
|
|
|
|
furi_assert(tx_buffer);
|
|
|
|
furi_assert(rx_buffer);
|
|
|
|
|
|
|
|
furi_assert(instance->poller_state == NfcPollerStateReady);
|
|
|
|
|
|
|
|
NfcError ret = NfcErrorNone;
|
|
|
|
FuriHalNfcError error = FuriHalNfcErrorNone;
|
|
|
|
do {
|
|
|
|
furi_hal_nfc_trx_reset();
|
|
|
|
while(furi_hal_nfc_timer_block_tx_is_running()) {
|
|
|
|
FuriHalNfcEvent event =
|
|
|
|
furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
|
|
|
|
if(event & FuriHalNfcEventTimerBlockTxExpired) break;
|
|
|
|
}
|
|
|
|
error =
|
|
|
|
furi_hal_nfc_poller_tx(bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer));
|
|
|
|
if(error != FuriHalNfcErrorNone) {
|
|
|
|
FURI_LOG_D(TAG, "Failed in poller TX");
|
|
|
|
ret = nfc_process_hal_error(error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
instance->comm_state = NfcCommStateWaitTxEnd;
|
|
|
|
ret = nfc_poller_trx_state_machine(instance, fwt);
|
|
|
|
if(ret != NfcErrorNone) {
|
|
|
|
FURI_LOG_T(TAG, "Failed TRX state machine");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = furi_hal_nfc_poller_rx(
|
|
|
|
instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);
|
|
|
|
if(error != FuriHalNfcErrorNone) {
|
|
|
|
FURI_LOG_D(TAG, "Failed in poller RX");
|
|
|
|
ret = nfc_process_hal_error(error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits);
|
|
|
|
} while(false);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
NfcError nfc_iso14443a_listener_set_col_res_data(
|
|
|
|
Nfc* instance,
|
|
|
|
uint8_t* uid,
|
|
|
|
uint8_t uid_len,
|
|
|
|
uint8_t* atqa,
|
|
|
|
uint8_t sak) {
|
|
|
|
furi_assert(instance);
|
|
|
|
|
|
|
|
FuriHalNfcError error =
|
|
|
|
furi_hal_nfc_iso14443a_listener_set_col_res_data(uid, uid_len, atqa, sak);
|
|
|
|
instance->comm_state = NfcCommStateIdle;
|
|
|
|
return nfc_process_hal_error(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
NfcError nfc_iso14443a_poller_trx_short_frame(
|
|
|
|
Nfc* instance,
|
|
|
|
NfcIso14443aShortFrame frame,
|
|
|
|
BitBuffer* rx_buffer,
|
|
|
|
uint32_t fwt) {
|
|
|
|
furi_assert(instance);
|
|
|
|
furi_assert(rx_buffer);
|
|
|
|
|
|
|
|
FuriHalNfcaShortFrame short_frame = (frame == NfcIso14443aShortFrameAllReqa) ?
|
|
|
|
FuriHalNfcaShortFrameAllReq :
|
|
|
|
FuriHalNfcaShortFrameSensReq;
|
|
|
|
|
|
|
|
furi_assert(instance->poller_state == NfcPollerStateReady);
|
|
|
|
|
|
|
|
NfcError ret = NfcErrorNone;
|
|
|
|
FuriHalNfcError error = FuriHalNfcErrorNone;
|
|
|
|
do {
|
|
|
|
furi_hal_nfc_trx_reset();
|
|
|
|
while(furi_hal_nfc_timer_block_tx_is_running()) {
|
|
|
|
FuriHalNfcEvent event =
|
|
|
|
furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
|
|
|
|
if(event & FuriHalNfcEventTimerBlockTxExpired) break;
|
|
|
|
}
|
|
|
|
error = furi_hal_nfc_iso14443a_poller_trx_short_frame(short_frame);
|
|
|
|
if(error != FuriHalNfcErrorNone) {
|
|
|
|
FURI_LOG_D(TAG, "Failed in poller TX");
|
|
|
|
ret = nfc_process_hal_error(error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
instance->comm_state = NfcCommStateWaitTxEnd;
|
|
|
|
ret = nfc_poller_trx_state_machine(instance, fwt);
|
|
|
|
if(ret != NfcErrorNone) {
|
|
|
|
FURI_LOG_T(TAG, "Failed TRX state machine");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = furi_hal_nfc_poller_rx(
|
|
|
|
instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);
|
|
|
|
if(error != FuriHalNfcErrorNone) {
|
|
|
|
FURI_LOG_D(TAG, "Failed in poller RX");
|
|
|
|
ret = nfc_process_hal_error(error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits);
|
|
|
|
} while(false);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
NfcError nfc_iso14443a_poller_trx_sdd_frame(
|
|
|
|
Nfc* instance,
|
|
|
|
const BitBuffer* tx_buffer,
|
|
|
|
BitBuffer* rx_buffer,
|
|
|
|
uint32_t fwt) {
|
|
|
|
furi_assert(instance);
|
|
|
|
furi_assert(tx_buffer);
|
|
|
|
furi_assert(rx_buffer);
|
|
|
|
|
|
|
|
furi_assert(instance->poller_state == NfcPollerStateReady);
|
|
|
|
|
|
|
|
NfcError ret = NfcErrorNone;
|
|
|
|
FuriHalNfcError error = FuriHalNfcErrorNone;
|
|
|
|
do {
|
|
|
|
furi_hal_nfc_trx_reset();
|
|
|
|
while(furi_hal_nfc_timer_block_tx_is_running()) {
|
|
|
|
FuriHalNfcEvent event =
|
|
|
|
furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER);
|
|
|
|
if(event & FuriHalNfcEventTimerBlockTxExpired) break;
|
|
|
|
}
|
|
|
|
error = furi_hal_nfc_iso14443a_tx_sdd_frame(
|
|
|
|
bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer));
|
|
|
|
if(error != FuriHalNfcErrorNone) {
|
|
|
|
FURI_LOG_D(TAG, "Failed in poller TX");
|
|
|
|
ret = nfc_process_hal_error(error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
instance->comm_state = NfcCommStateWaitTxEnd;
|
|
|
|
ret = nfc_poller_trx_state_machine(instance, fwt);
|
|
|
|
if(ret != NfcErrorNone) {
|
|
|
|
FURI_LOG_T(TAG, "Failed TRX state machine");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = furi_hal_nfc_poller_rx(
|
|
|
|
instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits);
|
|
|
|
if(error != FuriHalNfcErrorNone) {
|
|
|
|
FURI_LOG_D(TAG, "Failed in poller RX");
|
|
|
|
ret = nfc_process_hal_error(error);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits);
|
|
|
|
} while(false);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) {
|
|
|
|
furi_assert(instance);
|
|
|
|
furi_assert(tx_buffer);
|
|
|
|
|
|
|
|
NfcError ret = NfcErrorNone;
|
|
|
|
FuriHalNfcError error = FuriHalNfcErrorNone;
|
|
|
|
|
|
|
|
const uint8_t* tx_data = bit_buffer_get_data(tx_buffer);
|
|
|
|
const uint8_t* tx_parity = bit_buffer_get_parity(tx_buffer);
|
|
|
|
size_t tx_bits = bit_buffer_get_size(tx_buffer);
|
|
|
|
|
|
|
|
error = furi_hal_nfc_iso14443a_listener_tx_custom_parity(tx_data, tx_parity, tx_bits);
|
|
|
|
ret = nfc_process_hal_error(error);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) {
|
|
|
|
furi_assert(instance);
|
|
|
|
|
|
|
|
while(furi_hal_nfc_timer_block_tx_is_running()) {
|
|
|
|
}
|
|
|
|
|
|
|
|
FuriHalNfcError error = furi_hal_nfc_iso15693_listener_tx_sof();
|
|
|
|
NfcError ret = nfc_process_hal_error(error);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-11-26 11:10:33 +03:00
|
|
|
NfcError nfc_felica_listener_set_sensf_res_data(
|
|
|
|
Nfc* instance,
|
|
|
|
const uint8_t* idm,
|
|
|
|
const uint8_t idm_len,
|
|
|
|
const uint8_t* pmm,
|
|
|
|
const uint8_t pmm_len) {
|
|
|
|
furi_assert(instance);
|
|
|
|
furi_assert(idm);
|
|
|
|
furi_assert(pmm);
|
|
|
|
|
|
|
|
FuriHalNfcError error =
|
|
|
|
furi_hal_nfc_felica_listener_set_sensf_res_data(idm, idm_len, pmm, pmm_len);
|
|
|
|
instance->comm_state = NfcCommStateIdle;
|
|
|
|
return nfc_process_hal_error(error);
|
|
|
|
}
|
|
|
|
|
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.
2023-10-24 06:08:09 +03:00
|
|
|
#endif // APP_UNIT_TESTS
|