[FL-3569] NFC CLI commands (#4158)

* feat: FuriThread stdin

* ci: fix f18

* feat: stdio callback context

* feat: FuriPipe

* POTENTIALLY EXPLOSIVE pipe welding

* fix: non-explosive welding

* Revert welding

* docs: furi_pipe

* feat: pipe event loop integration

* update f18 sdk

* f18

* docs: make doxygen happy

* fix: event loop not triggering when pipe attached to stdio

* fix: partial stdout in pipe

* allow simultaneous in and out subscription in event loop

* feat: vcp i/o

* feat: cli ansi stuffs and history

* feat: more line editing

* working but slow cli rewrite

* restore previous speed after 4 days of debugging 🥲

* fix: cli_app_should_stop

* fix: cli and event_loop memory leaks

* style: remove commented out code

* ci: fix pvs warnings

* fix: unit tests, event_loop crash

* ci: fix build

* ci: silence pvs warning

* feat: cli gpio

* ci: fix formatting

* Fix memory leak during event loop unsubscription

* Event better memory leak fix

* feat: cli completions

* Merge remote-tracking branch 'origin/dev' into portasynthinca3/3928-cli-threads

* merge fixups

* temporarily exclude speaker_debug app

* pvs and unit tests fixups

* feat: commands in fals

* move commands out of flash, code cleanup

* ci: fix errors

* fix: run commands in buffer when stopping session

* speedup cli file transfer

* fix f18

* separate cli_shell into modules

* fix pvs warning

* fix qflipper refusing to connect

* remove temp debug logs

* remove erroneous conclusion

* Fix memory leak during event loop unsubscription

* Event better memory leak fix

* unit test for the fix

* improve thread stdio callback signatures

* pipe stdout timeout

* update api symbols

* fix f18, formatting

* fix pvs warnings

* increase stack size, hope to fix unit tests

* cli completions

* more key combos

* commands in fals

* move commands out of flash

* ci: fix errors

* speedup cli file transfer

* merge fixups

* fix f18

* cli: revert flag changes

* cli: fix formatting

* cli, fbt: loopback perf benchmark

* thread, event_loop: subscribing to thread flags

* cli: signal internal events using thread flags, improve performance

* fix f18, formatting

* event_loop: fix crash

* storage_cli: increase write_chunk buffer size again

* cli: explanation for order=0

* thread, event_loop: thread flags callback refactor

* cli: increase stack size

* cli: rename cli_app_should_stop -> cli_is_pipe_broken_or_is_etx_next_char

* cli: use plain array instead of mlib for history

* cli: prepend file name to static fns

* cli: fix formatting

* cli_shell: increase stack size

* Now cli_shell can be customized with another motd and another command set

* Added custom motd callback definition

* Now user can alloc and free his own cli command set

* cli_vcp can now restart shell with another command set

* Help command modified to show available commands from different command sets

* Api adjustement

* Reworked nfc_cli to start new shell with another command set

* Revert custom shell changes from vcp

* Custom motd callback moved to cli_shell

* Cli Shell now can be started from ongoing cli command

* Help command moved to a separate function so it can be used for custom shell

* Now nfc command spawns separate shell for further nfc commands

* cli_shell: give up pipe to command thread

* fix formatting

* cli_shell: separate into toolbox

* speaker_debug: fix

* fix: format

* Merge branch 'portasynthinca3/3928-3929-cli-fals-threads' into portasynthinca3/3965-cli_shell-toolbox

* fix merge

* fix. merge.

* fix formatting

* fix: cmd flags

* fix: formatting

* Added basic command descriptor structs and macros

* Basic nfc commands definitions added

* Nfc cli commands collection and functions added

* Raw skeleton of nfc cli processor added

* cli: increase default stack depth

* New callbacks for ctx alloc / free added

* nfc_cli moved to cli folder

* Some more logic for command processor

* Scanner command no works via command_processor

* plugin manifest adj

* Argument descriptors were removed, now only keys left

* Some helper command function implemented

* Command processor logic now mostly works

* Added all parsers and dummy implementation of raw cmd

* Now processor checks duplicated keys and treat them as errors

* Some renamings

* Arguments processing moved to separate function

* Now command processor can reuse context of previuos command for the next one if it's allowed

* can_reuse callback added for checking if context can be reused

* command processor is now freed on nfc cli exit

* Some cleanups

* First working version of raw command

* Now input data are placed directly to bit buffer

* Added tag

* Introduced request/response structs

* Moved raw command to a separate folder

* Moved some common types to header

* Added protocol specific handlers for iso14a and felica

* Opened felica crc header for referencing

* Added handler for iso14443_3b

* Opened iso15693_3_poller for referencing

* Added iso15693_3 handler for raw command

* NfcCliRawError enum introduced for response result

* Refactored handlers implementation

* Formatting functions now added as helpers

* New printing result logic

* Not present error value added to enum

* Timeout added to raw command

* Command processor now supports multivalue keys

* Apdu command implementation added

* NfcScanner moved to helpers and command now uses it

* Helper now can format protocol names

* Dump command added

* Added some more functions to scanner helper

* Dump main logic simplified

* Dump handlers moved to protocols folder

* Protocol parser added to simplify searching protocol by name

* Protocol and key arguments added to dump command

* Cleanups

* Apdu now parses protocol using helper parser

* Raw now parses protocol using helper parser

* Wrong naming fix

* Emulate command added to cli

* Description added to action descriptor and command macros

* Description field added to all commands

* Removed unnecessary enum for commands

* Added functions for formatting command and action info

* Proper error messages and help added

* Fix for unsupported single action command

* Function renamed to more appropriate

* Field command moved to all other commands

* Cleanups

* Nfc commands modified with new cli shell

* Removed previous nfc_cli.c after merge

* Removed nfc_cli.h header

* Some renamings and cleanups

* Some comments and instructions added

* Some comments and instructions added

* TODOs removed

* Fix for missing parse callback

* Added not implemented dummy for mfu actions, for now

* Fix name mismatch

* Remove unneeded header

* Mfu command moved to separate folder, also raw info action logic added

* Dictionary with id/vendors added to assets. It is used by nfc_cli_mfu_info_get_vendor function

* One more unneeded header removed

* Moved mfu info action to a separate file

* Info action now uses sync mfu poller

* mfu rdbl action added

* wrbl action added for mfu command

* Some formatting for rdbl command

* Function for formatting mfu errors added

* All mfu actions now show errors in the same way

* Fix error with sync poller. Previously when read failed function returned ErrorNone, now it processes iso14a error to get proper value

* Make PVS happy

* Nfc cli now doesn't start if desktop app is running

* Make action description look more common

* Scanner now has -t key and can show detected protocol hierarchies

* Apdu now checks max input payload data

* Proper format

* Proper error handling added to dump command

* Timeout key added dump command

* Fix merge issue

* formatting

* Pragma pack replaced with FURI_PACKED

* Fix felica memory leak

---------

Co-authored-by: Anna Antonenko <portasynthinca3@gmail.com>
Co-authored-by: Georgii Surkov <georgii.surkov@outlook.com>
Co-authored-by: あく <alleteam@gmail.com>
Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: hedger <hedger@nanode.su>
This commit is contained in:
RebornedBrain
2025-09-29 13:34:49 +03:00
committed by GitHub
parent 2ef556d456
commit eea53491de
84 changed files with 4180 additions and 79 deletions

View File

@@ -0,0 +1,352 @@
#include "nfc_cli_command_raw.h"
#include "../helpers/nfc_cli_format.h"
#include "../helpers/nfc_cli_protocol_parser.h"
#include "protocol_handlers/nfc_cli_raw_common_types.h"
#include "protocol_handlers/iso14443_3a/nfc_cli_raw_iso14443_3a.h"
#include "protocol_handlers/iso14443_3b/nfc_cli_raw_iso14443_3b.h"
#include "protocol_handlers/iso15693_3/nfc_cli_raw_iso15693_3.h"
#include "protocol_handlers/felica/nfc_cli_raw_felica.h"
#include <toolbox/args.h>
#define NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE (256)
#define TAG "RAW"
typedef enum {
NfcCliProtocolRequestTypeNormalExecute,
NfcCliProtocolRequestTypeAbort,
} NfcCliProtocolRequestType;
typedef enum {
NfcPollerStateStopped,
NfcPollerStateStarted,
} NfcPollerState;
typedef NfcCommand (*NfcCliRawProtocolSpecificHandler)(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response);
typedef struct {
Nfc* nfc;
NfcCliRawRequest request;
NfcCliRawResponse response;
NfcPoller* poller;
NfcPollerState poller_state;
NfcCliProtocolRequestType request_type;
FuriMessageQueue* input_queue;
FuriSemaphore* sem_done;
} NfcCliRawCmdContext;
static const char* raw_error_names[] = {
[NfcCliRawErrorNone] = "None",
[NfcCliRawErrorTimeout] = "Timeout",
[NfcCliRawErrorProtocol] = "Internal protocol",
[NfcCliRawErrorWrongCrc] = "Wrong CRC",
[NfcCliRawErrorNotPresent] = "No card",
};
static NfcCliActionContext* nfc_cli_raw_alloc_ctx(Nfc* nfc) {
furi_assert(nfc);
NfcCliRawCmdContext* instance = malloc(sizeof(NfcCliRawCmdContext));
instance->nfc = nfc;
instance->request.protocol = NfcProtocolInvalid;
instance->request.tx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);
instance->response.rx_buffer = bit_buffer_alloc(NFC_CLI_PROTOCOL_SUPPORT_MAX_BUFFER_SIZE);
instance->input_queue = furi_message_queue_alloc(5, sizeof(NfcCliProtocolRequestType));
instance->sem_done = furi_semaphore_alloc(1, 0);
instance->response.activation_string = furi_string_alloc();
instance->request.timeout = 0;
return instance;
}
static void nfc_cli_raw_abort_nfc_thread(NfcCliRawCmdContext* instance) {
if(instance->poller_state == NfcPollerStateStarted) {
instance->request_type = NfcCliProtocolRequestTypeAbort;
furi_message_queue_put(instance->input_queue, &instance->request_type, FuriWaitForever);
furi_semaphore_acquire(instance->sem_done, FuriWaitForever);
instance->poller_state = NfcPollerStateStopped;
}
if(instance->poller) nfc_poller_stop(instance->poller);
}
static void nfc_cli_raw_free_ctx(NfcCliActionContext* ctx) {
furi_assert(ctx);
NfcCliRawCmdContext* instance = ctx;
nfc_cli_raw_abort_nfc_thread(instance);
if(instance->poller) nfc_poller_free(instance->poller);
furi_message_queue_free(instance->input_queue);
furi_semaphore_free(instance->sem_done);
furi_string_free(instance->response.activation_string);
bit_buffer_free(instance->response.rx_buffer);
bit_buffer_free(instance->request.tx_buffer);
instance->nfc = NULL;
free(instance);
}
static bool nfc_cli_raw_can_reuse_ctx(NfcCliActionContext* ctx) {
furi_assert(ctx);
NfcCliRawCmdContext* instance = ctx;
NfcCliRawRequest* request = &instance->request;
bool result = request->keep_field;
request->keep_field = false;
request->append_crc = false;
request->select = false;
instance->request.timeout = 0;
return result;
}
const NfcCliRawProtocolSpecificHandler nfc_cli_raw_protocol_handlers[] = {
[NfcProtocolIso14443_3a] = nfc_cli_raw_iso14443_3a_handler,
[NfcProtocolIso14443_3b] = nfc_cli_raw_iso14443_3b_handler,
[NfcProtocolIso14443_4a] = NULL,
[NfcProtocolIso14443_4b] = NULL,
[NfcProtocolIso15693_3] = nfc_cli_raw_iso15693_3_handler,
[NfcProtocolFelica] = nfc_cli_raw_felica_handler,
[NfcProtocolMfUltralight] = NULL,
[NfcProtocolMfClassic] = NULL,
[NfcProtocolMfDesfire] = NULL,
[NfcProtocolSlix] = NULL,
[NfcProtocolSt25tb] = NULL,
};
static NfcCommand nfc_cli_raw_poller_callback(NfcGenericEventEx event, void* context) {
NfcEvent* nfc_event = event.parent_event_data;
NfcCliRawCmdContext* instance = context;
NfcCommand command = NfcCommandContinue;
if(nfc_event->type == NfcEventTypePollerReady) {
FURI_LOG_D(TAG, "Poller callback");
NfcCliProtocolRequestType request_type = NfcCliProtocolRequestTypeAbort;
furi_message_queue_get(instance->input_queue, &request_type, FuriWaitForever);
if(request_type == NfcCliProtocolRequestTypeAbort) {
command = NfcCommandStop;
} else {
const NfcCliRawProtocolSpecificHandler handler =
nfc_cli_raw_protocol_handlers[instance->request.protocol];
if(handler) handler(event.poller, &instance->request, &instance->response);
}
}
furi_semaphore_release(instance->sem_done);
if(command == NfcCommandStop) {
FURI_LOG_D(TAG, "Aborting poller callback");
instance->poller_state = NfcPollerStateStopped;
}
return command;
}
static inline void nfc_cli_raw_print_result(const NfcCliRawCmdContext* instance) {
if(!furi_string_empty(instance->response.activation_string))
printf("%s\r\n", furi_string_get_cstr(instance->response.activation_string));
nfc_cli_printf_array(
bit_buffer_get_data(instance->request.tx_buffer),
bit_buffer_get_size_bytes(instance->request.tx_buffer),
"Tx: ");
if(instance->response.result != NfcCliRawErrorNone)
printf("\r\nError: \"%s\"\r\n", raw_error_names[instance->response.result]);
size_t rx_size = bit_buffer_get_size_bytes(instance->response.rx_buffer);
if(rx_size > 0) {
nfc_cli_printf_array(
bit_buffer_get_data(instance->response.rx_buffer),
bit_buffer_get_size_bytes(instance->response.rx_buffer),
"\r\nRx: ");
}
}
static void nfc_cli_raw_execute(PipeSide* pipe, void* context) {
UNUSED(pipe);
furi_assert(context);
NfcCliRawCmdContext* instance = context;
furi_string_reset(instance->response.activation_string);
if(instance->poller_state == NfcPollerStateStopped) {
if(instance->poller == NULL)
instance->poller = nfc_poller_alloc(instance->nfc, instance->request.protocol);
nfc_poller_start_ex(instance->poller, nfc_cli_raw_poller_callback, instance);
instance->poller_state = NfcPollerStateStarted;
}
instance->request_type = NfcCliProtocolRequestTypeNormalExecute;
furi_message_queue_put(instance->input_queue, &instance->request_type, FuriWaitForever);
furi_semaphore_acquire(instance->sem_done, FuriWaitForever);
nfc_cli_raw_print_result(instance);
}
static const NfcProtocolNameValuePair supported_protocols[] = {
{.name = "14a", .value = NfcProtocolIso14443_3a},
{.name = "iso14a", .value = NfcProtocolIso14443_3a},
{.name = "14b", .value = NfcProtocolIso14443_3b},
{.name = "iso14b", .value = NfcProtocolIso14443_3b},
{.name = "15", .value = NfcProtocolIso15693_3},
{.name = "felica", .value = NfcProtocolFelica},
};
static bool nfc_cli_raw_parse_protocol(FuriString* value, void* output) {
NfcCliRawCmdContext* ctx = output;
NfcProtocol new_protocol = NfcProtocolInvalid;
NfcCliProtocolParser* parser =
nfc_cli_protocol_parser_alloc(supported_protocols, COUNT_OF(supported_protocols));
bool result = nfc_cli_protocol_parser_get(parser, value, &new_protocol);
nfc_cli_protocol_parser_free(parser);
if(result && ctx->request.protocol != NfcProtocolInvalid &&
ctx->request.protocol != new_protocol) {
printf(
ANSI_FG_RED "Error: previous %s != new %s. Unable to continue." ANSI_RESET,
nfc_cli_get_protocol_name(ctx->request.protocol),
nfc_cli_get_protocol_name(new_protocol));
result = false;
}
if(result) {
ctx->request.protocol = new_protocol;
}
return result;
}
static bool nfc_cli_raw_parse_data(FuriString* value, void* output) {
NfcCliRawCmdContext* ctx = output;
bool result = false;
do {
size_t len = furi_string_size(value);
if(len % 2 != 0) break;
size_t data_length = len / 2;
uint8_t* data = malloc(data_length);
if(args_read_hex_bytes(value, data, data_length)) {
bit_buffer_reset(ctx->request.tx_buffer);
bit_buffer_copy_bytes(ctx->request.tx_buffer, data, data_length);
result = true;
}
free(data);
} while(false);
return result;
}
static bool nfc_cli_raw_parse_timeout(FuriString* value, void* output) {
furi_assert(value);
furi_assert(output);
NfcCliRawCmdContext* ctx = output;
bool result = false;
int timeout = 0;
if(args_read_int_and_trim(value, &timeout)) {
ctx->request.timeout = timeout;
result = true;
}
return result;
}
static bool nfc_cli_raw_parse_select(FuriString* value, void* output) {
UNUSED(value);
NfcCliRawCmdContext* ctx = output;
ctx->request.select = true;
return true;
}
static bool nfc_cli_raw_parse_crc(FuriString* value, void* output) {
UNUSED(value);
NfcCliRawCmdContext* ctx = output;
ctx->request.append_crc = true;
return true;
}
static bool nfc_cli_raw_parse_keep(FuriString* value, void* output) {
UNUSED(value);
NfcCliRawCmdContext* ctx = output;
ctx->request.keep_field = true;
return true;
}
const NfcCliKeyDescriptor raw_action_keys[] = {
{
.long_name = NULL,
.short_name = "t",
.features = {.parameter = true, .required = false},
.description = "timeout in fc",
.parse = nfc_cli_raw_parse_timeout,
},
{
.long_name = NULL,
.short_name = "k",
.description = "keep signal field ON after receive",
.parse = nfc_cli_raw_parse_keep,
},
{
.long_name = NULL,
.short_name = "c",
.description = "calculate and append CRC",
.parse = nfc_cli_raw_parse_crc,
},
{
.long_name = NULL,
.short_name = "s",
.description = "Select on FieldOn",
.parse = nfc_cli_raw_parse_select,
},
{
.long_name = "protocol",
.short_name = "p",
.description = "desired protocol. Possible values: 14a, iso14a, 14b, iso14b, 15, felica",
.features = {.parameter = true, .required = true},
.parse = nfc_cli_raw_parse_protocol,
},
{
.long_name = "data",
.short_name = "d",
.description = "Raw bytes to send in HEX format",
.features = {.parameter = true, .required = true},
.parse = nfc_cli_raw_parse_data,
},
};
const NfcCliActionDescriptor raw_action = {
.name = "raw",
.description = "Sends raw bytes using different protocols",
.key_count = COUNT_OF(raw_action_keys),
.keys = raw_action_keys,
.execute = nfc_cli_raw_execute,
.alloc = nfc_cli_raw_alloc_ctx,
.free = nfc_cli_raw_free_ctx,
.can_reuse = nfc_cli_raw_can_reuse_ctx,
};
const NfcCliActionDescriptor* raw_actions_collection[] = {&raw_action};
ADD_NFC_CLI_COMMAND(raw, "", raw_actions_collection);
//Command usage: raw <protocol> [keys] <data>
//Command examples:
//raw iso14a -sc 3000
//raw iso14a 3000
//raw iso14a 3000 -sc

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../../nfc_cli_command_base_i.h"
extern const NfcCliCommandDescriptor raw_cmd;

View File

@@ -0,0 +1,101 @@
#include "nfc_cli_raw_felica.h"
#include "../../../helpers/nfc_cli_format.h"
#include <nfc/helpers/felica_crc.h>
#include <nfc/protocols/felica/felica.h>
#include <nfc/protocols/felica/felica_poller_i.h>
#define TAG "FELICA"
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
static inline void felica_format_activation_data(const FelicaData* data, FuriString* output) {
nfc_cli_format_array(data->idm.data, FELICA_IDM_SIZE, "IDm: ", output);
nfc_cli_format_array(data->pmm.data, FELICA_PMM_SIZE, " PMm: ", output);
}
static NfcCliRawError nfc_cli_raw_felica_process_error(FelicaError error) {
switch(error) {
case FelicaErrorNone:
return NfcCliRawErrorNone;
case FelicaErrorTimeout:
return NfcCliRawErrorTimeout;
case FelicaErrorWrongCrc:
return NfcCliRawErrorWrongCrc;
case FelicaErrorNotPresent:
return NfcCliRawErrorNotPresent;
default:
return NfcCliRawErrorProtocol;
}
}
static FelicaError nfc_cli_raw_felica_poller_process_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return FelicaErrorNone;
case NfcErrorTimeout:
return FelicaErrorTimeout;
default:
return FelicaErrorNotPresent;
}
}
static inline NfcCliRawError
nfc_cli_raw_felica_activate(NfcGenericInstance* poller, FuriString* activation_string) {
FelicaData felica_data = {};
FelicaPoller* felica_poller = poller;
FURI_LOG_D(TAG, "Activating...");
FelicaError error = felica_poller_activate(felica_poller, &felica_data);
if(error == FelicaErrorNone) {
felica_format_activation_data(&felica_data, activation_string);
}
return nfc_cli_raw_felica_process_error(error);
}
static inline NfcCliRawError nfc_cli_raw_felica_txrx(
NfcGenericInstance* poller,
BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t timeout) {
FURI_LOG_D(TAG, "TxRx");
FelicaPoller* felica_poller = poller;
bit_buffer_reset(rx_buffer);
FelicaError error = FelicaErrorNone;
NfcError nfc_error = nfc_poller_trx(felica_poller->nfc, tx_buffer, rx_buffer, timeout);
if(nfc_error != NfcErrorNone) {
error = nfc_cli_raw_felica_poller_process_error(nfc_error);
} else if(!felica_crc_check(rx_buffer)) {
error = FelicaErrorWrongCrc;
}
return nfc_cli_raw_felica_process_error(error);
}
NfcCommand nfc_cli_raw_felica_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response) {
do {
if(request->select) {
response->result = nfc_cli_raw_felica_activate(poller, response->activation_string);
}
if(response->result != NfcCliRawErrorNone) break;
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
if(request->append_crc) {
FURI_LOG_D(TAG, "Add CRC");
felica_crc_append(request->tx_buffer);
}
uint32_t timeout = request->timeout > 0 ? request->timeout : FELICA_FDT_POLL_FC;
response->result =
nfc_cli_raw_felica_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
} while(false);
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../nfc_cli_raw_common_types.h"
NfcCommand nfc_cli_raw_felica_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response);

View File

@@ -0,0 +1,81 @@
#include "nfc_cli_raw_iso14443_3a.h"
#include "../../../helpers/nfc_cli_format.h"
#include <nfc/helpers/iso14443_crc.h>
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller.h>
#define TAG "ISO14A"
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
static NfcCliRawError nfc_cli_raw_iso14443_3a_process_error(Iso14443_3aError error) {
switch(error) {
case Iso14443_3aErrorNone:
return NfcCliRawErrorNone;
case Iso14443_3aErrorTimeout:
return NfcCliRawErrorTimeout;
case Iso14443_3aErrorWrongCrc:
return NfcCliRawErrorWrongCrc;
case Iso14443_3aErrorNotPresent:
return NfcCliRawErrorNotPresent;
default:
return NfcCliRawErrorProtocol;
}
}
static void iso14443_3a_format_activation_data(const Iso14443_3aData* data, FuriString* output) {
nfc_cli_format_array(data->uid, data->uid_len, "UID: ", output);
furi_string_cat_printf(
output, " ATQA: %02X%02X SAK: %02X", data->atqa[0], data->atqa[1], data->sak);
}
static inline NfcCliRawError
nfc_cli_raw_iso14443_3a_activate(NfcGenericInstance* poller, FuriString* activation_string) {
Iso14443_3aData iso3_data = {};
FURI_LOG_D(TAG, "Activating...");
Iso14443_3aError error = iso14443_3a_poller_activate(poller, &iso3_data);
if(error == Iso14443_3aErrorNone)
iso14443_3a_format_activation_data(&iso3_data, activation_string);
return nfc_cli_raw_iso14443_3a_process_error(error);
}
static inline NfcCliRawError nfc_cli_raw_iso14443_3a_txrx(
NfcGenericInstance* poller,
BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t timeout) {
FURI_LOG_D(TAG, "TxRx");
bit_buffer_reset(rx_buffer);
Iso14443_3aError error = iso14443_3a_poller_txrx(poller, tx_buffer, rx_buffer, timeout);
return nfc_cli_raw_iso14443_3a_process_error(error);
}
NfcCommand nfc_cli_raw_iso14443_3a_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response) {
do {
response->result = NfcCliRawErrorNone;
if(request->select) {
response->result =
nfc_cli_raw_iso14443_3a_activate(poller, response->activation_string);
}
if(response->result != NfcCliRawErrorNone) break;
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
if(request->append_crc) {
FURI_LOG_D(TAG, "Add CRC");
iso14443_crc_append(Iso14443CrcTypeA, request->tx_buffer);
}
uint32_t timeout = request->timeout > 0 ? request->timeout : ISO14443_3A_FDT_LISTEN_FC;
response->result =
nfc_cli_raw_iso14443_3a_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
} while(false);
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../nfc_cli_raw_common_types.h"
NfcCommand nfc_cli_raw_iso14443_3a_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response);

View File

@@ -0,0 +1,121 @@
#include "nfc_cli_raw_iso14443_3b.h"
#include "../../../helpers/nfc_cli_format.h"
#include <nfc/helpers/iso14443_crc.h>
#include <nfc/protocols/iso14443_3b/iso14443_3b_i.h>
#include <nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h>
#define TAG "ISO14B"
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
static NfcCliRawError nfc_cli_raw_iso14443_3b_process_error(Iso14443_3bError error) {
switch(error) {
case Iso14443_3bErrorNone:
return NfcCliRawErrorNone;
case Iso14443_3bErrorTimeout:
return NfcCliRawErrorTimeout;
case Iso14443_3bErrorWrongCrc:
return NfcCliRawErrorWrongCrc;
case Iso14443_3bErrorNotPresent:
return NfcCliRawErrorNotPresent;
default:
return NfcCliRawErrorProtocol;
}
}
static Iso14443_3bError nfc_cli_raw_iso14443_3b_poller_process_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return Iso14443_3bErrorNone;
case NfcErrorTimeout:
return Iso14443_3bErrorTimeout;
default:
return Iso14443_3bErrorNotPresent;
}
}
static void iso14443_3b_format_activation_data(const Iso14443_3bData* data, FuriString* output) {
nfc_cli_format_array(data->uid, ISO14443_3B_UID_SIZE, "UID: ", output);
const Iso14443_3bProtocolInfo* info = &data->protocol_info;
furi_string_cat_printf(
output,
" BitRate: %d, Protocol: %d, Max Frame Size: %d, Fo: %d, Adc: %d, Fwi: %d",
info->bit_rate_capability,
info->protocol_type,
info->max_frame_size,
info->fo,
info->adc,
info->fwi);
}
static inline NfcCliRawError nfc_cli_raw_iso14443_3b_activate(
NfcGenericInstance* poller,
Iso14443_3bData* iso3b_data,
FuriString* activation_string) {
FURI_LOG_D(TAG, "Activating...");
Iso14443_3bError error = iso14443_3b_poller_activate(poller, iso3b_data);
if(error == Iso14443_3bErrorNone)
iso14443_3b_format_activation_data(iso3b_data, activation_string);
return nfc_cli_raw_iso14443_3b_process_error(error);
}
static inline NfcCliRawError nfc_cli_raw_iso14443_3b_txrx(
NfcGenericInstance* poller,
BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t timeout) {
FURI_LOG_D(TAG, "TxRx");
Iso14443_3bPoller* iso14b_poller = poller;
bit_buffer_reset(rx_buffer);
Iso14443_3bError error = Iso14443_3bErrorNone;
NfcError nfc_error = nfc_poller_trx(iso14b_poller->nfc, tx_buffer, rx_buffer, timeout);
if(nfc_error != NfcErrorNone) {
error = nfc_cli_raw_iso14443_3b_poller_process_error(nfc_error);
} else if(!iso14443_crc_check(Iso14443CrcTypeB, rx_buffer)) {
error = Iso14443_3bErrorWrongCrc;
}
return nfc_cli_raw_iso14443_3b_process_error(error);
}
NfcCommand nfc_cli_raw_iso14443_3b_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response) {
Iso14443_3bData iso3b_data = {0};
bool activated = false;
do {
response->result = NfcCliRawErrorNone;
if(request->select) {
response->result =
nfc_cli_raw_iso14443_3b_activate(poller, &iso3b_data, response->activation_string);
activated = response->result == NfcCliRawErrorNone;
}
if(response->result != NfcCliRawErrorNone) break;
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
uint32_t timeout = ISO14443_3B_FDT_POLL_FC;
if(request->timeout > 0) {
timeout = request->timeout;
} else if(activated) {
timeout = iso14443_3b_get_fwt_fc_max(&iso3b_data);
}
if(request->append_crc) {
FURI_LOG_D(TAG, "Add CRC");
iso14443_crc_append(Iso14443CrcTypeB, request->tx_buffer);
}
response->result =
nfc_cli_raw_iso14443_3b_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
} while(false);
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../nfc_cli_raw_common_types.h"
NfcCommand nfc_cli_raw_iso14443_3b_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response);

View File

@@ -0,0 +1,102 @@
#include "nfc_cli_raw_iso15693_3.h"
#include "../../../helpers/nfc_cli_format.h"
#include <nfc/helpers/iso13239_crc.h>
#include <nfc/protocols/iso15693_3/iso15693_3.h>
#include <nfc/protocols/iso15693_3/iso15693_3_poller_i.h>
#define TAG "ISO15"
#define BIT_BUFFER_EMPTY(buffer) ((bit_buffer_get_size_bytes(buffer) == 0))
static NfcCliRawError nfc_cli_raw_iso15693_3_process_error(Iso15693_3Error error) {
switch(error) {
case Iso15693_3ErrorNone:
return NfcCliRawErrorNone;
case Iso15693_3ErrorTimeout:
return NfcCliRawErrorTimeout;
case Iso15693_3ErrorWrongCrc:
return NfcCliRawErrorWrongCrc;
case Iso15693_3ErrorNotPresent:
return NfcCliRawErrorNotPresent;
default:
return NfcCliRawErrorProtocol;
}
}
static Iso15693_3Error nfc_cli_raw_iso15693_3_poller_process_nfc_error(NfcError error) {
switch(error) {
case NfcErrorNone:
return Iso15693_3ErrorNone;
case NfcErrorTimeout:
return Iso15693_3ErrorTimeout;
default:
return Iso15693_3ErrorNotPresent;
}
}
static inline void iso15693_3_format_activation_data(const uint8_t* data, FuriString* output) {
nfc_cli_format_array(data, ISO15693_3_UID_SIZE, "UID: ", output);
}
static inline NfcCliRawError
nfc_cli_raw_iso15693_3_activate(NfcGenericInstance* poller, FuriString* activation_string) {
FURI_LOG_D(TAG, "Activating...");
Iso15693_3Poller* iso15_poller = poller;
uint8_t uid[ISO15693_3_UID_SIZE] = {0};
Iso15693_3Error error = iso15693_3_poller_inventory(iso15_poller, uid);
if(error == Iso15693_3ErrorNone) {
iso15693_3_format_activation_data(uid, activation_string);
}
return nfc_cli_raw_iso15693_3_process_error(error);
}
static inline NfcCliRawError nfc_cli_raw_iso15693_3_txrx(
NfcGenericInstance* poller,
BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t timeout) {
FURI_LOG_D(TAG, "TxRx");
Iso15693_3Poller* iso15_poller = poller;
bit_buffer_reset(rx_buffer);
Iso15693_3Error error = Iso15693_3ErrorNone;
NfcError nfc_error = nfc_poller_trx(iso15_poller->nfc, tx_buffer, rx_buffer, timeout);
if(nfc_error != NfcErrorNone) {
error = nfc_cli_raw_iso15693_3_poller_process_nfc_error(nfc_error);
} else if(!iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) {
error = Iso15693_3ErrorWrongCrc;
}
return nfc_cli_raw_iso15693_3_process_error(error);
}
NfcCommand nfc_cli_raw_iso15693_3_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response) {
do {
if(request->select) {
response->result =
nfc_cli_raw_iso15693_3_activate(poller, response->activation_string);
}
if(response->result != NfcCliRawErrorNone) break;
if(BIT_BUFFER_EMPTY(request->tx_buffer)) break;
if(request->append_crc) {
FURI_LOG_D(TAG, "Add CRC");
iso13239_crc_append(Iso13239CrcTypeDefault, request->tx_buffer);
}
uint32_t timeout = request->timeout > 0 ? request->timeout : ISO15693_3_FDT_POLL_FC;
response->result =
nfc_cli_raw_iso15693_3_txrx(poller, request->tx_buffer, response->rx_buffer, timeout);
} while(false);
return request->keep_field ? NfcCommandContinue : NfcCommandStop;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../nfc_cli_raw_common_types.h"
NfcCommand nfc_cli_raw_iso15693_3_handler(
NfcGenericInstance* poller,
const NfcCliRawRequest* request,
NfcCliRawResponse* const response);

View File

@@ -0,0 +1,28 @@
#pragma once
#include <furi.h>
#include <nfc/nfc.h>
#include <nfc/nfc_poller.h>
typedef enum {
NfcCliRawErrorNone,
NfcCliRawErrorTimeout,
NfcCliRawErrorNotPresent,
NfcCliRawErrorWrongCrc,
NfcCliRawErrorProtocol,
} NfcCliRawError;
typedef struct {
bool select;
bool keep_field;
bool append_crc;
NfcProtocol protocol;
BitBuffer* tx_buffer;
uint32_t timeout;
} NfcCliRawRequest;
typedef struct {
NfcCliRawError result;
BitBuffer* rx_buffer;
FuriString* activation_string;
} NfcCliRawResponse;