Skip to content

Commit

Permalink
[FL-3666] NFC API improvements (#3214)
Browse files Browse the repository at this point in the history
* drivers: expose st25r3916 driver API
* nfc poller: add start with custom callback
* mf classic: rework sync API with poller custom start
* mf ultralight: rework sync API with poller custom start
* iso14443_3a poller: remove unused col res state
* nfc: rework nfc poller custom start
* mf ultralight: rename sync API
* mf classic: rename sync API
* iso14443-3a: rename sync API
* nfc: remove async prefix in internal functions
* nfc: expose internal API
* nfc: fix sync api include and docs
* targets: fix f18 build
* nfc: rework NfcGenericEventEx type
* nfc poller: add documentation
* iso14443-3a poller: add documentation
* felica poller: add documentation
* iso14443_3b poller: add documentation
* so14443_4a poller: add documentation
* iso14443_4b poller: add documentation
* iso15693 poller: add documentation
* slix poller: add documentation
* mf desfire poller: add documentation
* mf ultralight poller: fix API and add documentation
* mf classic poller: add documentation

Co-authored-by: あく <[email protected]>
  • Loading branch information
gornekich and skotopes authored Nov 15, 2023
1 parent d0b9a3a commit c00776c
Show file tree
Hide file tree
Showing 62 changed files with 1,712 additions and 723 deletions.
44 changes: 23 additions & 21 deletions applications/debug/unit_tests/nfc/nfc_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
#include <nfc/nfc_poller.h>
#include <nfc/nfc_listener.h>
#include <nfc/protocols/iso14443_3a/iso14443_3a.h>
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_sync_api.h>
#include <nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync_api.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync_api.h>
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>

#include <nfc/helpers/nfc_dict.h>
#include <nfc/nfc.h>
Expand Down Expand Up @@ -182,8 +182,8 @@ MU_TEST(iso14443_3a_reader) {

Iso14443_3aData iso14443_3a_poller_data = {};
mu_assert(
iso14443_3a_poller_read(poller, &iso14443_3a_poller_data) == Iso14443_3aErrorNone,
"iso14443_3a_poller_read() failed");
iso14443_3a_poller_sync_read(poller, &iso14443_3a_poller_data) == Iso14443_3aErrorNone,
"iso14443_3a_poller_sync_read() failed");

nfc_listener_stop(iso3_listener);
mu_assert(
Expand Down Expand Up @@ -221,8 +221,8 @@ static void mf_ultralight_reader_test(const char* path) {
nfc_listener_start(mfu_listener, NULL, NULL);

MfUltralightData* mfu_data = mf_ultralight_alloc();
MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed");
MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");

nfc_listener_stop(mfu_listener);
nfc_listener_free(mfu_listener);
Expand Down Expand Up @@ -270,8 +270,8 @@ MU_TEST(ntag_213_locked_reader) {
nfc_listener_start(mfu_listener, NULL, NULL);

MfUltralightData* mfu_data = mf_ultralight_alloc();
MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed");
MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");

nfc_listener_stop(mfu_listener);
nfc_listener_free(mfu_listener);
Expand Down Expand Up @@ -308,8 +308,8 @@ static void mf_ultralight_write() {
MfUltralightData* mfu_data = mf_ultralight_alloc();

// Initial read
MfUltralightError error = mf_ultralight_poller_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed");
MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");

mu_assert(
mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)),
Expand All @@ -321,13 +321,13 @@ static void mf_ultralight_write() {
FURI_LOG_D(TAG, "Writing page %d", i);
furi_hal_random_fill_buf(page.data, sizeof(MfUltralightPage));
mfu_data->page[i] = page;
error = mf_ultralight_poller_write_page(poller, i, &page);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_write_page() failed");
error = mf_ultralight_poller_sync_write_page(poller, i, &page);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_write_page() failed");
}

// Verification read
error = mf_ultralight_poller_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_read_card() failed");
error = mf_ultralight_poller_sync_read_card(poller, mfu_data);
mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed");

nfc_listener_stop(mfu_listener);
const MfUltralightData* mfu_listener_data =
Expand Down Expand Up @@ -355,7 +355,7 @@ static void mf_classic_reader() {
MfClassicBlock block = {};
MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};

mf_classic_poller_read_block(poller, 0, &key, MfClassicKeyTypeA, &block);
mf_classic_poller_sync_read_block(poller, 0, &key, MfClassicKeyTypeA, &block);

nfc_listener_stop(mfc_listener);
nfc_listener_free(mfc_listener);
Expand Down Expand Up @@ -383,8 +383,8 @@ static void mf_classic_write() {
furi_hal_random_fill_buf(block_write.data, sizeof(MfClassicBlock));
MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};

mf_classic_poller_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);
mf_classic_poller_read_block(poller, 1, &key, MfClassicKeyTypeA, &block_read);
mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);
mf_classic_poller_sync_read_block(poller, 1, &key, MfClassicKeyTypeA, &block_read);

nfc_listener_stop(mfc_listener);
nfc_listener_free(mfc_listener);
Expand Down Expand Up @@ -413,16 +413,18 @@ static void mf_classic_value_block() {
mf_classic_value_to_block(value, 1, &block_write);

MfClassicError error = MfClassicErrorNone;
error = mf_classic_poller_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);
error = mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write);
mu_assert(error == MfClassicErrorNone, "Write failed");

int32_t data = 200;
int32_t new_value = 0;
error = mf_classic_poller_change_value(poller, 1, &key, MfClassicKeyTypeA, data, &new_value);
error =
mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, data, &new_value);
mu_assert(error == MfClassicErrorNone, "Value increment failed");
mu_assert(new_value == value + data, "Value not match");

error = mf_classic_poller_change_value(poller, 1, &key, MfClassicKeyTypeA, -data, &new_value);
error =
mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, -data, &new_value);
mu_assert(error == MfClassicErrorNone, "Value decrement failed");
mu_assert(new_value == value, "Value not match");

Expand Down
8 changes: 4 additions & 4 deletions applications/main/nfc/plugins/supported_cards/plantain.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync_api.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>

#define TAG "Plantain"

Expand Down Expand Up @@ -91,7 +91,7 @@ static bool plantain_verify_type(Nfc* nfc, MfClassicType type) {

MfClassicAuthContext auth_context;
MfClassicError error =
mf_classic_poller_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);
mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
break;
Expand Down Expand Up @@ -119,7 +119,7 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) {

do {
MfClassicType type = MfClassicTypeMini;
MfClassicError error = mf_classic_poller_detect_type(nfc, &type);
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;

data->type = type;
Expand All @@ -134,7 +134,7 @@ static bool plantain_read(Nfc* nfc, NfcDevice* device) {
FURI_BIT_SET(keys.key_b_mask, i);
}

error = mf_classic_poller_read(nfc, &keys, data);
error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
break;
Expand Down
8 changes: 4 additions & 4 deletions applications/main/nfc/plugins/supported_cards/troika.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync_api.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>

#define TAG "Troika"

Expand Down Expand Up @@ -91,7 +91,7 @@ static bool troika_verify_type(Nfc* nfc, MfClassicType type) {

MfClassicAuthContext auth_context;
MfClassicError error =
mf_classic_poller_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);
mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
break;
Expand All @@ -118,7 +118,7 @@ static bool troika_read(Nfc* nfc, NfcDevice* device) {

do {
MfClassicType type = MfClassicTypeMini;
MfClassicError error = mf_classic_poller_detect_type(nfc, &type);
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;

data->type = type;
Expand All @@ -136,7 +136,7 @@ static bool troika_read(Nfc* nfc, NfcDevice* device) {
FURI_BIT_SET(keys.key_b_mask, i);
}

error = mf_classic_poller_read(nfc, &keys, data);
error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
break;
Expand Down
8 changes: 4 additions & 4 deletions applications/main/nfc/plugins/supported_cards/two_cities.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_util.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync_api.h>
#include <nfc/protocols/mf_classic/mf_classic_poller_sync.h>

#define TAG "TwoCities"

Expand Down Expand Up @@ -49,7 +49,7 @@ bool two_cities_verify(Nfc* nfc) {

MfClassicAuthContext auth_ctx = {};
MfClassicError error =
mf_classic_poller_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx);
mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error);
break;
Expand All @@ -72,7 +72,7 @@ static bool two_cities_read(Nfc* nfc, NfcDevice* device) {

do {
MfClassicType type = MfClassicTypeMini;
MfClassicError error = mf_classic_poller_detect_type(nfc, &type);
MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type);
if(error != MfClassicErrorNone) break;

data->type = type;
Expand All @@ -84,7 +84,7 @@ static bool two_cities_read(Nfc* nfc, NfcDevice* device) {
FURI_BIT_SET(keys.key_b_mask, i);
}

error = mf_classic_poller_read(nfc, &keys, data);
error = mf_classic_poller_sync_read(nfc, &keys, data);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "Failed to read data");
break;
Expand Down
2 changes: 2 additions & 0 deletions lib/drivers/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ env.Append(
],
SDK_HEADERS=[
File("cc1101_regs.h"),
File("st25r3916_reg.h"),
File("st25r3916.h"),
],
)

Expand Down
6 changes: 3 additions & 3 deletions lib/nfc/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ env.Append(
File("protocols/mf_ultralight/mf_ultralight_listener.h"),
File("protocols/mf_classic/mf_classic_listener.h"),
# Sync API
File("protocols/iso14443_3a/iso14443_3a_poller_sync_api.h"),
File("protocols/mf_ultralight/mf_ultralight_poller_sync_api.h"),
File("protocols/mf_classic/mf_classic_poller_sync_api.h"),
File("protocols/iso14443_3a/iso14443_3a_poller_sync.h"),
File("protocols/mf_ultralight/mf_ultralight_poller_sync.h"),
File("protocols/mf_classic/mf_classic_poller_sync.h"),
# Misc
File("helpers/nfc_util.h"),
File("helpers/iso14443_crc.h"),
Expand Down
72 changes: 72 additions & 0 deletions lib/nfc/nfc_poller.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ struct NfcPoller {
NfcPollerList list;
NfcPollerSessionState session_state;
bool protocol_detected;

NfcGenericCallbackEx callback;
void* context;
};

static void nfc_poller_list_alloc(NfcPoller* instance) {
Expand Down Expand Up @@ -127,6 +130,75 @@ void nfc_poller_start(NfcPoller* instance, NfcGenericCallback callback, void* co
nfc_start(instance->nfc, nfc_poller_start_callback, instance);
}

static NfcCommand nfc_poller_start_ex_tail_callback(NfcGenericEvent event, void* context) {
furi_assert(context);
furi_assert(event.protocol != NfcProtocolInvalid);

NfcPoller* instance = context;
NfcCommand command = NfcCommandContinue;

NfcGenericEventEx poller_event = {
.poller = instance->list.tail->poller,
.parent_event_data = event.event_data,
};

command = instance->callback(poller_event, instance->context);

return command;
}

static NfcCommand nfc_poller_start_ex_head_callback(NfcEvent event, void* context) {
furi_assert(context);

NfcCommand command = NfcCommandContinue;
NfcPoller* instance = context;

NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->protocol);

if(parent_protocol == NfcProtocolInvalid) {
NfcGenericEventEx poller_event = {
.poller = instance->list.tail->poller,
.parent_event_data = &event,
};

command = instance->callback(poller_event, instance->context);
} else {
NfcGenericEvent poller_event = {
.protocol = NfcProtocolInvalid,
.instance = instance->nfc,
.event_data = &event,
};
NfcPollerListElement* head_poller = instance->list.head;
command = head_poller->poller_api->run(poller_event, head_poller->poller);
}

if(instance->session_state == NfcPollerSessionStateStopRequest) {
command = NfcCommandStop;
}

return command;
}

void nfc_poller_start_ex(NfcPoller* instance, NfcGenericCallbackEx callback, void* context) {
furi_assert(instance);
furi_assert(callback);
furi_assert(instance->session_state == NfcPollerSessionStateIdle);

instance->callback = callback;
instance->context = context;

NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->protocol);
if(parent_protocol != NfcProtocolInvalid) {
NfcPollerListElement* iter = instance->list.head;
while(iter->protocol != parent_protocol) iter = iter->child;

iter->poller_api->set_callback(iter->poller, nfc_poller_start_ex_tail_callback, instance);
}

instance->session_state = NfcPollerSessionStateActive;
nfc_start(instance->nfc, nfc_poller_start_ex_head_callback, instance);
}

void nfc_poller_stop(NfcPoller* instance) {
furi_assert(instance);
furi_assert(instance->nfc);
Expand Down
37 changes: 37 additions & 0 deletions lib/nfc/nfc_poller.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,31 @@ extern "C" {
*/
typedef struct NfcPoller NfcPoller;

/**
* @brief Extended generic Nfc event type.
*
* An extended generic Nfc event contains protocol poller and it's parent protocol event data.
* If protocol has no parent, then events are produced by Nfc instance.
*
* The parent_event_data field is protocol-specific and should be cast to the appropriate type before use.
*/
typedef struct {
NfcGenericInstance* poller; /**< Pointer to the protocol poller. */
NfcGenericEventData*
parent_event_data /**< Pointer to the protocol's parent poller event data. */;
} NfcGenericEventEx;

/**
* @brief Extended generic Nfc event callback type.
*
* A function of this type must be passed as the callback parameter upon extended start of a poller.
*
* @param [in] event Nfc extended generic event, passed by value, complete with protocol type and data.
* @param [in,out] context pointer to the user-specific context (set when starting a poller/listener instance).
* @returns the command which the event producer must execute.
*/
typedef NfcCommand (*NfcGenericCallbackEx)(NfcGenericEventEx event, void* context);

/**
* @brief Allocate an NfcPoller instance.
*
Expand Down Expand Up @@ -57,6 +82,18 @@ void nfc_poller_free(NfcPoller* instance);
*/
void nfc_poller_start(NfcPoller* instance, NfcGenericCallback callback, void* context);

/**
* @brief Start an NfcPoller instance in extended mode.
*
* When nfc poller is started in extended mode, callback will be called with parent protocol events
* and protocol instance. This mode enables to make custom poller state machines.
*
* @param[in,out] instance pointer to the instance to be started.
* @param[in] callback pointer to a user-defined callback function which will receive events.
* @param[in] context pointer to a user-specific context (will be passed to the callback).
*/
void nfc_poller_start_ex(NfcPoller* instance, NfcGenericCallbackEx callback, void* context);

/**
* @brief Stop an NfcPoller instance.
*
Expand Down
Loading

0 comments on commit c00776c

Please sign in to comment.