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

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>
This commit is contained in:
dogtopus
2025-09-24 19:08:40 +09:00
committed by GitHub
parent ad94694fbd
commit dfd753703a
10 changed files with 182 additions and 20 deletions

View File

@@ -9,6 +9,9 @@
#define NFC_MAX_BUFFER_SIZE (256) #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 { typedef enum {
NfcStateIdle, NfcStateIdle,
NfcStateRunning, NfcStateRunning,
@@ -661,4 +664,20 @@ NfcError nfc_felica_listener_set_sensf_res_data(
return nfc_process_hal_error(error); 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 #endif // FW_CFG_unit_tests

View File

@@ -380,6 +380,24 @@ NfcError nfc_felica_listener_set_sensf_res_data(
*/ */
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance); NfcError nfc_iso15693_listener_tx_sof(Nfc* instance);
/**
* @brief Start the timer used for manual FeliCa collision resolution in listener mode.
*
* This blocks TX until the desired Time Slot, and should be called as soon as the listener
* determines that a collision resolution needs to be handled manually.
*
* @param[in, out] instance instance pointer to the instance to be configured.
* @param[in] target_time_slot Target Time Slot number. Should be a value within the range of 0-15 (double-inclusive).
*/
void nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot);
/**
* @brief Cancel the timer used for manual FeliCa collision resolution in listener mode.
*
* @param[in, out] instance instance pointer to the instance to be configured.
*/
void nfc_felica_listener_timer_anticol_stop(Nfc* instance);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -518,4 +518,14 @@ NfcError nfc_felica_listener_set_sensf_res_data(
return NfcErrorNone; return NfcErrorNone;
} }
void nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot) {
furi_check(instance);
UNUSED(target_time_slot);
}
void nfc_felica_listener_timer_anticol_stop(Nfc* instance) {
furi_check(instance);
}
#endif #endif

View File

@@ -38,7 +38,7 @@ extern "C" {
#define FELICA_FDT_POLL_FC (10000U) #define FELICA_FDT_POLL_FC (10000U)
#define FELICA_POLL_POLL_MIN_US (1280U) #define FELICA_POLL_POLL_MIN_US (1280U)
#define FELICA_FDT_LISTEN_FC (1172) #define FELICA_FDT_LISTEN_FC (0)
#define FELICA_SYSTEM_CODE_CODE (0xFFFFU) #define FELICA_SYSTEM_CODE_CODE (0xFFFFU)
#define FELICA_TIME_SLOT_1 (0x00U) #define FELICA_TIME_SLOT_1 (0x00U)
@@ -58,6 +58,7 @@ typedef enum {
FelicaErrorWrongCrc, FelicaErrorWrongCrc,
FelicaErrorProtocol, FelicaErrorProtocol,
FelicaErrorTimeout, FelicaErrorTimeout,
FelicaErrorFeatureUnsupported,
} FelicaError; } FelicaError;
typedef struct { typedef struct {

View File

@@ -5,9 +5,20 @@
#include <furi_hal_nfc.h> #include <furi_hal_nfc.h>
#define FELICA_LISTENER_MAX_BUFFER_SIZE (128) #define FELICA_LISTENER_MAX_BUFFER_SIZE (128)
#define FELICA_LISTENER_CMD_POLLING (0x00U)
#define FELICA_LISTENER_RESPONSE_POLLING (0x01U)
#define FELICA_LISTENER_RESPONSE_CODE_READ (0x07) #define FELICA_LISTENER_RESPONSE_CODE_READ (0x07)
#define FELICA_LISTENER_RESPONSE_CODE_WRITE (0x09) #define FELICA_LISTENER_RESPONSE_CODE_WRITE (0x09)
#define FELICA_LISTENER_REQUEST_NONE (0x00U)
#define FELICA_LISTENER_REQUEST_SYSTEM_CODE (0x01U)
#define FELICA_LISTENER_REQUEST_PERFORMANCE (0x02U)
#define FELICA_LISTENER_SYSTEM_CODE_NDEF (__builtin_bswap16(0x12FCU))
#define FELICA_LISTENER_SYSTEM_CODE_LITES (__builtin_bswap16(0x88B4U))
#define FELICA_LISTENER_PERFORMANCE_VALUE (__builtin_bswap16(0x0083U))
#define TAG "FelicaListener" #define TAG "FelicaListener"
FelicaListener* felica_listener_alloc(Nfc* nfc, FelicaData* data) { FelicaListener* felica_listener_alloc(Nfc* nfc, FelicaData* data) {
@@ -151,6 +162,70 @@ static FelicaError felica_listener_process_request(
} }
} }
static void felica_listener_populate_polling_response_header(
FelicaListener* instance,
FelicaListenerPollingResponseHeader* resp) {
resp->idm = instance->data->idm;
resp->pmm = instance->data->pmm;
resp->response_code = FELICA_LISTENER_RESPONSE_POLLING;
}
static bool felica_listener_check_system_code(
const FelicaListenerGenericRequest* const generic_request,
uint16_t code) {
return (
generic_request->polling.system_code == code ||
generic_request->polling.system_code == (code | 0x00FFU) ||
generic_request->polling.system_code == (code | 0xFF00U));
}
static uint16_t felica_listener_get_response_system_code(
FelicaListener* instance,
const FelicaListenerGenericRequest* const generic_request) {
uint16_t resp_system_code = FELICA_SYSTEM_CODE_CODE;
if(felica_listener_check_system_code(generic_request, FELICA_LISTENER_SYSTEM_CODE_NDEF) &&
instance->data->data.fs.mc.data[FELICA_MC_SYS_OP] == 1) {
// NDEF
resp_system_code = FELICA_LISTENER_SYSTEM_CODE_NDEF;
} else if(felica_listener_check_system_code(
generic_request, FELICA_LISTENER_SYSTEM_CODE_LITES)) {
// Lite-S
resp_system_code = FELICA_LISTENER_SYSTEM_CODE_LITES;
}
return resp_system_code;
}
static FelicaError felica_listener_process_system_code(
FelicaListener* instance,
const FelicaListenerGenericRequest* const generic_request) {
FelicaError result = FelicaErrorFeatureUnsupported;
do {
uint16_t resp_system_code =
felica_listener_get_response_system_code(instance, generic_request);
if(resp_system_code == FELICA_SYSTEM_CODE_CODE) break;
FelicaListenerPollingResponse* resp = malloc(sizeof(FelicaListenerPollingResponse));
felica_listener_populate_polling_response_header(instance, &resp->header);
resp->header.length = sizeof(FelicaListenerPollingResponse);
if(generic_request->polling.request_code == FELICA_LISTENER_REQUEST_SYSTEM_CODE) {
resp->optional_request_data = resp_system_code;
} else if(generic_request->polling.request_code == FELICA_LISTENER_REQUEST_PERFORMANCE) {
resp->optional_request_data = FELICA_LISTENER_PERFORMANCE_VALUE;
} else {
resp->header.length = sizeof(FelicaListenerPollingResponseHeader);
}
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)resp, resp->header.length);
free(resp);
result = felica_listener_frame_exchange(instance, instance->tx_buffer);
} while(false);
return result;
}
NfcCommand felica_listener_run(NfcGenericEvent event, void* context) { NfcCommand felica_listener_run(NfcGenericEvent event, void* context) {
furi_assert(context); furi_assert(context);
furi_assert(event.protocol == NfcProtocolInvalid); furi_assert(event.protocol == NfcProtocolInvalid);
@@ -187,7 +262,23 @@ NfcCommand felica_listener_run(NfcGenericEvent event, void* context) {
break; break;
} }
if(!felica_listener_check_idm(instance, &request->header.idm)) { if(request->header.code == FELICA_LISTENER_CMD_POLLING) {
// Will always respond at Time Slot 0 for now.
nfc_felica_listener_timer_anticol_start(instance->nfc, 0);
if(request->polling.system_code != FELICA_SYSTEM_CODE_CODE) {
FelicaError error = felica_listener_process_system_code(instance, request);
if(error == FelicaErrorFeatureUnsupported) {
command = NfcCommandReset;
} else if(error != FelicaErrorNone) {
FURI_LOG_E(
TAG, "Error when handling Polling with System Code: %2X", error);
}
break;
} else {
FURI_LOG_E(TAG, "Hardware Polling command leaking through");
break;
}
} else if(!felica_listener_check_idm(instance, &request->header.idm)) {
FURI_LOG_E(TAG, "Wrong IDm"); FURI_LOG_E(TAG, "Wrong IDm");
break; break;
} }

View File

@@ -7,19 +7,6 @@
#define FELICA_WCNT_MC2_00_WARNING_BEGIN_VALUE (0x00001027U) #define FELICA_WCNT_MC2_00_WARNING_BEGIN_VALUE (0x00001027U)
#define FELICA_WCNT_MC2_00_WARNING_END_VALUE (0x00FFFDFFU) #define FELICA_WCNT_MC2_00_WARNING_END_VALUE (0x00FFFDFFU)
#define FELICA_MC_SP_REG_ALL_RW_BYTES_0_1 (0U)
#define FELICA_MC_ALL_BYTE (2U)
#define FELICA_MC_SYS_OP (3U)
#define FELICA_MC_RF_PRM (4U)
#define FELICA_MC_CKCKV_W_MAC_A (5U)
#define FELICA_MC_SP_REG_R_RESTR_BYTES_6_7 (6U)
#define FELICA_MC_SP_REG_W_RESTR_BYTES_8_9 (8U)
#define FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11 (10U)
#define FELICA_MC_STATE_W_MAC_A (12U)
#define FELICA_MC_RESERVED_13 (13U)
#define FELICA_MC_RESERVED_14 (14U)
#define FELICA_MC_RESERVED_15 (15U)
#define FELICA_MC_BYTE_GET(data, byte) (data->data.fs.mc.data[byte]) #define FELICA_MC_BYTE_GET(data, byte) (data->data.fs.mc.data[byte])
#define FELICA_SYSTEM_BLOCK_RO_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0x00) #define FELICA_SYSTEM_BLOCK_RO_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0x00)
#define FELICA_SYSTEM_BLOCK_RW_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0xFF) #define FELICA_SYSTEM_BLOCK_RW_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0xFF)

View File

@@ -7,15 +7,50 @@
#define FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX (2U) #define FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX (2U)
#define FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN (1U) #define FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN (1U)
#define FELICA_MC_SP_REG_ALL_RW_BYTES_0_1 (0U)
#define FELICA_MC_ALL_BYTE (2U)
#define FELICA_MC_SYS_OP (3U)
#define FELICA_MC_RF_PRM (4U)
#define FELICA_MC_CKCKV_W_MAC_A (5U)
#define FELICA_MC_SP_REG_R_RESTR_BYTES_6_7 (6U)
#define FELICA_MC_SP_REG_W_RESTR_BYTES_8_9 (8U)
#define FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11 (10U)
#define FELICA_MC_STATE_W_MAC_A (12U)
#define FELICA_MC_RESERVED_13 (13U)
#define FELICA_MC_RESERVED_14 (14U)
#define FELICA_MC_RESERVED_15 (15U)
typedef enum { typedef enum {
Felica_ListenerStateIdle, Felica_ListenerStateIdle,
Felica_ListenerStateActivated, Felica_ListenerStateActivated,
} FelicaListenerState; } FelicaListenerState;
typedef struct FURI_PACKED {
uint8_t code;
uint16_t system_code;
uint8_t request_code;
uint8_t time_slot;
} FelicaListenerPollingHeader;
typedef struct {
uint8_t length;
uint8_t response_code;
FelicaIDm idm;
FelicaPMm pmm;
} FelicaListenerPollingResponseHeader;
typedef struct FURI_PACKED {
FelicaListenerPollingResponseHeader header;
uint16_t optional_request_data;
} FelicaListenerPollingResponse;
/** Generic Felica request same for both read and write operations. */ /** Generic Felica request same for both read and write operations. */
typedef struct { typedef struct {
uint8_t length; uint8_t length;
union {
FelicaCommandHeader header; FelicaCommandHeader header;
FelicaListenerPollingHeader polling;
};
} FelicaListenerGenericRequest; } FelicaListenerGenericRequest;
/** Generic request but with list of requested elements. */ /** Generic request but with list of requested elements. */

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,86.0,, Version,+,86.1,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli.h,,
1 entry status name type params
2 Version + 86.0 86.1
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/bt/bt_service/bt_keys_storage.h
5 Header + applications/services/cli/cli.h

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,86.0,, Version,+,86.1,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
@@ -2859,6 +2859,8 @@ Function,+,nfc_device_set_data,void,"NfcDevice*, NfcProtocol, const NfcDeviceDat
Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*"
Function,+,nfc_device_set_uid,_Bool,"NfcDevice*, const uint8_t*, size_t" Function,+,nfc_device_set_uid,_Bool,"NfcDevice*, const uint8_t*, size_t"
Function,+,nfc_felica_listener_set_sensf_res_data,NfcError,"Nfc*, const uint8_t*, const uint8_t, const uint8_t*, const uint8_t, const uint16_t" Function,+,nfc_felica_listener_set_sensf_res_data,NfcError,"Nfc*, const uint8_t*, const uint8_t, const uint8_t*, const uint8_t, const uint16_t"
Function,+,nfc_felica_listener_timer_anticol_start,void,"Nfc*, uint8_t"
Function,+,nfc_felica_listener_timer_anticol_stop,void,Nfc*
Function,+,nfc_free,void,Nfc* Function,+,nfc_free,void,Nfc*
Function,+,nfc_iso14443a_listener_set_col_res_data,NfcError,"Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t" Function,+,nfc_iso14443a_listener_set_col_res_data,NfcError,"Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t"
Function,+,nfc_iso14443a_listener_tx_custom_parity,NfcError,"Nfc*, const BitBuffer*" Function,+,nfc_iso14443a_listener_tx_custom_parity,NfcError,"Nfc*, const BitBuffer*"
1 entry status name type params
2 Version + 86.0 86.1
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/bt/bt_service/bt.h
5 Header + applications/services/bt/bt_service/bt_keys_storage.h
2859 Function + nfc_device_set_loading_callback void NfcDevice*, NfcLoadingCallback, void*
2860 Function + nfc_device_set_uid _Bool NfcDevice*, const uint8_t*, size_t
2861 Function + nfc_felica_listener_set_sensf_res_data NfcError Nfc*, const uint8_t*, const uint8_t, const uint8_t*, const uint8_t, const uint16_t
2862 Function + nfc_felica_listener_timer_anticol_start void Nfc*, uint8_t
2863 Function + nfc_felica_listener_timer_anticol_stop void Nfc*
2864 Function + nfc_free void Nfc*
2865 Function + nfc_iso14443a_listener_set_col_res_data NfcError Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t
2866 Function + nfc_iso14443a_listener_tx_custom_parity NfcError Nfc*, const BitBuffer*

View File

@@ -1,8 +1,7 @@
#include "furi_hal_nfc_i.h" #include "furi_hal_nfc_i.h"
#include "furi_hal_nfc_tech_i.h" #include "furi_hal_nfc_tech_i.h"
// Prevent FDT timer from starting #define FURI_HAL_NFC_FELICA_LISTENER_FDT_COMP_FC (0)
#define FURI_HAL_NFC_FELICA_LISTENER_FDT_COMP_FC (INT32_MAX)
#define FURI_HAL_FELICA_COMMUNICATION_PERFORMANCE (0x0083U) #define FURI_HAL_FELICA_COMMUNICATION_PERFORMANCE (0x0083U)
#define FURI_HAL_FELICA_RESPONSE_CODE (0x01) #define FURI_HAL_FELICA_RESPONSE_CODE (0x01)