mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-10-11 09:12:32 +02:00
* FeliCa: Handle non-hardware Polling commands NFC TagInfo and possibly other readers rely on Polling commands with Request Code of 1 (default System Code request) or non-FFFF System Code to detect card type. Since the NFC controller doesn't seem to handle them in hardware and simply bubbles them up, and then the Flipper firmware will just ignore them and refuse to respond afterwards, this causes the reading operation to fail. This commit adds a simple handler for such Polling commands so that readers behaving like NFC TagInfo could read the emulated card without failing. * Only handle cases when System Code is not FFFF The NFC controller should handle Polling commands with the System Code set to FFFF, so it's not necessary for the firmware to handle it. * Remove system code logging * More cleanups * Remove the claim that we need a poller change We already have enough information to determine whether or not the card supports NDEF since SYS_OP register value is included in all current Lite-S card dumps. * Respond to 12FC polling command when needed * Handle Polling with NDEF and Lite-S Service Code This allows the reader to specifically select the service by naming the Service Code. * Introduce API for manual handling of Polling commands Introduce nfc_felica_listener_timer_anticol_start() and nfc_felica_listener_timer_anticol_stop(). These are for now just wrappers around the block_tx timer that can be used to delay the response until the desired Time Slot. Thanks to the loose timing constraints of FeliCa collision resolution protocol, no compensation seems to be necessary. Also enabled the block_tx timer for FeliCa listener, but with both compensation and fdt set to 0 to keep the original behavior of not using the timer during normal data exchange. This API is now being used for handling Polling commands that are not handled by the NFC controller on the hardware side. * Document target_time_slot * Implement changes suggested by @RebornedBrain * api: f18 version sync * nfc: added stubs for `nfc_felica_listener_timer_anticol` for unit tests --------- Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: hedger <hedger@nanode.su>
684 lines
21 KiB
C
684 lines
21 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)
|
|
|
|
#define NFC_FELICA_LISTENER_RESPONSE_TIME_A_FC (512 * 64)
|
|
#define NFC_FELICA_LISTENER_RESPONSE_TIME_B_FC (256 * 64)
|
|
|
|
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(void) {
|
|
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_check(instance);
|
|
furi_check(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_check(instance);
|
|
furi_check(mode < NfcModeNum);
|
|
furi_check(tech < NfcTechNum);
|
|
furi_check(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_check(instance);
|
|
instance->fdt_poll_fc = fdt_poll_fc;
|
|
}
|
|
|
|
void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) {
|
|
furi_check(instance);
|
|
instance->fdt_listen_fc = fdt_listen_fc;
|
|
}
|
|
|
|
void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) {
|
|
furi_check(instance);
|
|
instance->fdt_poll_poll_us = fdt_poll_poll_us;
|
|
}
|
|
|
|
void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) {
|
|
furi_check(instance);
|
|
instance->guard_time_us = guard_time_us;
|
|
}
|
|
|
|
void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) {
|
|
furi_check(instance);
|
|
instance->mask_rx_time_fc = mask_rx_time_fc;
|
|
}
|
|
|
|
void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) {
|
|
furi_check(instance);
|
|
furi_check(instance->worker_thread);
|
|
furi_check(callback);
|
|
furi_check(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_check(instance);
|
|
furi_check(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_check(instance);
|
|
furi_check(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_check(instance);
|
|
furi_check(tx_buffer);
|
|
furi_check(rx_buffer);
|
|
|
|
furi_check(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_check(instance);
|
|
furi_check(tx_buffer);
|
|
furi_check(rx_buffer);
|
|
|
|
furi_check(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_check(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_check(instance);
|
|
furi_check(rx_buffer);
|
|
|
|
FuriHalNfcaShortFrame short_frame = (frame == NfcIso14443aShortFrameAllReqa) ?
|
|
FuriHalNfcaShortFrameAllReq :
|
|
FuriHalNfcaShortFrameSensReq;
|
|
|
|
furi_check(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_check(instance);
|
|
furi_check(tx_buffer);
|
|
furi_check(rx_buffer);
|
|
|
|
furi_check(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_check(instance);
|
|
furi_check(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_check(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,
|
|
const uint16_t sys_code) {
|
|
furi_check(instance);
|
|
|
|
FuriHalNfcError error =
|
|
furi_hal_nfc_felica_listener_set_sensf_res_data(idm, idm_len, pmm, pmm_len, sys_code);
|
|
instance->comm_state = NfcCommStateIdle;
|
|
return nfc_process_hal_error(error);
|
|
}
|
|
|
|
void nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot) {
|
|
furi_check(instance);
|
|
|
|
furi_hal_nfc_timer_block_tx_start(
|
|
NFC_FELICA_LISTENER_RESPONSE_TIME_A_FC +
|
|
target_time_slot * NFC_FELICA_LISTENER_RESPONSE_TIME_B_FC);
|
|
}
|
|
|
|
void nfc_felica_listener_timer_anticol_stop(Nfc* instance) {
|
|
furi_check(instance);
|
|
|
|
if(furi_hal_nfc_timer_block_tx_is_running()) {
|
|
furi_hal_nfc_timer_block_tx_stop();
|
|
}
|
|
}
|
|
|
|
#endif // FW_CFG_unit_tests
|