Skip to content

Commit

Permalink
[FL-3719] NFC Plugins loading rework (#3295)
Browse files Browse the repository at this point in the history
* nfc app: rework supported cards
* nfc app: supported cards optimization
* nfc app: remove old nfc support implementation
* nfc app: add documentation for supported cards
* nfc app: format sources
* nfc app: fix PVS warnings
* nfc app: one more PVS fix
* nfc app: PVS please stop
* nfc app: add missing documentation

Co-authored-by: あく <[email protected]>
  • Loading branch information
gornekich and skotopes authored Dec 15, 2023
1 parent 0954092 commit d6680d1
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 63 deletions.
242 changes: 188 additions & 54 deletions applications/main/nfc/helpers/nfc_supported_cards.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "nfc_supported_cards.h"

#include "../plugins/supported_cards/nfc_supported_card_plugin.h"

#include <flipper_application/flipper_application.h>
Expand All @@ -7,22 +8,72 @@

#include <furi.h>
#include <path.h>
#include <m-array.h>

#define TAG "NfcSupportedCards"

#define NFC_SUPPORTED_CARDS_PLUGINS_PATH APP_DATA_PATH("plugins")
#define NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX "_parser.fal"

typedef enum {
NfcSupportedCardsPluginFeatureHasVerify = (1U << 0),
NfcSupportedCardsPluginFeatureHasRead = (1U << 1),
NfcSupportedCardsPluginFeatureHasParse = (1U << 2),
} NfcSupportedCardsPluginFeature;

typedef struct {
FuriString* path;
NfcProtocol protocol;
NfcSupportedCardsPluginFeature feature;
} NfcSupportedCardsPluginCache;

ARRAY_DEF(NfcSupportedCardsPluginCache, NfcSupportedCardsPluginCache, M_POD_OPLIST);

typedef enum {
NfcSupportedCardsLoadStateIdle,
NfcSupportedCardsLoadStateInProgress,
NfcSupportedCardsLoadStateSuccess,
NfcSupportedCardsLoadStateFail,
} NfcSupportedCardsLoadState;

typedef struct {
Storage* storage;
File* directory;
FuriString* file_path;
char file_name[256];
FlipperApplication* app;
} NfcSupportedCards;
} NfcSupportedCardsLoadContext;

static NfcSupportedCards* nfc_supported_cards_alloc() {
struct NfcSupportedCards {
NfcSupportedCardsPluginCache_t plugins_cache_arr;
NfcSupportedCardsLoadState load_state;
NfcSupportedCardsLoadContext* load_context;
};

NfcSupportedCards* nfc_supported_cards_alloc() {
NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards));
NfcSupportedCardsPluginCache_init(instance->plugins_cache_arr);

return instance;
}

void nfc_supported_cards_free(NfcSupportedCards* instance) {
furi_assert(instance);

NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
furi_string_free(plugin_cache->path);
}

NfcSupportedCardsPluginCache_clear(instance->plugins_cache_arr);
free(instance);
}

static NfcSupportedCardsLoadContext* nfc_supported_cards_load_context_alloc() {
NfcSupportedCardsLoadContext* instance = malloc(sizeof(NfcSupportedCardsLoadContext));

instance->storage = furi_record_open(RECORD_STORAGE);
instance->directory = storage_file_alloc(instance->storage);
Expand All @@ -35,7 +86,7 @@ static NfcSupportedCards* nfc_supported_cards_alloc() {
return instance;
}

static void nfc_supported_cards_free(NfcSupportedCards* instance) {
static void nfc_supported_cards_load_context_free(NfcSupportedCardsLoadContext* instance) {
if(instance->app) {
flipper_application_free(instance->app);
}
Expand All @@ -50,7 +101,36 @@ static void nfc_supported_cards_free(NfcSupportedCards* instance) {
}

static const NfcSupportedCardsPlugin*
nfc_supported_cards_get_next_plugin(NfcSupportedCards* instance) {
nfc_supported_cards_get_plugin(NfcSupportedCardsLoadContext* instance, FuriString* path) {
furi_assert(instance);
furi_assert(path);

const NfcSupportedCardsPlugin* plugin = NULL;
do {
if(instance->app) flipper_application_free(instance->app);
instance->app = flipper_application_alloc(instance->storage, firmware_api_interface);
if(flipper_application_preload(instance->app, furi_string_get_cstr(path)) !=
FlipperApplicationPreloadStatusSuccess)
break;
if(!flipper_application_is_plugin(instance->app)) break;
if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess)
break;
const FlipperAppPluginDescriptor* descriptor =
flipper_application_plugin_get_descriptor(instance->app);

if(descriptor == NULL) break;

if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) break;
if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) break;

plugin = descriptor->entry_point;
} while(false);

return plugin;
}

static const NfcSupportedCardsPlugin*
nfc_supported_cards_get_next_plugin(NfcSupportedCardsLoadContext* instance) {
const NfcSupportedCardsPlugin* plugin = NULL;

do {
Expand All @@ -65,83 +145,137 @@ static const NfcSupportedCardsPlugin*

path_concat(NFC_SUPPORTED_CARDS_PLUGINS_PATH, instance->file_name, instance->file_path);

if(instance->app) flipper_application_free(instance->app);
instance->app = flipper_application_alloc(instance->storage, firmware_api_interface);
plugin = nfc_supported_cards_get_plugin(instance, instance->file_path);
} while(plugin == NULL); //-V654

if(flipper_application_preload(instance->app, furi_string_get_cstr(instance->file_path)) !=
FlipperApplicationPreloadStatusSuccess)
continue;
if(!flipper_application_is_plugin(instance->app)) continue;
return plugin;
}

if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess)
continue;
void nfc_supported_cards_load_cache(NfcSupportedCards* instance) {
furi_assert(instance);

const FlipperAppPluginDescriptor* descriptor =
flipper_application_plugin_get_descriptor(instance->app);
do {
if((instance->load_state == NfcSupportedCardsLoadStateSuccess) ||
(instance->load_state == NfcSupportedCardsLoadStateFail))
break;

if(descriptor == NULL) continue;
instance->load_context = nfc_supported_cards_load_context_alloc();

while(true) {
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_next_plugin(instance->load_context);
if(plugin == NULL) break; //-V547

NfcSupportedCardsPluginCache plugin_cache = {}; //-V779
plugin_cache.path = furi_string_alloc_set(instance->load_context->file_path);
plugin_cache.protocol = plugin->protocol;
if(plugin->verify) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasVerify;
}
if(plugin->read) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasRead;
}
if(plugin->parse) {
plugin_cache.feature |= NfcSupportedCardsPluginFeatureHasParse;
}
NfcSupportedCardsPluginCache_push_back(instance->plugins_cache_arr, plugin_cache);
}

if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) continue;
if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) continue;
nfc_supported_cards_load_context_free(instance->load_context);

plugin = descriptor->entry_point;
} while(plugin == NULL); //-V654
size_t plugins_loaded = NfcSupportedCardsPluginCache_size(instance->plugins_cache_arr);
if(plugins_loaded == 0) {
FURI_LOG_D(TAG, "Plugins not found");
instance->load_state = NfcSupportedCardsLoadStateFail;
} else {
FURI_LOG_D(TAG, "Loaded %zu plugins", plugins_loaded);
instance->load_state = NfcSupportedCardsLoadStateSuccess;
}

return plugin;
} while(false);
}

bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc) {
bool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc) {
furi_assert(instance);
furi_assert(device);
furi_assert(nfc);

bool card_read = false;

NfcSupportedCards* supported_cards = nfc_supported_cards_alloc();
NfcProtocol protocol = nfc_device_get_protocol(device);

do {
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_next_plugin(supported_cards);
if(plugin == NULL) break; //-V547

const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779
if(plugin->protocol != protocol) continue;

if(plugin->verify) {
if(!plugin->verify(nfc)) continue;
}

if(plugin->read) {
card_read = plugin->read(nfc, device);
if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break;

instance->load_context = nfc_supported_cards_load_context_alloc();

NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
if(plugin_cache->protocol != protocol) continue;
if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasRead) == 0) continue;

const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path);
if(plugin == NULL) continue;

if(plugin->verify) {
if(!plugin->verify(nfc)) continue;
}

if(plugin->read) {
if(plugin->read(nfc, device)) {
card_read = true;
break;
}
}
}

} while(!card_read);
nfc_supported_cards_load_context_free(instance->load_context);
} while(false);

nfc_supported_cards_free(supported_cards);
return card_read;
}

bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data) {
bool nfc_supported_cards_parse(
NfcSupportedCards* instance,
NfcDevice* device,
FuriString* parsed_data) {
furi_assert(instance);
furi_assert(device);
furi_assert(parsed_data);

bool parsed = false;

NfcSupportedCards* supported_cards = nfc_supported_cards_alloc();
bool card_parsed = false;
NfcProtocol protocol = nfc_device_get_protocol(device);

do {
const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_next_plugin(supported_cards);
if(plugin == NULL) break; //-V547

const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779
if(plugin->protocol != protocol) continue;

if(plugin->parse) {
parsed = plugin->parse(device, parsed_data);
if(instance->load_state != NfcSupportedCardsLoadStateSuccess) break;

instance->load_context = nfc_supported_cards_load_context_alloc();

NfcSupportedCardsPluginCache_it_t iter;
for(NfcSupportedCardsPluginCache_it(iter, instance->plugins_cache_arr);
!NfcSupportedCardsPluginCache_end_p(iter);
NfcSupportedCardsPluginCache_next(iter)) {
NfcSupportedCardsPluginCache* plugin_cache = NfcSupportedCardsPluginCache_ref(iter);
if(plugin_cache->protocol != protocol) continue;
if((plugin_cache->feature & NfcSupportedCardsPluginFeatureHasParse) == 0) continue;

const NfcSupportedCardsPlugin* plugin =
nfc_supported_cards_get_plugin(instance->load_context, plugin_cache->path);
if(plugin == NULL) continue;

if(plugin->parse) {
if(plugin->parse(device, parsed_data)) {
card_parsed = true;
break;
}
}
}

} while(!parsed);
nfc_supported_cards_load_context_free(instance->load_context);
} while(false);

nfc_supported_cards_free(supported_cards);
return parsed;
return card_parsed;
}
37 changes: 35 additions & 2 deletions applications/main/nfc/helpers/nfc_supported_cards.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,49 @@
extern "C" {
#endif

/**
* @brief NfcSupportedCards opaque type definition.
*/
typedef struct NfcSupportedCards NfcSupportedCards;

/**
* @brief Allocate NfcSupportedCards instance.
*
* @return pointer to allocated NfcSupportedCards instance.
*/
NfcSupportedCards* nfc_supported_cards_alloc();

/**
* @brief Delete an NfcSupportedCards instance
*
* @param[in] instance pointer to instance to be deleted.
*/
void nfc_supported_cards_free(NfcSupportedCards* instance);

/**
* @brief Load plugins information to cache.
*
* @note This function must be called before calling read and parse fanctions.
*
* @param[in, out] instance pointer to NfcSupportedCards instance.
*/
void nfc_supported_cards_load_cache(NfcSupportedCards* instance);

/**
* @brief Read the card using a custom procedure.
*
* This function will load all suitable supported card plugins one by one and
* try to execute the custom read procedure specified in each. Upon first success,
* no further attempts will be made and the function will return.
*
* @param[in, out] instance pointer to NfcSupportedCards instance.
* @param[in,out] device pointer to a device instance to hold the read data.
* @param[in,out] nfc pointer to an Nfc instance.
* @returns true if the card was successfully read, false otherwise.
*
* @see NfcSupportedCardPluginRead for detailed description.
*/
bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc);
bool nfc_supported_cards_read(NfcSupportedCards* instance, NfcDevice* device, Nfc* nfc);

/**
* @brief Parse raw data into human-readable representation.
Expand All @@ -37,13 +66,17 @@ bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc);
* try to parse the data according to each implementation. Upon first success,
* no further attempts will be made and the function will return.
*
* @param[in, out] instance pointer to NfcSupportedCards instance.
* @param[in] device pointer to a device instance holding the data is to be parsed.
* @param[out] parsed_data pointer to the string to contain the formatted result.
* @returns true if the card was successfully parsed, false otherwise.
*
* @see NfcSupportedCardPluginParse for detailed description.
*/
bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data);
bool nfc_supported_cards_parse(
NfcSupportedCards* instance,
NfcDevice* device,
FuriString* parsed_data);

#ifdef __cplusplus
}
Expand Down
Loading

0 comments on commit d6680d1

Please sign in to comment.