unleashed-firmware/lib/nfc/nfc.c
gornekich a7b60bf2a6
MFC emulation fixes (#3324)
* mf classic listener: fix write block
* nfc: go to idle state instead of sleep
* lib nfc: fix documentation
2023-12-29 12:24:20 +09:00

665 lines
20 KiB
C

#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,
[NfcTechFelica] = FuriHalNfcTechFelica,
},
};
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) {
furi_hal_nfc_listener_idle();
}
}
}
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;
}
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);
}
#endif // APP_UNIT_TESTS