Merge pull request #3670 from 0x6r1an0y/20260926-felica-raw

Add FeliCa system-specific polling
This commit is contained in:
Iceman authored and GitHub committed 2026-09-27 19:32:25 +07:00
commit 2dea00f183
4 files changed
+85 -14

No files matched your search

+21 -9
View File
@@ -29,7 +29,7 @@
#include "iso18.h"
#define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len))
static uint8_t felica_select_card(felica_card_select_t *card);
static uint8_t felica_select_card(felica_card_select_t *card, uint16_t system_code, uint8_t request_code);
//structure to hold outgoing NFC frame
static uint8_t frameSpace[FELICA_MAX_RF_FRAME_SIZE];
@@ -244,15 +244,15 @@ void Process18092Byte(felica_frame_t *f, uint8_t bt, uint32_t byte_start_time) {
* It expects 0-1 cards in the device's range.
* return 0 if selection was successful
*/
static uint8_t felica_select_card(felica_card_select_t *card) {
static uint8_t felica_select_card(felica_card_select_t *card, uint16_t system_code, uint8_t request_code) {
// POLL command
// 0xB2 0x4B = sync code
// 0x06 = len
// 0x00 = rfu
// 0xff = system code service
// 0xff = system code service
// 0x00 = request code
// system code service (high byte)
// system code service (low byte)
// request code
// b7 = automatic switching of data rate
// b6-b2 = reserved
// b1 = fc/32 (414kbps)
@@ -260,6 +260,9 @@ static uint8_t felica_select_card(felica_card_select_t *card) {
// 0x00 = timeslot
// 0x09 0x21 = crc
uint8_t poll[10] = {0xb2, 0x4d, 0x06, FELICA_POLLING_REQ, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x21};
poll[4] = (uint8_t)(system_code >> 8);
poll[5] = (uint8_t)system_code;
poll[6] = request_code;
// Number of time slots offered to the card(s), the FeliCa polling TSN field.
// Slot 0 only is what a single card wants, but when several cards share the
@@ -692,8 +695,14 @@ void felica_sendraw(const PacketCommandNG *c) {
bool do_connect = ((param & FELICA_CONNECT) == FELICA_CONNECT);
bool no_disconnect = ((param & FELICA_NO_DISCONNECT) == FELICA_NO_DISCONNECT);
bool system_select = ((param & FELICA_SYSTEM_SELECT) == FELICA_SYSTEM_SELECT);
bool replied = false;
if (system_select && (len != 2 || !do_connect || (param & (FELICA_RAW | FELICA_NO_SELECT)))) {
reply_ng(CMD_HF_FELICA_COMMAND, PM3_EINVARG, NULL, 0);
return;
}
// Signal probe. The FPGA hands us envelope peak-to-peak per 8 bit periods
// instead of demodulated bits, so nothing will decode while this is on.
// It is how you tell "tag out of range" apart from "demodulator not
@@ -708,16 +717,17 @@ void felica_sendraw(const PacketCommandNG *c) {
// Preserve compatibility with existing commands that do not send CONNECT:
// set up reader path when starting from field-off state.
if (do_connect || felica_field_is_active() == false) {
if (!felica_field_is_active() || (do_connect && !system_select)) {
iso18092_setup(FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD);
}
if (do_connect && ((param & FELICA_NO_SELECT) != FELICA_NO_SELECT)) {
// notify client selecting status.
// if failed selecting, turn off antenna and quit.
// on failed selection, retain the field only for system-specific polling.
felica_card_select_t card = {0};
uint8_t select_result = felica_select_card(&card);
uint16_t system_code = system_select ? ((uint16_t)payload[0] << 8) | payload[1] : 0xFFFF;
uint8_t select_result = felica_select_card(&card, system_code, system_select ? 0x01 : 0x00);
int select_status = PM3_SUCCESS;
switch (select_result) {
@@ -744,7 +754,9 @@ void felica_sendraw(const PacketCommandNG *c) {
reply_ng(CMD_HF_FELICA_COMMAND, select_status, (uint8_t *)&card, sizeof(felica_card_select_t));
replied = true;
if (select_status != PM3_SUCCESS) {
felica_reset_frame_mode();
if (!system_select || !no_disconnect) {
felica_reset_frame_mode();
}
return;
}
}
+56 -1
View File
@@ -66,6 +66,8 @@
#define FELICA_OPTIONAL_CMD_RETRIES 3U
// Per FeliCa spec, Polling max response at 16 timeslots is ~25ms; keep extra margin.
#define FELICA_POLLING_TIMEOUT_MS 100U
#define FELICA_READER_SYSTEM_POLL_TIMEOUT_MS 2500U
#define FELICA_READER_SYSTEM_POLL_ATTEMPTS 4U
#define FELICA_SEAC_POLL_TIMEOUT_MS 200U
#define FELICA_SEAC_POLL_RETRY_COUNT 5U
#define FELICA_SEAC_POLL_FRAME_LEN 6U
@@ -466,6 +468,7 @@ typedef enum {
static int CmdHelp(const char *Cmd);
static void clear_and_send_command(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose);
static void clear_and_send_command_ex(uint8_t flags, uint16_t datalen, uint8_t *data, uint16_t numbits, bool verbose, bool normalize_frame);
static int send_felica_payload_with_retries(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose,
int expected_response_cmd, uint32_t timeout_ms, uint32_t retries, uint32_t backoff_ms, bool logging,
PacketResponseNG *resp, const char *request_name);
@@ -2685,27 +2688,79 @@ int read_felica_uid(bool loop, bool verbose) {
return res;
}
static int read_felica_system(uint16_t system_code, bool loop, bool verbose) {
uint8_t system_bytes[2];
felica_system_code_to_bytes(system_code, system_bytes);
const uint8_t flags = FELICA_CONNECT | FELICA_CLEARTRACE | FELICA_NO_DISCONNECT | FELICA_SYSTEM_SELECT;
int res = PM3_ETIMEOUT;
for (uint32_t attempt = 0; loop || attempt < FELICA_READER_SYSTEM_POLL_ATTEMPTS; attempt++) {
clear_and_send_command_ex(flags, sizeof(system_bytes), system_bytes, 0, false, false);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_FELICA_COMMAND, &resp, FELICA_READER_SYSTEM_POLL_TIMEOUT_MS) &&
resp.status == PM3_SUCCESS && resp.length >= sizeof(felica_card_select_t)) {
felica_card_select_t card;
memcpy(&card, resp.data.asBytes, sizeof(card));
set_last_known_card(card);
if (verbose) {
PrintAndLogEx(SUCCESS, "IDm: " _GREEN_("%s"), sprint_hex_inrow(card.IDm, sizeof(card.IDm)));
}
res = PM3_SUCCESS;
if (loop == false) {
break;
}
}
if (kbd_enter_pressed()) {
if (res != PM3_SUCCESS) {
res = PM3_EOPABORTED;
}
break;
}
}
DropField();
return res;
}
static int CmdHFFelicaReader(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf felica reader",
"Act as a ISO 18092 / FeliCa reader. Look for FeliCa tags until Enter or the pm3 button is pressed",
"hf felica reader -@ -> Continuous mode");
"hf felica reader -@\n"
"hf felica reader --sys 0003 -@ -> Continuous polling for system 0003");
void *argtable[] = {
arg_param_begin,
arg_lit0("s", "silent", "silent (no messages)"),
arg_lit0("@", NULL, "optional - continuous reader mode"),
arg_str0(NULL, "sys", "<hex>", "poll a specific 2-byte system code (4 attempts, or continuous with -@)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool verbose = (arg_get_lit(ctx, 1) == false);
bool cm = arg_get_lit(ctx, 2);
struct arg_str *system_arg = arg_get_str(ctx, 3);
bool has_system = system_arg->count > 0;
uint8_t system_bytes[2] = {0};
int system_len = 0;
int parse_status = has_system ? CLIParamHexToBuf(system_arg, system_bytes, sizeof(system_bytes), &system_len) : PM3_SUCCESS;
bool invalid_system = parse_status != PM3_SUCCESS ||
(has_system && (strlen(system_arg->sval[0]) != 4 || system_len != 2));
CLIParserFree(ctx);
if (invalid_system) {
PrintAndLogEx(ERR, "System code must be exactly 4 hex digits");
return PM3_EINVARG;
}
if (cm) {
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
}
if (has_system) {
uint16_t system_code = felica_system_code_from_bytes(system_bytes);
return read_felica_system(system_code, cm, verbose);
}
return read_felica_uid(cm, verbose);
}
+6 -4
View File
@@ -3208,15 +3208,17 @@
"command": "hf felica reader",
"description": "Act as a ISO 18092 / FeliCa reader. Look for FeliCa tags until Enter or the pm3 button is pressed",
"notes": [
"hf felica reader -@ -> Continuous mode"
"hf felica reader -@",
"hf felica reader --sys 0003 -@ -> Continuous polling for system 0003"
],
"offline": false,
"options": [
"-h, --help This help",
"-s, --silent silent (no messages)",
"-@ optional - continuous reader mode"
"-@ optional - continuous reader mode",
"--sys <hex> poll a specific 2-byte system code (4 attempts, or continuous with -@)"
],
"usage": "hf felica reader [-hs@]"
"usage": "hf felica reader [-hs@] [--sys <hex>]"
},
"hf felica resetmode": {
"command": "hf felica resetmode",
@@ -16784,6 +16786,6 @@
"metadata": {
"commands_extracted": 935,
"extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2026-09-24T08:32:19+00:00"
"extracted_on": "2026-09-26T16:59:35+00:00"
}
}
+2
View File
@@ -90,6 +90,8 @@ typedef enum FELICA_COMMAND {
// so reading distance and coupling can be measured rather than guessed
FELICA_PROBE = (1 << 2),
FELICA_RAW = (1 << 3),
// With CONNECT, raw[] holds the two-byte polling system code.
FELICA_SYSTEM_SELECT = (1 << 4),
FELICA_APPEND_CRC = (1 << 5),
FELICA_NO_SELECT = (1 << 6),
FELICA_CLEARTRACE = (1 << 7),