1
mirror of https://github.com/flipperdevices/flipperzero-firmware.git synced 2025-12-12 12:51:22 +04:00
Files

684 lines
21 KiB
C
Raw Permalink Normal View History

#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)
FeliCa Emulation: Handle certain Polling commands in firmware (#4204) * 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>
2025-09-24 19:08:40 +09:00
#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);
}
FeliCa Emulation: Handle certain Polling commands in firmware (#4204) * 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>
2025-09-24 19:08:40 +09:00
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();
}
}
Icons: compression fixes & larger dimension support (#3564) * toolbox, gui: fixes for compressed icon handling * ufbt: fixes for generated vscode project * scripts: increased max dimensions for image converter * icon type changes * linter fixes; api sync * gui: docs fix * toolbox: fixed potential decoder buffer overflow * minor cleanup * fbt: sdk: suppressed deprecation warnings in API table * toolbox: compress: added unit tests vscode: now installs resources for unit_tests unit_tests: now loads subghz region data * toolbox: compress: review fixes, pt 1 * compress: now passes decoder buffer size as constructor argument; auto-resize decoder buffer; crash on failed icon decompression * PVS fixes * pvs fixes, pt2 * doxygen fixes * investigating unit test failures * investigating unit test failures * investigating unit test failures * investigating unit test failures * investigating unit test failures * UnitTests: move all tests into plugins, brakes testing * UnitTests: add plugin API and update plugin entrypoints * UniTests: Test runner that works with plugins * fbt: extra filtering for extapps to include in build * UnitTests: filter tests by name * loader: restored API table for unit_test build config * Add various missing symbols to API table * UnitTest: fail on plugin load error * UnitTests: cleanup plugin api and reporting * unit_tests: composite resolver * UnitTests: remove unused declaration * unit_tests, nfc: moved mock nfc implementation to libnfc * unit_tests: api: removed redundant #define * toolbox: compress: removed size_hint for icons; triggering furi_check on oversized icons * gui: icon, icon_animation: removed size hit APIs * Format Sources. Cleanup code. * loader: refuse to start .fal as app * toolbox: compress: fixed memory corruption in operations with small destination buffer; added unit tests for that case * unit_tests: proper test skipping; better selective test interface * unit_tests: moved 'loading' logging to proper location Co-authored-by: あく <alleteam@gmail.com>
2024-05-20 21:23:47 +04:00
#endif // FW_CFG_unit_tests