Skip to content

Commit dfd7537

Browse files
dogtopushedger
andauthored
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>
1 parent ad94694 commit dfd7537

File tree

10 files changed

+182
-20
lines changed

10 files changed

+182
-20
lines changed

lib/nfc/nfc.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
#define NFC_MAX_BUFFER_SIZE (256)
1111

12+
#define NFC_FELICA_LISTENER_RESPONSE_TIME_A_FC (512 * 64)
13+
#define NFC_FELICA_LISTENER_RESPONSE_TIME_B_FC (256 * 64)
14+
1215
typedef enum {
1316
NfcStateIdle,
1417
NfcStateRunning,
@@ -661,4 +664,20 @@ NfcError nfc_felica_listener_set_sensf_res_data(
661664
return nfc_process_hal_error(error);
662665
}
663666

667+
void nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot) {
668+
furi_check(instance);
669+
670+
furi_hal_nfc_timer_block_tx_start(
671+
NFC_FELICA_LISTENER_RESPONSE_TIME_A_FC +
672+
target_time_slot * NFC_FELICA_LISTENER_RESPONSE_TIME_B_FC);
673+
}
674+
675+
void nfc_felica_listener_timer_anticol_stop(Nfc* instance) {
676+
furi_check(instance);
677+
678+
if(furi_hal_nfc_timer_block_tx_is_running()) {
679+
furi_hal_nfc_timer_block_tx_stop();
680+
}
681+
}
682+
664683
#endif // FW_CFG_unit_tests

lib/nfc/nfc.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,24 @@ NfcError nfc_felica_listener_set_sensf_res_data(
380380
*/
381381
NfcError nfc_iso15693_listener_tx_sof(Nfc* instance);
382382

383+
/**
384+
* @brief Start the timer used for manual FeliCa collision resolution in listener mode.
385+
*
386+
* This blocks TX until the desired Time Slot, and should be called as soon as the listener
387+
* determines that a collision resolution needs to be handled manually.
388+
*
389+
* @param[in, out] instance instance pointer to the instance to be configured.
390+
* @param[in] target_time_slot Target Time Slot number. Should be a value within the range of 0-15 (double-inclusive).
391+
*/
392+
void nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot);
393+
394+
/**
395+
* @brief Cancel the timer used for manual FeliCa collision resolution in listener mode.
396+
*
397+
* @param[in, out] instance instance pointer to the instance to be configured.
398+
*/
399+
void nfc_felica_listener_timer_anticol_stop(Nfc* instance);
400+
383401
#ifdef __cplusplus
384402
}
385403
#endif

lib/nfc/nfc_mock.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,4 +518,14 @@ NfcError nfc_felica_listener_set_sensf_res_data(
518518
return NfcErrorNone;
519519
}
520520

521+
void nfc_felica_listener_timer_anticol_start(Nfc* instance, uint8_t target_time_slot) {
522+
furi_check(instance);
523+
524+
UNUSED(target_time_slot);
525+
}
526+
527+
void nfc_felica_listener_timer_anticol_stop(Nfc* instance) {
528+
furi_check(instance);
529+
}
530+
521531
#endif

lib/nfc/protocols/felica/felica.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ extern "C" {
3838
#define FELICA_FDT_POLL_FC (10000U)
3939
#define FELICA_POLL_POLL_MIN_US (1280U)
4040

41-
#define FELICA_FDT_LISTEN_FC (1172)
41+
#define FELICA_FDT_LISTEN_FC (0)
4242

4343
#define FELICA_SYSTEM_CODE_CODE (0xFFFFU)
4444
#define FELICA_TIME_SLOT_1 (0x00U)
@@ -58,6 +58,7 @@ typedef enum {
5858
FelicaErrorWrongCrc,
5959
FelicaErrorProtocol,
6060
FelicaErrorTimeout,
61+
FelicaErrorFeatureUnsupported,
6162
} FelicaError;
6263

6364
typedef struct {

lib/nfc/protocols/felica/felica_listener.c

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,20 @@
55
#include <furi_hal_nfc.h>
66

77
#define FELICA_LISTENER_MAX_BUFFER_SIZE (128)
8+
#define FELICA_LISTENER_CMD_POLLING (0x00U)
9+
#define FELICA_LISTENER_RESPONSE_POLLING (0x01U)
810
#define FELICA_LISTENER_RESPONSE_CODE_READ (0x07)
911
#define FELICA_LISTENER_RESPONSE_CODE_WRITE (0x09)
1012

13+
#define FELICA_LISTENER_REQUEST_NONE (0x00U)
14+
#define FELICA_LISTENER_REQUEST_SYSTEM_CODE (0x01U)
15+
#define FELICA_LISTENER_REQUEST_PERFORMANCE (0x02U)
16+
17+
#define FELICA_LISTENER_SYSTEM_CODE_NDEF (__builtin_bswap16(0x12FCU))
18+
#define FELICA_LISTENER_SYSTEM_CODE_LITES (__builtin_bswap16(0x88B4U))
19+
20+
#define FELICA_LISTENER_PERFORMANCE_VALUE (__builtin_bswap16(0x0083U))
21+
1122
#define TAG "FelicaListener"
1223

1324
FelicaListener* felica_listener_alloc(Nfc* nfc, FelicaData* data) {
@@ -151,6 +162,70 @@ static FelicaError felica_listener_process_request(
151162
}
152163
}
153164

165+
static void felica_listener_populate_polling_response_header(
166+
FelicaListener* instance,
167+
FelicaListenerPollingResponseHeader* resp) {
168+
resp->idm = instance->data->idm;
169+
resp->pmm = instance->data->pmm;
170+
resp->response_code = FELICA_LISTENER_RESPONSE_POLLING;
171+
}
172+
173+
static bool felica_listener_check_system_code(
174+
const FelicaListenerGenericRequest* const generic_request,
175+
uint16_t code) {
176+
return (
177+
generic_request->polling.system_code == code ||
178+
generic_request->polling.system_code == (code | 0x00FFU) ||
179+
generic_request->polling.system_code == (code | 0xFF00U));
180+
}
181+
182+
static uint16_t felica_listener_get_response_system_code(
183+
FelicaListener* instance,
184+
const FelicaListenerGenericRequest* const generic_request) {
185+
uint16_t resp_system_code = FELICA_SYSTEM_CODE_CODE;
186+
if(felica_listener_check_system_code(generic_request, FELICA_LISTENER_SYSTEM_CODE_NDEF) &&
187+
instance->data->data.fs.mc.data[FELICA_MC_SYS_OP] == 1) {
188+
// NDEF
189+
resp_system_code = FELICA_LISTENER_SYSTEM_CODE_NDEF;
190+
} else if(felica_listener_check_system_code(
191+
generic_request, FELICA_LISTENER_SYSTEM_CODE_LITES)) {
192+
// Lite-S
193+
resp_system_code = FELICA_LISTENER_SYSTEM_CODE_LITES;
194+
}
195+
return resp_system_code;
196+
}
197+
198+
static FelicaError felica_listener_process_system_code(
199+
FelicaListener* instance,
200+
const FelicaListenerGenericRequest* const generic_request) {
201+
FelicaError result = FelicaErrorFeatureUnsupported;
202+
do {
203+
uint16_t resp_system_code =
204+
felica_listener_get_response_system_code(instance, generic_request);
205+
if(resp_system_code == FELICA_SYSTEM_CODE_CODE) break;
206+
207+
FelicaListenerPollingResponse* resp = malloc(sizeof(FelicaListenerPollingResponse));
208+
felica_listener_populate_polling_response_header(instance, &resp->header);
209+
210+
resp->header.length = sizeof(FelicaListenerPollingResponse);
211+
if(generic_request->polling.request_code == FELICA_LISTENER_REQUEST_SYSTEM_CODE) {
212+
resp->optional_request_data = resp_system_code;
213+
} else if(generic_request->polling.request_code == FELICA_LISTENER_REQUEST_PERFORMANCE) {
214+
resp->optional_request_data = FELICA_LISTENER_PERFORMANCE_VALUE;
215+
} else {
216+
resp->header.length = sizeof(FelicaListenerPollingResponseHeader);
217+
}
218+
219+
bit_buffer_reset(instance->tx_buffer);
220+
bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)resp, resp->header.length);
221+
free(resp);
222+
223+
result = felica_listener_frame_exchange(instance, instance->tx_buffer);
224+
} while(false);
225+
226+
return result;
227+
}
228+
154229
NfcCommand felica_listener_run(NfcGenericEvent event, void* context) {
155230
furi_assert(context);
156231
furi_assert(event.protocol == NfcProtocolInvalid);
@@ -187,7 +262,23 @@ NfcCommand felica_listener_run(NfcGenericEvent event, void* context) {
187262
break;
188263
}
189264

190-
if(!felica_listener_check_idm(instance, &request->header.idm)) {
265+
if(request->header.code == FELICA_LISTENER_CMD_POLLING) {
266+
// Will always respond at Time Slot 0 for now.
267+
nfc_felica_listener_timer_anticol_start(instance->nfc, 0);
268+
if(request->polling.system_code != FELICA_SYSTEM_CODE_CODE) {
269+
FelicaError error = felica_listener_process_system_code(instance, request);
270+
if(error == FelicaErrorFeatureUnsupported) {
271+
command = NfcCommandReset;
272+
} else if(error != FelicaErrorNone) {
273+
FURI_LOG_E(
274+
TAG, "Error when handling Polling with System Code: %2X", error);
275+
}
276+
break;
277+
} else {
278+
FURI_LOG_E(TAG, "Hardware Polling command leaking through");
279+
break;
280+
}
281+
} else if(!felica_listener_check_idm(instance, &request->header.idm)) {
191282
FURI_LOG_E(TAG, "Wrong IDm");
192283
break;
193284
}

lib/nfc/protocols/felica/felica_listener_i.c

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,6 @@
77
#define FELICA_WCNT_MC2_00_WARNING_BEGIN_VALUE (0x00001027U)
88
#define FELICA_WCNT_MC2_00_WARNING_END_VALUE (0x00FFFDFFU)
99

10-
#define FELICA_MC_SP_REG_ALL_RW_BYTES_0_1 (0U)
11-
#define FELICA_MC_ALL_BYTE (2U)
12-
#define FELICA_MC_SYS_OP (3U)
13-
#define FELICA_MC_RF_PRM (4U)
14-
#define FELICA_MC_CKCKV_W_MAC_A (5U)
15-
#define FELICA_MC_SP_REG_R_RESTR_BYTES_6_7 (6U)
16-
#define FELICA_MC_SP_REG_W_RESTR_BYTES_8_9 (8U)
17-
#define FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11 (10U)
18-
#define FELICA_MC_STATE_W_MAC_A (12U)
19-
#define FELICA_MC_RESERVED_13 (13U)
20-
#define FELICA_MC_RESERVED_14 (14U)
21-
#define FELICA_MC_RESERVED_15 (15U)
22-
2310
#define FELICA_MC_BYTE_GET(data, byte) (data->data.fs.mc.data[byte])
2411
#define FELICA_SYSTEM_BLOCK_RO_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0x00)
2512
#define FELICA_SYSTEM_BLOCK_RW_ACCESS(data) (FELICA_MC_BYTE_GET(data, FELICA_MC_ALL_BYTE) == 0xFF)

lib/nfc/protocols/felica/felica_listener_i.h

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,50 @@
77
#define FELICA_LISTENER_WRITE_BLOCK_COUNT_MAX (2U)
88
#define FELICA_LISTENER_WRITE_BLOCK_COUNT_MIN (1U)
99

10+
#define FELICA_MC_SP_REG_ALL_RW_BYTES_0_1 (0U)
11+
#define FELICA_MC_ALL_BYTE (2U)
12+
#define FELICA_MC_SYS_OP (3U)
13+
#define FELICA_MC_RF_PRM (4U)
14+
#define FELICA_MC_CKCKV_W_MAC_A (5U)
15+
#define FELICA_MC_SP_REG_R_RESTR_BYTES_6_7 (6U)
16+
#define FELICA_MC_SP_REG_W_RESTR_BYTES_8_9 (8U)
17+
#define FELICA_MC_SP_REG_W_MAC_A_BYTES_10_11 (10U)
18+
#define FELICA_MC_STATE_W_MAC_A (12U)
19+
#define FELICA_MC_RESERVED_13 (13U)
20+
#define FELICA_MC_RESERVED_14 (14U)
21+
#define FELICA_MC_RESERVED_15 (15U)
22+
1023
typedef enum {
1124
Felica_ListenerStateIdle,
1225
Felica_ListenerStateActivated,
1326
} FelicaListenerState;
1427

28+
typedef struct FURI_PACKED {
29+
uint8_t code;
30+
uint16_t system_code;
31+
uint8_t request_code;
32+
uint8_t time_slot;
33+
} FelicaListenerPollingHeader;
34+
35+
typedef struct {
36+
uint8_t length;
37+
uint8_t response_code;
38+
FelicaIDm idm;
39+
FelicaPMm pmm;
40+
} FelicaListenerPollingResponseHeader;
41+
42+
typedef struct FURI_PACKED {
43+
FelicaListenerPollingResponseHeader header;
44+
uint16_t optional_request_data;
45+
} FelicaListenerPollingResponse;
46+
1547
/** Generic Felica request same for both read and write operations. */
1648
typedef struct {
1749
uint8_t length;
18-
FelicaCommandHeader header;
50+
union {
51+
FelicaCommandHeader header;
52+
FelicaListenerPollingHeader polling;
53+
};
1954
} FelicaListenerGenericRequest;
2055

2156
/** Generic request but with list of requested elements. */

targets/f18/api_symbols.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
entry,status,name,type,params
2-
Version,+,86.0,,
2+
Version,+,86.1,,
33
Header,+,applications/services/bt/bt_service/bt.h,,
44
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
55
Header,+,applications/services/cli/cli.h,,

targets/f7/api_symbols.csv

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
entry,status,name,type,params
2-
Version,+,86.0,,
2+
Version,+,86.1,,
33
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
44
Header,+,applications/services/bt/bt_service/bt.h,,
55
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
@@ -2859,6 +2859,8 @@ Function,+,nfc_device_set_data,void,"NfcDevice*, NfcProtocol, const NfcDeviceDat
28592859
Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*"
28602860
Function,+,nfc_device_set_uid,_Bool,"NfcDevice*, const uint8_t*, size_t"
28612861
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*
28622864
Function,+,nfc_free,void,Nfc*
28632865
Function,+,nfc_iso14443a_listener_set_col_res_data,NfcError,"Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t"
28642866
Function,+,nfc_iso14443a_listener_tx_custom_parity,NfcError,"Nfc*, const BitBuffer*"

targets/f7/furi_hal/furi_hal_nfc_felica.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
#include "furi_hal_nfc_i.h"
22
#include "furi_hal_nfc_tech_i.h"
33

4-
// Prevent FDT timer from starting
5-
#define FURI_HAL_NFC_FELICA_LISTENER_FDT_COMP_FC (INT32_MAX)
4+
#define FURI_HAL_NFC_FELICA_LISTENER_FDT_COMP_FC (0)
65

76
#define FURI_HAL_FELICA_COMMUNICATION_PERFORMANCE (0x0083U)
87
#define FURI_HAL_FELICA_RESPONSE_CODE (0x01)

0 commit comments

Comments
 (0)