diff --git a/applications/main/nfc/helpers/nfc_supported_cards.h b/applications/main/nfc/helpers/nfc_supported_cards.h index 2bf57211c78..0c25a5b1180 100644 --- a/applications/main/nfc/helpers/nfc_supported_cards.h +++ b/applications/main/nfc/helpers/nfc_supported_cards.h @@ -1,3 +1,9 @@ +/** + * @file nfc_supported_cards.h + * @brief Supported card plugin loader interface. + * + * @see nfc_supported_card_plugin.h for instructions on adding a new plugin. + */ #pragma once #include @@ -9,8 +15,34 @@ extern "C" { #endif +/** + * @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] 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); +/** + * @brief Parse raw data into human-readable representation. + * + * This function will load all suitable supported card plugins one by one and + * 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] 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); #ifdef __cplusplus diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index 318c6ffcbb4..f26b9e0d8f3 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -5,6 +5,7 @@ #include "nfc/nfc_app_i.h" +#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" static void nfc_scene_info_on_enter_felica(NfcApp* instance) { @@ -67,17 +68,17 @@ static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, uint32_t even } const NfcProtocolSupportBase nfc_protocol_support_felica = { - .features = NfcProtocolFeatureNone, // TODO: Implement better UID editing, + .features = NfcProtocolFeatureNone, // TODO: Implement better UID editing .scene_info = { .on_enter = nfc_scene_info_on_enter_felica, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = { .on_enter = nfc_scene_read_on_enter_felica, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_menu = { @@ -87,7 +88,7 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = { .scene_read_success = { .on_enter = nfc_scene_read_success_on_enter_felica, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_saved_menu = { @@ -96,12 +97,12 @@ const NfcProtocolSupportBase nfc_protocol_support_felica = { }, .scene_save_name = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_emulate = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c index 316ab2ba7b2..c0d502d0380 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c @@ -5,6 +5,7 @@ #include "nfc/nfc_app_i.h" +#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" static void nfc_scene_info_on_enter_iso14443_3a(NfcApp* instance) { @@ -109,12 +110,12 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = { .scene_info = { .on_enter = nfc_scene_info_on_enter_iso14443_3a, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = { .on_enter = nfc_scene_read_on_enter_iso14443_3a, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_menu = { @@ -124,21 +125,21 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = { .scene_read_success = { .on_enter = nfc_scene_read_success_on_enter_iso14443_3a, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_saved_menu = { .on_enter = nfc_protocol_support_common_on_enter_empty, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_emulate = { .on_enter = nfc_scene_emulate_on_enter_iso14443_3a, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c index a42f8da7afa..7306f1072d6 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c @@ -17,8 +17,7 @@ void nfc_render_iso14443_3a_info( nfc_render_iso14443_3a_brief(data, str); - if(format_type == NfcProtocolFormatTypeFull || - format_type == NfcProtocolFormatTypeShortExtra) { + if(format_type == NfcProtocolFormatTypeFull) { nfc_render_iso14443_3a_extra(data, str); } } diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c index bb4d613a97b..fee23184624 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c @@ -5,6 +5,7 @@ #include "nfc/nfc_app_i.h" +#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" static void nfc_scene_info_on_enter_iso14443_3b(NfcApp* instance) { @@ -77,12 +78,12 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { .scene_info = { .on_enter = nfc_scene_info_on_enter_iso14443_3b, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = { .on_enter = nfc_scene_read_on_enter_iso14443_3b, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_menu = { @@ -92,7 +93,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { .scene_read_success = { .on_enter = nfc_scene_read_success_on_enter_iso14443_3b, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_saved_menu = { @@ -101,12 +102,12 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { }, .scene_save_name = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_emulate = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c index 12681445316..0a3a592e172 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c @@ -6,6 +6,7 @@ #include "nfc/nfc_app_i.h" +#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" static void nfc_scene_info_on_enter_iso14443_4a(NfcApp* instance) { @@ -113,12 +114,12 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = { .scene_info = { .on_enter = nfc_scene_info_on_enter_iso14443_4a, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = { .on_enter = nfc_scene_read_on_enter_iso14443_4a, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_menu = { @@ -128,21 +129,21 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = { .scene_read_success = { .on_enter = nfc_scene_read_success_on_enter_iso14443_4a, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_saved_menu = { .on_enter = nfc_scene_saved_menu_on_enter_iso14443_4a, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_save_name = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_emulate = { .on_enter = nfc_scene_emulate_on_enter_iso14443_4a, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c index dad2aeaf50c..a0c70a22e91 100644 --- a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c @@ -5,6 +5,7 @@ #include "nfc/nfc_app_i.h" +#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" #include "../iso14443_3b/iso14443_3b_i.h" @@ -82,12 +83,12 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { .scene_info = { .on_enter = nfc_scene_info_on_enter_iso14443_4b, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = { .on_enter = nfc_scene_read_on_enter_iso14443_4b, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_menu = { @@ -97,7 +98,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { .scene_read_success = { .on_enter = nfc_scene_read_success_on_enter_iso14443_4b, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_saved_menu = { @@ -106,12 +107,12 @@ const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { }, .scene_save_name = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_emulate = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, }; diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c index cc39980a995..7f861a03265 100644 --- a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -6,6 +6,7 @@ #include "nfc/nfc_app_i.h" +#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" static void nfc_scene_info_on_enter_iso15693_3(NfcApp* instance) { @@ -108,12 +109,12 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { .scene_info = { .on_enter = nfc_scene_info_on_enter_iso15693_3, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = { .on_enter = nfc_scene_read_on_enter_iso15693_3, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_menu = { @@ -123,7 +124,7 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { .scene_read_success = { .on_enter = nfc_scene_read_success_on_enter_iso15693_3, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_saved_menu = { @@ -132,12 +133,12 @@ const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { }, .scene_save_name = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_emulate = { .on_enter = nfc_scene_emulate_on_enter_iso15693_3, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, }; diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index ef59d95da85..3e0468cd91e 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -5,6 +5,7 @@ #include "nfc/nfc_app_i.h" +#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" #define TAG "MfClassicApp" @@ -27,17 +28,10 @@ static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { widget_add_text_scroll_element( instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); - widget_add_button_element( - instance->widget, - GuiButtonTypeRight, - "More", - nfc_protocol_support_common_widget_callback, - instance); - furi_string_free(temp_str); } -static void nfc_scene_card_dump_on_enter_mf_classic(NfcApp* instance) { +static void nfc_scene_more_info_on_enter_mf_classic(NfcApp* instance) { const NfcDevice* device = instance->nfc_device; const MfClassicData* mfc_data = nfc_device_get_data(device, NfcProtocolMfClassic); @@ -172,15 +166,6 @@ static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) { nfc_listener_start(instance->listener, NULL, NULL); } -static bool nfc_scene_info_on_event_mf_classic(NfcApp* instance, uint32_t event) { - if(event == GuiButtonTypeRight) { - scene_manager_next_scene(instance->scene_manager, NfcSceneCardDump); - return true; - } - - return false; -} - static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { if(event == SubmenuIndexDetectReader) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); @@ -222,17 +207,17 @@ static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, uint32_t e } const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { - .features = NfcProtocolFeatureEmulateFull, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, .scene_info = { .on_enter = nfc_scene_info_on_enter_mf_classic, - .on_event = nfc_scene_info_on_event_mf_classic, + .on_event = nfc_protocol_support_common_on_event_empty, }, - .scene_card_dump = + .scene_more_info = { - .on_enter = nfc_scene_card_dump_on_enter_mf_classic, - .on_event = NULL, + .on_enter = nfc_scene_more_info_on_enter_mf_classic, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = { @@ -247,7 +232,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { .scene_read_success = { .on_enter = nfc_scene_read_success_on_enter_mf_classic, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_saved_menu = { @@ -256,12 +241,12 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { }, .scene_save_name = { - .on_enter = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, .on_event = nfc_scene_save_name_on_event_mf_classic, }, .scene_emulate = { .on_enter = nfc_scene_emulate_on_enter_mf_classic, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, }; diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c index f829e3758b0..bc05c2a4c36 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c @@ -5,6 +5,7 @@ #include "nfc/nfc_app_i.h" +#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" #include "../iso14443_4a/iso14443_4a_i.h" @@ -20,16 +21,14 @@ static void nfc_scene_info_on_enter_mf_desfire(NfcApp* instance) { widget_add_text_scroll_element( instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); - widget_add_button_element( - instance->widget, - GuiButtonTypeRight, - "More", - nfc_protocol_support_common_widget_callback, - instance); - furi_string_free(temp_str); } +static void nfc_scene_more_info_on_enter_mf_desfire(NfcApp* instance) { + // Jump to advanced scene right away + scene_manager_next_scene(instance->scene_manager, NfcSceneMfDesfireMoreInfo); +} + static NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent event, void* context) { furi_assert(event.protocol == NfcProtocolMfDesfire); @@ -75,27 +74,23 @@ static void nfc_scene_emulate_on_enter_mf_desfire(NfcApp* instance) { instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); } -static bool nfc_scene_info_on_event_mf_desfire(NfcApp* instance, uint32_t event) { - if(event == GuiButtonTypeRight) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfDesfireData); - return true; - } - - return false; -} - const NfcProtocolSupportBase nfc_protocol_support_mf_desfire = { - .features = NfcProtocolFeatureEmulateUid, + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo, .scene_info = { .on_enter = nfc_scene_info_on_enter_mf_desfire, - .on_event = nfc_scene_info_on_event_mf_desfire, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_mf_desfire, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = { .on_enter = nfc_scene_read_on_enter_mf_desfire, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_menu = { @@ -105,7 +100,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_desfire = { .scene_read_success = { .on_enter = nfc_scene_read_success_on_enter_mf_desfire, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_saved_menu = { @@ -114,12 +109,12 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_desfire = { }, .scene_save_name = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_emulate = { .on_enter = nfc_scene_emulate_on_enter_mf_desfire, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, }; diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 0d4915fb7fa..68968cd6f48 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -5,6 +5,7 @@ #include "nfc/nfc_app_i.h" +#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" enum { @@ -25,17 +26,10 @@ static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) { widget_add_text_scroll_element( instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); - widget_add_button_element( - instance->widget, - GuiButtonTypeRight, - "More", - nfc_protocol_support_common_widget_callback, - instance); - furi_string_free(temp_str); } -static void nfc_scene_card_dump_on_enter_mf_ultralight(NfcApp* instance) { +static void nfc_scene_more_info_on_enter_mf_ultralight(NfcApp* instance) { const NfcDevice* device = instance->nfc_device; const MfUltralightData* mfu = nfc_device_get_data(device, NfcProtocolMfUltralight); @@ -147,15 +141,6 @@ static void nfc_scene_emulate_on_enter_mf_ultralight(NfcApp* instance) { nfc_listener_start(instance->listener, NULL, NULL); } -static bool nfc_scene_info_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { - if(event == GuiButtonTypeRight) { - scene_manager_next_scene(instance->scene_manager, NfcSceneCardDump); - return true; - } - - return false; -} - static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { if(event == SubmenuIndexUnlock) { @@ -166,22 +151,22 @@ static bool } const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { - .features = NfcProtocolFeatureEmulateFull, + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, .scene_info = { .on_enter = nfc_scene_info_on_enter_mf_ultralight, - .on_event = nfc_scene_info_on_event_mf_ultralight, + .on_event = nfc_protocol_support_common_on_event_empty, }, - .scene_card_dump = + .scene_more_info = { - .on_enter = nfc_scene_card_dump_on_enter_mf_ultralight, - .on_event = NULL, + .on_enter = nfc_scene_more_info_on_enter_mf_ultralight, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = { .on_enter = nfc_scene_read_on_enter_mf_ultralight, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_menu = { @@ -191,7 +176,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { .scene_read_success = { .on_enter = nfc_scene_read_success_on_enter_mf_ultralight, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_saved_menu = { @@ -200,12 +185,12 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { }, .scene_save_name = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_emulate = { .on_enter = nfc_scene_emulate_on_enter_mf_ultralight, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, }; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c index c93cf0d9f49..eb84aea6d4f 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -1,3 +1,10 @@ +/** + * @file nfc_protocol_support.c + * @brief Common implementation of application-level protocol support. + * + * @see nfc_protocol_support_base.h + * @see nfc_protocol_support_common.h + */ #include "nfc_protocol_support.h" #include "nfc/nfc_app_i.h" @@ -6,14 +13,36 @@ #include "nfc_protocol_support_defs.h" #include "nfc_protocol_support_gui_common.h" +/** + * @brief Common scene entry handler. + * + * @param[in,out] instance pointer to the NFC application instance. + */ typedef void (*NfcProtocolSupportCommonOnEnter)(NfcApp* instance); + +/** + * @brief Common scene custom event handler. + * + * @param[in,out] instance pointer to the NFC application instance. + * @param[in] event custom event to be handled. + * @returns true if the event was handled, false otherwise. + */ typedef bool (*NfcProtocolSupportCommonOnEvent)(NfcApp* instance, SceneManagerEvent event); + +/** + * @brief Common scene exit handler. + * + * @param[in,out] instance pointer to the NFC application instance. + */ typedef void (*NfcProtocolSupportCommonOnExit)(NfcApp* instance); +/** + * @brief Structure containing common scene handler pointers. + */ typedef struct { - NfcProtocolSupportCommonOnEnter on_enter; - NfcProtocolSupportCommonOnEvent on_event; - NfcProtocolSupportCommonOnExit on_exit; + NfcProtocolSupportCommonOnEnter on_enter; /**< Pointer to the on_enter() function. */ + NfcProtocolSupportCommonOnEvent on_event; /**< Pointer to the on_event() function. */ + NfcProtocolSupportCommonOnExit on_exit; /**< Pointer to the on_exit() function. */ } NfcProtocolSupportCommonSceneBase; static const NfcProtocolSupportCommonSceneBase nfc_protocol_support_scenes[]; @@ -55,6 +84,16 @@ static bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFe static void nfc_protocol_support_scene_info_on_enter(NfcApp* instance) { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); nfc_protocol_support[protocol]->scene_info.on_enter(instance); + + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureMoreInfo)) { + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "More", + nfc_protocol_support_common_widget_callback, + instance); + } + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); } @@ -62,9 +101,9 @@ static bool nfc_protocol_support_scene_info_on_event(NfcApp* instance, SceneMana bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - if(nfc_protocol_support[protocol]->scene_info.on_event) { - consumed = nfc_protocol_support[protocol]->scene_info.on_event(instance, event.event); + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo); + consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { // If the card could not be parsed, return to the respective menu @@ -83,20 +122,25 @@ static void nfc_protocol_support_scene_info_on_exit(NfcApp* instance) { widget_reset(instance->widget); } -static void nfc_protocol_support_scene_card_dump_on_enter(NfcApp* instance) { +// SceneMoreInfo +static void nfc_protocol_support_scene_more_info_on_enter(NfcApp* instance) { const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); - nfc_protocol_support[protocol]->scene_card_dump.on_enter(instance); - view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); + nfc_protocol_support[protocol]->scene_more_info.on_enter(instance); } static bool - nfc_protocol_support_scene_card_dump_on_event(NfcApp* instance, SceneManagerEvent event) { - UNUSED(instance); - UNUSED(event); - return false; + nfc_protocol_support_scene_more_info_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event.event); + } + + return consumed; } -static void nfc_protocol_support_scene_card_dump_on_exit(NfcApp* instance) { +static void nfc_protocol_support_scene_more_info_on_exit(NfcApp* instance) { text_box_reset(instance->text_box); furi_string_reset(instance->text_box_store); } @@ -143,10 +187,8 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana } else { const NfcProtocol protocol = instance->protocols_detected[instance->protocols_detected_selected_idx]; - if(nfc_protocol_support[protocol]->scene_read.on_event) { - consumed = - nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event); - } + consumed = + nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event); } } else if(event.event == NfcCustomEventPollerFailure) { nfc_poller_stop(instance->poller); @@ -470,19 +512,14 @@ static bool if(nfc_save(instance)) { scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess); - if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType)) { - dolphin_deed(DolphinDeedNfcAddSave); - } else { - dolphin_deed(DolphinDeedNfcSave); - } + dolphin_deed( + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType) ? + DolphinDeedNfcAddSave : + DolphinDeedNfcSave); const NfcProtocol protocol = instance->protocols_detected[instance->protocols_detected_selected_idx]; - if(nfc_protocol_support[protocol]->scene_save_name.on_event) { - consumed = nfc_protocol_support[protocol]->scene_save_name.on_event( - instance, event.event); - } else { - consumed = true; - } + consumed = nfc_protocol_support[protocol]->scene_save_name.on_event( + instance, event.event); } else { consumed = scene_manager_search_and_switch_to_previous_scene( instance->scene_manager, NfcSceneStart); @@ -502,9 +539,18 @@ static void nfc_protocol_support_scene_save_name_on_exit(NfcApp* instance) { } // SceneEmulate +/** + * @brief Current view displayed on the emulation scene. + * + * The emulation scehe has two states: the default one showing information about + * the card being emulated, and the logs which show the raw data received from the reader. + * + * The user has the ability to switch betweeen these two scenes, however the prompt to switch is + * only shown after some information had appered in the log view. + */ enum { - NfcSceneEmulateStateWidget, - NfcSceneEmulateStateTextBox, + NfcSceneEmulateStateWidget, /**< Widget view is displayed. */ + NfcSceneEmulateStateTextBox, /**< TextBox view is displayed. */ }; static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { @@ -689,11 +735,11 @@ static const NfcProtocolSupportCommonSceneBase .on_event = nfc_protocol_support_scene_info_on_event, .on_exit = nfc_protocol_support_scene_info_on_exit, }, - [NfcProtocolSupportSceneCardDump] = + [NfcProtocolSupportSceneMoreInfo] = { - .on_enter = nfc_protocol_support_scene_card_dump_on_enter, - .on_event = nfc_protocol_support_scene_card_dump_on_event, - .on_exit = nfc_protocol_support_scene_card_dump_on_exit, + .on_enter = nfc_protocol_support_scene_more_info_on_enter, + .on_event = nfc_protocol_support_scene_more_info_on_event, + .on_exit = nfc_protocol_support_scene_more_info_on_exit, }, [NfcProtocolSupportSceneRead] = { diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h index c5b295d5759..b6bfde45c04 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h @@ -1,12 +1,113 @@ +/** + * @file nfc_protocol_support.h + * @brief Interface for application-level protocol support. + * + * NFC protocol support helper abstracts common scenes with a single interface + * and lets each protocol decide on concrete implementation. + * + * # Integrating a new protocol into the application + * + * Most of the scenes in the NFC application work through abstract APIs, so they do not need + * protocol-specific versions of themselves. However, when such a situation + * occurs, the protocol support helper provides another level of abstraction to hide + * the protocol-specific details and isolate them to separate modules. + * + * @see nfc_protocol.h for more information on adding library protocols. + * + * The steps for adding support for a library protocol are described below. + * + * ## 1. Create the files + * + * ### 1.1 Recommended file structure + * + * The recommended file structure for a protocol support is as follows: + * + * ```text + * protocol_support + * | + * +- protocol_name + * | + * +- protocol_name.h + * | + * +- protocol_name.c + * | + * +- protocol_name_render.h + * | + * +- protocol_name_render.c + * | + * ``` + * ### 1.2 File structure explanation + * + * | Filename | Explanation | + * |:-----------------------|:------------| + * | protocol_name.h | Interface structure declaration used in `nfc_protocol_support_defs.c`. | + * | protocol_name.c | Protocol-specific scene implemenatations and definitions. | + * | protocol_name_render.h | Protocol-specific rendering (formatting) functions. Used for converting protocol data into textual descriptions. | + * | protocol_name_render.c | Implementations for functions declared in `protocol_name_render.h`.| + * + * ## 2. Implement the code + * + * ### 2.1 Features + * + * Decide what features the protocol will be providing. The features can be combined using bitwise OR (`"|"`). + * This choice influences which scenes will have to be implemented in step 2.2. + * + * @see NfcProtocolFeature for the enumeration of possible features to implement. + * + * ### 2.2 Scenes + * + * If a particular scene is not implemented, its empty placeholder from nfc_protocol_support_gui_common.h must be used instead. + * + * @see nfc_protocol_support_common.h for the enumeration of all scenes that can be implemented. + * @see nfc_protocol_support_base.h for the scene implementation details. + * + * ### 2.3. Registering the protocol support + * + * After completing the protocol support, it must be registered within the application in order for it to be usable. + * + * In nfc_protocol_support_defs.c, include the `protocol_name.h` file and add a new entry in the `nfc_protocol_support[]` + * array under the appropriate index. + * + * ## Done! + * + * @note It will not always be possible to abstract all of the protocol's functionality using the protocol support helper. + * In such cases, creating separate protocol-specific scenes is okay (as an example, note the `nfc/scenes/nfc_scene_mf_classic_*` scenes which didn't fit this paradigm). + */ #pragma once +#include + #include "nfc_protocol_support_common.h" +/** + * @brief Abstract interface for on_enter() scene handler. + * + * Is to be called whenever a scene is entered to. + * + * @param[in] scene identifier of the scene associated with the handler. + * @param[in,out] context pointer to a user-specified context (will be passed to concrete handler). + */ void nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context); +/** + * @brief Abstract interface for on_event() scene handler. + * + * @param[in] scene identifier of the scene associated with the handler. + * @param[in,out] context pointer to a user-specified context (will be passed to concrete handler). + * @param[in] event SceneManager event to be handled by the scene. + * @returns true if the event was consumed, false otherwise. + */ bool nfc_protocol_support_on_event( NfcProtocolSupportScene scene, void* context, SceneManagerEvent event); +/** + * @brief Abstract interface for on_exit() scene handler. + * + * Is to be called whenever a scene is exited from. + * + * @param[in] scene identifier of the scene associated with the handler. + * @param[in,out] context pointer to a user-specified context (will be passed to concrete handler). + */ void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h index 37ee051b1c2..69a6d34d291 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h @@ -1,30 +1,116 @@ +/** + * @file nfc_protocol_support_base.h + * @brief Abstract interface for application-level protocol support. + */ #pragma once #include -#include "nfc_protocol_support_common.h" +#include "../../nfc_app.h" +/** + * @brief Scene entry handler. + * + * @param[in,out] instance pointer to the NFC application instance. + */ typedef void (*NfcProtocolSupportOnEnter)(NfcApp* instance); +/** + * @brief Scene event handler. + * + * @param[in,out] instance pointer to the NFC application instance. + * @param[in] event custom event that has occurred. + * @returns true if the event was handled, false otherwise. + */ typedef bool (*NfcProtocolSupportOnEvent)(NfcApp* instance, uint32_t event); -typedef void (*NfcProtocolSupportOnExit)(NfcApp* instance); - +/** + * @brief Abstract scene interface. + * + * on_exit() handler is not implemented due to being redundant. + */ typedef struct { - NfcProtocolSupportOnEnter on_enter; - NfcProtocolSupportOnEvent on_event; - /*NfcProtocolSupportOnExit on_exit; is not necessary */ + NfcProtocolSupportOnEnter on_enter; /**< Pointer to the on_enter() function. */ + NfcProtocolSupportOnEvent on_event; /**< Pointer to the on_event() function. */ } NfcProtocolSupportSceneBase; +/** + * @brief Abstract protocol support interface. + */ typedef struct { - const uint32_t features; + const uint32_t features; /**< Feature bitmask supported by the protocol. */ + /** + * @brief Handlers for protocol-specific info scene. + * + * This scene displays general information about a saved or recently read card. + * It may include a button that will lead to more information being shown. + */ NfcProtocolSupportSceneBase scene_info; - NfcProtocolSupportSceneBase scene_card_dump; + + /** + * @brief Handlers for protocol-specific extended info scene. + * + * This scene shows more information about a saved or + * recently read card, such as memory dumps. + * + * It may include (a) button(s) and/or menu(s) that will lead to + * protocol-specific scenes not covered in this helper. + */ + NfcProtocolSupportSceneBase scene_more_info; + + /** + * @brief Handlers for protocol-specific read scene. + * + * This scene is activated when a read operation is in progress. + * It is responsible for creating a poller and for handling its events. + */ NfcProtocolSupportSceneBase scene_read; + + /** + * @brief Handlers for protocol-specific read menu scene. + * + * This scene presents the user with options available for the + * recenly read card. Such options may include: + * * Saving + * * Getting information + * * Emulating etc. + */ NfcProtocolSupportSceneBase scene_read_menu; + + /** + * @brief Handlers for protocol-specific read success scene. + * + * This scene is activated after a successful read operation. + * It is responsible for displaying a very short summary about + * the card that was just read. + */ NfcProtocolSupportSceneBase scene_read_success; + + /** + * @brief Handlers for protocol-specific saved file menu scene. + * + * This scene presents the user with options available for a + * card loaded from file. Such options may include: + * * Renaming + * * Deleting + * * Getting information + * * Emulating etc. + */ NfcProtocolSupportSceneBase scene_saved_menu; + + /** + * @brief Handlers for protocol-specific name entry scene. + * + * This scene is used to enter a file name when saving or renaming a file. + */ NfcProtocolSupportSceneBase scene_save_name; + + /** + * @brief Handlers for protocol-specific emulate scene. + * + * This scene is activated when an emulation operation is in progress. + * It is responsible for creating a listener and for handling its events. + */ NfcProtocolSupportSceneBase scene_emulate; } NfcProtocolSupportBase; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h index d4176032faf..6e3214106ad 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h @@ -1,31 +1,36 @@ +/** + * @file nfc_protocol_support_common.h + * @brief Common application-level protocol support definitions. + */ #pragma once -#include -#include - -#include "nfc_protocol_support_render_common.h" - -#include "../nfc_custom_event.h" -#include "../../scenes/nfc_scene.h" -#include "../../nfc_app.h" - +/** + * @brief Enumeration of protocol features. + */ typedef enum { - NfcProtocolFeatureNone = 0, - NfcProtocolFeatureEmulateUid = 1UL << 0, - NfcProtocolFeatureEmulateFull = 1UL << 1, - NfcProtocolFeatureEditUid = 1UL << 2, + NfcProtocolFeatureNone = 0, /**< No features are supported. */ + NfcProtocolFeatureEmulateUid = 1UL << 0, /**< Partial emulation is supported. */ + NfcProtocolFeatureEmulateFull = 1UL << 1, /**< Complete emulation is supported. */ + NfcProtocolFeatureEditUid = 1UL << 2, /**< UID editing is supported. */ + NfcProtocolFeatureMoreInfo = 1UL << 3, /**< More information is provided. */ } NfcProtocolFeature; +/** + * @brief Enumeration of protocol-aware scenes. + * + * These are the scenes that are common to all protocols, but require + * a protocol-specific implementation. + */ typedef enum { - NfcProtocolSupportSceneInfo = 0, - NfcProtocolSupportSceneRead, - NfcProtocolSupportSceneReadMenu, - NfcProtocolSupportSceneReadSuccess, - NfcProtocolSupportSceneSavedMenu, - NfcProtocolSupportSceneSaveName, - NfcProtocolSupportSceneEmulate, - NfcProtocolSupportSceneCardDump, - NfcProtocolSupportSceneRpc, + NfcProtocolSupportSceneInfo, /**< Display general card information. */ + NfcProtocolSupportSceneMoreInfo, /**< Display more card information. */ + NfcProtocolSupportSceneRead, /**< Shown when reading a card. */ + NfcProtocolSupportSceneReadMenu, /**< Menu with options available for the recently read card. */ + NfcProtocolSupportSceneReadSuccess, /**< Shown after having successfully read a card. */ + NfcProtocolSupportSceneSavedMenu, /**< Menu for the card that was loaded from file. */ + NfcProtocolSupportSceneSaveName, /**< Shown when saving or renaming a file. */ + NfcProtocolSupportSceneEmulate, /**< Shown when emulating a card. */ + NfcProtocolSupportSceneRpc, /**< Shown in remote-controlled (RPC) mode. */ - NfcProtocolSupportSceneCount, + NfcProtocolSupportSceneCount, /**< Special value equal to total scene count. Internal use. */ } NfcProtocolSupportScene; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c index 8d4a715cab7..215ffc4553a 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c @@ -1,4 +1,13 @@ -#include "nfc_protocol_support_defs.h" +/** + * @file nfc_protocol_support_defs.c + * @brief Application-level protocol support definitions. + * + * This file is to be modified whenever support for + * a new protocol is to be added. + */ +#include "nfc_protocol_support_base.h" + +#include #include "iso14443_3a/iso14443_3a.h" #include "iso14443_3b/iso14443_3b.h" @@ -12,6 +21,14 @@ #include "slix/slix.h" #include "st25tb/st25tb.h" +/** + * @brief Array of pointers to concrete protocol support implementations. + * + * When adding support for a new protocol, add it to the end of this array + * under its respective index. + * + * @see nfc_protocol.h + */ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = &nfc_protocol_support_iso14443_3a, [NfcProtocolIso14443_3b] = &nfc_protocol_support_iso14443_3b, @@ -24,5 +41,5 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, [NfcProtocolSlix] = &nfc_protocol_support_slix, [NfcProtocolSt25tb] = &nfc_protocol_support_st25tb, - /* Add new protocols here */ + /* Add new protocol support implementations here */ }; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h index 8a15a56ec57..7a9d5b6374d 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h @@ -1,5 +1,12 @@ +/** + * @file nfc_protocol_support_defs.h + * @brief Application-level protocol support declarations. + */ #pragma once #include "nfc_protocol_support_base.h" +/** + * @brief Declaraion of array of pointers to protocol support implementations. + */ extern const NfcProtocolSupportBase* nfc_protocol_support[]; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c index 13c8c7dfef6..f3a85512555 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c @@ -38,5 +38,5 @@ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) { bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event) { UNUSED(instance); UNUSED(event); - return false; + return true; } diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h index 2c7e7e74fe5..40ba40c8ec1 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h @@ -1,31 +1,85 @@ +/** + * @file nfc_protocol_support_gui_common.h + * @brief Common GUI functions and definitions. + */ #pragma once #include #include "nfc/nfc_app.h" +/** + * @brief Common submenu indices. + */ enum { - SubmenuIndexCommonSave, - SubmenuIndexCommonEmulate, - SubmenuIndexCommonEdit, - SubmenuIndexCommonInfo, - SubmenuIndexCommonRename, - SubmenuIndexCommonDelete, - SubmenuIndexCommonRestore, - SubmenuIndexCommonMax, + SubmenuIndexCommonSave, /**< Save menu option. */ + SubmenuIndexCommonEmulate, /**< Emulate menu option. */ + SubmenuIndexCommonEdit, /**< Edit menu option. */ + SubmenuIndexCommonInfo, /**< Info menu option. */ + SubmenuIndexCommonRename, /**< Rename menu option. */ + SubmenuIndexCommonDelete, /**< Delete menu option. */ + SubmenuIndexCommonRestore, /**< Restore menu option. */ + SubmenuIndexCommonMax, /**< Special value, internal use. */ }; +/** + * @brief Common submenu callback. + * + * Called each time the user presses on a selected submenu item. + * + * @param[in,out] context pointer to a user-defined context object. + * @param[in] index index of the item that was activated. + */ void nfc_protocol_support_common_submenu_callback(void* context, uint32_t index); +/** + * @brief Common widget callback. + * + * Called each time the user presses on a selected widget element. + * + * @param[in] result identifier of the activated button. + * @param[in] type type of press action. + * @param[in,out] context pointer to a user-defined context object. + */ void nfc_protocol_support_common_widget_callback( GuiButtonType result, InputType type, void* context); +/** + * @brief Common byte input callback. + * + * Called each time the user accepts the byte input. + * + * @param[in,out] context pointer to a user-defined context object. + */ void nfc_protocol_support_common_byte_input_done_callback(void* context); +/** + * @brief Common text input callback. + * + * Called each time the user accepts the text input. + * + * @param[in,out] context pointer to a user-defined context object. + */ void nfc_protocol_support_common_text_input_done_callback(void* context); +/** + * @brief Empty on_enter() handler. + * + * Does nothing. + * + * @param[in] instance pointer to the NFC application instance. + */ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance); +/** + * @brief Empty on_event() handler. + * + * Does nothing and returns true. + * + * @param[in] instance pointer to the NFC application instance. + * @param[in] event custom event type that has occurred. + * @returns always true. + */ bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_render_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_render_common.h index c2bc089f375..a2e82ea1535 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_render_common.h +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_render_common.h @@ -1,7 +1,13 @@ +/** + * @file nfc_protocol_support_render_common.h + * @brief Common formatting-related defines. + */ #pragma once +/** + * @brief Displayed information verbosity level. + */ typedef enum { - NfcProtocolFormatTypeShort, - NfcProtocolFormatTypeShortExtra, - NfcProtocolFormatTypeFull, + NfcProtocolFormatTypeShort, /**< Short format, terse info. */ + NfcProtocolFormatTypeFull, /**< Full format, verbose info. */ } NfcProtocolFormatType; diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c index bf1ff073e72..ad858a75fce 100644 --- a/applications/main/nfc/helpers/protocol_support/slix/slix.c +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -6,6 +6,7 @@ #include "nfc/nfc_app_i.h" +#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" static void nfc_scene_info_on_enter_slix(NfcApp* instance) { @@ -105,12 +106,12 @@ const NfcProtocolSupportBase nfc_protocol_support_slix = { .scene_info = { .on_enter = nfc_scene_info_on_enter_slix, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read = { .on_enter = nfc_scene_read_on_enter_slix, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_menu = { @@ -120,7 +121,7 @@ const NfcProtocolSupportBase nfc_protocol_support_slix = { .scene_read_success = { .on_enter = nfc_scene_read_success_on_enter_slix, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_saved_menu = { @@ -129,12 +130,12 @@ const NfcProtocolSupportBase nfc_protocol_support_slix = { }, .scene_save_name = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_emulate = { .on_enter = nfc_scene_emulate_on_enter_slix, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, }; diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c index 036e1f21c5b..82403c6ea8f 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c @@ -1,9 +1,11 @@ -#include +#include "st25tb.h" +#include "st25tb_render.h" + #include #include "nfc/nfc_app_i.h" -#include "st25tb_render.h" -#include "../nfc_protocol_support_base.h" + +#include "../nfc_protocol_support_common.h" #include "../nfc_protocol_support_gui_common.h" static void nfc_scene_info_on_enter_st25tb(NfcApp* instance) { @@ -75,7 +77,7 @@ static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, uint32_t even } const NfcProtocolSupportBase nfc_protocol_support_st25tb = { - .features = NfcProtocolFeatureNone, // TODO: Implement better UID editing, + .features = NfcProtocolFeatureNone, .scene_info = { @@ -85,7 +87,7 @@ const NfcProtocolSupportBase nfc_protocol_support_st25tb = { .scene_read = { .on_enter = nfc_scene_read_on_enter_st25tb, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_read_menu = { @@ -95,7 +97,7 @@ const NfcProtocolSupportBase nfc_protocol_support_st25tb = { .scene_read_success = { .on_enter = nfc_scene_read_success_on_enter_st25tb, - .on_event = NULL, + .on_event = nfc_protocol_support_common_on_event_empty, }, .scene_saved_menu = { @@ -104,7 +106,7 @@ const NfcProtocolSupportBase nfc_protocol_support_st25tb = { }, .scene_emulate = { - .on_enter = NULL, - .on_event = NULL, + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, }, }; diff --git a/applications/main/nfc/nfc_app.h b/applications/main/nfc/nfc_app.h index 0e456fd48b2..c3026d30287 100644 --- a/applications/main/nfc/nfc_app.h +++ b/applications/main/nfc/nfc_app.h @@ -1,3 +1,19 @@ +/** + * @file nfc_app.h + * @brief NFC application -- start here. + * + * Application for interfacing with NFC cards and other devices via Flipper's built-in NFC hardware. + * + * Main features: + * * Multiple protocols support + * * Card emulation + * * Shadow file support + * * Dynamically loaded parser plugins + * + * @see nfc_protocol.h for information on adding a new library protocol. + * @see nfc_protocol_support.h for information on integrating a library protocol into the app. + * @see nfc_supported_card_plugin.h for information on adding supported card plugins (parsers). + */ #pragma once typedef struct NfcApp NfcApp; diff --git a/applications/main/nfc/plugins/supported_cards/nfc_supported_card_plugin.h b/applications/main/nfc/plugins/supported_cards/nfc_supported_card_plugin.h index f87e2108318..7883aeb6b07 100644 --- a/applications/main/nfc/plugins/supported_cards/nfc_supported_card_plugin.h +++ b/applications/main/nfc/plugins/supported_cards/nfc_supported_card_plugin.h @@ -1,3 +1,26 @@ +/** + * @file nfc_supported_card_plugin.h + * @brief Supported card plugin abstract interface. + * + * Supported card plugins are dynamically loaded libraries that help making sense of + * a particular card's raw data, if a suitable plugin exists. + * + * For example, if some card serves as a bus ticket, instead of just displaying a data dump, + * a suitable plugin will transform that data into a human-readable format, showing the number + * of rides or balance left. + * Because of the highly specialised nature of application-specific cards, a separate plugin + * for each such card type must be implemented. + * + * To add a new plugin, create a uniquely-named .c file in the `supported_cards` directory + * and implement at least the parse() function in the NfcSupportedCardsPlugin structure. + * Then, register the plugin in the `application.fam` file in the `nfc` directory. Use the existing + * entries as an example. After being registered, the plugin will be automatically deployed with the application. + * + * @note the APPID field MUST end with `_parser` so the applicaton would know that this particular file + * is a supported card plugin. + * + * @see nfc_supported_cards.h + */ #pragma once #include @@ -5,18 +28,67 @@ #include #include +/** + * @brief Unique string identifier for supported card plugins. + */ #define NFC_SUPPORTED_CARD_PLUGIN_APP_ID "NfcSupportedCardPlugin" + +/** + * @brief Currently supported plugin API version. + */ #define NFC_SUPPORTED_CARD_PLUGIN_API_VERSION 1 +/** + * @brief Verify that the card is of a supported type. + * + * This function should be implemented if a quick check exists + * allowing to verify that the plugin is working with the appropriate card type. + * Such checks may include, but are not limited to: reading a specific sector, + * performing a certain read operation, etc. + * + * @param[in,out] nfc pointer to an Nfc instance. + * @returns true if the card was successfully verified, false otherwise. + */ typedef bool (*NfcSupportedCardPluginVerify)(Nfc* nfc); +/** + * @brief Read the card using a custom procedure. + * + * This function should be implemented if a card requires some special reading + * procedure not covered in the vanilla poller. Examples include, but are not + * limited to: reading with particular security keys, mandatory order of read + * operations, etc. + * + * @param[in,out] nfc pointer to an Nfc instance. + * @param[in,out] device pointer to a device instance to hold the read data. + * @returns true if the card was successfully read, false otherwise. + */ typedef bool (*NfcSupportedCardPluginRead)(Nfc* nfc, NfcDevice* device); +/** + * @brief Parse raw data into human-readable representation. + * + * A supported card plugin may contain only this function, if no special verification + * or reading procedures are not required. In any case, the data must be complete and + * available through the `device` parameter at the time of calling. + * + * The output format is free and application-dependent. Multiple lines should + * be separated by newline character. + * + * @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. + */ typedef bool (*NfcSupportedCardPluginParse)(const NfcDevice* device, FuriString* parsed_data); +/** + * @brief Supported card plugin interface. + * + * For a minimally functional plugin, only the parse() function must be implemented. + */ typedef struct { - NfcProtocol protocol; - NfcSupportedCardPluginVerify verify; - NfcSupportedCardPluginRead read; - NfcSupportedCardPluginParse parse; + NfcProtocol protocol; /**< Identifier of the protocol this card type works on top of. */ + NfcSupportedCardPluginVerify verify; /**< Pointer to the verify() function. */ + NfcSupportedCardPluginRead read; /**< Pointer to the read() function. */ + NfcSupportedCardPluginParse parse; /**< Pointer to the parse() function. */ } NfcSupportedCardsPlugin; diff --git a/applications/main/nfc/scenes/nfc_scene_card_dump.c b/applications/main/nfc/scenes/nfc_scene_card_dump.c deleted file mode 100644 index 490bb5b4f8e..00000000000 --- a/applications/main/nfc/scenes/nfc_scene_card_dump.c +++ /dev/null @@ -1,16 +0,0 @@ -#include - -#include "../nfc_app_i.h" -#include "../helpers/protocol_support/nfc_protocol_support.h" - -void nfc_scene_card_dump_on_enter(void* context) { - nfc_protocol_support_on_enter(NfcProtocolSupportSceneCardDump, context); -} - -bool nfc_scene_card_dump_on_event(void* context, SceneManagerEvent event) { - return nfc_protocol_support_on_event(NfcProtocolSupportSceneCardDump, context, event); -} - -void nfc_scene_card_dump_on_exit(void* context) { - nfc_protocol_support_on_exit(NfcProtocolSupportSceneCardDump, context); -} \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 2069733520c..0305213fec8 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -11,6 +11,7 @@ ADD_SCENE(nfc, restore_original, RestoreOriginal) ADD_SCENE(nfc, detect, Detect) ADD_SCENE(nfc, read, Read) ADD_SCENE(nfc, info, Info) +ADD_SCENE(nfc, more_info, MoreInfo) ADD_SCENE(nfc, supported_card, SupportedCard) ADD_SCENE(nfc, select_protocol, SelectProtocol) ADD_SCENE(nfc, extra_actions, ExtraActions) @@ -22,14 +23,13 @@ ADD_SCENE(nfc, debug, Debug) ADD_SCENE(nfc, field, Field) ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, exit_confirm, ExitConfirm) -ADD_SCENE(nfc, card_dump, CardDump) ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass) -ADD_SCENE(nfc, mf_desfire_data, MfDesfireData) +ADD_SCENE(nfc, mf_desfire_more_info, MfDesfireMoreInfo) ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_data.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_more_info.c similarity index 65% rename from applications/main/nfc/scenes/nfc_scene_mf_desfire_data.c rename to applications/main/nfc/scenes/nfc_scene_mf_desfire_more_info.c index f26aa06a1bc..76834e3f4f7 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_data.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_more_info.c @@ -1,29 +1,24 @@ #include "../nfc_app_i.h" +#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h" #include "../helpers/protocol_support/mf_desfire/mf_desfire_render.h" enum { - MifareDesfireDataStateMenu, - MifareDesfireDataStateItem, // MUST be last, states >= this correspond with submenu index + MifareDesfireMoreInfoStateMenu, + MifareDesfireMoreInfoStateItem, // MUST be last, states >= this correspond with submenu index }; enum SubmenuIndex { SubmenuIndexCardInfo, - SubmenuIndexDynamic, // dynamic indexes start here + SubmenuIndexDynamic, // dynamic indices start here }; -static void nfc_scene_mf_desfire_data_submenu_callback(void* context, uint32_t index) { - NfcApp* nfc = (NfcApp*)context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_mf_desfire_data_on_enter(void* context) { +void nfc_scene_mf_desfire_more_info_on_enter(void* context) { NfcApp* nfc = context; Submenu* submenu = nfc->submenu; const uint32_t state = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireMoreInfo); const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire); text_box_set_font(nfc->text_box, TextBoxFontHex); @@ -32,7 +27,7 @@ void nfc_scene_mf_desfire_data_on_enter(void* context) { submenu, "Card info", SubmenuIndexCardInfo, - nfc_scene_mf_desfire_data_submenu_callback, + nfc_protocol_support_common_submenu_callback, nfc); FuriString* label = furi_string_alloc(); @@ -45,28 +40,28 @@ void nfc_scene_mf_desfire_data_on_enter(void* context) { submenu, furi_string_get_cstr(label), i + SubmenuIndexDynamic, - nfc_scene_mf_desfire_data_submenu_callback, + nfc_protocol_support_common_submenu_callback, nfc); } furi_string_free(label); - if(state >= MifareDesfireDataStateItem) { + if(state >= MifareDesfireMoreInfoStateItem) { submenu_set_selected_item( - nfc->submenu, state - MifareDesfireDataStateItem + SubmenuIndexDynamic); + nfc->submenu, state - MifareDesfireMoreInfoStateItem + SubmenuIndexDynamic); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateMenu); + nfc->scene_manager, NfcSceneMfDesfireMoreInfo, MifareDesfireMoreInfoStateMenu); } view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } -bool nfc_scene_mf_desfire_data_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_mf_desfire_more_info_on_event(void* context, SceneManagerEvent event) { NfcApp* nfc = context; bool consumed = false; const uint32_t state = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireMoreInfo); const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire); if(event.type == SceneManagerEventTypeCustom) { @@ -79,30 +74,35 @@ bool nfc_scene_mf_desfire_data_on_event(void* context, SceneManagerEvent event) view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); scene_manager_set_scene_state( nfc->scene_manager, - NfcSceneMfDesfireData, - MifareDesfireDataStateItem + SubmenuIndexCardInfo); + NfcSceneMfDesfireMoreInfo, + MifareDesfireMoreInfoStateItem + SubmenuIndexCardInfo); consumed = true; } else { const uint32_t index = event.event - SubmenuIndexDynamic; scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateItem + index); + nfc->scene_manager, + NfcSceneMfDesfireMoreInfo, + MifareDesfireMoreInfoStateItem + index); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, index << 1); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp); consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { - if(state >= MifareDesfireDataStateItem) { + if(state >= MifareDesfireMoreInfoStateItem) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateMenu); - consumed = true; + nfc->scene_manager, NfcSceneMfDesfireMoreInfo, MifareDesfireMoreInfoStateMenu); + } else { + // Return directly to the Info scene + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneInfo); } + consumed = true; } return consumed; } -void nfc_scene_mf_desfire_data_on_exit(void* context) { +void nfc_scene_mf_desfire_more_info_on_exit(void* context) { NfcApp* nfc = context; // Clear views diff --git a/applications/main/nfc/scenes/nfc_scene_more_info.c b/applications/main/nfc/scenes/nfc_scene_more_info.c new file mode 100644 index 00000000000..b74e30295d3 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_more_info.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_more_info_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneMoreInfo, context); +} + +bool nfc_scene_more_info_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneMoreInfo, context, event); +} + +void nfc_scene_more_info_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneMoreInfo, context); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 3d7fd38549e..4fb5f0eab19 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -1,4 +1,5 @@ #include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" #include @@ -12,6 +13,7 @@ const FuriHalNfcTechBase* furi_hal_nfc_tech[FuriHalNfcTechNum] = { [FuriHalNfcTechIso14443b] = &furi_hal_nfc_iso14443b, [FuriHalNfcTechIso15693] = &furi_hal_nfc_iso15693, [FuriHalNfcTechFelica] = &furi_hal_nfc_felica, + // Add new technologies here }; FuriHalNfc furi_hal_nfc; diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc_felica.c b/firmware/targets/f7/furi_hal/furi_hal_nfc_felica.c index 27758ad489a..e4b8ac0ee6b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc_felica.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc_felica.c @@ -1,4 +1,5 @@ #include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" static FuriHalNfcError furi_hal_nfc_felica_poller_init(FuriHalSpiBusHandle* handle) { // Enable Felica mode, AM modulation diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc_i.h b/firmware/targets/f7/furi_hal/furi_hal_nfc_i.h index 052bc7265cf..53a25644d0d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc_i.h +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc_i.h @@ -1,3 +1,10 @@ +/** + * @file furi_hal_nfc_i.h + * @brief NFC HAL library (private definitions). + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + */ #pragma once #include @@ -11,125 +18,169 @@ extern "C" { #endif +/** @brief Common frame delay time compensation for pollers. */ #define FURI_HAL_NFC_POLLER_FDT_COMP_FC (-500) +/** @brief Common frame wait time compensation for pollers. */ #define FURI_HAL_NFC_POLLER_FWT_COMP_FC (FURI_HAL_NFC_POLLER_FDT_COMP_FC) +/** + * @brief Enumeration containing bitmask values for NFC HAL internal events. + */ typedef enum { - FuriHalNfcEventInternalTypeAbort = (1U << 0), - FuriHalNfcEventInternalTypeIrq = (1U << 1), - FuriHalNfcEventInternalTypeTimerFwtExpired = (1U << 2), - FuriHalNfcEventInternalTypeTimerBlockTxExpired = (1U << 3), - FuriHalNfcEventInternalTypeTransparentDataReceived = (1U << 4), + FuriHalNfcEventInternalTypeAbort = (1U << 0), /**< Abort waiting for hardware events. */ + FuriHalNfcEventInternalTypeIrq = (1U << 1), /**< NFC hardware interrupt has occurred. */ + FuriHalNfcEventInternalTypeTimerFwtExpired = + (1U << 2), /**< Frame wait time timeout has expired. */ + FuriHalNfcEventInternalTypeTimerBlockTxExpired = + (1U << 3), /**< Transmission block timeout has expired. */ + FuriHalNfcEventInternalTypeTransparentDataReceived = + (1U << 4), /**< Data was received in transparent mode. */ } FuriHalNfcEventInternalType; +/** @brief Special bitmask value of all internal events. */ #define FURI_HAL_NFC_EVENT_INTERNAL_ALL \ ((FuriHalNfcEventInternalTypeAbort | FuriHalNfcEventInternalTypeIrq | \ FuriHalNfcEventInternalTypeTimerFwtExpired | \ FuriHalNfcEventInternalTypeTimerBlockTxExpired | \ FuriHalNfcEventInternalTypeTransparentDataReceived)) +/** + * @brief NFC HAL internal event structure. + */ typedef struct { - FuriThreadId thread; - void* context; + FuriThreadId thread; /**< Identifier of the thread that will be receiving events. */ + void* context; /**< Pointer to the user-provided context (will be passed to the event callback). */ } FuriHalNfcEventInternal; +/** + * @brief NFC HAL global state structure. + */ typedef struct { - FuriMutex* mutex; - FuriHalNfcMode mode; - FuriHalNfcTech tech; + FuriMutex* mutex; /**< Pointer to the mutex serving as global NFC HAL lock. */ + FuriHalNfcMode mode; /**< Currently selected operating mode. */ + FuriHalNfcTech tech; /**< Currently selected NFC technology. */ } FuriHalNfc; -// Technology specific API -typedef FuriHalNfcError (*FuriHalNfcChipConfig)(FuriHalSpiBusHandle* handle); -typedef FuriHalNfcError ( - *FuriHalNfcTx)(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); -typedef FuriHalNfcError (*FuriHalNfcRx)( - FuriHalSpiBusHandle* handle, - uint8_t* rx_data, - size_t rx_data_size, - size_t* rx_bits); -typedef FuriHalNfcEvent (*FuriHalNfcWaitEvent)(uint32_t timeout_ms); -typedef FuriHalNfcError (*FuriHalNfcSleep)(FuriHalSpiBusHandle* handle); -typedef FuriHalNfcError (*FuriHalNfcIdle)(FuriHalSpiBusHandle* handle); - -typedef struct { - int32_t fdt; - int32_t fwt; -} FuriHalNfcPollerCompensation; - -typedef struct { - FuriHalNfcPollerCompensation compensation; - FuriHalNfcChipConfig init; - FuriHalNfcChipConfig deinit; - FuriHalNfcWaitEvent wait_event; - FuriHalNfcTx tx; - FuriHalNfcRx rx; -} FuriHalNfcTechPollerBase; - -typedef struct { - int32_t fdt; -} FuriHalNfcListenerCompensation; - -typedef struct { - FuriHalNfcListenerCompensation compensation; - FuriHalNfcChipConfig init; - FuriHalNfcChipConfig deinit; - FuriHalNfcWaitEvent wait_event; - FuriHalNfcTx tx; - FuriHalNfcRx rx; - FuriHalNfcSleep sleep; - FuriHalNfcIdle idle; -} FuriHalNfcTechListenerBase; - -typedef struct { - FuriHalNfcTechPollerBase poller; - FuriHalNfcTechListenerBase listener; -} FuriHalNfcTechBase; - -extern const FuriHalNfcTechBase furi_hal_nfc_iso14443a; -extern const FuriHalNfcTechBase furi_hal_nfc_iso14443b; -extern const FuriHalNfcTechBase furi_hal_nfc_iso15693; -extern const FuriHalNfcTechBase furi_hal_nfc_felica; - -extern const FuriHalNfcTechBase* furi_hal_nfc_tech[]; - +/** + * @brief NFC HAL global state object declaration. + */ extern FuriHalNfc furi_hal_nfc; +/** + * @brief Initialise NFC HAL event system. + */ void furi_hal_nfc_event_init(); +/** + * @brief Forcibly emit (a) particular internal event(s). + * + * @param[in] event bitmask of one or more events to be emitted. + */ void furi_hal_nfc_event_set(FuriHalNfcEventInternalType event); +/** + * @brief Initialise GPIO to generate an interrupt from the NFC hardware. + */ void furi_hal_nfc_init_gpio_isr(); +/** + * @brief Disable interrupts from the NFC hardware. + */ void furi_hal_nfc_deinit_gpio_isr(); +/** + * @brief Initialise all NFC timers. + */ void furi_hal_nfc_timers_init(); +/** + * @brief Disable all NFC timers. + */ void furi_hal_nfc_timers_deinit(); +/** + * @brief Get the interrupt bitmask from the NFC hardware. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @returns bitmask of zero or more occurred interrupts. + */ uint32_t furi_hal_nfc_get_irq(FuriHalSpiBusHandle* handle); +/** + * @brief Wait until a specified type of interrupt occurs. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @param[in] mask bitmask of one or more interrupts to wait for. + * @param[in] timeout_ms maximum time to wait for an interrupt, in milliseconds. + * @returns true if specified interrupt(s) have occured within timeout, false otherwise. + */ bool furi_hal_nfc_event_wait_for_specific_irq( FuriHalSpiBusHandle* handle, uint32_t mask, uint32_t timeout_ms); -// Common technology methods +/** + * @brief Wait for any event to occur. + * + * This function is common to all technologies. + * + * @param[in] timeout_ms maximum time to wait for an event, in milliseconds. + * @returns bitmask of zero or more occurred events. + */ FuriHalNfcEvent furi_hal_nfc_wait_event_common(uint32_t timeout_ms); +/** + * @brief Start reception in listener mode. + * + * This function is common to all technologies. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ FuriHalNfcError furi_hal_nfc_common_listener_rx_start(FuriHalSpiBusHandle* handle); +/** + * @brief Transmit data using on-chip FIFO. + * + * This function is common to all technologies. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ FuriHalNfcError furi_hal_nfc_common_fifo_tx( FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); +/** + * @brief Receive data using on-chip FIFO. + * + * This function is common to all technologies. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @param[out] rx_data pointer to a byte array to be filled with received data. + * @param[in] rx_data_size maximum received data size, in bytes. + * @param[out] rx_bits pointer to the variable to contain the received data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ FuriHalNfcError furi_hal_nfc_common_fifo_rx( FuriHalSpiBusHandle* handle, uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); +/** + * @brief Transmit data in poller mode. + * + * This function is common to all technologies. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ FuriHalNfcError furi_hal_nfc_poller_tx_common( FuriHalSpiBusHandle* handle, const uint8_t* tx_data, diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc_iso14443a.c b/firmware/targets/f7/furi_hal/furi_hal_nfc_iso14443a.c index 4e488e281c3..1c150a2f048 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc_iso14443a.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc_iso14443a.c @@ -1,4 +1,5 @@ -#include +#include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc_iso14443b.c b/firmware/targets/f7/furi_hal/furi_hal_nfc_iso14443b.c index bb1f9f59ec0..bb1a63515b2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc_iso14443b.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc_iso14443b.c @@ -1,4 +1,5 @@ #include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" static FuriHalNfcError furi_hal_nfc_iso14443b_common_init(FuriHalSpiBusHandle* handle) { // Common NFC-B settings, 106kbps diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc_iso15693.c b/firmware/targets/f7/furi_hal/furi_hal_nfc_iso15693.c index 944dbe98dc4..107fbe5bb21 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc_iso15693.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc_iso15693.c @@ -1,4 +1,5 @@ #include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc_tech_i.h b/firmware/targets/f7/furi_hal/furi_hal_nfc_tech_i.h new file mode 100644 index 00000000000..e36dc852e8c --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc_tech_i.h @@ -0,0 +1,167 @@ +/** + * @file furi_hal_nfc_tech_i.h + * @brief NFC HAL technology-related private definitions. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + * + * This file is to be changed in an unlikely event of adding support + * for a new NFC technology. + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configure the NFC chip for use with this technology. + * + * Used for init() and deinit() functions. + * + * @param[in,out] handle pointer to the NFC chip SPI handle. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +typedef FuriHalNfcError (*FuriHalNfcChipConfig)(FuriHalSpiBusHandle* handle); + +/** + * @brief Transmit data using technology-specific framing and timings. + * + * @param[in,out] handle pointer to the NFC chip SPI handle. + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +typedef FuriHalNfcError ( + *FuriHalNfcTx)(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); + +/** + * @brief Receive data using technology-specific framing and timings. + * + * @param[in,out] handle pointer to the NFC chip SPI handle. + * @param[out] rx_data pointer to a byte array to be filled with received data. + * @param[in] rx_data_size maximum received data length, in bytes. + * @param[out] rx_bits pointer to a variable to contain received data length, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +typedef FuriHalNfcError (*FuriHalNfcRx)( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits); + +/** + * @brief Wait for an event using technology-specific method. + * + * @param[in] timeout_ms maximum time to wait, in milliseconds. + * @return bitmask of occurred events. + */ +typedef FuriHalNfcEvent (*FuriHalNfcWaitEvent)(uint32_t timeout_ms); + +/** + * @brief Go to sleep in listener mode. + * + * Puts the passive target logic into Sleep (Halt) state. + * + * @param[in,out] handle pointer to the NFC chip SPI handle. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +typedef FuriHalNfcError (*FuriHalNfcSleep)(FuriHalSpiBusHandle* handle); + +/** + * @brief Go to idle in listener mode. + * + * Puts the passive target logic into Sense (Idle) state. + * + * @param[in,out] handle pointer to the NFC chip SPI handle. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +typedef FuriHalNfcError (*FuriHalNfcIdle)(FuriHalSpiBusHandle* handle); + +/** + * @brief Technology-specific compenstaion values for pollers. + * + * Timing compensations are needed due to execution delays not accounted for + * in standards, they are usually found out experimentally. + * + * The compensation value will be subtracted from the respective timer running + * time, so positive values shorten timeouts, and negative ones make them longer. + */ +typedef struct { + int32_t fdt; /**< Frame delay time compensation, in carrier cycles. */ + int32_t fwt; /**< Frame wait time compensaton, in carrier cycles. */ +} FuriHalNfcPollerCompensation; + +/** + * @brief Abstract technology-specific poller structure. + */ +typedef struct { + FuriHalNfcPollerCompensation compensation; /**< Compensation values in poller mode. */ + FuriHalNfcChipConfig init; /**< Pointer to the init() function. */ + FuriHalNfcChipConfig deinit; /**< Pointer to the deinit() function. */ + FuriHalNfcWaitEvent wait_event; /**< Pointer to the wait_event() function. */ + FuriHalNfcTx tx; /**< Pointer to the tx() function. */ + FuriHalNfcRx rx; /**< Pointer to the rx() function. */ +} FuriHalNfcTechPollerBase; + +/** + * @brief Technology-specific compenstaion values for listeners. + * + * Same considerations apply as with FuriHalNfcPollerCompensation. + */ +typedef struct { + int32_t fdt; /**< Frame delay time compensation, in carrier cycles. */ +} FuriHalNfcListenerCompensation; + +/** + * @brief Abstract technology-specific listener structure. + * + * If the listener operating mode is not supported for a particular + * technology, fill this structure with zeroes. + */ +typedef struct { + FuriHalNfcListenerCompensation compensation; /**< Compensation values in listener mode. */ + FuriHalNfcChipConfig init; /**< Pointer to the init() function. */ + FuriHalNfcChipConfig deinit; /**< Pointer to the deinit() function. */ + FuriHalNfcWaitEvent wait_event; /**< Pointer to the wait_event() function. */ + FuriHalNfcTx tx; /**< Pointer to the tx() function. */ + FuriHalNfcRx rx; /**< Pointer to the rx() function. */ + FuriHalNfcSleep sleep; /**< Pointer to the sleep() function. */ + FuriHalNfcIdle idle; /**< Pointer to the idle() function. */ +} FuriHalNfcTechListenerBase; + +/** + * @brief Abstract NFC technology definition structure. + * + * Each concrete technology implementation must fill this structure + * with its proper functions and constants. + */ +typedef struct { + FuriHalNfcTechPollerBase poller; /**< Structure containing the poller definition. */ + FuriHalNfcTechListenerBase listener; /**< Structure containing the listener definition. */ +} FuriHalNfcTechBase; + +/** @brief Technology declaration for ISO14443 (Type A). */ +extern const FuriHalNfcTechBase furi_hal_nfc_iso14443a; +/** @brief Technology declaration for ISO14443 (Type B). */ +extern const FuriHalNfcTechBase furi_hal_nfc_iso14443b; +/** @brief Technology declaration for ISO15693. */ +extern const FuriHalNfcTechBase furi_hal_nfc_iso15693; +/** @brief Technology declaration for FeliCa. */ +extern const FuriHalNfcTechBase furi_hal_nfc_felica; +/* Declare new tehcnologies here. */ + +/** + * @brief Array of pointers to every supported technology. + * + * This variable is defined in furi_hal_nfc.c. It will need to be modified + * in case when a new technology is to be added. + */ +extern const FuriHalNfcTechBase* furi_hal_nfc_tech[]; + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc_timer.c b/firmware/targets/f7/furi_hal/furi_hal_nfc_timer.c index 32c08457424..8dd45737021 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc_timer.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc_timer.c @@ -1,4 +1,5 @@ #include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" #include diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index 3d34bb8b3fc..796752b2fbc 100644 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -1,3 +1,20 @@ +/** + * @file furi_hal_nfc.h + * @brief NFC HAL library. + * + * This library contains functions and definitions needed for NFC hardware low-level access. + * + * Application developers should first consider using the NFC protocol stack or + * the NFC transport layer and see if the APIs provided there sufficient + * for the applicaton's intended purpose. + * + * @see nfc.h + * @see nfc_protocol.h + * + * If any of the above mentioned options is used, calling any of the functions provided by this + * library is hardly necessary, as it will be taken care of under the hood. + * + */ #pragma once #include @@ -8,309 +25,402 @@ extern "C" { #endif +/** + * @brief Special value indicating that waiting for an event shall never time out. + */ #define FURI_HAL_NFC_EVENT_WAIT_FOREVER (0xFFFFFFFFU) +/** + * @brief Enumeration of possible NFC HAL events. + */ typedef enum { - FuriHalNfcEventOscOn = (1U << 0), - FuriHalNfcEventFieldOn = (1U << 1), - FuriHalNfcEventFieldOff = (1U << 2), - FuriHalNfcEventListenerActive = (1U << 3), - FuriHalNfcEventListenerActiveA = (1U << 4), - FuriHalNfcEventTxStart = (1U << 5), - FuriHalNfcEventTxEnd = (1U << 6), - FuriHalNfcEventRxStart = (1U << 7), - FuriHalNfcEventRxEnd = (1U << 8), - FuriHalNfcEventCollision = (1U << 9), - FuriHalNfcEventTimerFwtExpired = (1U << 10), - FuriHalNfcEventTimerBlockTxExpired = (1U << 11), - FuriHalNfcEventTimeout = (1U << 12), - FuriHalNfcEventAbortRequest = (1U << 13), + FuriHalNfcEventOscOn = (1U << 0), /**< Oscillator has been started. */ + FuriHalNfcEventFieldOn = (1U << 1), /**< External field (carrier) has been detected. */ + FuriHalNfcEventFieldOff = (1U << 2), /**< External field (carrier) has been lost. */ + FuriHalNfcEventListenerActive = (1U << 3), /**< Reader has issued a wake-up command. */ + FuriHalNfcEventTxStart = (1U << 4), /**< Transmission has started. */ + FuriHalNfcEventTxEnd = (1U << 5), /**< Transmission has ended. */ + FuriHalNfcEventRxStart = (1U << 6), /**< Reception has started. */ + FuriHalNfcEventRxEnd = (1U << 7), /**< Reception has ended. */ + FuriHalNfcEventCollision = (1U << 8), /**< A collision has occurred. */ + FuriHalNfcEventTimerFwtExpired = (1U << 9), /**< Frame wait timer has expired. */ + FuriHalNfcEventTimerBlockTxExpired = (1U << 10), /**< Transmission block timer has expired. */ + FuriHalNfcEventTimeout = + (1U << 11), /**< No events have occurred in a specified time period. */ + FuriHalNfcEventAbortRequest = + (1U << 12), /**< User has requested to abort current operation. */ } FuriHalNfcEvent; +/** + * @brief Enumeration of possible NFC HAL errors. + */ typedef enum { - FuriHalNfcErrorNone, - FuriHalNfcErrorBusy, - FuriHalNfcErrorChipCommunication, - FuriHalNfcErrorCommunication, - FuriHalNfcErrorOscillator, - FuriHalNfcErrorIsrTimeout, - FuriHalNfcErrorCommunicationTimeout, - FuriHalNfcErrorBufferOverflow, - FuriHalNfcErrorIncompleteFrame, - FuriHalNfcErrorDataFormat, + FuriHalNfcErrorNone, /**< No error has occurred. */ + FuriHalNfcErrorBusy, /**< The communication bus is busy. */ + FuriHalNfcErrorCommunication, /**< NFC hardware did not respond or responded unexpectedly. */ + FuriHalNfcErrorOscillator, /**< Oscillator failed to start. */ + FuriHalNfcErrorCommunicationTimeout, /**< NFC hardware did not respond in time. */ + FuriHalNfcErrorBufferOverflow, /**< Receive buffer was too small for the received data. */ + FuriHalNfcErrorIncompleteFrame, /**< Not enough data was received to parse a valid frame. */ + FuriHalNfcErrorDataFormat, /**< Cannot parse a frame due to unexpected/invalid data. */ } FuriHalNfcError; +/** + * @brief Enumeration of possible NFC HAL operating modes. + */ typedef enum { - FuriHalNfcModePoller, - FuriHalNfcModeListener, + FuriHalNfcModePoller, /**< Configure NFC HAL to operate as a poller. */ + FuriHalNfcModeListener, /**< Configure NFC HAL to operate as a listener. */ - FuriHalNfcModeNum, + FuriHalNfcModeNum, /**< Special value equal to the operating modes count. Internal use. */ } FuriHalNfcMode; +/** + * @brief Enumeration of supported NFC technologies. + */ typedef enum { - FuriHalNfcTechIso14443a, - FuriHalNfcTechIso14443b, - FuriHalNfcTechIso15693, - FuriHalNfcTechFelica, + FuriHalNfcTechIso14443a, /**< Configure NFC HAL to use the ISO14443 (type A) technology. */ + FuriHalNfcTechIso14443b, /**< Configure NFC HAL to use the ISO14443 (type B) technology. */ + FuriHalNfcTechIso15693, /**< Configure NFC HAL to use the ISO15693 technology. */ + FuriHalNfcTechFelica, /**< Configure NFC HAL to use the FeliCa technology. */ - FuriHalNfcTechNum, - FuriHalNfcTechInvalid, + FuriHalNfcTechNum, /**< Special value equal to the supported technologies count. Internal use. */ + FuriHalNfcTechInvalid, /**< Special value indicating the unconfigured state. Internal use. */ } FuriHalNfcTech; -typedef enum { - FuriHalNfcaShortFrameAllReq, - FuriHalNfcaShortFrameSensReq, -} FuriHalNfcaShortFrame; - -/** Nfc HAL initialization +/** + * @brief Initialise the NFC HAL and associated hardware. + * + * This function is called automatically during the firmware initialisation, + * so there is no need to call it explicitly. * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_init(); -/** Check if Nfc HAL is ready +/** + * @brief Check whether the NFC HAL was properly initialised and is ready. * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone if ready, any other error code if not ready. */ FuriHalNfcError furi_hal_nfc_is_hal_ready(); -/** Acquire Nfc HAL +/** + * @brief Exclusively take over the NFC HAL and associated hardware. + * + * This function needs to be called whenever an interaction with the NFC HAL + * is to take place (usually once upon the application start). * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_acquire(); -/** Release Nfc HAL +/** + * @brief Release the exclusive lock and make the NFC HAL available for others. * - * @return FuriHalNfcError + * This function needs to be called when the user code is done working + * with the NFC HAL (usually once upon application exit). It must be called + * from the same thread that has called furi_hal_nfc_acquire(). + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_release(); -/** Enter low power mode +/** + * @brief Configure the NFC hardware to enter the low-power mode. + * + * This function must be called each time when the user code is done working + * with the NFC HAL for the time being (e.g. waiting on user input). * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_low_power_mode_start(); -/** Exit low power mode +/** + * @brief Configure the NFC hardware to exit the low-power mode. + * + * This function must be called each time when the user code begins working + * with the NFC HAL, as the default state is low-power mode. * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_low_power_mode_stop(); -/** Set NFC mode +/** + * @brief Configure the NFC HAL to work in a particular mode. * - * @param mode - FuriHalNfcMode value - * @param tech - FuriHalNfcTech value + * Not all technologies implement the listener operating mode. * - * @return FuriHalNfcError + * @param[in] mode required operating mode. + * @param[in] tech required technology configuration. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_set_mode(FuriHalNfcMode mode, FuriHalNfcTech tech); -/** Reset mode +/** + * @brief Reset the NFC HAL to its default (unconfigured) state. * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_reset_mode(); -/** Start field detection +/** + * @brief Enable field (carrier) detection by the NFC hardware. * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_field_detect_start(); -/** Stop field detection +/** + * @brief Disable field (carrier) detection by the NFC hardware. * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_field_detect_stop(); -/** Check if external NFC field is present +/** + * @brief Check if the reader field (carrier) was detected by the NFC hardware. * - * @return FuriHalNfcError + * @returns true if the field was detected, false otherwise. */ bool furi_hal_nfc_field_is_present(); -/** Turn on field +/** + * @brief Enable field (carrier) generation by the NFC hardware. * - * @return FuriHalNfcError + * No carrier modulation will occur unless a transmission is explicitly started. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_poller_field_on(); -/** Wait for Nfc HAL event in poller mode +/** + * @brief Wait for an NFC HAL event in poller mode. * - * @param timeout_ms - timeout in milliseconds - * - * @return FuriHalNfcEvent value + * @param[in] timeout_ms time to wait (timeout) in milliseconds. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcEvent furi_hal_nfc_poller_wait_event(uint32_t timeout_ms); -/** Wait for Nfc HAL event in listener mode - * - * @param timeout_ms - timeout in milliseconds - * - * @return FuriHalNfcEvent value +/** + * @brief Wait for an NFC HAL event in listener mode. + * @param[in] timeout_ms time to wait (timeout) in milliseconds. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcEvent furi_hal_nfc_listener_wait_event(uint32_t timeout_ms); -/** Transmit data in poller mode - * - * @param tx_data - buffer to transmit - * @param tx_bits - number of bits to transmit +/** + * @brief Transmit data in poller mode. * - * @return FuriHalNfcError + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits); -/** Receive data in poller mode +/** + * @brief Receive data in poller mode. * - * @param rx_data - buffer to receive - * @param rx_data_size - size of receive buffer in bytes - * @param rx_bits - number of bits received + * The receive buffer must be big enough to accomodate all of the expected data. * - * @return FuriHalNfcError + * @param rx_data[out] pointer to a byte array to be filled with received data. + * @param rx_data_size[in] maximum received data size, in bytes. + * @param rx_bits[out] pointer to the variable to hold received data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_poller_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); -/** Tranmit data in listener mode - * - * @param tx_data - buffer to transmit - * @param tx_bits - number of bits to transmit +/** + * @brief Transmit data in listener mode. * - * @return FuriHalNfcError + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_listener_tx(const uint8_t* tx_data, size_t tx_bits); -/** Receive data in listener mode +/** + * @brief Receive data in listener mode. * - * @param rx_data - buffer to receive - * @param rx_data_size - size of receive buffer in bytes - * @param rx_bits - number of bits received + * The receive buffer must be big enough to accomodate all of the expected data. * - * @return FuriHalNfcError + * @param rx_data[out] pointer to a byte array to be filled with received data. + * @param rx_data_size[in] maximum received data size, in bytes. + * @param rx_bits[out] pointer to the variable to hold received data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_listener_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); -/** Go to sleep in listener mode +/** + * @brief Go to sleep in listener mode. * - * @return FuriHalNfcError + * Puts the passive target logic into Sleep (Halt) state. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_listener_sleep(); -/** Go to idle in listener mode +/** + * @brief Go to idle in listener mode. + * + * Puts the passive target logic into Sense (Idle) state. * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_listener_idle(); -/** Enable receive in listener mode +/** + * @brief Enable reception in listener mode. + * + * Starts hardware receivers and receive decoders. * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_listener_enable_rx(); -/** Reset communication +/** + * @brief Reset communication. * - * @return FuriHalNfcError + * Resets the communication state and stops all activities: transmission, reception, etc. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_trx_reset(); -/** Start Nfc HAL events +/** + * @brief Enable generation of NFC HAL events. + * + * @warning This function must be called from the same thread from which + * the the furi_hal_nfc_*_wait_event() calls will be made. * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_event_start(); -/** Stop Nfc HAL events +/** + * @brief Disable generation of NFC HAL events. + * + * Unlike furi_hal_nfc_event_start(), this function may be called from any thread. * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_event_stop(); -/** Emit Abort event +/** + * @brief Manually emit the FuriHalNfcEventAbortRequest event. * - * @return FuriHalNfcError + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_abort(); -/** Start Frame Wait Timeout timer +/** + * @brief Start frame wait timeout timer. * - * @param time_fc - time in NFC frequency clock + * @param[in] time_fc time to wait, in carrier cycles. */ void furi_hal_nfc_timer_fwt_start(uint32_t time_fc); -/** Stop Frame Wait Timeout timer */ +/** + * @brief Stop frame wait timeout timer. + */ void furi_hal_nfc_timer_fwt_stop(); -/** Start block transmit timer +/** + * @brief Start block transmit (frame delay) timer. * - * @param time_fc - time in NFC frequency clock + * @param[in] time_fc time to wait, in carrier cycles. */ void furi_hal_nfc_timer_block_tx_start(uint32_t time_fc); -/** Start block transmit timer +/** + * @brief Start block transmit (frame delay) timer. * - * @param time_us - time in microseconds + * @param[in] time_us time to wait, in microseconds. */ void furi_hal_nfc_timer_block_tx_start_us(uint32_t time_us); -/** Stop block transmit timer */ +/** + * @brief Stop block transmit (frame delay) timer. + */ void furi_hal_nfc_timer_block_tx_stop(); -/** Check if block transmit timer is running +/** + * @brief Check whether block transmit (frame delay) timer is running. * - * @return true if timer is running + * @returns true if timer is running, false otherwise. */ bool furi_hal_nfc_timer_block_tx_is_running(); +/* + * Technology-specific functions. + * + * In a perfect world, this would not be necessary. + * However, the current implementation employs NFC hardware that partially implements + * certain protocols (e.g. ISO14443-3A), thus requiring methods to access such features. + */ + /******************* Iso14443a specific API *******************/ -/** Send Iso14443a Short frame in poller mode - * - * @param frame - FuriHalNfcaShortFrame value +/** + * @brief Enumeration of ISO14443 (Type A) short frame types. + */ +typedef enum { + FuriHalNfcaShortFrameAllReq, + FuriHalNfcaShortFrameSensReq, +} FuriHalNfcaShortFrame; + +/** + * @brief Transmit ISO14443 (Type A) short frame in poller mode. * - * @return FuriHalNfcError + * @param[in] frame short frame type to be transmitted. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_iso14443a_poller_trx_short_frame(FuriHalNfcaShortFrame frame); -/** Send Iso14443a SDD frame in poller mode +/** Transmit ISO14443 (Type A) SDD frame in poller mode. * - * @param tx_data - buffer to transmit - * @param tx_bits - number of bits to transmit - * - * @return FuriHalNfcError + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_iso14443a_tx_sdd_frame(const uint8_t* tx_data, size_t tx_bits); -/** Receive Iso14443a SDD frame in poller mode +/** + * Receive ISO14443 (Type A) SDD frame in poller mode. * - * @param rx_data - buffer to receive - * @param rx_data_size - size of receive buffer in bytes - * @param rx_bits - number of bits received + * The receive buffer must be big enough to accomodate all of the expected data. * - * @return FuriHalNfcError + * @param rx_data[out] pointer to a byte array to be filled with received data. + * @param rx_data_size[in] maximum received data size, in bytes. + * @param rx_bits[out] pointer to the variable to hold received data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_iso14443a_rx_sdd_frame(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); -/** Send Iso14443a frame with custom parity bits in poller mode +/** + * @brief Transmit ISO14443 (Type A) frame with custom parity bits in poller mode. * - * @param tx_data - buffer to transmit - * @param tx_bits - number of bits to transmit + * Same as furi_hal_nfc_poller_tx(), but uses the parity bits provided + * by the user code instead of calculating them automatically. * - * @return FuriHalNfcError + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_iso14443a_poller_tx_custom_parity(const uint8_t* tx_data, size_t tx_bits); -/** Set Iso14443a collision resolution data in listener mode +/** + * @brief Set ISO14443 (Type A) collision resolution parameters in listener mode. * - * @param uid - pointer to UID buffer - * @param uid_len - UID length in bytes - * @param atqa - pointer to ATQA buffer - * @param sak - SAK value + * Configures the NFC hardware for automatic collision resolution. * - * @return FuriHalNfcError + * @param[in] uid pointer to a byte array containing the UID. + * @param[in] uid_len UID length in bytes (must be supported by the protocol). + * @param[in] atqa ATQA byte value. + * @param[in] sak SAK byte value. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_iso14443a_listener_set_col_res_data( uint8_t* uid, @@ -318,13 +428,13 @@ FuriHalNfcError furi_hal_nfc_iso14443a_listener_set_col_res_data( uint8_t* atqa, uint8_t sak); -/** Send ISO14443a frame with custom parity bits in listener mode - * - * @param tx_data - buffer to transmit - * @param tx_parity - buffer with parity bits - * @param tx_bits - number of bits to transmit +/** + * @brief Transmit ISO14443 (Type A) frame with custom parity bits in listener mode. * - * @return FuriHalNfcError + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_parity pointer to a (bit-packed) byte array containing the parity to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. */ FuriHalNfcError furi_hal_nfc_iso14443a_listener_tx_custom_parity( const uint8_t* tx_data, diff --git a/lib/digital_signal/digital_sequence.h b/lib/digital_signal/digital_sequence.h index 8f95f8d191d..4f04ecc464b 100644 --- a/lib/digital_signal/digital_sequence.h +++ b/lib/digital_signal/digital_sequence.h @@ -1,6 +1,6 @@ /** * @file digital_sequence.h - * @brief The DigitalSequence library is used to generate fast and precise digital signals. + * @brief Fast and precise digital signal generation library. * * Each sequence is represented by one or more (up to 32) registered signals, which are addressed * by their indices, and a list of signal indices to be transmitted. @@ -9,15 +9,17 @@ * * Example: A sequence containing 4 registered signals and n indices to transmit. * - * |Signal | Index | - * |:-----:|:-----:| - * | SOF | 0 | - * | EOF | 1 | - * | Zero | 2 | - * | One | 3 | - * - * Signal index | 0 | 3 | 2 | 2 | ... | 3 | 1 | - * 0 1 2 3 ... n - 2 n - 1 + * |Signal | Index | + * |:-----:|:-----:| + * | SOF | 0 | + * | EOF | 1 | + * | Zero | 2 | + * | One | 3 | + * + * ``` + * Signal index | 0 | 3 | 2 | 2 | ... | 3 | 1 | + * 0 1 2 3 ... n - 2 n - 1 + * ``` * * The above sequence starts by transmitting the signal with index 0, which is SOF in this case, * then it proceeds with indices 3, 2, 2, which are One, Zero, Zero and after n - 2 signals, diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index c9db37e8bfe..a0408900de8 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -1,3 +1,4 @@ +#include "digital_signal.h" #include "digital_signal_i.h" #include diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index a120d02d439..f29facfd83d 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -1,15 +1,17 @@ /** * @file digital_signal.h - * @brief DigitalSignal is a simple digital signal container for the DigitalSequence library. + * @brief Simple digital signal container for the DigitalSequence library. * * Each signal is represented by its start level (high or low) and one or more periods. * The output will transition to its inverse value on each period boundary. * * Example: A signal with n periods and HIGH start level. * + * ``` * ----+ +------+ +- ... -+ * t0 | t1 | t2 | t3 | | tn - 1 * +--------+ +----+ +-------- + * ``` * */ #pragma once @@ -21,10 +23,9 @@ extern "C" { #endif -/** - * DigitalSignal uses 10 picosecond time units (1 tick = 10 ps). - * The macros below is used to convert the time from other units. - */ +// DigitalSignal uses 10 picosecond time units (1 tick = 10 ps). +// Use the macros below to convert the time from other units. + #define DIGITAL_SIGNAL_MS(x) ((x)*100000000UL) #define DIGITAL_SIGNAL_US(x) ((x)*100000UL) #define DIGITAL_SIGNAL_NS(x) ((x)*100UL) @@ -62,26 +63,27 @@ void digital_signal_add_period(DigitalSignal* signal, uint32_t ticks); * by the given ticks value. Otherwise, the behaviour is identical to digital_signal_add_period(). * * Example 1: add tc with HIGH level - * - * before: - * ... ------+ - * ta | tb - * +------- - * after: - * ... ------+ +------- - * ta | tb | tc - * +------+ - * + * ``` + * before: + * ... ------+ + * ta | tb + * +------- + * after: + * ... ------+ +------- + * ta | tb | tc + * +------+ + * ``` * Example 2: add tc with LOW level - * - * before: - * ... ------+ - * ta | tb - * +------- - * after: - * ... ------+ - * ta | tb + tc - * +-------------- + * ``` + * before: + * ... ------+ + * ta | tb + * +------- + * after: + * ... ------+ + * ta | tb + tc + * +-------------- + * ``` * * @param[in,out] signal pointer to the instance to append to. * @param[in] ticks the period length, in 10 picosecond units. diff --git a/lib/digital_signal/digital_signal_i.h b/lib/digital_signal/digital_signal_i.h index 39c29c291b7..e473c80c091 100644 --- a/lib/digital_signal/digital_signal_i.h +++ b/lib/digital_signal/digital_signal_i.h @@ -1,12 +1,23 @@ -#include "digital_signal.h" +/** + * @file digital_signal_i.h + * @brief DigitalSignal private definitions. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + */ +#include +#include -#define DIGITAL_SIGNAL_T_TIM 1562 /* 15.625 ns *100 */ -#define DIGITAL_SIGNAL_T_TIM_DIV2 (DIGITAL_SIGNAL_T_TIM / 2) /* 15.625 ns / 2 *100 */ +#define DIGITAL_SIGNAL_T_TIM 1562 /**< 15.625 ns *100 */ +#define DIGITAL_SIGNAL_T_TIM_DIV2 (DIGITAL_SIGNAL_T_TIM / 2) /**< 15.625 ns / 2 *100 */ +/** + * @brief DigitalSignal structure type. + */ struct DigitalSignal { - bool start_level; - uint32_t size; - uint32_t max_size; - uint32_t* data; - int32_t remainder; + bool start_level; /**< The level to begin the signal with. */ + uint32_t size; /**< Current period count contained in the instance. */ + uint32_t max_size; /**< Maximum period count this instance can hold. */ + uint32_t* data; /**< Pointer to the array of time periods. */ + int32_t remainder; /**< Remainder left after converting all periods into timer ticks. */ }; diff --git a/lib/digital_signal/presets/nfc/iso14443_3a_signal.h b/lib/digital_signal/presets/nfc/iso14443_3a_signal.h index f712ee17cd1..73269e66c8d 100644 --- a/lib/digital_signal/presets/nfc/iso14443_3a_signal.h +++ b/lib/digital_signal/presets/nfc/iso14443_3a_signal.h @@ -1,6 +1,6 @@ /** * @file iso14443_3a_signal.h - * @brief A DigitalSequence preset for generating ISO14443-3A compliant signals. + * @brief DigitalSequence preset for generating ISO14443-3A compliant signals. */ #pragma once diff --git a/lib/digital_signal/presets/nfc/iso15693_signal.h b/lib/digital_signal/presets/nfc/iso15693_signal.h index e56d55c63f3..d702ecbe17b 100644 --- a/lib/digital_signal/presets/nfc/iso15693_signal.h +++ b/lib/digital_signal/presets/nfc/iso15693_signal.h @@ -1,6 +1,6 @@ /** * @file iso15693_signal.h - * @brief A DigitalSequence preset for generating ISO15693-compliant signals. + * @brief DigitalSequence preset for generating ISO15693-compliant signals. * */ #pragma once @@ -16,10 +16,13 @@ extern "C" { typedef struct Iso15693Signal Iso15693Signal; +/** + * @brief Supported data rates. + */ typedef enum { - Iso15693SignalDataRateHi, - Iso15693SignalDataRateLo, - Iso15693SignalDataRateNum, + Iso15693SignalDataRateHi, /**< High data rate. */ + Iso15693SignalDataRateLo, /**< Low data rate. */ + Iso15693SignalDataRateNum, /**< Data rate mode count. Internal use. */ } Iso15693SignalDataRate; /** diff --git a/lib/nfc/nfc.h b/lib/nfc/nfc.h index 0846a1c3186..de20e882e70 100644 --- a/lib/nfc/nfc.h +++ b/lib/nfc/nfc.h @@ -1,3 +1,20 @@ +/** + * @file nfc.h + * @brief Transport layer Nfc library. + * + * The Nfc layer is responsible for setting the operating mode (poller or listener) + * and technology (ISO14443-3A/B, ISO15693, ...), data exchange between higher + * protocol-specific levels and underlying NFC hardware, as well as timings handling. + * + * In applications using the NFC protocol system there is no need to neiter explicitly + * create an Nfc instance nor call any of its functions, as it is all handled + * automatically under the hood. + * + * If the NFC protocol system is not suitable for the application's intended purpose + * or there is need of having direct access to the NFC transport layer, then an Nfc + * instance must be allocated and the below functions shall be used to implement + * the required logic. + */ #pragma once #include @@ -6,176 +23,263 @@ extern "C" { #endif +/** + * @brief Nfc opaque type definition. + */ typedef struct Nfc Nfc; +/** + * @brief Enumeration of possible Nfc event types. + * + * Not all technologies implement all events (this is due to hardware limitations). + */ typedef enum { - NfcEventTypeUserAbort, - NfcEventTypeFieldOn, - NfcEventTypeFieldOff, - NfcEventTypeTxStart, - NfcEventTypeTxEnd, - NfcEventTypeRxStart, - NfcEventTypeRxEnd, - - NfcEventTypeListenerActivated, - NfcEventTypePollerReady, + NfcEventTypeUserAbort, /**< User code explicitly aborted the current operation. */ + NfcEventTypeFieldOn, /**< Reader's field was detected by the NFC hardware. */ + NfcEventTypeFieldOff, /**< Reader's field was lost. */ + NfcEventTypeTxStart, /**< Data transmission has started. */ + NfcEventTypeTxEnd, /**< Data transmission has ended. */ + NfcEventTypeRxStart, /**< Data reception has started. */ + NfcEventTypeRxEnd, /**< Data reception has ended. */ + + NfcEventTypeListenerActivated, /**< The listener has been activated by the reader. */ + NfcEventTypePollerReady, /**< The card has been activated by the poller. */ } NfcEventType; +/** + * @brief Nfc event data structure. + */ typedef struct { - BitBuffer* buffer; + BitBuffer* buffer; /**< Pointer to the received data buffer. */ } NfcEventData; +/** + * @brief Nfc event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ typedef struct { - NfcEventType type; - NfcEventData data; + NfcEventType type; /**< Type of the emitted event. */ + NfcEventData data; /**< Event-specific data. */ } NfcEvent; +/** + * @brief Enumeration of possible Nfc commands. + * + * The event callback must return one of these to determine the next action. + */ typedef enum { - NfcCommandContinue, - NfcCommandReset, - NfcCommandStop, - NfcCommandSleep, + NfcCommandContinue, /**< Continue operation normally. */ + NfcCommandReset, /**< Reset the current state. */ + NfcCommandStop, /**< Stop the current operation. */ + NfcCommandSleep, /**< Switch Nfc hardware to low-power mode. */ } NfcCommand; +/** + * @brief Nfc event callback type. + * + * A function of this type must be passed as the callback parameter upon start of a an Nfc instance. + * + * @param [in] event Nfc event, passed by value, complete with protocol type and data. + * @param [in,out] context pointer to the user-specific context (set when starting an Nfc instance). + * @returns command which the event producer must execute. + */ typedef NfcCommand (*NfcEventCallback)(NfcEvent event, void* context); +/** + * @brief Enumeration of possible operating modes. + * + * Not all technologies implement the listener operating mode. + */ typedef enum { - NfcModePoller, - NfcModeListener, + NfcModePoller, /**< Configure the Nfc instance as a poller. */ + NfcModeListener, /**< Configure the Nfc instance as a listener. */ - NfcModeNum, + NfcModeNum, /**< Operating mode count. Internal use. */ } NfcMode; +/** + * @brief Enumeration of available technologies. + */ typedef enum { - NfcTechIso14443a, - NfcTechIso14443b, - NfcTechIso15693, - NfcTechFelica, + NfcTechIso14443a, /**< Configure the Nfc instance to use the ISO14443-3A technology. */ + NfcTechIso14443b, /**< Configure the Nfc instance to use the ISO14443-3B technology. */ + NfcTechIso15693, /**< Configure the Nfc instance to use the ISO15693 technology. */ + NfcTechFelica, /**< Configure the Nfc instance to use the FeliCa technology. */ - NfcTechNum, + NfcTechNum, /**< Technologies count. Internal use. */ } NfcTech; +/** + * @brief Enumeration of possible Nfc error codes. + */ typedef enum { - NfcErrorNone, - NfcErrorInternal, - NfcErrorTimeout, - NfcErrorWrongState, - NfcErrorCollision, - NfcErrorLinkLoss, - NfcErrorAbortRequest, - NfcErrorIncompleteFrame, - NfcErrorDataFormat, + NfcErrorNone, /**< No error has occurred. */ + NfcErrorInternal, /**< An unknown error has occured on the lower level. */ + NfcErrorTimeout, /**< Operation is taking too long (e.g. card does not respond). */ + NfcErrorIncompleteFrame, /**< An incomplete data frame has been received. */ + NfcErrorDataFormat, /**< Data has not been parsed due to wrong/unknown format. */ } NfcError; -typedef enum { - NfcIso14443aShortFrameSensReq, - NfcIso14443aShortFrameAllReqa, -} NfcIso14443aShortFrame; - -/** Allocate Nfc instance - * Acquires Nfc HAL +/** + * @brief Allocate an Nfc instance. + * + * Will exclusively take over the NFC HAL until deleted. * - * @return Nfc instance + * @returns pointer to the allocated Nfc instance. */ Nfc* nfc_alloc(); -/** Delete Nfc instance - * Releases Nfc HAL +/** + * @brief Delete an Nfc instance. + * + * Will release the NFC HAL lock, making it available for use by others. * - * @param instance - Nfc instance + * @param[in,out] instance pointer to the instance to be deleted. */ void nfc_free(Nfc* instance); -/** Configure Nfc +/** + * @brief Configure the Nfc instance to work in a particular mode. * - * @param instance - Nfc instance - * @param mode - NfcMode value - * @param tech - NfcTech value + * Not all technologies implement the listener operating mode. + * + * @param[in,out] instance pointer to the instance to be configured. + * @param[in] mode required operating mode. + * @param[in] tech required technology configuration. */ void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech); -/** Set poller frame delay time +/** + * @brief Set poller frame delay time. * - * @param instance - Nfc instance - * @param fdt_poll_fc - FDT Poll value in Nfc frequency units + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] fdt_poll_fc frame delay time, in carrier cycles. */ void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc); -/** Set listener frame delay time +/** + * @brief Set listener frame delay time. * - * @param instance - Nfc instance - * @param fdt_listen_fc - FDT Listen value in Nfc frequency units + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] fdt_listen_fc frame delay time, in carrier cycles. */ void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc); -/** Set mask receive time +/** + * @brief Set mask receive time. * - * @param instance - Nfc instance - * @param mask_rx_time - Mask Receive Time value in Nfc frequency units + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] mask_rx_time mask receive time, in carrier cycles. */ void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc); -/** Set frame delay time between two consecutive poll frames +/** + * @brief Set frame delay time. + * + * Frame delay time is the minimum time between two consecutive poll frames. * - * @param instance - Nfc instance - * @param fdt_poll_poll_us - FDT Poll-Poll value in microseconds + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] fdt_poll_poll_us frame delay time, in microseconds. */ void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us); -/** Set Guard time +/** + * @brief Set guard time. * - * @param instance - Nfc instance - * @param guard_time_us - Guard Time value in microseconds + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] guard_time_us guard time, in microseconds. */ void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us); -/** Start Nfc worker +/** + * @brief Start the Nfc instance. + * + * The instance must be configured to work with a specific technology + * in a specific operating mode with a nfc_config() call before starting. * - * @param instance - Nfc instance - * @param callback - NfcEventCallback function - * @param context - context pointer + * Once started, the user code will be receiving events through the provided + * callback which must handle them according to the logic required. + * + * @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_start(Nfc* instance, NfcEventCallback callback, void* context); -/** Stop Nfc worker +/** + * @brief Stop Nfc instance. + * + * The instance can only be stopped if it is running. * - * @param instance - Nfc instance + * @param[in,out] instance pointer to the instance to be stopped. */ void nfc_stop(Nfc* instance); -/** Transmit and receive frames in poller mode - * Must be called from Nfc worker thread +/** + * @brief Transmit and receive a data frame in poller mode. + * + * The rx_buffer will be filled with any data received as a response to data + * sent from tx_buffer, with a timeout defined by the fwt parameter. * - * @param instance - Nfc instance - * @param tx_buffer - buffer to transmit - * @param rx_buffer - buffer to receive - * @param fwt - FWT value in Nfc frequency units + * The data being transmitted and received may be either bit- or byte-oriented. + * It shall not contain any technology-specific sequences as start or stop bits + * and/or other special symbols, as this is handled on the underlying HAL level. * - * @return NfcError value + * Must ONLY be used inside the callback function. + * + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @returns NfcErrorNone on success, any other error code on failure. */ NfcError nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt); -/** Transmit frame in listener mode - * Must be called from Nfc worker thread +/** + * @brief Transmit a data frame in listener mode. + * + * Used to transmit a response to the reader request in listener mode. + * + * The data being transmitted may be either bit- or byte-oriented. + * It shall not contain any technology-specific sequences as start or stop bits + * and/or other special symbols, as this is handled on the underlying HAL level. * - * @param instance - Nfc instance - * @param tx_buffer - buffer to transmit + * Must ONLY be used inside the callback function. * - * @return NfcError value + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @returns NfcErrorNone on success, any other error code on failure. */ NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer); -/******************* Iso14443a specific API *******************/ - -/** Send Iso14443a Short frame and receive response in poller mode +/* + * Technology-specific functions. * - * @param instance - Nfc instance - * @param frame - NfcIso14443aShortFrame value - * @param rx_buffer - buffer to receive - * @param fwt - FWT value in Nfc frequency units + * In a perfect world, this would not be necessary. + * However, the current implementation employs NFC hardware that partially implements + * certain protocols (e.g. ISO14443-3A), thus requiring methods to access such features. + */ + +/******************* ISO14443-3A specific API *******************/ + +/** + * @brief Enumeration of possible ISO14443-3A short frame types. + */ +typedef enum { + NfcIso14443aShortFrameSensReq, + NfcIso14443aShortFrameAllReqa, +} NfcIso14443aShortFrame; + +/** + * @brief Transmit an ISO14443-3A short frame and receive the response in poller mode. * - * @return NfcError value + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] frame type of short frame to be sent. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @returns NfcErrorNone on success, any other error code on failure. */ NfcError nfc_iso14443a_poller_trx_short_frame( Nfc* instance, @@ -183,14 +287,14 @@ NfcError nfc_iso14443a_poller_trx_short_frame( BitBuffer* rx_buffer, uint32_t fwt); -/** Send Iso14443a SDD frame and receive response in poller mode - * - * @param instance - Nfc instance - * @param tx_buffer - buffer to transmit - * @param rx_buffer - buffer to receive - * @param fwt - FWT value in Nfc frequency units +/** + * @brief Transmit an ISO14443-3A SDD frame and receive the response in poller mode. * - * @return NfcError value + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @returns NfcErrorNone on success, any other error code on failure. */ NfcError nfc_iso14443a_poller_trx_sdd_frame( Nfc* instance, @@ -198,14 +302,17 @@ NfcError nfc_iso14443a_poller_trx_sdd_frame( BitBuffer* rx_buffer, uint32_t fwt); -/** Transmit Iso14443a frame with custom parity bits and receive response in poller mode +/** + * @brief Transmit an ISO14443-3A data frame with custom parity bits and receive the response in poller mode. * - * @param instance - Nfc instance - * @param tx_buffer - buffer to transmit - * @param rx_buffer - buffer to receive - * @param fwt - FWT value in Nfc frequency units + * Same as nfc_poller_trx(), but uses the parity bits provided by the user code + * instead of calculating them automatically. * - * @return NfcError value + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @returns NfcErrorNone on success, any other error code on failure. */ NfcError nfc_iso14443a_poller_trx_custom_parity( Nfc* instance, @@ -213,24 +320,29 @@ NfcError nfc_iso14443a_poller_trx_custom_parity( BitBuffer* rx_buffer, uint32_t fwt); -/** Transmit Iso14443a frame with custom parity bits in listener mode +/** + * @brief Transmit an ISO14443-3A frame with custom parity bits in listener mode. * - * @param instance - Nfc instance - * @param tx_buffer - buffer to transmit + * Same as nfc_listener_tx(), but uses the parity bits provided by the user code + * instead of calculating them automatically. * - * @return NfcError value + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @returns NfcErrorNone on success, any other error code on failure. */ NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer); -/** Set Iso14443 collision resolution parameters in listener mode +/** + * @brief Set ISO14443-3A collision resolution parameters in listener mode. * - * @param instance - Nfc instance - * @param uid - UID value - * @param uid_len - UID length in bytes - * @param atqa - ATQA value - * @param sak - SAK value + * Configures the NFC hardware for automatic collision resolution. * - * @return NfcError value + * @param[in,out] instance pointer to the instance to be configured. + * @param[in] uid pointer to a byte array containing the UID. + * @param[in] uid_len UID length in bytes (must be supported by the protocol). + * @param[in] atqa ATQA byte value. + * @param[in] sak SAK byte value. + * @returns NfcErrorNone on success, any other error code on failure. */ NfcError nfc_iso14443a_listener_set_col_res_data( Nfc* instance, diff --git a/lib/nfc/nfc_common.h b/lib/nfc/nfc_common.h index c6fe19c2034..c3cccf697d3 100644 --- a/lib/nfc/nfc_common.h +++ b/lib/nfc/nfc_common.h @@ -1,12 +1,20 @@ +/** + * @file nfc_common.h + * @brief Various common NFC-related macros. + */ #pragma once #ifdef __cplusplus extern "C" { #endif +/* NFC file format version which changed ATQA format. Deprecated. */ #define NFC_LSB_ATQA_FORMAT_VERSION (2) +/* NFC file format version which is still supported as backwards compatible. */ #define NFC_MINIMUM_SUPPORTED_FORMAT_VERSION NFC_LSB_ATQA_FORMAT_VERSION +/* NFC file format version which implemented the unified loading process. */ #define NFC_UNIFIED_FORMAT_VERSION (4) +/* Current NFC file format version. */ #define NFC_CURRENT_FORMAT_VERSION NFC_UNIFIED_FORMAT_VERSION #ifdef __cplusplus diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index 6a1dd4ea87c..6636e7c7683 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -1,3 +1,13 @@ +/** + * @file nfc_device.h + * @brief Abstract interface for managing NFC device data. + * + * Under the hood, it makes use of the protocol-specific functions that each one of them provides + * and abstracts it with a protocol-independent API. + * + * It does not perform any signal processing, but merely serves as a container with some handy + * operations such as loading and saving from and to a file. + */ #pragma once #include @@ -11,54 +21,244 @@ extern "C" { #endif +/** + * @brief NfcDevice opaque type definition. + */ typedef struct NfcDevice NfcDevice; +/** + * @brief Loading callback function signature. + * + * A function with such signature can be set as a callback to indicate + * the completion (or a failure) of nfc_device_load() and nfc_device_save() functions. + * + * This facility is commonly used to control GUI elements, such as progress dialogs. + * + * @param[in] context user-defined context that was passed in nfc_device_set_loading_callback(). + * @param[in] state true if the data was loaded successfully, false otherwise. + */ typedef void (*NfcLoadingCallback)(void* context, bool state); +/** + * @brief Allocate an NfcDevice instance. + * + * A newly created instance does not hold any data and thus is considered invalid. The most common + * use case would be to set its data by calling nfc_device_set_data() right afterwards. + * + * @returns pointer to the allocated instance. + */ NfcDevice* nfc_device_alloc(); +/** + * @brief Delete an NfcDevice instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ void nfc_device_free(NfcDevice* instance); +/** + * @brief Clear an NfcDevice instance. + * + * All data contained in the instance will be deleted and the instance itself will become invalid + * as if it was just allocated. + * + * @param[in,out] instance pointer to the instance to be cleared. + */ void nfc_device_clear(NfcDevice* instance); +/** + * @brief Reset an NfcDevice instance. + * + * The data contained in the instance will be reset according to the protocol-defined procedure. + * Unlike the nfc_device_clear() function, the instance will remain valid. + * + * @param[in,out] instance pointer to the instance to be reset. + */ void nfc_device_reset(NfcDevice* instance); +/** + * @brief Get the protocol identifier from an NfcDevice instance. + * + * If the instance is invalid, the return value will be NfcProtocolInvalid. + * + * @param[in] instance pointer to the instance to be queried. + * @returns protocol identifier contained in the instance. + */ NfcProtocol nfc_device_get_protocol(const NfcDevice* instance); +/** + * @brief Get the protocol-specific data from an NfcDevice instance. + * + * The protocol parameter's behaviour is a bit tricky. The function will check + * whether there is such a protocol somewhere in the protocol hierarchy and return + * the data exactly from that level. + * + * Example: Call nfc_device_get_data() on an instance with Mf DESFire protocol. + * The protocol hierarchy will look like the following: + * + * `Mf DESFire --> ISO14443-4A --> ISO14443-3A` + * + * Thus, the following values of the protocol parameter are valid: + * + * * NfcProtocolIso14443_3a + * * NfcProtocolIso14443_4a + * * NfcProtocolMfDesfire + * + * and passing them to the call would result in the respective data being returned. + * + * However, supplying a protocol identifier which is not in the hierarchy will + * result in a crash. This is to improve type safety. + * + * @param instance pointer to the instance to be queried + * @param protocol protocol identifier of the data to be retrieved. + * @returns pointer to the instance's data. + */ const NfcDeviceData* nfc_device_get_data(const NfcDevice* instance, NfcProtocol protocol); +/** + * @brief Get the protocol name by its identifier. + * + * This function does not require an instance as its return result depends only + * the protocol identifier. + * + * @param[in] protocol numeric identifier of the protocol in question. + * @returns pointer to a statically allocated string containing the protocol name. + */ const char* nfc_device_get_protocol_name(NfcProtocol protocol); +/** + * @brief Get the name of an NfcDevice instance. + * + * The return value may change depending on the instance's internal state and the name_type parameter. + * + * @param[in] instance pointer to the instance to be queried. + * @param[in] name_type type of the name to be displayed. + * @returns pointer to a statically allocated string containing the device name. + */ const char* nfc_device_get_name(const NfcDevice* instance, NfcDeviceNameType name_type); +/** + * @brief Get the unique identifier (UID) of an NfcDevice instance. + * + * The UID length is protocol-dependent. Additionally, a particular protocol might support + * several UID lengths. + * + * @param[in] instance pointer to the instance to be queried. + * @param[out] uid_len pointer to the variable to contain the UID length. + * @returns pointer to the byte array containing the instance's UID. + */ const uint8_t* nfc_device_get_uid(const NfcDevice* instance, size_t* uid_len); +/** + * @brief Set the unique identifier (UID) of an NfcDevice instance. + * + * The UID length must be supported by the instance's protocol. + * + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] uid pointer to the byte array containing the new UID. + * @param[in] uid_len length of the UID. + * @return true if the UID was valid and set, false otherwise. + */ bool nfc_device_set_uid(NfcDevice* instance, const uint8_t* uid, size_t uid_len); +/** + * @brief Set the data and protocol of an NfcDevice instance. + * + * Any data previously contained in the instance will be deleted. + * + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] protocol numeric identifier of the data's protocol. + * @param[in] protocol_data pointer to the protocol-specific data. + */ void nfc_device_set_data( NfcDevice* instance, NfcProtocol protocol, const NfcDeviceData* protocol_data); +/** + * @brief Copy (export) the data contained in an NfcDevice instance to an outside NfcDeviceData instance. + * + * This function does the inverse of nfc_device_set_data(). + + * The protocol identifier passed as the protocol parameter MUST match the one + * stored in the instance, otherwise a crash will occur. + * This is to improve type safety. + * + * @param[in] instance pointer to the instance to be copied from. + * @param[in] protocol numeric identifier of the instance's protocol. + * @param[out] protocol_data pointer to the destination data. + */ void nfc_device_copy_data( const NfcDevice* instance, NfcProtocol protocol, NfcDeviceData* protocol_data); +/** + * @brief Check whether an NfcDevice instance holds certain data. + * + * This function's behaviour is similar to nfc_device_is_equal(), with the difference + * that it takes NfcProtocol and NfcDeviceData* instead of the second NfcDevice*. + * + * The following code snippets [1] and [2] are equivalent: + * + * [1] + * ```c + * bool is_equal = nfc_device_is_equal(device1, device2); + * ``` + * [2] + * ```c + * NfcProtocol protocol = nfc_device_get_protocol(device2); + * const NfcDeviceData* data = nfc_device_get_data(device2, protocol); + * bool is_equal = nfc_device_is_equal_data(device1, protocol, data); + * ``` + * + * @param[in] instance pointer to the instance to be compared. + * @param[in] protocol protocol identifier of the data to be compared. + * @param[in] protocol_data pointer to the NFC device data to be compared. + * @returns true if the instance is of the right type and the data matches, false otherwise. + */ bool nfc_device_is_equal_data( const NfcDevice* instance, NfcProtocol protocol, const NfcDeviceData* protocol_data); +/** + * @brief Compare two NfcDevice instances to determine whether they are equal. + * + * @param[in] instance pointer to the first instance to be compared. + * @param[in] other pointer to the second instance to be compared. + * @returns true if both instances are considered equal, false otherwise. + */ bool nfc_device_is_equal(const NfcDevice* instance, const NfcDevice* other); +/** + * @brief Set the loading callback function. + * + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] callback pointer to a function to be called when the load operation completes. + * @param[in] context pointer to a user-specific context (will be passed to the callback). + */ void nfc_device_set_loading_callback( NfcDevice* instance, NfcLoadingCallback callback, void* context); +/** + * @brief Save NFC device data form an NfcDevice instance to a file. + * + * @param[in] instance pointer to the instance to be saved. + * @param[in] path pointer to a character string with a full file path. + * @returns true if the data was successfully saved, false otherwise. + */ bool nfc_device_save(NfcDevice* instance, const char* path); +/** + * @brief Load NFC device data to an NfcDevice instance from a file. + * + * @param[in,out] instance pointer to the instance to be loaded into. + * @param[in] path pointer to a character string with a full file path. + * @returns true if the data was successfully loaded, false otherwise. + */ bool nfc_device_load(NfcDevice* instance, const char* path); #ifdef __cplusplus diff --git a/lib/nfc/nfc_device_i.h b/lib/nfc/nfc_device_i.h index 437d77ad4c7..1a9017d03f6 100644 --- a/lib/nfc/nfc_device_i.h +++ b/lib/nfc/nfc_device_i.h @@ -1,3 +1,10 @@ +/** + * @file nfc_device_i.h + * @brief NfcDevice private types and definitions. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + */ #pragma once #include "nfc_device.h" @@ -6,14 +13,32 @@ extern "C" { #endif +/** + * @brief NfcDevice structure definition. + */ struct NfcDevice { - NfcProtocol protocol; - NfcDeviceData* protocol_data; + NfcProtocol protocol; /**< Numeric identifier of the data's protocol*/ + NfcDeviceData* protocol_data; /**< Pointer to the NFC device data. */ - NfcLoadingCallback loading_callback; - void* loading_callback_context; + NfcLoadingCallback + loading_callback; /**< Pointer to the function to be called upon loading completion. */ + void* loading_callback_context; /**< Pointer to the context to be passed to the loading callback. */ }; +/** + * @brief Get the mutable (non-const) data from an NfcDevice instance. + * + * The behaviour is the same as with nfc_device_get_data(), but the + * return pointer is non-const, allowing for changing data it is pointing to. + * + * @see nfc_device.h + * + * Under the hood, nfc_device_get_data() calls this and then adds const-ness to the return value. + * + * @param instance pointer to the instance to be queried + * @param protocol protocol identifier of the data to be retrieved. + * @returns pointer to the instance's (mutable) data. + */ NfcDeviceData* nfc_device_get_data_ptr(const NfcDevice* instance, NfcProtocol protocol); #ifdef __cplusplus diff --git a/lib/nfc/nfc_listener.h b/lib/nfc/nfc_listener.h index 9eb0c09b1da..dbfeb23bff9 100644 --- a/lib/nfc/nfc_listener.h +++ b/lib/nfc/nfc_listener.h @@ -1,3 +1,18 @@ +/** + * @file nfc_listener.h + * @brief NFC card emulation library. + * + * Once started, it will respond to supported commands from an NFC reader, thus imitating + * (or emulating) an NFC card. The responses will depend on the data that was supplied to + * the listener, so various card types and different cards of the same type can be emulated. + * + * It will also make any changes necessary to the emulated data in response to the + * reader commands if the protocol supports it. + * + * When running, NfcListener will generate events that the calling code must handle + * by providing a callback function. The events passed to the callback are protocol-specific + * and may include errors, state changes, data reception, special function requests and more. + */ #pragma once #include @@ -7,18 +22,71 @@ extern "C" { #endif +/** + * @brief NfcListener opaque type definition. + */ typedef struct NfcListener NfcListener; +/** + * @brief Allocate an NfcListener instance. + * + * @param[in] nfc pointer to an Nfc instance. + * @param[in] protocol identifier of the protocol to be used. + * @param[in] data pointer to the data to use during emulation. + * @returns pointer to an allocated instance. + * + * @see nfc.h + */ NfcListener* nfc_listener_alloc(Nfc* nfc, NfcProtocol protocol, const NfcDeviceData* data); +/** + * @brief Delete an NfcListener instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ void nfc_listener_free(NfcListener* instance); +/** + * @brief Start an NfcListener instance. + * + * The callback logic is protocol-specific, so it cannot be described here in detail. + * However, the callback return value ALWAYS determines what the listener should do next: + * to continue whatever it was doing prior to the callback run or to stop. + * + * @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_listener_start(NfcListener* instance, NfcGenericCallback callback, void* context); +/** + * @brief Stop an NfcListener instance. + * + * The emulation process can be stopped explicitly (the other way is via the callback return value). + * + * @param[in,out] instance pointer to the instance to be stopped. + */ void nfc_listener_stop(NfcListener* instance); +/** + * @brief Get the protocol identifier an NfcListener instance was created with. + * + * @param[in] instance pointer to the instance to be queried. + * @returns identifier of the protocol used by the instance. + */ NfcProtocol nfc_listener_get_protocol(const NfcListener* instance); +/** + * @brief Get the data that was that was provided for emulation. + * + * The protocol identifier passed as the protocol parameter MUST match the one + * stored in the instance, otherwise a crash will occur. + * This is to improve type safety. + * + * @param[in] instance pointer to the instance to be queried. + * @param[in] protocol assumed protocol identifier of the data to be retrieved. + * @returns pointer to the NFC device data. + */ const NfcDeviceData* nfc_listener_get_data(const NfcListener* instance, NfcProtocol protocol); #ifdef __cplusplus diff --git a/lib/nfc/nfc_poller.h b/lib/nfc/nfc_poller.h index 17f750bbeba..8ae01a8e43c 100644 --- a/lib/nfc/nfc_poller.h +++ b/lib/nfc/nfc_poller.h @@ -1,3 +1,17 @@ +/** + * @file nfc_poller.h + * @brief NFC card reading library. + * + * Once started, it will try to activate and read a card using the designated protocol, + * which is usually obtained by creating and starting an NfcScanner first. + * + * @see nfc_scanner.h + * + * When running, NfcPoller will generate events that the calling code must handle + * by providing a callback function. The events passed to the callback are protocol-specific + * and may include errors, state changes, data reception, special function requests and more. + * + */ #pragma once #include @@ -7,20 +21,82 @@ extern "C" { #endif +/** + * @brief NfcPoller opaque type definition. + */ typedef struct NfcPoller NfcPoller; +/** + * @brief Allocate an NfcPoller instance. + * + * @param[in] nfc pointer to an Nfc instance. + * @param[in] protocol identifier of the protocol to be used. + * @returns pointer to an allocated instance. + * + * @see nfc.h + */ NfcPoller* nfc_poller_alloc(Nfc* nfc, NfcProtocol protocol); +/** + * @brief Delete an NfcPoller instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ void nfc_poller_free(NfcPoller* instance); +/** + * @brief Start an NfcPoller instance. + * + * The callback logic is protocol-specific, so it cannot be described here in detail. + * However, the callback return value ALWAYS determines what the poller should do next: + * to continue whatever it was doing prior to the callback run or to stop. + * + * @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(NfcPoller* instance, NfcGenericCallback callback, void* context); +/** + * @brief Stop an NfcPoller instance. + * + * The reading process can be stopped explicitly (the other way is via the callback return value). + * + * @param[in,out] instance pointer to the instance to be stopped. + */ void nfc_poller_stop(NfcPoller* instance); +/** + * @brief Detect whether there is a card supporting a particular protocol in the vicinity. + * + * The behaviour of this function is protocol-defined, in general, it will do whatever is + * necessary to determine whether a card supporting the current protocol is in the vicinity + * and whether it is functioning normally. + * + * It is used automatically inside NfcScanner, so there is usually no need + * to call it explicitly. + * + * @see nfc_scanner.h + * + * @param[in,out] instance pointer to the instance to perform the detection with. + * @returns true if a supported card was detected, false otherwise. + */ bool nfc_poller_detect(NfcPoller* instance); +/** + * @brief Get the protocol identifier an NfcPoller instance was created with. + * + * @param[in] instance pointer to the instance to be queried. + * @returns identifier of the protocol used by the instance. + */ NfcProtocol nfc_poller_get_protocol(const NfcPoller* instance); +/** + * @brief Get the data that was that was gathered during the reading process. + * + * @param[in] instance pointer to the instance to be queried. + * @returns pointer to the NFC device data. + */ const NfcDeviceData* nfc_poller_get_data(const NfcPoller* instance); #ifdef __cplusplus diff --git a/lib/nfc/nfc_scanner.h b/lib/nfc/nfc_scanner.h index bcf1ddd51e5..a1b4aabcda3 100644 --- a/lib/nfc/nfc_scanner.h +++ b/lib/nfc/nfc_scanner.h @@ -1,3 +1,17 @@ +/** + * @file nfc_scanner.h + * @brief NFC card detection library. + * + * Once started, a NfcScanner instance will iterate over all available protocols + * and return a list of one or more detected protocol identifiers via a user-provided callback. + * + * The NfcScanner behaviour is greedy, i.e. it will not stop scanning upon detection of + * a just one protocol and will try others as well until all possibilities are exhausted. + * This is to allow for multi-protocol card support. + * + * If no supported cards are in the vicinity, the scanning process will continue + * until stopped explicitly. + */ #pragma once #include @@ -7,30 +21,75 @@ extern "C" { #endif +/** + * @brief NfcScanner opaque type definition. + */ typedef struct NfcScanner NfcScanner; +/** + * @brief Event type passed to the user callback. + */ typedef enum { - NfcScannerEventTypeDetected, + NfcScannerEventTypeDetected, /**< One or more protocols have been detected. */ } NfcScannerEventType; +/** + * @brief Event data passed to the user callback. + */ typedef struct { - size_t protocol_num; - NfcProtocol* protocols; + size_t protocol_num; /**< Number of detected protocols (one or more). */ + NfcProtocol* protocols; /**< Pointer to the array of detected protocol identifiers. */ } NfcScannerEventData; +/** + * @brief Event passed to the user callback. + */ typedef struct { - NfcScannerEventType type; - NfcScannerEventData data; + NfcScannerEventType type; /**< Type of event. Determines how the data must be handled. */ + NfcScannerEventData data; /**< Event-specific data. Handled accordingly to the even type. */ } NfcScannerEvent; +/** + * @brief User callback function signature. + * + * A function with such signature must be provided by the user upon calling nfc_scanner_start(). + * + * @param[in] event occurred event, complete with type and data. + * @param[in] context pointer to the context data provided in nfc_scanner_start() call. + */ typedef void (*NfcScannerCallback)(NfcScannerEvent event, void* context); +/** + * @brief Allocate an NfcScanner instance. + * + * @param[in] nfc pointer to an Nfc instance. + * @returns pointer to the allocated NfcScanner instance. + * + * @see nfc.h + */ NfcScanner* nfc_scanner_alloc(Nfc* nfc); +/** + * @brief Delete an NfcScanner instance. + * + * @param[in,out] pointer to the instance to be deleted. + */ void nfc_scanner_free(NfcScanner* instance); +/** + * @brief Start an NfcScanner. + * + * @param[in,out] pointer to the instance to be started. + * @param[in] callback pointer to the callback function (will be called upon a detection event). + * @param[in] context pointer to the caller-specific context (will be passed to the callback). + */ void nfc_scanner_start(NfcScanner* instance, NfcScannerCallback callback, void* context); +/** + * @brief Stop an NfcScanner. + * + * @param[in,out] pointer to the instance to be stopped. + */ void nfc_scanner_stop(NfcScanner* instance); #ifdef __cplusplus diff --git a/lib/nfc/protocols/nfc_device_base.h b/lib/nfc/protocols/nfc_device_base.h index 11905e30b11..4f3480d455a 100644 --- a/lib/nfc/protocols/nfc_device_base.h +++ b/lib/nfc/protocols/nfc_device_base.h @@ -1,14 +1,24 @@ +/** + * @file nfc_device_base.h + * @brief Common top-level types for the NFC protocol stack. + */ #pragma once #ifdef __cplusplus extern "C" { #endif +/** + * @brief Verbosity level of the displayed NFC device name. + */ typedef enum { - NfcDeviceNameTypeFull, - NfcDeviceNameTypeShort, + NfcDeviceNameTypeFull, /**< Display full(verbose) name. */ + NfcDeviceNameTypeShort, /**< Display shortened name. */ } NfcDeviceNameType; +/** + * @brief Generic opaque type for protocol-specific NFC device data. + */ typedef void NfcDeviceData; #ifdef __cplusplus diff --git a/lib/nfc/protocols/nfc_device_base_i.h b/lib/nfc/protocols/nfc_device_base_i.h index 99b0f1d2353..946ae76dcc0 100644 --- a/lib/nfc/protocols/nfc_device_base_i.h +++ b/lib/nfc/protocols/nfc_device_base_i.h @@ -1,3 +1,10 @@ +/** + * @file nfc_device_base_i.h + * @brief Abstract interface definitions for the NFC device system. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + */ #pragma once #include "nfc_device_base.h" @@ -8,33 +15,145 @@ extern "C" { #endif +/** + * @brief Allocate the protocol-specific NFC device data instance. + * + * @returns pointer to the allocated instance. + */ typedef NfcDeviceData* (*NfcDeviceAlloc)(); + +/** + * @brief Delete the protocol-specific NFC device data instance. + * + * @param[in,out] data pointer to the instance to be deleted. + */ typedef void (*NfcDeviceFree)(NfcDeviceData* data); + +/** + * @brief Reset the NFC device data instance. + * + * The behaviour is protocol-specific. Usually, required fields are zeroed or + * set to their initial values. + * + * @param[in,out] data pointer to the instance to be reset. + */ typedef void (*NfcDeviceReset)(NfcDeviceData* data); + +/** + * @brief Copy source instance's data into the destination so that they become equal. + * + * @param[in,out] data pointer to the destination instance. + * @param[in] other pointer to the source instance. + */ typedef void (*NfcDeviceCopy)(NfcDeviceData* data, const NfcDeviceData* other); + +/** + * @brief Deprecated. Do not use in new protocols. + * @deprecated do not use in new protocols. + * + * @param[in,out] data pointer to the instance to be tested. + * @param[in] device_type pointer to a FuriString containing a device type identifier. + * @returns true if data was verified, false otherwise. + */ typedef bool (*NfcDeviceVerify)(NfcDeviceData* data, const FuriString* device_type); + +/** + * @brief Load NFC device data from a FlipperFormat file. + * + * The FlipperFormat file structure must be initialised and open by the calling code. + * + * @param[in,out] data pointer to the instance to be loaded into. + * @param[in] ff pointer to the FlipperFormat file instance. + * @param[in] version file format version to use when loading. + * @returns true if loaded successfully, false otherwise. + */ typedef bool (*NfcDeviceLoad)(NfcDeviceData* data, FlipperFormat* ff, uint32_t version); + +/** + * @brief Save NFC device data to a FlipperFormat file. + * + * The FlipperFormat file structure must be initialised and open by the calling code. + * + * @param[in] data pointer to the instance to be saved. + * @param[in] ff pointer to the FlipperFormat file instance. + * @returns true if saved successfully, false otherwise. + */ typedef bool (*NfcDeviceSave)(const NfcDeviceData* data, FlipperFormat* ff); + +/** + * @brief Compare two NFC device data instances. + * + * @param[in] data pointer to the first instance to be compared. + * @param[in] other pointer to the second instance to be compared. + * @returns true if instances are equal, false otherwise. + */ typedef bool (*NfcDeviceEqual)(const NfcDeviceData* data, const NfcDeviceData* other); + +/** + * @brief Get a protocol-specific stateful NFC device name. + * + * The return value may change depending on the instance's internal state and the name_type parameter. + * + * @param[in] data pointer to the instance to be queried. + * @param[in] name_type type of the name to be displayed. + * @returns pointer to a statically allocated character string containing the appropriate name. + */ typedef const char* (*NfcDeviceGetName)(const NfcDeviceData* data, NfcDeviceNameType name_type); + +/** + * @brief Get the NFC device's unique identifier (UID). + * + * The UID length is protocol-dependent. Additionally, a particular protocol might support + * several UID lengths. + * + * @param[in] data pointer to the instance to be queried. + * @param[out] uid_len pointer to the variable to contain the UID length. + * @returns pointer to the byte array containing the device's UID. + */ typedef const uint8_t* (*NfcDeviceGetUid)(const NfcDeviceData* data, size_t* uid_len); + +/** + * @brief Set the NFC device's unique identifier (UID). + * + * The UID length must be supported by the protocol in question. + * + * @param[in,out] data pointer to the instance to be modified. + * @param[in] uid pointer to the byte array containing the new UID. + * @param[in] uid_len length of the UID. + * @return true if the UID was valid and set, false otherwise. + */ typedef bool (*NfcDeviceSetUid)(NfcDeviceData* data, const uint8_t* uid, size_t uid_len); + +/** + * @brief Get the NFC device data associated with the parent protocol. + * + * The protocol the instance's data is associated with must have a parent. + * + * @param[in] data pointer to the instance to be queried. + * @returns pointer to the data instance associated with the parent protocol. + */ typedef NfcDeviceData* (*NfcDeviceGetBaseData)(const NfcDeviceData* data); +/** + * @brief Generic NFC device interface. + * + * Each protocol must fill this structure with its own function implementations. + */ typedef struct { - const char* protocol_name; - NfcDeviceAlloc alloc; - NfcDeviceFree free; - NfcDeviceReset reset; - NfcDeviceCopy copy; - NfcDeviceVerify verify; - NfcDeviceLoad load; - NfcDeviceSave save; - NfcDeviceEqual is_equal; - NfcDeviceGetName get_name; - NfcDeviceGetUid get_uid; - NfcDeviceSetUid set_uid; - NfcDeviceGetBaseData get_base_data; + const char* + protocol_name; /**< Pointer to a statically-allocated string with the protocol name. */ + NfcDeviceAlloc alloc; /**< Pointer to the alloc() function. */ + NfcDeviceFree free; /**< Pointer to the free() function. */ + NfcDeviceReset reset; /**< Pointer to the reset() function. */ + NfcDeviceCopy copy; /**< Pointer to the copy() function. */ + NfcDeviceVerify verify; /**< Deprecated. Set to NULL in new protocols. */ + NfcDeviceLoad load; /**< Pointer to the load() function. */ + NfcDeviceSave save; /**< Pointer to the save() function. */ + NfcDeviceEqual is_equal; /**< Pointer to the is_equal() function. */ + NfcDeviceGetName get_name; /**< Pointer to the get_name() function. */ + NfcDeviceGetUid get_uid; /**< Pointer to the get_uid() function. */ + NfcDeviceSetUid set_uid; /**< Pointer to the set_uid() function. */ + NfcDeviceGetBaseData get_base_data; /**< Pointer to the get_base_data() function. */ } NfcDeviceBase; #ifdef __cplusplus diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c index 78cd6387bee..870bcafd9e0 100644 --- a/lib/nfc/protocols/nfc_device_defs.c +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -1,5 +1,15 @@ -#include "nfc_device_defs.h" - +/** + * @file nfc_device_defs.c + * @brief Main NFC device implementation definitions. + * + * All NFC device implementations must be registered here in order to be used + * by the NfcDevice library. + * + * @see nfc_device.h + * + * This file is to be modified upon adding a new protocol (see below). + */ +#include "nfc_device_base_i.h" #include "nfc_protocol.h" #include @@ -14,6 +24,12 @@ #include #include +/** + * @brief List of registered NFC device implementations. + * + * When implementing a new protocol, add its implementation + * here under its own index defined in nfc_protocol.h. + */ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = &nfc_device_iso14443_3a, [NfcProtocolIso14443_3b] = &nfc_device_iso14443_3b, diff --git a/lib/nfc/protocols/nfc_generic_event.h b/lib/nfc/protocols/nfc_generic_event.h index 1572408d449..ec3bd68dc57 100644 --- a/lib/nfc/protocols/nfc_generic_event.h +++ b/lib/nfc/protocols/nfc_generic_event.h @@ -1,3 +1,19 @@ +/** + * @file nfc_generic_event.h + * @brief Generic Nfc stack event definitions. + * + * Events are the main way of passing information about, well, various events + * that occur across the Nfc protocol stack. + * + * In order to subscribe to events from a certain instance, the user code must call + * its corresponding start() function while providing the appropriate callback. + * During this call, an additional context pointer can be provided, which will be passed + * to the context parameter at the time of the callback execution. + * + * For additional information on how events are passed around and processed, see protocol-specific + * poller and listener implementations found in the respectively named subfolders. + * + */ #pragma once #include "nfc_protocol.h" @@ -7,16 +23,55 @@ extern "C" { #endif +/** + * @brief Generic Nfc instance type. + * + * Must be cast to a concrete type before use. + * Depending on the context, a pointer of this type + * may point to an object of the following types: + * - Nfc type, + * - Concrete poller type, + * - Concrete listener type. + */ typedef void NfcGenericInstance; +/** + * @brief Generic Nfc event data type. + * + * Must be cast to a concrete type before use. + * Usually, it will be the protocol-specific event type. + */ typedef void NfcGenericEventData; +/** + * @brief Generic Nfc event type. + * + * A generic Nfc event contains a protocol identifier, can be used to determine + * the remaing fields' type. + * + * If the value of the protocol field is NfcProtocolInvalid, then it means that + * the event was emitted from an Nfc instance, otherwise it originated from + * a concrete poller or listener instance. + * + * The event_data field is protocol-specific and should be cast to the appropriate type before use. + */ typedef struct { - NfcProtocol protocol; - NfcGenericInstance* instance; - NfcGenericEventData* event_data; + NfcProtocol protocol; /**< Protocol identifier of the instance that produced the event. */ + NfcGenericInstance* + instance; /**< Pointer to the protocol-specific instance that produced the event. */ + NfcGenericEventData* event_data; /**< Pointer to the protocol-specific event. */ } NfcGenericEvent; +/** + * @brief Generic Nfc event callback type. + * + * A function of this type must be passed as the callback parameter upon start + * of a poller, listener or Nfc instance. + * + * @param [in] event Nfc 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 (*NfcGenericCallback)(NfcGenericEvent event, void* context); #ifdef __cplusplus diff --git a/lib/nfc/protocols/nfc_listener_base.h b/lib/nfc/protocols/nfc_listener_base.h index 8336b60e29e..6d0cdcd094c 100644 --- a/lib/nfc/protocols/nfc_listener_base.h +++ b/lib/nfc/protocols/nfc_listener_base.h @@ -1,3 +1,13 @@ +/** + * @file nfc_listener_base.h + * @brief Abstract interface definitions for the NFC listener system. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + * + * @see nfc_listener.h + * + */ #pragma once #include "nfc_generic_event.h" @@ -7,24 +17,80 @@ extern "C" { #endif +/** + * @brief Allocate a protocol-specific listener instance. + * + * For base listeners pass a pointer to an instance of type Nfc + * as the base_listener parameter, otherwise it must be a pointer to another listener instance + * (compare iso14443_3a/iso14443_3a_listener.c and iso14443_4a/iso14443_4a_listener.c). + * + * @see nfc_protocol.c + * + * The NFC device data passed as the data parameter is copied to the instance and may + * change during the emulation in response to reader commands. + * + * To retrieve the modified data, NfcListenerGetData gets called by the NfcListener + * implementation when the user code calls nfc_listener_get_data(). + * + * @param[in] base_listener pointer to the parent listener instance. + * @param[in] data pointer to the protocol-specific data to use during emulation. + * @returns pointer to the allocated listener instance. + */ typedef NfcGenericInstance* ( *NfcListenerAlloc)(NfcGenericInstance* base_listener, NfcDeviceData* data); + +/** + * @brief Delete a protocol-specific listener instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ typedef void (*NfcListenerFree)(NfcGenericInstance* instance); +/** + * @brief Set the callback function to handle events emitted by the listener instance. + * + * @see nfc_generic_event.h + * + * @param[in,out] listener + * @param[in] callback pointer to the user-defined callback function which will receive events. + * @param[in] context pointer to the user-specific context (will be passed to the callback). + */ typedef void (*NfcListenerSetCallback)( NfcGenericInstance* listener, NfcGenericCallback callback, void* context); + +/** + * @brief Emulate a supported NFC card with given device data. + * + * @param[in] event protocol-specific event passed by the parent listener instance. + * @param[in,out] context pointer to the protocol-specific listener instance. + * @returns command to be executed by the parent listener instance. + */ typedef NfcCommand (*NfcListenerRun)(NfcGenericEvent event, void* context); +/** + * @brief Get the protocol-specific data that was that was provided for emulation. + * + * @param[in] instance pointer to the protocol-specific listener instance. + * @returns pointer to the NFC device data. + */ typedef const NfcDeviceData* (*NfcListenerGetData)(const NfcGenericInstance* instance); +/** + * @brief Generic NFC listener interface. + * + * Each protocol must fill this structure with its own function implementations. + * See above for the function signatures and descriptions. + * + * Additionally, see ${PROTOCOL_NAME}/${PROTOCOL_NAME}_listener.c for usage examples. + */ typedef struct { - NfcListenerAlloc alloc; - NfcListenerFree free; - NfcListenerSetCallback set_callback; - NfcListenerRun run; - NfcListenerGetData get_data; + NfcListenerAlloc alloc; /**< Pointer to the alloc() function. */ + NfcListenerFree free; /**< Pointer to the free() function. */ + NfcListenerSetCallback set_callback; /**< Pointer to the set_callback() function. */ + NfcListenerRun run; /**< Pointer to the run() function. */ + NfcListenerGetData get_data; /**< Pointer to the get_data() function. */ } NfcListenerBase; #ifdef __cplusplus diff --git a/lib/nfc/protocols/nfc_poller_base.h b/lib/nfc/protocols/nfc_poller_base.h index 7f05ea33f5b..9c4a2b65208 100644 --- a/lib/nfc/protocols/nfc_poller_base.h +++ b/lib/nfc/protocols/nfc_poller_base.h @@ -1,3 +1,13 @@ +/** + * @file nfc_poller_base.h + * @brief Abstract interface definitions for the NFC poller system. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + * + * @see nfc_poller.h + * + */ #pragma once #include "nfc_generic_event.h" @@ -7,22 +17,114 @@ extern "C" { #endif +/** + * @brief Allocate a protocol-specific poller instance. + * + * For base pollers pass a pointer to an instance of type Nfc + * as the base_poller parameter, otherwise it must be a pointer to another poller instance + * (compare iso14443_3a/iso14443_3a_poller.c and iso14443_4a/iso14443_4a_poller.c). + * + * @see nfc_protocol.c + * + * @param[in] base_poller pointer to the parent poller instance. + * @returns pointer to the allocated poller instance. + */ typedef NfcGenericInstance* (*NfcPollerAlloc)(NfcGenericInstance* base_poller); + +/** + * @brief Delete a protocol-specific poller instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ typedef void (*NfcPollerFree)(NfcGenericInstance* instance); +/** + * @brief Set the callback function to handle events emitted by the poller instance. + * + * @see nfc_generic_event.h + * + * @param[in,out] poller pointer to the protocol-specific poller instance. + * @param[in] callback pointer to the user-defined callback function which will receive events. + * @param[in] context pointer to the user-specific context (will be passed to the callback). + */ typedef void ( *NfcPollerSetCallback)(NfcGenericInstance* poller, NfcGenericCallback callback, void* context); + +/** + * @brief Activate and read a supported NFC card. + * + * Ths function is passed to the parent poller's ${POLLER_NAME}_set_callback function as + * the callback parameter. This is done automatically by the NfcPoller implementation based + * on the protocol hierarchy defined in nfc_protocol.c, so there is no need to call it explicitly. + * + * Thus, it will be called each time the parent poller emits an event. Usually it happens + * only after the parent poller has successfully completed its job. + * + * Example for an application reading a card with a compound (non-base) protocol (simplified): + * + * ``` + * start() <-- set_callback() <-- set_callback() <-- nfc_poller_start() + * | | | + * Nfc | Base Poller | Child Poller | Application + * | | | + * worker() --> run() --> run() ---> handle_event() + * ``` + * + * The base poller receives events directly from an Nfc instance, from which they are + * propagated as needed to however many other pollers there are in the current hierarchy. + * + * This function can be thought of as the poller's "main loop" function. Depending + * on the particular poller implementation, it may perform actions such as reading + * and writing to an NFC card, state changes and control of the parent poller. + * + * @see nfc_generic_event.h + * + * @param[in] event protocol-specific event passed by the parent poller instance. + * @param[in,out] context pointer to the protocol-specific poller instance. + * @returns command to be executed by the parent poller instance. + */ typedef NfcCommand (*NfcPollerRun)(NfcGenericEvent event, void* context); + +/** + * @brief Determine whether there is a supported card in the vicinity. + * + * The behaviour is mostly the same as of NfcPollerRun, with the difference in the + * procedure and return value. + * The procedure implemented in this function must do whatever it needs to unambigiously + * determine whether a supported and valid NFC card is in the vicinity. + * + * Like the previously described NfcPollerRun, it is called automatically by the NfcPoller + * implementation, so there is no need to call it explicitly. + * + * @param[in] event protocol-specific event passed by the parent poller instance. + * @param[in,out] context pointer to the protocol-specific poller instance. + * @returns true if a supported card was detected, false otherwise. + */ typedef bool (*NfcPollerDetect)(NfcGenericEvent event, void* context); + +/** + * @brief Get the data that was that was gathered during the reading process. + * + * @param[in] instance pointer to the protocol-specific poller instance. + * @returns pointer to the NFC device data. + */ typedef const NfcDeviceData* (*NfcPollerGetData)(const NfcGenericInstance* instance); +/** + * @brief Generic NFC poller interface. + * + * Each protocol must fill this structure with its own function implementations. + * See above for the function signatures and descriptions. + * + * Additionally, see ${PROTOCOL_NAME}/${PROTOCOL_NAME}_poller.c for usage examples. + */ typedef struct { - NfcPollerAlloc alloc; - NfcPollerFree free; - NfcPollerSetCallback set_callback; - NfcPollerRun run; - NfcPollerDetect detect; - NfcPollerGetData get_data; + NfcPollerAlloc alloc; /**< Pointer to the alloc() function. */ + NfcPollerFree free; /**< Pointer to the free() function. */ + NfcPollerSetCallback set_callback; /**< Pointer to the set_callback() function. */ + NfcPollerRun run; /**< Pointer to the run() function. */ + NfcPollerDetect detect; /**< Pointer to the detect() function. */ + NfcPollerGetData get_data; /**< Pointer to the get_data() function. */ } NfcPollerBase; #ifdef __cplusplus diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c index 5f579465894..2ea9b39820a 100644 --- a/lib/nfc/protocols/nfc_protocol.c +++ b/lib/nfc/protocols/nfc_protocol.c @@ -1,48 +1,84 @@ +/** + * @file nfc_protocol.c + * @brief Main protocol hierarchy definitions. + * + * To reduce code duplication, all NFC protocols are described as a tree, whose + * structure is shown in the diagram below. The (Start) node is actually + * nonexistent and is there only for clarity. + * + * All its child protocols are considered base protocols, which in turn serve + * as parents to other, usually vendor-specific ones. + * + * ``` + * **************************** Protocol tree structure *************************** + * + * (Start) + * | + * +------------------------+-----------+---------+------------+ + * | | | | | + * ISO14443-3A ISO14443-3B Felica ISO15693-3 ST25TB + * | | | + * +---------------+-------------+ ISO14443-4B SLIX + * | | | + * ISO14443-4A Mf Ultralight Mf Classic + * | + * Mf Desfire + * ``` + * + * When implementing a new protocol, its place in the tree must be determined first. + * If no appropriate base protocols exists, then it must be a base protocol itself. + * + * This file is to be modified upon adding a new protocol (see below). + * + */ #include "nfc_protocol.h" #include -typedef struct { - NfcProtocol parent_protocol; - size_t children_num; - const NfcProtocol* children_protocol; -} NfcProtocolTreeNode; - -/**************************** Protocol tree structure **************************** - * - * (Start) - * | - * +------------------------+-----------+---------+------------+ - * | | | | | - * ISO14443-3A ISO14443-3B Felica ISO15693-3 ST25TB - * | | | - * +---------------+-------------+ ISO14443-4B SLIX - * | | | - * ISO14443-4A Mf Ultralight Mf Classic - * | - * +-----+-----+ - * | | - * Mf Desfire Bank Card +/** + * @brief Tree node describing a protocol. * + * All base protocols (see above) have NfcProtocolInvalid + * in the parent_protocol field. */ +typedef struct { + NfcProtocol parent_protocol; /**< Parent protocol identifier. */ + size_t children_num; /** < Number of the child protocols. */ + const NfcProtocol* children_protocol; /**< Pointer to an array of child protocol identifiers. */ +} NfcProtocolTreeNode; +/** List of ISO14443-3A child protocols. */ static const NfcProtocol nfc_protocol_iso14443_3a_children_protocol[] = { NfcProtocolIso14443_4a, NfcProtocolMfUltralight, }; +/** List of ISO14443-3B child protocols. */ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = { NfcProtocolIso14443_4b, }; +/** List of ISO14443-4A child protocols. */ static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { NfcProtocolMfDesfire, }; +/** List of ISO115693-3 child protocols. */ static const NfcProtocol nfc_protocol_iso15693_3_children_protocol[] = { NfcProtocolSlix, }; +/* Add new child protocol lists here (if necessary) */ + +/** + * @brief Flattened description of the NFC protocol tree. + * + * When implementing a new protocol, add the node here under its + * own index defined in nfc_protocol.h. + * + * Additionally, if it has an already implemented protocol as a parent, + * add its identifier to its respective list of child protocols (see above). + */ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { [NfcProtocolIso14443_3a] = { @@ -110,6 +146,7 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { .children_num = 0, .children_protocol = NULL, }, + /* Add new protocols here */ }; NfcProtocol nfc_protocol_get_parent(NfcProtocol protocol) { diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h index 4e4a5567c47..55aa8a5895e 100644 --- a/lib/nfc/protocols/nfc_protocol.h +++ b/lib/nfc/protocols/nfc_protocol.h @@ -1,3 +1,166 @@ +/** + * @file nfc_protocol.h + * @brief Top-level NFC protocol definitions. + * + * This file is to be modified upon adding a new protocol (see below). + * + * # How to add a new NFC protocol + * + * ## 1. Gather information + * + * Having proper protocol documentation would be ideal, although they are often available only for a fee, or given under an NDA. + * As an alternative, reading code from relevant open-source projects or notes gathered by means of reverse engineering will do. + * + * ### 1.1 Technology + * + * Check whether the NFC technology required for the protocol is supported. If no support exists, adding the protocol may + * be problematic, since it is highly hardware-dependent. + * + * @see NfcTech for the enumeration of supported NFC technologies. + * + * ### 1.2 Base protocols + * + * Check whether the protocol to be implemented is built on top of some already supported protocol. + * + * @see NfcProtocol for the enumeration of supported NFC protocols. + * + * If the answer is "yes", then the protocol to be implemented is a child protocol. If no, then it will become a base protocol. + * Sometimes it will be necessary to implement both, e.g. when the target protocol is built on top of a base one, + * but the latter is currently not supported. + * + * ## 2. Create the files + * + * ### 2.1 Recommended file structure + * + * The recommended file structure for a protocol is as follows: + * + * ```text + * protocols + * | + * +- protocol_name + * | + * +- protocol_name.h + * | + * +- protocol_name.c + * | + * +- protocol_name_device_defs.h + * | + * +- protocol_name_poller.h + * | + * +- protocol_name_poller.c + * | + * +- protocol_name_poller_defs.h + * | + * . + * . (files below are optional) + * . + * | + * +- protocol_name_listener.h | + * | | + * +- protocol_name_listener.c |- add for emulation support + * | | + * +- protocol_name_listener_defs.h | + * | + * +- protocol_name_sync_api.h | + * | |- add for synchronous API support + * +- protocol_name_sync_api.c | + * | + * ``` + * + * Additionally, an arbitrary amount of private `protocol_name_*_i.h` header files may be created. Do not put implementation + * details in the regular header files, as they will be exposed in the public firmware API later on. + * + * ### 2.2 File structure explanation + * + * | Filename | Explanation | + * |:------------------------------|:------------| + * | protocol_name.h | Main protocol data structure and associated functions declarations. It is recommended to keep the former as opaque pointer. | + * | protocol_name.c | Implementations of functions declared in `protocol_name.h`. | + * | protocol_name_device_defs.h | Declarations for use by the NfcDevice library. See nfc_device_base_i.h for more info. | + * | protocol_name_poller.h | Protocol-specific poller and associated functions declarations. | + * | protocol_name_poller.c | Implementation of functions declared in `protocol_name_poller.h`. | + * | protocol_name_poller_defs.h | Declarations for use by the NfcPoller library. See nfc_poller_base.h for more info. | + * | protocol_name_listener.h | Protocol-specific listener and associated functions declarations. Optional, needed for emulation support. | + * | protocol_name_listener.c | Implementation of functions declared in `protocol_name_listener.h`. Optional, needed for emulation support. | + * | protocol_name_listener_defs.h | Declarations for use by the NfcListener library. See nfc_listener_base.h for more info. Optional, needed for emulation support. | + * | protocol_name_sync_api.h | Synchronous API declarations. (See below for sync API explanation). Optional.| + * | protocol_name_sync_api.c | Synchronous API implementation. Optional. | + * + * ## 3 Implement the code + * + * ### 3.1 Protocol data structure + * + * A protocol data structure is what holds all data that can be possibly read from a card of a certain type. It may include a unique identifier (UID), + * configuration bytes and flags, built-in memory, and so on. + * Additionally, it must implement the NfcDevice interface so that it could be used by the firmware. + * + * @see nfc_device_base_i.h for the device interface description. + * + * @note It is strongly recommended to implement such a structure as an opaque type and access it via specialised methods only. + * + * If the protocol being implemented is a child protocol, then its data must include a pointer to the parent protocol data structure. + * It is the protocol's responsibility to correctly create and delete the instance the pointer is pointing to. + * + * ### 3.2 Poller (reading functionality) + * + * A poller contains the functions necessary to successfully read a card of supported type. It must also implement a specific interface, + * namely described by the NfcPollerBase type. + * + * Upon creation, a poller instance will receive either a pointer to the Nfc instance (if it's a base protocol), or a pointer to another poller + * instance (if it is a child protocol) as the `alloc()` parameter. + * + * @see nfc_poller_base.h for the poller interface description. + * + * ### 3.3 Listener (emulation functionality) + * + * A listener implementation is optional, needed only when emulation is required/possible. + * + * Same considerations apply to the listener as for the poller. Additionally, upon creation it will receive an additional parameter + * in the form of a pointer to the matching protocol data structure, which will be used during emulation. The listener instance + * does not own this data and does not need to worry about its deletion. + * + * @see nfc_listener_base.h for the listener interface description. + * + * ### 3.4 Synchronous API + * + * Synchronous API does exaclty what it says -- it provides a set of blocking functions for easier use of pollers. + * Instead of creating all necessary instances and setting up callbacks manually, it does it automatically, at the + * expense of some flexibility. + * + * The most notable use of sync API is in the supported card plugins, so it's a good idea to implement it if such a plugin + * is to be implemented afterwards. + * + * ### 3.5 Registering the protocol + * + * After completing the protocol, it must be registered within the NfcProtocol system in order for it to be usable. + * + * 1. In nfc_protocol.h, add a new entry in the NfcProtocol enum in the form of NfcProtocolProtocolName. + * 2. In nfc_protocol.c, add a new entry in the `nfc_protocol_nodes[]` array under the appropriate index. + * 1. If it is a child protocol, register it as a child in the respective `nfc_base_protocol_name_children_protocol[]` array. + * 2. If the protocol has children on its own, create a `nfc_protocol_name_children_protocol[]` array + * with respective identifiers and register it in the protocol entry added in step 2. + * 3. In nfc_device_defs.c, include `protocol_name_device_defs.h` and add a pointer to the + * `protocol_name_device_defs` structure under the appropriate index. + * 4. In nfc_poller_defs.c, include `protocol_name_poller_defs.h` and add a pointer to the + * `protocol_name_poller_defs` structure under the appropriate index. + * 5. (Optional) If emulation support was implemented, do the step 4, but for the listener. + * 6. Add `protocol_name.h`, `protocol_name_poller.h`, and optionally, `protocol_name_listener.h` + * and `protocol_name_sync_api.h` into the `SDK_HEADERS` list in the SConscript file. + * This will export the protocol's types and functions for use by the applications. + * 7. Done! + * + * ## What's next? + * + * It's about time to integrate the newly implemented protocol into the main NFC application. Without that, reading a card + * of this type would crash it. + * + * @see nfc_protocol_support.h for more information on protocol integration. + * + * After having done that, a supported card plugin may be implemented to take further advantage of the new protocol. + * + * @see nfc_supported_card_plugin.h for more information on supported card plugins. + * + */ #pragma once #include @@ -6,6 +169,12 @@ extern "C" { #endif +/** + * @brief Enumeration of all available NFC protocols. + * + * When implementing a new protocol, add its identifier before the + * NfcProtocolNum entry. + */ typedef enum { NfcProtocolIso14443_3a, NfcProtocolIso14443_3b, @@ -20,13 +189,29 @@ typedef enum { NfcProtocolSt25tb, /* Add new protocols here */ - NfcProtocolNum, + NfcProtocolNum, /**< Special value representing the number of available protocols. */ - NfcProtocolInvalid, + NfcProtocolInvalid, /**< Special value representing an invalid state. */ } NfcProtocol; +/** + * @brief Get the immediate parent of a specific protocol. + * + * @param[in] protocol identifier of the protocol in question. + * @returns parent protocol identifier if it has one, or NfcProtocolInvalid otherwise. + */ NfcProtocol nfc_protocol_get_parent(NfcProtocol protocol); +/** + * @brief Determine if a specific protocol has a parent on an arbitrary level. + * + * Unlike nfc_protocol_get_parent(), this function will traverse the full protocol hierarchy + * and check each parent node for the matching protocol type. + * + * @param[in] protocol identifier of the protocol in question. + * @param[in] parent_protocol identifier of the parent protocol in question. + * @returns true if the parent of given type exists, false otherwise. + */ bool nfc_protocol_has_parent(NfcProtocol protocol, NfcProtocol parent_protocol); #ifdef __cplusplus