From c840e9ad49576aabcea7fa640d69680a927b1158 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Wed, 9 Nov 2022 14:08:37 +0100 Subject: [PATCH 01/34] added ISO15693 reading, saving and revealing from privacy mode (unlock) --- .../main/nfc/scenes/nfc_scene_config.h | 4 + .../main/nfc/scenes/nfc_scene_extra_actions.c | 9 + .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 47 ++++- .../nfc/scenes/nfc_scene_nfcv_key_input.c | 47 +++++ .../main/nfc/scenes/nfc_scene_nfcv_menu.c | 53 ++++++ .../nfc/scenes/nfc_scene_nfcv_read_auth.c | 133 ++++++++++++++ .../nfc/scenes/nfc_scene_nfcv_unlock_menu.c | 62 +++++++ applications/main/nfc/scenes/nfc_scene_read.c | 5 + .../main/nfc/scenes/nfc_scene_save_success.c | 3 + fbt_options.py | 6 +- firmware/targets/f7/api_symbols.csv | 9 +- lib/nfc/nfc_device.c | 115 +++++++++++- lib/nfc/nfc_device.h | 4 + lib/nfc/nfc_worker.c | 170 ++++++++++++++++++ lib/nfc/nfc_worker.h | 5 + lib/nfc/nfc_worker_i.h | 2 + lib/nfc/protocols/nfcv.c | 79 ++++++++ lib/nfc/protocols/nfcv.h | 54 ++++++ lib/nfc/protocols/slix_l.c | 83 +++++++++ lib/nfc/protocols/slix_l.h | 18 ++ 20 files changed, 888 insertions(+), 20 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_menu.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_read_auth.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c create mode 100644 lib/nfc/protocols/nfcv.c create mode 100644 lib/nfc/protocols/nfcv.h create mode 100644 lib/nfc/protocols/slix_l.c create mode 100644 lib/nfc/protocols/slix_l.h diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 9b922add2..20c8a6faa 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -14,6 +14,10 @@ ADD_SCENE(nfc, file_select, FileSelect) ADD_SCENE(nfc, emulate_uid, EmulateUid) ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) ADD_SCENE(nfc, nfca_menu, NfcaMenu) +ADD_SCENE(nfc, nfcv_menu, NfcVMenu) +ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu) +ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) +ADD_SCENE(nfc, nfcv_read_auth, NfcVReadAuth) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index e888e9d35..0e2d4db37 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -3,6 +3,7 @@ enum SubmenuIndex { SubmenuIndexMfClassicKeys, SubmenuIndexMfUltralightUnlock, + SubmenuIndexNfcVUnlock, }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -27,6 +28,12 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexMfUltralightUnlock, nfc_scene_extra_actions_submenu_callback, nfc); + submenu_add_item( + submenu, + "Unlock SLIX-L", + SubmenuIndexNfcVUnlock, + nfc_scene_extra_actions_submenu_callback, + nfc); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } @@ -44,6 +51,8 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == SubmenuIndexMfUltralightUnlock) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } else if(event.event == SubmenuIndexNfcVUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu); } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); } diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 8f33972e0..38289e041 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -14,7 +14,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { NfcDeviceData* dev_data = &nfc->dev->dev_data; NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; - if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)) { + if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)|| (protocol == NfcDeviceProtocolSlixL)) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); text_scroll_height = 52; @@ -40,19 +40,47 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n"); + } else if(protocol == NfcDeviceProtocolSlixL) { + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); } else { furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); } // Set tag iso data - char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; - furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); - furi_string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + if(protocol == NfcDeviceProtocolSlixL) { + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + + furi_string_cat_printf(temp_str, "UID:\n"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf(temp_str, "\n"); + + furi_string_cat_printf(temp_str, "DSFID: %02X\n", nfcv_data->dsfid); + furi_string_cat_printf(temp_str, "AFI: %02X\n", nfcv_data->afi); + furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref); + furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); + furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + + furi_string_cat_printf(temp_str, "Data (%d byte)\n", nfcv_data->block_num * nfcv_data->block_size); + + for(int block = 0; block < nfcv_data->block_num; block++) { + for(int pos = 0; pos < nfcv_data->block_size; pos++) { + furi_string_cat_printf(temp_str, " %02X", nfcv_data->data[block * nfcv_data->block_size + pos]); + } + furi_string_cat_printf(temp_str, "\n"); + } + furi_string_cat_printf(temp_str, "\n"); + } else { + char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; + furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); + furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); } - furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); - furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); // Set application specific data if(protocol == NfcDeviceProtocolMifareDesfire) { @@ -120,6 +148,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { } else if(protocol == NfcDeviceProtocolMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData); consumed = true; + } else if(protocol == NfcDeviceProtocolSlixL) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); + consumed = true; } } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c new file mode 100644 index 000000000..c5c2254eb --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c @@ -0,0 +1,47 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_nfcv_key_input_byte_input_callback(void* context) { + Nfc* nfc = context; + + memcpy(nfc->dev->dev_data.nfcv_data.key_privacy, nfc->byte_input_store, 4); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_nfcv_key_input_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter The Password In Hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_nfcv_key_input_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 4); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadAuth); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_nfcv_key_input_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c new file mode 100644 index 000000000..5605c6c07 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -0,0 +1,53 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexSave, +}; + +void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); + + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSave) { + nfc->dev->format = NfcDeviceSaveFormatSlixL; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_nfcv_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_read_auth.c b/applications/main/nfc/scenes/nfc_scene_nfcv_read_auth.c new file mode 100644 index 000000000..db0076500 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_read_auth.c @@ -0,0 +1,133 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneNfcVReadStateIdle, + NfcSceneNfcVReadStateDetecting, + NfcSceneNfcVReadStateUnlocked, + NfcSceneNfcVReadStateAlreadyUnlocked, + NfcSceneNfcVReadStateNotSupportedCard, +} NfcSceneNfcVReadState; + +bool nfc_scene_nfcv_read_auth_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + + if(event == NfcWorkerEventNfcVPassKey) { + memcpy(nfc->dev->dev_data.nfcv_data.key_privacy, nfc->byte_input_store, 4); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + } + return true; +} + +void nfc_scene_nfcv_read_auth_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_nfcv_read_auth_set_state(Nfc* nfc, NfcSceneNfcVReadState state) { + NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data); + + uint32_t curr_state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVReadAuth); + if(curr_state != state) { + Popup* popup = nfc->popup; + if(state == NfcSceneNfcVReadStateDetecting) { + popup_reset(popup); + popup_set_text( + popup, "Put Tonie On\nFlipper's Back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50); + } else if(state == NfcSceneNfcVReadStateUnlocked) { + popup_reset(popup); + + notification_message(nfc->notifications, &sequence_success); + + popup_set_header(popup, "Successfully\nUnlocked!", 94, 3, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_read_auth_popup_callback); + popup_set_timeout(popup, 1500); + //popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + } else if(state == NfcSceneNfcVReadStateAlreadyUnlocked) { + popup_reset(popup); + + popup_set_header(popup, "Already\nUnlocked!", 94, 3, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_read_auth_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + } else if(state == NfcSceneNfcVReadStateNotSupportedCard) { + popup_reset(popup); + popup_set_header(popup, "Wrong Type Of Card!", 64, 3, AlignCenter, AlignTop); + popup_set_text( + popup, + nfcv_data->error, + 4, + 22, + AlignLeft, + AlignTop); + popup_set_icon(popup, 73, 20, &I_DolphinCommon_56x48); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVReadAuth, state); + } +} + +void nfc_scene_nfcv_read_auth_on_enter(void* context) { + Nfc* nfc = context; + + nfc_device_clear(nfc->dev); + // Setup view + nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateDetecting); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVReadAuth, + &nfc->dev->dev_data, + nfc_scene_nfcv_read_auth_worker_callback, + nfc); + + nfc_blink_read_start(nfc); +} + +bool nfc_scene_nfcv_read_auth_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventCardDetected) { + nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventAborted) { + nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateAlreadyUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateDetecting); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCardDetected) { + nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateNotSupportedCard); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneNfcVUnlockMenu); + } + return consumed; +} + +void nfc_scene_nfcv_read_auth_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + nfc_blink_stop(nfc); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVReadAuth, NfcSceneNfcVReadStateIdle); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c new file mode 100644 index 000000000..00dee01f9 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c @@ -0,0 +1,62 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexNfcVUnlockMenuManual, + SubmenuIndexNfcVUnlockMenuTonieBox, +}; + +void nfc_scene_nfcv_unlock_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_unlock_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + submenu_add_item( + submenu, + "Enter PWD Manually", + SubmenuIndexNfcVUnlockMenuManual, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As TonieBox", + SubmenuIndexNfcVUnlockMenuTonieBox, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNfcVUnlockMenuManual) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodManual; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadAuth); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVUnlockMenu, event.event); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_menu_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 1f82aef08..6b251a651 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -68,6 +68,11 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; + } else if(event.event == NfcWorkerEventReadNfcV) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index 34919cbd8..29901a67c 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -31,6 +31,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneSavedMenu); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneNfcDataInfo)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneNfcDataInfo); } else { consumed = scene_manager_search_and_switch_to_another_scene( nfc->scene_manager, NfcSceneFileSelect); diff --git a/fbt_options.py b/fbt_options.py index 11124b936..7c05b1305 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -8,13 +8,13 @@ TARGET_HW = 7 # Optimization flags ## Optimize for size -COMPACT = 0 +COMPACT = 1 ## Optimize for debugging experience -DEBUG = 1 +DEBUG = 0 # Suffix to add to files when building distribution # If OS environment has DIST_SUFFIX set, it will be used instead -DIST_SUFFIX = "local" +DIST_SUFFIX = "RevvoX" # Coprocessor firmware COPRO_OB_DATA = "scripts/ob.data" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 0c10258f7..c4d451ec5 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.3,, +Version,+,7.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1948,12 +1948,19 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* +Function,-,nfc_util_bytes2num,uint64_t,"uint8_t*, uint8_t" +Function,-,nfc_util_even_parity32,uint8_t,uint32_t +Function,-,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" +Function,-,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" Function,-,nfca_signal_alloc,NfcaSignal*, Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" Function,-,nfca_signal_free,void,NfcaSignal* +Function,-,nfcv_inventory,ReturnCode,uint8_t* +Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*" +Function,-,nfcv_read_sysinfo,ReturnCode,NfcVData* Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index a5e3fc14f..c7aeb9a06 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -50,6 +50,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_ furi_string_set(format_string, "Mifare Classic"); } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { furi_string_set(format_string, "Mifare DESFire"); + } else if(dev->format == NfcDeviceSaveFormatSlixL) { + furi_string_set(format_string, "NXP SLIX-L"); } else { furi_string_set(format_string, "Unknown"); } @@ -85,6 +87,11 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_st dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire; return true; } + if(furi_string_start_with_str(format_string, "NXP SLIX-L")) { + dev->format = NfcDeviceSaveFormatSlixL; + dev->dev_data.protocol = NfcDeviceProtocolSlixL; + return true; + } return false; } @@ -636,7 +643,87 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { return parsed; } -// Leave for backward compatibility +static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVData* data = &dev->dev_data.nfcv_data; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break; + if(!flipper_format_write_hex(file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) break; + if(!flipper_format_write_hex(file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) break; + if(!flipper_format_write_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_write_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_write_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + if(!flipper_format_write_hex(file, "Block Count", &(data->block_num), 1)) break; + if(!flipper_format_write_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_write_hex(file, "Data Content", data->data, data->block_num * data->block_size)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVData* data = &dev->dev_data.nfcv_data; + memset(data, 0, sizeof(NfcVData)); + + do { + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex( + file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + + if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + if(!flipper_format_read_hex(file, "Block Count", &(data->block_num), 1)) break; + if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_read_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) + break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + EmvData* data = &dev->dev_data.emv_data; + uint32_t data_temp = 0; + + do { + // Write Bank card specific data + if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break; + if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break; + if(!flipper_format_write_string_cstr(file, "Name", data->name)) break; + if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break; + if(data->exp_mon) { + uint8_t exp_data[2] = {data->exp_mon, data->exp_year}; + if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break; + } + if(data->country_code) { + data_temp = data->country_code; + if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break; + } + if(data->currency_code) { + data_temp = data->currency_code; + if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break; + } + saved = true; + } while(false); + + return saved; +} + bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; EmvData* data = &dev->dev_data.emv_data; @@ -1044,17 +1131,25 @@ static bool nfc_device_save_file( break; nfc_device_prepare_format_string(dev, temp_str); if(!flipper_format_write_string(file, "Device type", temp_str)) break; - // Write UID, ATQA, SAK - if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) + // Write UID + if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats except ISO15693")) break; if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - if(!flipper_format_write_hex(file, "ATQA", data->atqa, 2)) break; - if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + + if(dev->format != NfcDeviceSaveFormatSlixL) { + // Write ATQA, SAK + if(!flipper_format_write_hex(file, "ATQA", data->atqa, 2)) break; + if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + } // Save more data if necessary if(dev->format == NfcDeviceSaveFormatMifareUl) { if(!nfc_device_save_mifare_ul_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_save_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatSlixL) { + if(!nfc_device_save_slix_l_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatBankCard) { + if(!nfc_device_save_bank_card_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { // Save data if(!nfc_device_save_mifare_classic_data(file, dev)) break; @@ -1117,11 +1212,13 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_parse_format_string(dev, temp_str)) break; // Read and parse UID, ATQA and SAK if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break; - if(!(data_cnt == 4 || data_cnt == 7)) break; + if(!(data_cnt == 4 || data_cnt == 7 || data_cnt == 8)) break; data->uid_len = data_cnt; if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; - if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; - if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; + if(dev->format != NfcDeviceSaveFormatSlixL) { + if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; + if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; + } // Load CUID uint8_t* cuid_start = data->uid; if(data->uid_len == 7) { @@ -1136,6 +1233,8 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_load_mifare_classic_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_load_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatSlixL) { + if(!nfc_device_load_slix_l_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatBankCard) { if(!nfc_device_load_bank_card_data(file, dev)) break; } diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index c8e8517ae..eab3137b3 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -32,6 +33,7 @@ typedef enum { NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, + NfcDeviceProtocolSlixL, } NfcProtocol; typedef enum { @@ -40,6 +42,7 @@ typedef enum { NfcDeviceSaveFormatMifareUl, NfcDeviceSaveFormatMifareClassic, NfcDeviceSaveFormatMifareDesfire, + NfcDeviceSaveFormatSlixL, } NfcDeviceSaveFormat; typedef struct { @@ -63,6 +66,7 @@ typedef struct { MfUltralightData mf_ul_data; MfClassicData mf_classic_data; MifareDesfireData mf_df_data; + NfcVData nfcv_data; }; FuriString* parsed_data; } NfcDeviceData; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index e1e379a06..aecfa84de 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -109,6 +109,8 @@ int32_t nfc_worker_task(void* context) { nfc_worker_mf_classic_dict_attack(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { nfc_worker_analyze_reader(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVReadAuth) { + nfc_worker_nfcv_unlock(nfc_worker); } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); @@ -116,6 +118,145 @@ int32_t nfc_worker_task(void* context) { return 0; } + +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + FuriHalNfcTxRxContext tx_rx = {}; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + furi_hal_nfc_sleep(); + + while(nfc_worker->state == NfcWorkerStateNfcVReadAuth) { + + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_ll_txrx_on(); + furi_hal_nfc_ll_poll(); + if(furi_hal_nfc_ll_set_mode(FuriHalNfcModePollNfcv, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48) != FuriHalNfcReturnOk) { + break; + } + + furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER); + furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCV_POLLER); + furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); + furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCV); + + uint8_t rand[2]; + + furi_hal_console_printf("Detect presence\r\n"); + ReturnCode ret = slix_l_get_random(rand); + + if(ret == ERR_NONE) { + /* there is some chip, responding with a RAND */ + furi_hal_console_printf(" Chip detected. In privacy?\r\n"); + ret = nfcv_inventory(NULL); + + if(ret == ERR_NONE) { + /* chip is also visible, so no action required */ + furi_hal_console_printf(" => chip is already visible, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); + while(slix_l_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + } else { + /* chip is invisible, try to unlock */ + furi_hal_console_printf(" chip is invisible, unlocking\r\n"); + + if(nfcv_data->auth_method == NfcVAuthMethodManual) { + uint32_t key = 0; + + key |= nfc_worker->dev_data->nfcv_data.key_privacy[0] << 24; + key |= nfc_worker->dev_data->nfcv_data.key_privacy[1] << 16; + key |= nfc_worker->dev_data->nfcv_data.key_privacy[2] << 8; + key |= nfc_worker->dev_data->nfcv_data.key_privacy[3] << 0; + ret = slix_l_unlock(4, rand, key); + } else { + ret = slix_l_unlock(4, rand, 0x7FFD6E5B); + if(ret != ERR_NONE) { + /* main key failed, trying second one */ + furi_hal_console_printf(" trying second key after resetting\r\n"); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + if(slix_l_get_random(rand) != ERR_NONE) { + furi_hal_console_printf(" reset failed\r\n"); + } + ret = slix_l_unlock(4, rand, 0x0F0F0F0F); + } + } + if(ret == ERR_NONE) { + /* unlock succesful */ + furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + + while(slix_l_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + } else { + /* unlock failed */ + furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); + snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Passwords not\naccepted"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + /* wait for disappearing */ + while(slix_l_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + } + } + } else { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + } + + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_sleep(); + furi_delay_ms(100); + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + +static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + NfcVReader reader = {}; + NfcVData data = {}; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + do { + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; + if(!slix_l_read_card(&reader, &data)) break; + // Copy data + nfc_worker->dev_data->nfcv_data = data; + read_success = true; + } while(false); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + + return read_success; +} + static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MfUltralightReader reader = {}; @@ -329,6 +470,27 @@ static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t return card_read; } +static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + furi_assert(nfc_worker); + furi_assert(tx_rx); + + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + + bool card_read = false; + furi_hal_nfc_sleep(); + if(slix_l_check_card_type(nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5])) { + FURI_LOG_I(TAG, "NXP SLIX-L detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolSlixL; + card_read = nfc_worker_read_slix_l(nfc_worker, tx_rx); + } else { + FURI_LOG_I(TAG, "unknown detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; + card_read = true; + } + + return card_read; +} + void nfc_worker_read(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); @@ -376,6 +538,14 @@ void nfc_worker_read(NfcWorker* nfc_worker) { event = NfcWorkerEventReadUidNfcF; break; } else if(nfc_data->type == FuriHalNfcTypeV) { + FURI_LOG_I(TAG, "NfcV detected"); + if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) { + FURI_LOG_I(TAG, "nfc_worker_read_nfcv success"); + if(dev_data->protocol == NfcDeviceProtocolSlixL) { + event = NfcWorkerEventReadNfcV; + break; + } + } event = NfcWorkerEventReadUidNfcV; break; } diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index ce3a18241..e0361da0a 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -19,6 +19,7 @@ typedef enum { NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, + NfcWorkerStateNfcVReadAuth, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -40,6 +41,7 @@ typedef enum { NfcWorkerEventReadMfClassicDone, NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, + NfcWorkerEventReadNfcV, NfcWorkerEventReadBankCard, // Nfc worker common events @@ -67,6 +69,7 @@ typedef enum { // Mifare Ultralight events NfcWorkerEventMfUltralightPassKey, + NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key } NfcWorkerEvent; @@ -86,3 +89,5 @@ void nfc_worker_start( void* context); void nfc_worker_stop(NfcWorker* nfc_worker); +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); + diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index b9f69e620..d50d2a274 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include struct NfcWorker { diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c new file mode 100644 index 000000000..814d2e08a --- /dev/null +++ b/lib/nfc/protocols/nfcv.c @@ -0,0 +1,79 @@ +#include +#include "nfcv.h" +#include "nfc_util.h" +#include +#include "furi_hal_nfc.h" + +#define TAG "NfcV" + +ReturnCode nfcv_inventory(uint8_t* uid) { + uint16_t received = 0; + rfalNfcvInventoryRes res; + + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ReturnCode ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received); + + if(ret == ERR_NONE) { + if(uid != NULL) { + memcpy(uid, res.UID, 8); + } + } + + return ret; +} + +ReturnCode nfcv_read_blocks( + NfcVReader* reader, + NfcVData* data) { + + reader->blocks_read = 0; + + uint16_t received = 0; + for(size_t block = 0; block < data->block_num; block++) { + uint8_t rxBuf[32]; + FURI_LOG_D(TAG, "Reading block %d", block); + + ReturnCode ret = rfalNfcvPollerReadSingleBlock( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block, + rxBuf, sizeof(rxBuf), &received); + + if(ret != ERR_NONE) { + FURI_LOG_D(TAG, "failed to read: %d", ret); + return ret; + } + memcpy(&(data->data[block * data->block_size]), &rxBuf[1], data->block_size); + FURI_LOG_D(TAG, " %02X %02X %02X %02X", + data->data[block * data->block_size + 0], data->data[block * data->block_size + 1], + data->data[block * data->block_size + 2], data->data[block * data->block_size + 3]); + + reader->blocks_read++; + } + + FURI_LOG_D(TAG, "Read %d blocks", reader->blocks_read); + + return ERR_NONE; +} + +ReturnCode nfcv_read_sysinfo(NfcVData* data) { + uint8_t rxBuf[32]; + uint16_t received = 0; + + FURI_LOG_D(TAG, "Read SystemInformation..."); + + ReturnCode ret = rfalNfcvPollerGetSystemInformation( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, + rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + data->dsfid = rxBuf[10]; + data->afi = rxBuf[11]; + data->block_num = rxBuf[12] + 1; + data->block_size = rxBuf[13] + 1; + data->ic_ref = rxBuf[14]; + FURI_LOG_D(TAG, " DSFID %d, AFI %d, Blocks %d, Size %d, IC Ref %d", data->dsfid, data->afi, data->block_num, data->block_size, data->ic_ref); + return ret; + } + FURI_LOG_D(TAG, "Failed: %d", ret); + + return ret; +} \ No newline at end of file diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h new file mode 100644 index 000000000..5fd349963 --- /dev/null +++ b/lib/nfc/protocols/nfcv.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +#include +#include "nfc_util.h" +#include + + +#define NFCV_TOTAL_BLOCKS_MAX 32 +#define NFCV_BLOCK_SIZE 4 +#define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE*NFCV_TOTAL_BLOCKS_MAX) + +typedef enum { + NfcVAuthMethodManual, + NfcVAuthMethodTonieBox, +} NfcVAuthMethod; + +typedef enum { + NfcVTypeSlix, + NfcVTypeSlixS, + NfcVTypeSlixL, + NfcVTypeSlix2, +} NfcVType; + +typedef struct { + NfcVType type; + NfcVAuthMethod auth_method; + bool auth_success; + + uint8_t key_privacy[4]; + uint8_t key_destroy[4]; + uint8_t key_eas[4]; + + uint8_t dsfid; + uint8_t afi; + uint8_t ic_ref; + uint8_t block_num; + uint8_t block_size; + uint8_t data[NFCV_MAX_DUMP_SIZE]; + + char error[32]; +} NfcVData; + +typedef struct { + uint16_t blocks_to_read; + int16_t blocks_read; +} NfcVReader; + +ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data); +ReturnCode nfcv_read_sysinfo(NfcVData* data); +ReturnCode nfcv_inventory(uint8_t* uid); + diff --git a/lib/nfc/protocols/slix_l.c b/lib/nfc/protocols/slix_l.c new file mode 100644 index 000000000..0ce92d338 --- /dev/null +++ b/lib/nfc/protocols/slix_l.c @@ -0,0 +1,83 @@ + +#include +#include "nfcv.h" +#include "slix_l.h" +#include "nfc_util.h" +#include +#include "furi_hal_nfc.h" + + +bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2) { + if((UID0 == 0xE0) && (UID1 == 0x04) && (UID2 == 0x03)) { + return true; + } + return false; +} + +bool slix_l_read_card( + NfcVReader* reader, + NfcVData* data) { + furi_assert(reader); + furi_assert(data); + + if(nfcv_read_sysinfo(data) != ERR_NONE) { + return false; + } + + reader->blocks_to_read = data->block_num; + return (nfcv_read_blocks(reader, data) == ERR_NONE); +} + +ReturnCode slix_l_get_random(uint8_t* rand) { + uint16_t received = 0; + uint8_t rxBuf[32]; + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + ISO15693_CMD_NXP_GET_RANDOM_NUMBER, + RFAL_NFCV_REQ_FLAG_DEFAULT, + ISO15693_MANUFACTURER_NXP, + NULL, + NULL, + 0, + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + if(received != 3) { + return ERR_PROTO; + } + if(rand != NULL) { + memcpy(rand, &rxBuf[1], 2); + } + } + + return ret; +} + +ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password) { + furi_assert(rand); + + uint16_t received = 0; + uint8_t rxBuf[32]; + uint8_t cmd_set_pass[] = { + id, + rand[0] ^ ((password>>0) & 0xFF), + rand[1] ^ ((password>>8) & 0xFF), + rand[0] ^ ((password>>16) & 0xFF), + rand[1] ^ ((password>>24) & 0xFF) + }; + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + ISO15693_CMD_NXP_SET_PASSWORD, + RFAL_NFCV_REQ_FLAG_DATA_RATE, + ISO15693_MANUFACTURER_NXP, + NULL, + cmd_set_pass, + sizeof(cmd_set_pass), + rxBuf, + sizeof(rxBuf), + &received); + + return ret; +} diff --git a/lib/nfc/protocols/slix_l.h b/lib/nfc/protocols/slix_l.h new file mode 100644 index 000000000..94d51c74a --- /dev/null +++ b/lib/nfc/protocols/slix_l.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include "nfc_util.h" +#include + +#define ISO15693_CMD_NXP_GET_RANDOM_NUMBER 0xB2 +#define ISO15693_CMD_NXP_SET_PASSWORD 0xB3 +#define ISO15693_MANUFACTURER_NXP 0x04 + + +bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2); +bool slix_l_read_card(NfcVReader* reader, NfcVData* data); + +ReturnCode slix_l_get_random(uint8_t* rand); +ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password); + From fa74fc954121a476cab5e4efb6304cb1a4711bec Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Wed, 9 Nov 2022 21:19:12 +0100 Subject: [PATCH 02/34] fix naming ReadAuth->Unlock --- .../main/nfc/scenes/nfc_scene_config.h | 2 +- .../nfc/scenes/nfc_scene_nfcv_key_input.c | 2 +- .../nfc/scenes/nfc_scene_nfcv_read_auth.c | 56 +++++++++---------- .../nfc/scenes/nfc_scene_nfcv_unlock_menu.c | 2 +- lib/nfc/nfc_worker.c | 4 +- lib/nfc/nfc_worker.h | 2 +- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 20c8a6faa..1653a7edd 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -17,7 +17,7 @@ ADD_SCENE(nfc, nfca_menu, NfcaMenu) ADD_SCENE(nfc, nfcv_menu, NfcVMenu) ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu) ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) -ADD_SCENE(nfc, nfcv_read_auth, NfcVReadAuth) +ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c index c5c2254eb..d73498e7a 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c @@ -30,7 +30,7 @@ bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadAuth); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); DOLPHIN_DEED(DolphinDeedNfcRead); consumed = true; } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_read_auth.c b/applications/main/nfc/scenes/nfc_scene_nfcv_read_auth.c index db0076500..9e31e9743 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_read_auth.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_read_auth.c @@ -2,14 +2,14 @@ #include typedef enum { - NfcSceneNfcVReadStateIdle, - NfcSceneNfcVReadStateDetecting, - NfcSceneNfcVReadStateUnlocked, - NfcSceneNfcVReadStateAlreadyUnlocked, - NfcSceneNfcVReadStateNotSupportedCard, -} NfcSceneNfcVReadState; + NfcSceneNfcVUnlockStateIdle, + NfcSceneNfcVUnlockStateDetecting, + NfcSceneNfcVUnlockStateUnlocked, + NfcSceneNfcVUnlockStateAlreadyUnlocked, + NfcSceneNfcVUnlockStateNotSupportedCard, +} NfcSceneNfcVUnlockState; -bool nfc_scene_nfcv_read_auth_worker_callback(NfcWorkerEvent event, void* context) { +bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) { Nfc* nfc = context; if(event == NfcWorkerEventNfcVPassKey) { @@ -20,24 +20,24 @@ bool nfc_scene_nfcv_read_auth_worker_callback(NfcWorkerEvent event, void* contex return true; } -void nfc_scene_nfcv_read_auth_popup_callback(void* context) { +void nfc_scene_nfcv_unlock_popup_callback(void* context) { Nfc* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); } -void nfc_scene_nfcv_read_auth_set_state(Nfc* nfc, NfcSceneNfcVReadState state) { +void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data); uint32_t curr_state = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVReadAuth); + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock); if(curr_state != state) { Popup* popup = nfc->popup; - if(state == NfcSceneNfcVReadStateDetecting) { + if(state == NfcSceneNfcVUnlockStateDetecting) { popup_reset(popup); popup_set_text( popup, "Put Tonie On\nFlipper's Back", 97, 24, AlignCenter, AlignTop); popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50); - } else if(state == NfcSceneNfcVReadStateUnlocked) { + } else if(state == NfcSceneNfcVUnlockStateUnlocked) { popup_reset(popup); notification_message(nfc->notifications, &sequence_success); @@ -45,24 +45,24 @@ void nfc_scene_nfcv_read_auth_set_state(Nfc* nfc, NfcSceneNfcVReadState state) { popup_set_header(popup, "Successfully\nUnlocked!", 94, 3, AlignCenter, AlignTop); popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); popup_set_context(popup, nfc); - popup_set_callback(popup, nfc_scene_nfcv_read_auth_popup_callback); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); popup_set_timeout(popup, 1500); //popup_enable_timeout(popup); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - } else if(state == NfcSceneNfcVReadStateAlreadyUnlocked) { + } else if(state == NfcSceneNfcVUnlockStateAlreadyUnlocked) { popup_reset(popup); popup_set_header(popup, "Already\nUnlocked!", 94, 3, AlignCenter, AlignTop); popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); popup_set_context(popup, nfc); - popup_set_callback(popup, nfc_scene_nfcv_read_auth_popup_callback); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); popup_set_timeout(popup, 1500); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - } else if(state == NfcSceneNfcVReadStateNotSupportedCard) { + } else if(state == NfcSceneNfcVUnlockStateNotSupportedCard) { popup_reset(popup); popup_set_header(popup, "Wrong Type Of Card!", 64, 3, AlignCenter, AlignTop); popup_set_text( @@ -74,44 +74,44 @@ void nfc_scene_nfcv_read_auth_set_state(Nfc* nfc, NfcSceneNfcVReadState state) { AlignTop); popup_set_icon(popup, 73, 20, &I_DolphinCommon_56x48); } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVReadAuth, state); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock, state); } } -void nfc_scene_nfcv_read_auth_on_enter(void* context) { +void nfc_scene_nfcv_unlock_on_enter(void* context) { Nfc* nfc = context; nfc_device_clear(nfc->dev); // Setup view - nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateDetecting); + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); // Start worker nfc_worker_start( nfc->worker, - NfcWorkerStateNfcVReadAuth, + NfcWorkerStateNfcVUnlock, &nfc->dev->dev_data, - nfc_scene_nfcv_read_auth_worker_callback, + nfc_scene_nfcv_unlock_worker_callback, nfc); nfc_blink_read_start(nfc); } -bool nfc_scene_nfcv_read_auth_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_nfcv_unlock_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcWorkerEventCardDetected) { - nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateUnlocked); + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateUnlocked); consumed = true; } else if(event.event == NfcWorkerEventAborted) { - nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateAlreadyUnlocked); + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateAlreadyUnlocked); consumed = true; } else if(event.event == NfcWorkerEventNoCardDetected) { - nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateDetecting); + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); consumed = true; } else if(event.event == NfcWorkerEventWrongCardDetected) { - nfc_scene_nfcv_read_auth_set_state(nfc, NfcSceneNfcVReadStateNotSupportedCard); + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateNotSupportedCard); } } else if(event.type == SceneManagerEventTypeBack) { consumed = scene_manager_search_and_switch_to_previous_scene( @@ -120,7 +120,7 @@ bool nfc_scene_nfcv_read_auth_on_event(void* context, SceneManagerEvent event) { return consumed; } -void nfc_scene_nfcv_read_auth_on_exit(void* context) { +void nfc_scene_nfcv_unlock_on_exit(void* context) { Nfc* nfc = context; // Stop worker @@ -129,5 +129,5 @@ void nfc_scene_nfcv_read_auth_on_exit(void* context) { popup_reset(nfc->popup); nfc_blink_stop(nfc); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneNfcVReadAuth, NfcSceneNfcVReadStateIdle); + nfc->scene_manager, NfcSceneNfcVUnlock, NfcSceneNfcVUnlockStateIdle); } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c index 00dee01f9..afa19ee41 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c @@ -45,7 +45,7 @@ bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) consumed = true; } else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) { nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox; - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadAuth); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); DOLPHIN_DEED(DolphinDeedNfcRead); consumed = true; } diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index aecfa84de..76b21762d 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -109,7 +109,7 @@ int32_t nfc_worker_task(void* context) { nfc_worker_mf_classic_dict_attack(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { nfc_worker_analyze_reader(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateNfcVReadAuth) { + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlock) { nfc_worker_nfcv_unlock(nfc_worker); } furi_hal_nfc_sleep(); @@ -133,7 +133,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { furi_hal_nfc_sleep(); - while(nfc_worker->state == NfcWorkerStateNfcVReadAuth) { + while(nfc_worker->state == NfcWorkerStateNfcVUnlock) { furi_hal_nfc_exit_sleep(); furi_hal_nfc_ll_txrx_on(); diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index e0361da0a..8801ca025 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -19,7 +19,7 @@ typedef enum { NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, - NfcWorkerStateNfcVReadAuth, + NfcWorkerStateNfcVUnlock, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, From 8d2daa79862f0511ea444d97cd39c73da5573a0f Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Wed, 9 Nov 2022 21:20:33 +0100 Subject: [PATCH 03/34] rename file to match scene name --- .../{nfc_scene_nfcv_read_auth.c => nfc_scene_nfcv_unlock.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename applications/main/nfc/scenes/{nfc_scene_nfcv_read_auth.c => nfc_scene_nfcv_unlock.c} (100%) diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_read_auth.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c similarity index 100% rename from applications/main/nfc/scenes/nfc_scene_nfcv_read_auth.c rename to applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c From fe216b4ddd77256874572d66133f83248f2c9f0c Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Wed, 9 Nov 2022 22:59:09 +0100 Subject: [PATCH 04/34] implemented unlock, read, save sequence --- .../main/nfc/scenes/nfc_scene_nfcv_unlock.c | 63 +++++++++++++++++-- firmware/targets/f7/api_symbols.csv | 4 +- lib/nfc/nfc_worker.c | 28 ++++++--- lib/nfc/nfc_worker.h | 1 + lib/nfc/protocols/nfcv.c | 8 ++- lib/nfc/protocols/nfcv.h | 2 +- lib/nfc/protocols/slix_l.c | 11 ++-- lib/nfc/protocols/slix_l.h | 2 +- 8 files changed, 98 insertions(+), 21 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c index 9e31e9743..4b15c777c 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -9,7 +9,7 @@ typedef enum { NfcSceneNfcVUnlockStateNotSupportedCard, } NfcSceneNfcVUnlockState; -bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) { +static bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) { Nfc* nfc = context; if(event == NfcWorkerEventNfcVPassKey) { @@ -19,6 +19,45 @@ bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) } return true; } +/* +static void nfc_scene_nfcv_unlock_button_callback(GuiButtonType event, InputType type, void* context) { + furi_assert(context); + furi_assert(type); + Nfc* nfc = context; + + if(event == GuiButtonTypeCenter) { + if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { + nfc_worker_stop(nfc->worker); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVUnlock, + &nfc->dev->dev_data, + nfc_scene_nfcv_unlock_worker_callback, + nfc); + widget_add_button_element( + nfc->widget, + GuiButtonTypeCenter, + "Autosave", + nfc_scene_nfcv_unlock_button_callback, + nfc); + } else { + nfc_worker_stop(nfc->worker); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVUnlockAndSave, + &nfc->dev->dev_data, + nfc_scene_nfcv_unlock_worker_callback, + nfc); + widget_add_button_element( + nfc->widget, + GuiButtonTypeCenter, + "Unlock", + nfc_scene_nfcv_unlock_button_callback, + nfc); + } + notification_message(nfc->notifications, &sequence_single_vibro); + } +}*/ void nfc_scene_nfcv_unlock_popup_callback(void* context) { Nfc* nfc = context; @@ -26,6 +65,7 @@ void nfc_scene_nfcv_unlock_popup_callback(void* context) { } void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { + FuriHalNfcDevData* nfc_data = &(nfc->dev->dev_data.nfc_data); NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data); uint32_t curr_state = @@ -40,14 +80,28 @@ void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { } else if(state == NfcSceneNfcVUnlockStateUnlocked) { popup_reset(popup); + if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { + nfc_text_store_set(nfc, "SLIX-L_%02X%02X%02X%02X%02X%02X%02X%02X", + nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5], nfc_data->uid[4], + nfc_data->uid[3], nfc_data->uid[2], nfc_data->uid[1], nfc_data->uid[0]); + + nfc->dev->format = NfcDeviceSaveFormatSlixL; + + if(nfc_device_save(nfc->dev, nfc->text_store)) { + popup_set_header(popup, "Successfully\nsaved", 94, 3, AlignCenter, AlignTop); + } else { + popup_set_header(popup, "Unlocked but\nsave failed!", 94, 3, AlignCenter, AlignTop); + } + } else { + popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop); + } + notification_message(nfc->notifications, &sequence_success); - popup_set_header(popup, "Successfully\nUnlocked!", 94, 3, AlignCenter, AlignTop); popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); popup_set_context(popup, nfc); popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); popup_set_timeout(popup, 1500); - //popup_enable_timeout(popup); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); @@ -85,10 +139,11 @@ void nfc_scene_nfcv_unlock_on_enter(void* context) { // Setup view nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + // Start worker nfc_worker_start( nfc->worker, - NfcWorkerStateNfcVUnlock, + NfcWorkerStateNfcVUnlockAndSave, &nfc->dev->dev_data, nfc_scene_nfcv_unlock_worker_callback, nfc); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index c4d451ec5..306e2e559 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.4,, +Version,+,7.5,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1960,7 +1960,7 @@ Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" Function,-,nfca_signal_free,void,NfcaSignal* Function,-,nfcv_inventory,ReturnCode,uint8_t* Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*" -Function,-,nfcv_read_sysinfo,ReturnCode,NfcVData* +Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*" Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 76b21762d..a36ee4da3 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -111,6 +111,8 @@ int32_t nfc_worker_task(void* context) { nfc_worker_analyze_reader(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateNfcVUnlock) { nfc_worker_nfcv_unlock(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + nfc_worker_nfcv_unlock(nfc_worker); } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); @@ -133,7 +135,8 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { furi_hal_nfc_sleep(); - while(nfc_worker->state == NfcWorkerStateNfcVUnlock) { + while((nfc_worker->state == NfcWorkerStateNfcVUnlock) || + (nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) { furi_hal_nfc_exit_sleep(); furi_hal_nfc_ll_txrx_on(); @@ -195,12 +198,26 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { } if(ret == ERR_NONE) { /* unlock succesful */ - furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); - nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + NfcVReader reader; + + if(!slix_l_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { + furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); + snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + } else { + furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + } else { + furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } while(slix_l_get_random(NULL) == ERR_NONE) { furi_delay_ms(100); } + } else { /* unlock failed */ furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); @@ -235,7 +252,6 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; NfcVReader reader = {}; - NfcVData data = {}; if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); @@ -244,9 +260,7 @@ static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* do { if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; - if(!slix_l_read_card(&reader, &data)) break; - // Copy data - nfc_worker->dev_data->nfcv_data = data; + if(!slix_l_read_card(&reader, &nfc_worker->dev_data->nfc_data, &nfc_worker->dev_data->nfcv_data)) break; read_success = true; } while(false); diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 8801ca025..2910cb240 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -20,6 +20,7 @@ typedef enum { NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, NfcWorkerStateNfcVUnlock, + NfcWorkerStateNfcVUnlockAndSave, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 814d2e08a..b8933d438 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -54,7 +54,7 @@ ReturnCode nfcv_read_blocks( return ERR_NONE; } -ReturnCode nfcv_read_sysinfo(NfcVData* data) { +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data) { uint8_t rxBuf[32]; uint16_t received = 0; @@ -65,6 +65,12 @@ ReturnCode nfcv_read_sysinfo(NfcVData* data) { rxBuf, sizeof(rxBuf), &received); if(ret == ERR_NONE) { + nfc_data->type = FuriHalNfcTypeV; + nfc_data->uid_len = 8; + /* UID is stored reversed in this structure */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + nfc_data->uid[pos] = rxBuf[2 + (7 - pos)]; + } data->dsfid = rxBuf[10]; data->afi = rxBuf[11]; data->block_num = rxBuf[12] + 1; diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index 5fd349963..024dea42e 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -49,6 +49,6 @@ typedef struct { } NfcVReader; ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data); -ReturnCode nfcv_read_sysinfo(NfcVData* data); +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data); ReturnCode nfcv_inventory(uint8_t* uid); diff --git a/lib/nfc/protocols/slix_l.c b/lib/nfc/protocols/slix_l.c index 0ce92d338..b7d1bd174 100644 --- a/lib/nfc/protocols/slix_l.c +++ b/lib/nfc/protocols/slix_l.c @@ -16,16 +16,17 @@ bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2) { bool slix_l_read_card( NfcVReader* reader, - NfcVData* data) { + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data) { furi_assert(reader); - furi_assert(data); + furi_assert(nfcv_data); - if(nfcv_read_sysinfo(data) != ERR_NONE) { + if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) { return false; } - reader->blocks_to_read = data->block_num; - return (nfcv_read_blocks(reader, data) == ERR_NONE); + reader->blocks_to_read = nfcv_data->block_num; + return (nfcv_read_blocks(reader, nfcv_data) == ERR_NONE); } ReturnCode slix_l_get_random(uint8_t* rand) { diff --git a/lib/nfc/protocols/slix_l.h b/lib/nfc/protocols/slix_l.h index 94d51c74a..c40ced942 100644 --- a/lib/nfc/protocols/slix_l.h +++ b/lib/nfc/protocols/slix_l.h @@ -11,7 +11,7 @@ bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2); -bool slix_l_read_card(NfcVReader* reader, NfcVData* data); +bool slix_l_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data); ReturnCode slix_l_get_random(uint8_t* rand); ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password); From bcd33ca12589837a210225ecdc26bb98d357c860 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 14 Nov 2022 21:12:18 +0100 Subject: [PATCH 05/34] added NfcV emulation --- applications/main/nfc/nfc_cli.c | 224 +++++++ .../main/nfc/scenes/nfc_scene_config.h | 1 + .../main/nfc/scenes/nfc_scene_emulate_nfcv.c | 141 +++++ .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 10 +- .../main/nfc/scenes/nfc_scene_nfcv_menu.c | 13 +- .../main/nfc/scenes/nfc_scene_nfcv_unlock.c | 3 +- firmware/targets/f7/api_symbols.csv | 7 +- firmware/targets/f7/furi_hal/furi_hal_nfc.c | 33 + .../targets/furi_hal_include/furi_hal_nfc.h | 6 + furi/core/common_defines.h | 37 +- lib/digital_signal/digital_signal.c | 11 +- lib/nfc/nfc_device.h | 1 + lib/nfc/nfc_worker.c | 37 +- lib/nfc/nfc_worker.h | 3 +- lib/nfc/protocols/nfcv.c | 587 +++++++++++++++++- lib/nfc/protocols/nfcv.h | 69 ++ lib/nfc/protocols/slix_l.c | 14 - lib/nfc/protocols/slix_l.h | 1 - 18 files changed, 1150 insertions(+), 48 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index a6475ca68..cc2770731 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -1,11 +1,20 @@ #include #include +#include #include #include +#include +#include +#include +#include +#include +#include #include #include + + static void nfc_cli_print_usage() { printf("Usage:\r\n"); printf("nfc \r\n"); @@ -14,6 +23,10 @@ static void nfc_cli_print_usage() { printf("\temulate\t - emulate predefined nfca card\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("\tfield\t - turn field on\r\n"); + printf("\tst25r_read\t - read ST25R3916 register\r\n"); + printf("\tst25r_write\t - write ST25R3916 register\r\n"); + printf("\tst25r_cmd\t - execute ST25R3916 command\r\n"); + printf("\tst25r_fifo\t - wait for FIFO data\r\n"); } } @@ -98,6 +111,197 @@ static void nfc_cli_field(Cli* cli, FuriString* args) { furi_hal_nfc_sleep(); } +static uint8_t hexdigit(char hex) { + return (hex <= '9') ? hex - '0' : toupper(hex) - 'A' + 10 ; +} + +static uint8_t hexbyte(const char* hex) { + return (hexdigit(*hex) << 4) | hexdigit(*(hex+1)); +} + +static void nfc_cli_st25r_write(Cli* cli, FuriString* args) { + UNUSED(cli); + FuriString* reg = furi_string_alloc(); + FuriString* value = furi_string_alloc(); + + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + if(!args_read_string_and_trim(args, reg) || !args_read_string_and_trim(args, value)) { + printf("You didn't specify and \r\n"); + nfc_cli_print_usage(); + return; + } + + if(furi_string_utf8_length(reg) != 2 || furi_string_utf8_length(value) != 2) { + printf("You didn't specify and as 2-character hex values\r\n"); + nfc_cli_print_usage(); + return; + } + + uint8_t reg_val = hexbyte(furi_string_get_cstr(reg)); + uint8_t value_val = hexbyte(furi_string_get_cstr(value)); + + printf("W %02X <- %02X\r\n", reg_val, value_val); + if(st25r3916WriteRegister(reg_val, value_val) != ERR_NONE) { + printf("Failed to write register\r\n"); + return; + } +} + +static void nfc_cli_st25r_read(Cli* cli, FuriString* args) { + UNUSED(cli); + FuriString* reg = furi_string_alloc(); + + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + if(!args_read_string_and_trim(args, reg)) { + printf("You didn't specify \r\n"); + nfc_cli_print_usage(); + return; + } + + if(furi_string_utf8_length(reg) != 2) { + printf("You didn't specify as 2-character hex value\r\n"); + nfc_cli_print_usage(); + return; + } + + uint8_t reg_val = hexbyte(furi_string_get_cstr(reg)); + uint8_t value_val = 0; + + if(st25r3916ReadRegister(reg_val, &value_val) != ERR_NONE) { + printf("Failed to read register\r\n"); + return; + } + + printf("R %02X -> %02X\r\n", reg_val, value_val); +} + +static void nfc_cli_st25r_cmd(Cli* cli, FuriString* args) { + UNUSED(cli); + FuriString* cmd = furi_string_alloc(); + + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + if(!args_read_string_and_trim(args, cmd)) { + printf("You didn't specify \r\n"); + nfc_cli_print_usage(); + return; + } + + if(furi_string_utf8_length(cmd) != 2) { + printf("You didn't specify as 2-character hex value\r\n"); + nfc_cli_print_usage(); + return; + } + + uint8_t cmd_val = hexbyte(furi_string_get_cstr(cmd)); + + if(st25r3916ExecuteCommand(cmd_val) != ERR_NONE) { + printf("Failed to execute\r\n"); + return; + } + + printf("X %02X\r\n", cmd_val); +} + +static FuriHalNfcTxRxContext tx_rx = {}; + +static void nfc_cli_st25r_fifo(Cli* cli, FuriString* args) { + UNUSED(args); + + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + bool field = false; + + tx_rx.sniff_tx = NULL; + tx_rx.sniff_rx = NULL; + tx_rx.tx_bits = 0; + tx_rx.tx_rx_type = FuriHalNfcTxRxTypeRxRaw; + + rfal_platform_spi_acquire(); + + furi_hal_nfcv_listen_start(); + + printf("Reading FIFO...\r\nPress Ctrl+C to abort\r\n"); + while(!cli_cmd_interrupt_received(cli)) { + + if(st25r3916IsExtFieldOn() && !field) { + printf("Field entered\r\n"); + field = true; + } + if(!st25r3916IsExtFieldOn() && field) { + printf("Field left\r\n"); + field = false; + } + + if(furi_hal_nfc_listen_rx(&tx_rx, 50)) { + for(int pos = 0; pos < tx_rx.rx_bits / 8; pos++) { + printf(" %02X", tx_rx.rx_data[pos]); + } + printf("\r\n"); + } + + furi_delay_ms(50); + } + rfal_platform_spi_release(); +} + + +static void nfc_cli_st25r_trans(Cli* cli, FuriString* args) { + UNUSED(args); + + // Check if nfc worker is not busy + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + printf("ISO15693 emulator...\r\nPress Ctrl+C to abort\r\n"); + + FuriHalNfcDevData nfc_data = { + .uid = { 0x36, 0x78, 0x45, 0x0E, 0x50, 0x03, 0x04, 0xE0 }, + .uid_len = 8, + .type = FuriHalNfcTypeV, + }; + NfcVData nfcv_data = { + .afi = 0, + .dsfid = 0, + .block_num = 8, + .block_size = 4, + .ic_ref = 3, + .key_privacy = { 0x0F, 0x0F, 0x0F, 0x0F }, + .privacy = false + }; + + memset(nfcv_data.data, 0xAE, 4 * 8); + + nfcv_emu_init(); + while(!cli_cmd_interrupt_received(cli)) { + if(nfcv_emu_loop(&nfc_data, &nfcv_data, 1000)) { + printf("[NfcV-Emu] %s\r\n", nfcv_data.last_command); + } + } + + nfcv_emu_deinit(); +} + static void nfc_cli(Cli* cli, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; @@ -122,6 +326,26 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) { nfc_cli_field(cli, args); break; } + if(furi_string_cmp_str(cmd, "st25r_read") == 0) { + nfc_cli_st25r_read(cli, args); + break; + } + if(furi_string_cmp_str(cmd, "st25r_write") == 0) { + nfc_cli_st25r_write(cli, args); + break; + } + if(furi_string_cmp_str(cmd, "st25r_cmd") == 0) { + nfc_cli_st25r_cmd(cli, args); + break; + } + if(furi_string_cmp_str(cmd, "st25r_fifo") == 0) { + nfc_cli_st25r_fifo(cli, args); + break; + } + if(furi_string_cmp_str(cmd, "st25r_trans") == 0) { + nfc_cli_st25r_trans(cli, args); + break; + } } nfc_cli_print_usage(); diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 1653a7edd..33f37f3e3 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -12,6 +12,7 @@ ADD_SCENE(nfc, save_name, SaveName) ADD_SCENE(nfc, save_success, SaveSuccess) ADD_SCENE(nfc, file_select, FileSelect) ADD_SCENE(nfc, emulate_uid, EmulateUid) +ADD_SCENE(nfc, emulate_nfcv, EmulateNfcV) ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) ADD_SCENE(nfc, nfca_menu, NfcaMenu) ADD_SCENE(nfc, nfcv_menu, NfcVMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c new file mode 100644 index 000000000..70f0c8ba0 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c @@ -0,0 +1,141 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200) + +enum { + NfcSceneEmulateNfcVStateWidget, + NfcSceneEmulateNfcVStateTextBox, +}; + +bool nfc_emulate_nfcv_worker_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); + return true; +} + +void nfc_scene_emulate_nfcv_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_emulate_nfcv_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +// Add widget with device name or inform that data received +static void nfc_scene_emulate_nfcv_widget_config(Nfc* nfc, bool data_received) { + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); + widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating NfcV"); + if(strcmp(nfc->dev->dev_name, "")) { + furi_string_printf(info_str, "%s", nfc->dev->dev_name); + } else { + for(uint8_t i = 0; i < data->uid_len; i++) { + furi_string_cat_printf(info_str, "%02X ", data->uid[i]); + } + } + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_emulate_nfcv_widget_callback, nfc); + } +} + +void nfc_scene_emulate_nfcv_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_emulate_nfcv_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_emulate_nfcv_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_emulate_nfcv_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateNfcV); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventWorkerExit) { + // Add data button to widget if data is received for the first time + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_emulate_nfcv_widget_config(nfc, true); + } + // Update TextBox data + if(furi_string_size(nfc->text_box_store) < NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX) { + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneEmulateNfcVStateWidget) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateTextBox); + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneEmulateNfcVStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneEmulateNfcVStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_emulate_nfcv_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 38289e041..98d11f2dc 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -14,7 +14,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { NfcDeviceData* dev_data = &nfc->dev->dev_data; NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; - if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)|| (protocol == NfcDeviceProtocolSlixL)) { + if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) + || (protocol == NfcDeviceProtocolSlixL) || (protocol == NfcDeviceProtocolNfcV)) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); text_scroll_height = 52; @@ -40,6 +41,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n"); + } else if(protocol == NfcDeviceProtocolNfcV) { + furi_string_cat_printf(temp_str, "\e#ISO15693\n"); } else if(protocol == NfcDeviceProtocolSlixL) { furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); } else { @@ -47,7 +50,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { } // Set tag iso data - if(protocol == NfcDeviceProtocolSlixL) { + if((protocol == NfcDeviceProtocolSlixL) || (protocol == NfcDeviceProtocolNfcV)) { NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; furi_string_cat_printf(temp_str, "UID:\n"); @@ -151,6 +154,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { } else if(protocol == NfcDeviceProtocolSlixL) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); consumed = true; + } else if(protocol == NfcDeviceProtocolNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); + consumed = true; } } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c index 5605c6c07..fdf7de9ff 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -3,6 +3,7 @@ enum SubmenuIndex { SubmenuIndexSave, + SubmenuIndexEmulate, }; void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) { @@ -17,6 +18,8 @@ void nfc_scene_nfcv_menu_on_enter(void* context) { submenu_add_item( submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_nfcv_menu_submenu_callback, nfc); submenu_set_selected_item( nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu)); @@ -35,7 +38,15 @@ bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { nfc_device_set_name(nfc->dev, ""); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; - } + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } + consumed = true; + } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event); } else if(event.type == SceneManagerEventTypeBack) { diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c index 4b15c777c..b9050e0fc 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -96,7 +96,8 @@ void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop); } - notification_message(nfc->notifications, &sequence_success); + notification_message(nfc->notifications, &sequence_single_vibro); + //notification_message(nfc->notifications, &sequence_success); popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); popup_set_context(popup, nfc); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 306e2e559..2aaf7c8dc 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.5,, +Version,+,7.12,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1181,6 +1181,7 @@ Function,+,furi_hal_nfc_stop,void, Function,+,furi_hal_nfc_stop_cmd,void, Function,+,furi_hal_nfc_tx_rx,_Bool,"FuriHalNfcTxRxContext*, uint16_t" Function,+,furi_hal_nfc_tx_rx_full,_Bool,FuriHalNfcTxRxContext* +Function,-,furi_hal_nfcv_listen_start,void, Function,-,furi_hal_os_init,void, Function,+,furi_hal_os_tick,void, Function,+,furi_hal_power_check_otg_status,void, @@ -1958,8 +1959,12 @@ Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" Function,-,nfca_signal_alloc,NfcaSignal*, Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" Function,-,nfca_signal_free,void,NfcaSignal* +Function,-,nfcv_emu_deinit,void, +Function,-,nfcv_emu_init,void, +Function,-,nfcv_emu_loop,_Bool,"FuriHalNfcDevData*, NfcVData*, uint32_t" Function,-,nfcv_inventory,ReturnCode,uint8_t* Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*" +Function,-,nfcv_read_card,_Bool,"NfcVReader*, FuriHalNfcDevData*, NfcVData*" Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*" Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 2d27313ae..b95d490af 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -367,6 +367,39 @@ void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data) { st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE); } +void furi_hal_nfcv_listen_start() { + + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + // Clear interrupts + st25r3916ClearInterrupts(); + // Mask all interrupts + st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_ALL); + // RESET + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); + // Setup registers + st25r3916WriteRegister( + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + st25r3916WriteRegister( + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om3 | ST25R3916_REG_MODE_om0); + st25r3916WriteRegister( + ST25R3916_REG_PASSIVE_TARGET, + ST25R3916_REG_PASSIVE_TARGET_fdel_2 | ST25R3916_REG_PASSIVE_TARGET_fdel_0 | + ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p | ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r); + st25r3916WriteRegister(ST25R3916_REG_MASK_RX_TIMER, 0x02); + + // Mask interrupts + uint32_t clear_irq_mask = + (ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_RXE_PTA | ST25R3916_IRQ_MASK_WU_A_X | + ST25R3916_IRQ_MASK_WU_A); + st25r3916EnableInterrupts(clear_irq_mask); + + // Go to sense + st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE); +} + void rfal_interrupt_callback_handler() { furi_event_flag_set(event, EVENT_FLAG_INTERRUPT); } diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index d3f6de602..1363a1572 100644 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -177,6 +177,12 @@ bool furi_hal_nfc_listen( */ void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data); +/** Start Target Listen mode + * @note RFAL free implementation + * + */ +void furi_hal_nfcv_listen_start(); + /** Read data in Target Listen mode * @note Must be called only after furi_hal_nfc_listen_start() * diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index 31be7fff0..c5ffbf454 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -23,18 +23,33 @@ extern "C" { #define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED()) #endif +#ifndef FURI_CRITICAL_DEFINE +#define FURI_CRITICAL_DEFINE() \ + uint32_t __isrm = 0; \ + bool __from_isr = false; \ + bool __kernel_running = false; +#endif + +#ifndef FURI_CRITICAL_ENTER_ADV +#define FURI_CRITICAL_ENTER_ADV() \ + do { \ + __isrm = 0; \ + __from_isr = FURI_IS_ISR(); \ + __kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); \ + if(__from_isr) { \ + __isrm = taskENTER_CRITICAL_FROM_ISR(); \ + } else if(__kernel_running) { \ + taskENTER_CRITICAL(); \ + } else { \ + __disable_irq(); \ + } \ + } while(0) +#endif + #ifndef FURI_CRITICAL_ENTER -#define FURI_CRITICAL_ENTER() \ - uint32_t __isrm = 0; \ - bool __from_isr = FURI_IS_ISR(); \ - bool __kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); \ - if(__from_isr) { \ - __isrm = taskENTER_CRITICAL_FROM_ISR(); \ - } else if(__kernel_running) { \ - taskENTER_CRITICAL(); \ - } else { \ - __disable_irq(); \ - } +#define FURI_CRITICAL_ENTER() \ + FURI_CRITICAL_DEFINE(); \ + FURI_CRITICAL_ENTER_ADV(); #endif #ifndef FURI_CRITICAL_EXIT diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 46ca307a7..c855f188b 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -16,7 +16,7 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->start_level = true; signal->edges_max_cnt = max_edges_cnt; signal->edge_timings = malloc(max_edges_cnt * sizeof(uint32_t)); - signal->reload_reg_buff = malloc(max_edges_cnt * sizeof(uint32_t)); + signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); signal->edge_cnt = 0; return signal; @@ -37,7 +37,10 @@ bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) { if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) { return false; } - + /* in case there are no edges in our target signal, the signal to append makes the rules */ + if(!signal_a->edge_cnt) { + signal_a->start_level = signal_b->start_level; + } bool end_level = signal_a->start_level; if(signal_a->edge_cnt) { end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2); @@ -84,6 +87,7 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { uint32_t r_count_tick_arr = 0; uint32_t r_rest_div = 0; + for(size_t i = 0; i < signal->edge_cnt - 1; i++) { r_count_tick_arr = t_signal_rest / T_TIM; r_rest_div = t_signal_rest % T_TIM; @@ -162,7 +166,8 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { LL_TIM_EnableCounter(TIM2); while(!LL_DMA_IsActiveFlag_TC2(DMA1)) - ; + { + } LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC2(DMA1); diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index eab3137b3..d0299fea2 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -33,6 +33,7 @@ typedef enum { NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, + NfcDeviceProtocolNfcV, NfcDeviceProtocolSlixL, } NfcProtocol; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index a36ee4da3..98f88a0ab 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -93,6 +93,8 @@ int32_t nfc_worker_task(void* context) { nfc_worker_read(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateUidEmulate) { nfc_worker_emulate_uid(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + nfc_worker_emulate_nfcv(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { nfc_worker_emulate_apdu(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { @@ -135,6 +137,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { furi_hal_nfc_sleep(); + while((nfc_worker->state == NfcWorkerStateNfcVUnlock) || (nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) { @@ -201,7 +204,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { NfcVReader reader; - if(!slix_l_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { + if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); @@ -249,7 +252,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { } } -static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { +static bool nfc_worker_read_nfcv_content(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; NfcVReader reader = {}; @@ -260,7 +263,7 @@ static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* do { if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; - if(!slix_l_read_card(&reader, &nfc_worker->dev_data->nfc_data, &nfc_worker->dev_data->nfcv_data)) break; + if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, &nfc_worker->dev_data->nfcv_data)) break; read_success = true; } while(false); @@ -271,6 +274,7 @@ static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* return read_success; } + static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MfUltralightReader reader = {}; @@ -495,11 +499,11 @@ static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t if(slix_l_check_card_type(nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5])) { FURI_LOG_I(TAG, "NXP SLIX-L detected"); nfc_worker->dev_data->protocol = NfcDeviceProtocolSlixL; - card_read = nfc_worker_read_slix_l(nfc_worker, tx_rx); + card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx); } else { FURI_LOG_I(TAG, "unknown detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; - card_read = true; + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; + card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx); } return card_read; @@ -555,10 +559,8 @@ void nfc_worker_read(NfcWorker* nfc_worker) { FURI_LOG_I(TAG, "NfcV detected"); if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) { FURI_LOG_I(TAG, "nfc_worker_read_nfcv success"); - if(dev_data->protocol == NfcDeviceProtocolSlixL) { - event = NfcWorkerEventReadNfcV; - break; - } + event = NfcWorkerEventReadNfcV; + break; } event = NfcWorkerEventReadUidNfcV; break; @@ -603,6 +605,21 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { } } +void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker) { + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + nfcv_emu_init(); + while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + if(nfcv_emu_loop(nfc_data, nfcv_data, 1000)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + } + } + } + nfcv_emu_deinit(); +} + void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; FuriHalNfcDevData params = { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 2910cb240..6e8ef854d 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -19,6 +19,7 @@ typedef enum { NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, + NfcWorkerStateNfcVEmulate, NfcWorkerStateNfcVUnlock, NfcWorkerStateNfcVUnlockAndSave, // Debug @@ -91,4 +92,4 @@ void nfc_worker_start( void nfc_worker_stop(NfcWorker* nfc_worker); void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); - +void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker); diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index b8933d438..11604afef 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -1,8 +1,16 @@ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "nfcv.h" #include "nfc_util.h" -#include -#include "furi_hal_nfc.h" #define TAG "NfcV" @@ -82,4 +90,577 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data) { FURI_LOG_D(TAG, "Failed: %d", ret); return ret; -} \ No newline at end of file +} + +bool nfcv_read_card( + NfcVReader* reader, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data) { + furi_assert(reader); + furi_assert(nfcv_data); + + if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) { + return false; + } + + reader->blocks_to_read = nfcv_data->block_num; + return (nfcv_read_blocks(reader, nfcv_data) == ERR_NONE); +} + +/* emulation part */ + +static const uint32_t clocks_in_ms = 64 * 1000; +static const uint32_t bit_time = 64 * 9.44f; + +DigitalSignal* nfcv_resp_pulse_32 = NULL; +DigitalSignal* nfcv_resp_unmod = NULL; +DigitalSignal* nfcv_resp_one = NULL; +DigitalSignal* nfcv_resp_zero = NULL; +DigitalSignal* nfcv_resp_sof = NULL; +DigitalSignal* nfcv_resp_eof = NULL; +DigitalSignal* nfcv_resp_unmod_256 = NULL; +DigitalSignal* nfcv_resp_unmod_768 = NULL; + +DigitalSignal* nfcv_signal = NULL; + + +void nfcv_crc(uint8_t* data, uint32_t length, uint8_t* out) { + uint32_t reg = 0xFFFF; + uint32_t i = 0; + uint32_t j = 0; + + for (i = 0; i < length; i++) { + reg = reg ^ ((uint32_t)data[i]); + for (j = 0; j < 8; j++) { + if (reg & 0x0001) { + reg = (reg >> 1) ^ 0x8408; + } else { + reg = (reg >> 1); + } + } + } + + uint16_t crc = ~(uint16_t)(reg & 0xffff); + + out[0] = crc & 0xFF; + out[1] = crc >> 8; +} + +void nfcv_emu_free() { + digital_signal_free(nfcv_signal); + digital_signal_free(nfcv_resp_unmod_256); + digital_signal_free(nfcv_resp_pulse_32); + digital_signal_free(nfcv_resp_one); + digital_signal_free(nfcv_resp_zero); + digital_signal_free(nfcv_resp_sof); + digital_signal_free(nfcv_resp_eof); + + nfcv_signal = NULL; + nfcv_resp_unmod_256 = NULL; + nfcv_resp_pulse_32 = NULL; + nfcv_resp_one = NULL; + nfcv_resp_zero = NULL; + nfcv_resp_sof = NULL; + nfcv_resp_eof = NULL; +} + +void nfcv_emu_alloc() { + + if(!nfcv_signal) { + nfcv_signal = digital_signal_alloc(8192); + } + + if(!nfcv_resp_unmod_256) { + /* unmodulated 256/fc signal as building block */ + nfcv_resp_unmod_256 = digital_signal_alloc(4); + nfcv_resp_unmod_256->start_level = false; + nfcv_resp_unmod_256->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S); + nfcv_resp_unmod_256->edge_cnt = 1; + } + if(!nfcv_resp_pulse_32) { + /* modulated fc/32 pulse as building block */ + nfcv_resp_pulse_32 = digital_signal_alloc(4); + nfcv_resp_pulse_32->start_level = true; + nfcv_resp_pulse_32->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_resp_pulse_32->edge_timings[1] = (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_resp_pulse_32->edge_cnt = 2; + } + if(!nfcv_resp_one) { + /* logical one: 256/fc unmodulated then 8 pulses fc/32 */ + nfcv_resp_one = digital_signal_alloc(24); + digital_signal_append(nfcv_resp_one, nfcv_resp_unmod_256); + for(size_t i = 0; i < 8; i++) { + digital_signal_append(nfcv_resp_one, nfcv_resp_pulse_32); + } + } + if(!nfcv_resp_zero) { + /* logical zero: 8 pulses fc/32 then 256/fc unmodulated */ + nfcv_resp_zero = digital_signal_alloc(24); + for(size_t i = 0; i < 8; i++) { + digital_signal_append(nfcv_resp_zero, nfcv_resp_pulse_32); + } + digital_signal_append(nfcv_resp_zero, nfcv_resp_unmod_256); + } + if(!nfcv_resp_sof) { + /* SOF: unmodulated 768/fc, 24 pulses fc/32, logic 1 */ + nfcv_resp_sof = digital_signal_alloc(128); + digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256); + digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256); + digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256); + for(size_t i = 0; i < 24; i++) { + digital_signal_append(nfcv_resp_sof, nfcv_resp_pulse_32); + } + digital_signal_append(nfcv_resp_sof, nfcv_resp_one); + } + if(!nfcv_resp_eof) { + /* EOF: logic 0, 24 pulses fc/32, unmodulated 768/fc */ + nfcv_resp_eof = digital_signal_alloc(128); + digital_signal_append(nfcv_resp_eof, nfcv_resp_zero); + for(size_t i = 0; i < 24; i++) { + digital_signal_append(nfcv_resp_eof, nfcv_resp_pulse_32); + } + digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256); + digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256); + digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256); + /* add extra silence */ + digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256); + } +} + + +void nfcv_emu_send_raw(uint8_t* data, uint8_t length) { + + int bits = length * 8; + + nfcv_signal->start_level = false; + nfcv_signal->edge_cnt = 0; + + digital_signal_append(nfcv_signal, nfcv_resp_sof); + + for(int bit_total = 0; bit_total < bits; bit_total++) { + uint32_t byte_pos = bit_total / 8; + uint32_t bit_pos = bit_total % 8; + uint8_t bit_val = 0x01 << bit_pos; + + if(data[byte_pos] & bit_val) { + digital_signal_append(nfcv_signal, nfcv_resp_one); + } else { + digital_signal_append(nfcv_signal, nfcv_resp_zero); + } + } + + digital_signal_append(nfcv_signal, nfcv_resp_eof); + + /* digital signal setup will take some time. win some time by tricking the VCD into thinking that something happens */ + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, true); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + + FURI_CRITICAL_ENTER(); + digital_signal_send(nfcv_signal, &gpio_spi_r_mosi); + FURI_CRITICAL_EXIT(); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); +} + +void nfcv_emu_send(uint8_t* data, uint8_t length) { + uint8_t buffer[64]; + + if(length + 2 > (uint8_t)sizeof(buffer)) { + return; + } + + memcpy(buffer, data, length); + nfcv_crc(buffer, length, &buffer[length]); + nfcv_emu_send_raw(buffer, length + 2); +} + + +void nfcv_uidcpy(uint8_t *dst, uint8_t *src) { + for(int pos = 0; pos < 8; pos++) { + dst[pos] = src[7-pos]; + } +} + +int nfcv_uidcmp(uint8_t *dst, uint8_t *src) { + for(int pos = 0; pos < 8; pos++) { + if(dst[pos] != src[7-pos]) { + return 1; + } + } + return 0; +} + + + +void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint8_t* payload, uint32_t payload_length) { + + if(!payload_length) { + return; + } + + uint8_t flags = payload[0]; + uint8_t command = payload[1]; + bool addressed = !(flags & RFAL_NFCV_REQ_FLAG_INVENTORY) && (flags & RFAL_NFCV_REQ_FLAG_ADDRESS); + bool advanced = addressed && (command >= 0xA0); + uint8_t address_offset = 2 + (advanced ? 1 : 0); + uint8_t payload_offset = address_offset + (addressed ? 8 : 0); + uint8_t *address = &payload[address_offset]; + + if(addressed && nfcv_uidcmp(address, nfc_data->uid)) { + printf("addressed packet, but not for us:\r\n"); + printf(" destination: %02X%02X%02X%02X%02X%02X%02X%02X\r\n", address[7], address[6], address[5], address[4], address[3], address[2], address[1], address[0]); + printf(" our UID: %02X%02X%02X%02X%02X%02X%02X%02X\r\n", nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5], nfc_data->uid[4], nfc_data->uid[3], nfc_data->uid[2], nfc_data->uid[1], nfc_data->uid[0]); + return; + } + + uint8_t response_buffer[32]; + + switch(command) { + case ISO15693_GET_SYSTEM_INFO: + { + if(nfcv_data->privacy) { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO (ignored, privacy)"); + } else { + response_buffer[0] = ISO15693_NOERROR; + response_buffer[1] = 0x0F; + nfcv_uidcpy(&response_buffer[2], nfc_data->uid); + response_buffer[10] = nfcv_data->dsfid; /* DSFID */ + response_buffer[11] = nfcv_data->afi; /* AFI */ + response_buffer[12] = nfcv_data->block_num - 1; /* number of blocks */ + response_buffer[13] = nfcv_data->block_size - 1; /* block size */ + response_buffer[14] = nfcv_data->ic_ref; /* IC reference */ + + nfcv_emu_send(response_buffer, 15); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO"); + } + + break; + } + + case ISO15693_INVENTORY: + { + if(nfcv_data->privacy) { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY (ignored, privacy)"); + } else { + response_buffer[0] = ISO15693_NOERROR; + response_buffer[1] = nfcv_data->dsfid; /* DSFID */ + nfcv_uidcpy(&response_buffer[2], nfc_data->uid); + + nfcv_emu_send(response_buffer, 10); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + } + break; + } + + case ISO15693_READBLOCK: + { + uint8_t block = payload[payload_offset]; + + if(block >= nfcv_data->block_num) { + response_buffer[0] = ISO15693_ERROR_BLOCK_WRITE; + nfcv_emu_send(response_buffer, 1); + } else { + response_buffer[0] = ISO15693_NOERROR; + memcpy(&response_buffer[1], &nfcv_data->data[nfcv_data->block_size * block], nfcv_data->block_size); + nfcv_emu_send(response_buffer, 1 + nfcv_data->block_size); + } + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); + break; + } + + case ISO15693_WRITEBLOCK: + { + uint8_t block = payload[payload_offset]; + uint8_t *data = &payload[payload_offset + 1]; + + if(block >= nfcv_data->block_num) { + response_buffer[0] = ISO15693_ERROR_BLOCK_WRITE; + } else { + response_buffer[0] = ISO15693_NOERROR; + memcpy(&nfcv_data->data[nfcv_data->block_size * block], &response_buffer[1], nfcv_data->block_size); + } + nfcv_emu_send(response_buffer, 1); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE BLOCK %d <- %02X %02X %02X %02X", block, data[0], data[1], data[2], data[3]); + break; + } + + case ISO15693_CMD_NXP_GET_RANDOM_NUMBER: + { + response_buffer[0] = ISO15693_NOERROR; + response_buffer[1] = 0x00; /* set number to 0x0000 so we get the key in plaintext */ + response_buffer[2] = 0x00; + + nfcv_emu_send(response_buffer, 3); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "GET_RANDOM_NUMBER"); + break; + } + + case ISO15693_CMD_NXP_SET_PASSWORD: + { + uint32_t pass = (payload[payload_offset + 1] << 24) + | (payload[payload_offset + 2] << 16) + | (payload[payload_offset + 3] << 8) + | (payload[payload_offset + 4] << 0); + + response_buffer[0] = ISO15693_NOERROR; + + nfcv_emu_send(response_buffer, 1); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X: %08lX", payload[payload_offset + 0], pass); + + nfcv_data->privacy = false; + break; + } + + case ISO15693_CMD_NXP_ENABLE_PRIVACY: + { + response_buffer[0] = ISO15693_NOERROR; + + nfcv_emu_send(response_buffer, 1); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ISO15693_CMD_NXP_ENABLE_PRIVACY"); + + nfcv_data->privacy = true; + break; + } + + default: + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "unsupported: %02X", command); + break; + } +} + +void nfcv_emu_init() { + nfcv_emu_alloc(); + rfal_platform_spi_acquire(); + + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); + st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3); + st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88); + st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); +} + +void nfcv_emu_deinit() { + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + rfal_platform_spi_release(); + nfcv_emu_free(); +} + +bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t timeout_ms) { + + bool ret = false; + uint32_t next_sleep = DWT->CYCCNT + (timeout_ms * clocks_in_ms); + uint32_t timeout = 0; + uint32_t last_change = 0; + uint32_t edges_received = 0; + + uint32_t frame_state = NFCV_FRAME_STATE_SOF1; + uint32_t periods_previous = 0; + uint8_t frame_payload[128]; + uint32_t frame_pos = 0; + uint32_t byte_value = 0; + uint32_t bits_received = 0; + char reset_reason[128]; + + bool prev = furi_hal_gpio_read(&gpio_spi_r_miso); + + bool in_critical = false; + FURI_CRITICAL_DEFINE(); + + while(true) { + + bool state = furi_hal_gpio_read(&gpio_spi_r_miso); + uint32_t cur_time = DWT->CYCCNT; + + if(state != prev) { + uint32_t delta = cur_time - last_change; + uint32_t periods = (delta + bit_time/2) / bit_time; + + last_change = cur_time; + prev = state; + next_sleep = cur_time + (timeout_ms * clocks_in_ms); + + /* start edge counting on first rising edge */ + if(state || edges_received) { + edges_received++; + + /* ignore periods which are too long, might happen on field start */ + if(periods > 1024) { + continue; + } + + switch(frame_state) { + case NFCV_FRAME_STATE_SOF1: + /* got a rising edge, was it one period? */ + if(state) { + if(periods == 1) { + FURI_CRITICAL_ENTER_ADV(); + timeout = cur_time + bit_time * 16; + in_critical = true; + frame_state = NFCV_FRAME_STATE_SOF2; + } else { + snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 1 period, got %lu", periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + } + break; + + case NFCV_FRAME_STATE_SOF2: + /* waiting for the second low period, telling us about coding */ + if(!state) { + timeout = cur_time + bit_time * 16; + if(periods == 6) { + frame_state = NFCV_FRAME_STATE_CODING_256; + periods_previous = 0; + } else if(periods == 4) { + frame_state = NFCV_FRAME_STATE_CODING_4; + periods_previous = 2; + } else { + snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 4/6 periods, got %lu", periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + } + break; + + case NFCV_FRAME_STATE_CODING_256: + if(!state) { + timeout = cur_time + bit_time * 1024; + if(periods_previous > periods) { + snprintf(reset_reason, sizeof(reset_reason), "1oo256: Missing %lu periods from previous symbol, got %lu", periods_previous, periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + + if(periods > 512) { + snprintf(reset_reason, sizeof(reset_reason), "1oo256: %lu periods is too much", periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } + + periods_previous = 512 - (periods + 1); + byte_value = (periods - 1) / 2; + frame_payload[frame_pos++] = (uint8_t)byte_value; + + } else { + if(periods != 1) { + snprintf(reset_reason, sizeof(reset_reason), "1oo256: Expected a single low pulse"); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + } + break; + + case NFCV_FRAME_STATE_CODING_4: + /* evaluate high periods on falling edge */ + if(!state) { + timeout = cur_time + bit_time * 16; + if(periods_previous > periods) { + snprintf(reset_reason, sizeof(reset_reason), "1oo4: Missing %lu periods from previous symbol, got %lu", periods_previous, periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + periods_previous = 0; + + byte_value >>= 2; + bits_received += 2; + + if(periods == 1) { + byte_value |= 0x00 << 6; + periods_previous = 6; + } else if(periods == 3) { + byte_value |= 0x01 << 6; + periods_previous = 4; + } else if(periods == 5) { + byte_value |= 0x02 << 6; + periods_previous = 2; + } else if(periods == 7) { + byte_value |= 0x03 << 6; + periods_previous = 0; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } else { + snprintf(reset_reason, sizeof(reset_reason), "1oo4: Expected 1/3/5/7 low pulses, but got %lu", periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(bits_received >= 8) { + frame_payload[frame_pos++] = (uint8_t)byte_value; + bits_received = 0; + } + } else { + if(periods != 1) { + snprintf(reset_reason, sizeof(reset_reason), "1oo4: Expected a single low pulse"); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + } + break; + } + } + } + + /* post-state-machine cleanup and reset */ + if(frame_state == NFCV_FRAME_STATE_RESET) { + timeout = 0; + edges_received = 0; + frame_state = NFCV_FRAME_STATE_SOF1; + FURI_CRITICAL_EXIT(); + printf("Reset state machine, reason: %s\r\n", reset_reason); + in_critical = false; + furi_delay_ms(50); + } else if(frame_state == NFCV_FRAME_STATE_EOF) { + FURI_CRITICAL_EXIT(); + in_critical = false; + break; + } + + /* no edges detected */ + if(timeout && cur_time > timeout) { + break; + } + /* might exit early on overflows. guess thats okay. */ + if(cur_time > next_sleep) { + break; + } + } + + if(in_critical) { + FURI_CRITICAL_EXIT(); + in_critical = false; + } + + if(frame_state == NFCV_FRAME_STATE_EOF) { + + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, true); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, true); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + + nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos); + ret = true; + } + + furi_delay_ms(0); + + return ret; +} diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index 024dea42e..c2efcd1a1 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -8,10 +8,73 @@ #include +#define NFCV_FC (13560000.0f) +#define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC/32) / 2.0f) /* 1.1799 µs */ +#define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */ + +#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) +#define DIGITAL_SIGNAL_UNIT_US (100000.0f) + #define NFCV_TOTAL_BLOCKS_MAX 32 #define NFCV_BLOCK_SIZE 4 #define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE*NFCV_TOTAL_BLOCKS_MAX) + +#define NFCV_FRAME_STATE_SOF1 0 +#define NFCV_FRAME_STATE_SOF2 1 +#define NFCV_FRAME_STATE_CODING_4 2 +#define NFCV_FRAME_STATE_CODING_256 3 +#define NFCV_FRAME_STATE_EOF 4 +#define NFCV_FRAME_STATE_RESET 5 + +/* */ +#define ISO15693_INVENTORY 0x01 +#define ISO15693_STAYQUIET 0x02 +#define ISO15693_READBLOCK 0x20 +#define ISO15693_WRITEBLOCK 0x21 +#define ISO15693_LOCKBLOCK 0x22 +#define ISO15693_READ_MULTI_BLOCK 0x23 +#define ISO15693_WRITE_MULTI_BLOCK 0x24 +#define ISO15693_SELECT 0x25 +#define ISO15693_RESET_TO_READY 0x26 +#define ISO15693_WRITE_AFI 0x27 +#define ISO15693_LOCK_AFI 0x28 +#define ISO15693_WRITE_DSFID 0x29 +#define ISO15693_LOCK_DSFID 0x2A +#define ISO15693_GET_SYSTEM_INFO 0x2B +#define ISO15693_READ_MULTI_SECSTATUS 0x2C + +// ISO15693 MANUFACTURER CODES +#define ISO15693_MANUFACTURER_NXP 0x04 + +// ISO15693-3 CUSTOM NXP COMMANDS +#define ISO15693_CMD_NXP_SET_EAS 0xA2 +#define ISO15693_CMD_NXP_RESET_EAS 0xA3 +#define ISO15693_CMD_NXP_LOCK_EAS 0xA4 +#define ISO15693_CMD_NXP_EAS_ALARM 0xA5 +#define ISO15693_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 +#define ISO15693_CMD_NXP_WRITE_EAS_ID 0xA7 +#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ 0xB0 +#define ISO15693_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 +#define ISO15693_CMD_NXP_GET_RANDOM_NUMBER 0xB2 +#define ISO15693_CMD_NXP_SET_PASSWORD 0xB3 +#define ISO15693_CMD_NXP_WRITE_PASSWORD 0xB4 +#define ISO15693_CMD_NXP_DESTROY 0xB9 +#define ISO15693_CMD_NXP_ENABLE_PRIVACY 0xBA + +// ISO15693 RESPONSE ERROR CODES +#define ISO15693_NOERROR 0x00 +#define ISO15693_ERROR_CMD_NOT_SUP 0x01 // Command not supported +#define ISO15693_ERROR_CMD_NOT_REC 0x02 // Command not recognized (eg. parameter error) +#define ISO15693_ERROR_CMD_OPTION 0x03 // Command option not supported +#define ISO15693_ERROR_GENERIC 0x0F // No additional Info about this error +#define ISO15693_ERROR_BLOCK_UNAVAILABLE 0x10 +#define ISO15693_ERROR_BLOCK_LOCKED_ALREADY 0x11 // cannot lock again +#define ISO15693_ERROR_BLOCK_LOCKED 0x12 // cannot be changed +#define ISO15693_ERROR_BLOCK_WRITE 0x13 // Writing was unsuccessful +#define ISO15693_ERROR_BLOCL_WRITELOCK 0x14 // Locking was unsuccessful + + typedef enum { NfcVAuthMethodManual, NfcVAuthMethodTonieBox, @@ -39,8 +102,10 @@ typedef struct { uint8_t block_num; uint8_t block_size; uint8_t data[NFCV_MAX_DUMP_SIZE]; + bool privacy; char error[32]; + char last_command[128]; } NfcVData; typedef struct { @@ -51,4 +116,8 @@ typedef struct { ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data); ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data); ReturnCode nfcv_inventory(uint8_t* uid); +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data); +void nfcv_emu_init(); +void nfcv_emu_deinit(); +bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t timeout_ms); diff --git a/lib/nfc/protocols/slix_l.c b/lib/nfc/protocols/slix_l.c index b7d1bd174..d22a8470e 100644 --- a/lib/nfc/protocols/slix_l.c +++ b/lib/nfc/protocols/slix_l.c @@ -14,20 +14,6 @@ bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2) { return false; } -bool slix_l_read_card( - NfcVReader* reader, - FuriHalNfcDevData* nfc_data, - NfcVData* nfcv_data) { - furi_assert(reader); - furi_assert(nfcv_data); - - if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) { - return false; - } - - reader->blocks_to_read = nfcv_data->block_num; - return (nfcv_read_blocks(reader, nfcv_data) == ERR_NONE); -} ReturnCode slix_l_get_random(uint8_t* rand) { uint16_t received = 0; diff --git a/lib/nfc/protocols/slix_l.h b/lib/nfc/protocols/slix_l.h index c40ced942..fb7a9c4c0 100644 --- a/lib/nfc/protocols/slix_l.h +++ b/lib/nfc/protocols/slix_l.h @@ -11,7 +11,6 @@ bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2); -bool slix_l_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data); ReturnCode slix_l_get_random(uint8_t* rand); ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password); From 75f5e6604bf76d65a733bab6ee53b973e80d8924 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 17 Nov 2022 01:35:39 +0100 Subject: [PATCH 06/34] separate ISO15693 basic and SLIX extensions a bit more changed save format to reflect this executing emulation from browser now also works --- applications/main/nfc/nfc.c | 3 + applications/main/nfc/nfc_cli.c | 7 +- .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 31 ++- .../nfc/scenes/nfc_scene_nfcv_key_input.c | 3 +- .../main/nfc/scenes/nfc_scene_nfcv_menu.c | 2 +- .../main/nfc/scenes/nfc_scene_nfcv_unlock.c | 48 +--- applications/main/nfc/scenes/nfc_scene_rpc.c | 7 + .../main/nfc/scenes/nfc_scene_saved_menu.c | 2 + lib/nfc/nfc_device.c | 236 ++++++++++++++++-- lib/nfc/nfc_device.h | 5 +- lib/nfc/nfc_worker.c | 35 ++- lib/nfc/nfc_worker_i.h | 2 +- lib/nfc/protocols/nfcv.c | 216 ++++++++++++---- lib/nfc/protocols/nfcv.h | 58 ++++- lib/nfc/protocols/{slix_l.c => slix.c} | 45 +++- lib/nfc/protocols/{slix_l.h => slix.h} | 6 +- 16 files changed, 555 insertions(+), 151 deletions(-) rename lib/nfc/protocols/{slix_l.c => slix.c} (52%) rename lib/nfc/protocols/{slix_l.h => slix.h} (63%) diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 55c68a450..aeddc5904 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -280,6 +280,9 @@ int32_t nfc_app(void* p) { } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); DOLPHIN_DEED(DolphinDeedNfcEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); } else { diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index cc2770731..180c7ba8f 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -286,8 +286,11 @@ static void nfc_cli_st25r_trans(Cli* cli, FuriString* args) { .block_num = 8, .block_size = 4, .ic_ref = 3, - .key_privacy = { 0x0F, 0x0F, 0x0F, 0x0F }, - .privacy = false + .type = NfcVTypeSlixL, + .sub_data.slix_l = { + .key_privacy = { 0x0F, 0x0F, 0x0F, 0x0F }, + .privacy = false + } }; memset(nfcv_data.data, 0xAE, 4 * 8); diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index 98d11f2dc..bee3cc36d 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -15,7 +15,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { NfcProtocol protocol = dev_data->protocol; uint8_t text_scroll_height = 0; if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) - || (protocol == NfcDeviceProtocolSlixL) || (protocol == NfcDeviceProtocolNfcV)) { + || (protocol == NfcDeviceProtocolNfcV)) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); text_scroll_height = 52; @@ -42,15 +42,33 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n"); } else if(protocol == NfcDeviceProtocolNfcV) { - furi_string_cat_printf(temp_str, "\e#ISO15693\n"); - } else if(protocol == NfcDeviceProtocolSlixL) { - furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); + switch (dev_data->nfcv_data.type) + { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "\e#ISO15693\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n"); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n"); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n"); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } } else { furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); } // Set tag iso data - if((protocol == NfcDeviceProtocolSlixL) || (protocol == NfcDeviceProtocolNfcV)) { + if(protocol == NfcDeviceProtocolNfcV) { NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; furi_string_cat_printf(temp_str, "UID:\n"); @@ -151,9 +169,6 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { } else if(protocol == NfcDeviceProtocolMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData); consumed = true; - } else if(protocol == NfcDeviceProtocolSlixL) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); - consumed = true; } else if(protocol == NfcDeviceProtocolNfcV) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); consumed = true; diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c index d73498e7a..d0e223c15 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c @@ -3,8 +3,9 @@ void nfc_scene_nfcv_key_input_byte_input_callback(void* context) { Nfc* nfc = context; + NfcVSlixLData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix_l; - memcpy(nfc->dev->dev_data.nfcv_data.key_privacy, nfc->byte_input_store, 4); + memcpy(data->key_privacy, nfc->byte_input_store, 4); view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c index fdf7de9ff..ee26baf69 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -33,7 +33,7 @@ bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexSave) { - nfc->dev->format = NfcDeviceSaveFormatSlixL; + nfc->dev->format = NfcDeviceSaveFormatNfcV; // Clear device name nfc_device_set_name(nfc->dev, ""); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c index b9050e0fc..4fabd2d99 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -11,53 +11,15 @@ typedef enum { static bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) { Nfc* nfc = context; + NfcVSlixLData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix_l; if(event == NfcWorkerEventNfcVPassKey) { - memcpy(nfc->dev->dev_data.nfcv_data.key_privacy, nfc->byte_input_store, 4); + memcpy(data->key_privacy, nfc->byte_input_store, 4); } else { view_dispatcher_send_custom_event(nfc->view_dispatcher, event); } return true; } -/* -static void nfc_scene_nfcv_unlock_button_callback(GuiButtonType event, InputType type, void* context) { - furi_assert(context); - furi_assert(type); - Nfc* nfc = context; - - if(event == GuiButtonTypeCenter) { - if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { - nfc_worker_stop(nfc->worker); - nfc_worker_start( - nfc->worker, - NfcWorkerStateNfcVUnlock, - &nfc->dev->dev_data, - nfc_scene_nfcv_unlock_worker_callback, - nfc); - widget_add_button_element( - nfc->widget, - GuiButtonTypeCenter, - "Autosave", - nfc_scene_nfcv_unlock_button_callback, - nfc); - } else { - nfc_worker_stop(nfc->worker); - nfc_worker_start( - nfc->worker, - NfcWorkerStateNfcVUnlockAndSave, - &nfc->dev->dev_data, - nfc_scene_nfcv_unlock_worker_callback, - nfc); - widget_add_button_element( - nfc->widget, - GuiButtonTypeCenter, - "Unlock", - nfc_scene_nfcv_unlock_button_callback, - nfc); - } - notification_message(nfc->notifications, &sequence_single_vibro); - } -}*/ void nfc_scene_nfcv_unlock_popup_callback(void* context) { Nfc* nfc = context; @@ -82,10 +44,10 @@ void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { nfc_text_store_set(nfc, "SLIX-L_%02X%02X%02X%02X%02X%02X%02X%02X", - nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5], nfc_data->uid[4], - nfc_data->uid[3], nfc_data->uid[2], nfc_data->uid[1], nfc_data->uid[0]); + nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3], + nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]); - nfc->dev->format = NfcDeviceSaveFormatSlixL; + nfc->dev->format = NfcDeviceSaveFormatNfcV; if(nfc_device_save(nfc->dev, nfc->text_store)) { popup_set_header(popup, "Successfully\nsaved", 94, 3, AlignCenter, AlignTop); diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc/scenes/nfc_scene_rpc.c index e5128a52f..1c0e4fa85 100644 --- a/applications/main/nfc/scenes/nfc_scene_rpc.c +++ b/applications/main/nfc/scenes/nfc_scene_rpc.c @@ -55,6 +55,13 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { &nfc->dev->dev_data, nfc_scene_rpc_emulate_callback, nfc); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_scene_rpc_emulate_callback, + nfc); } else { nfc_worker_start( nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 231f12089..2a5c93fb8 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -99,6 +99,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateNfcV); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index c7aeb9a06..886944fb1 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -50,8 +50,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_ furi_string_set(format_string, "Mifare Classic"); } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { furi_string_set(format_string, "Mifare DESFire"); - } else if(dev->format == NfcDeviceSaveFormatSlixL) { - furi_string_set(format_string, "NXP SLIX-L"); + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + furi_string_set(format_string, "ISO15693"); } else { furi_string_set(format_string, "Unknown"); } @@ -87,9 +87,9 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_st dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire; return true; } - if(furi_string_start_with_str(format_string, "NXP SLIX-L")) { - dev->format = NfcDeviceSaveFormatSlixL; - dev->dev_data.protocol = NfcDeviceProtocolSlixL; + if(furi_string_start_with_str(format_string, "ISO15693")) { + dev->format = NfcDeviceSaveFormatNfcV; + dev->dev_data.protocol = NfcDeviceProtocolNfcV; return true; } return false; @@ -643,21 +643,92 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { return parsed; } +static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVData)); + + do { + if(!flipper_format_read_hex( + file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixSData* data = &dev->dev_data.nfcv_data.sub_data.slix_s; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) break; + if(!flipper_format_write_hex(file, "Password Write", data->key_write, sizeof(data->key_write))) break; + if(!flipper_format_write_hex(file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) break; + if(!flipper_format_write_hex(file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixSData* data = &dev->dev_data.nfcv_data.sub_data.slix_s; + memset(data, 0, sizeof(NfcVData)); + + do { + if(!flipper_format_read_hex( + file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex( + file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { bool saved = false; - NfcVData* data = &dev->dev_data.nfcv_data; + NfcVSlixLData* data = &dev->dev_data.nfcv_data.sub_data.slix_l; do { if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break; if(!flipper_format_write_hex(file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) break; if(!flipper_format_write_hex(file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) break; if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) break; - if(!flipper_format_write_hex(file, "DSFID", &(data->dsfid), 1)) break; - if(!flipper_format_write_hex(file, "AFI", &(data->afi), 1)) break; - if(!flipper_format_write_hex(file, "IC Reference", &(data->ic_ref), 1)) break; - if(!flipper_format_write_hex(file, "Block Count", &(data->block_num), 1)) break; - if(!flipper_format_write_hex(file, "Block Size", &(data->block_size), 1)) break; - if(!flipper_format_write_hex(file, "Data Content", data->data, data->block_num * data->block_size)) break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; saved = true; } while(false); @@ -666,7 +737,7 @@ static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; - NfcVData* data = &dev->dev_data.nfcv_data; + NfcVSlixLData* data = &dev->dev_data.nfcv_data.sub_data.slix_l; memset(data, 0, sizeof(NfcVData)); do { @@ -679,7 +750,108 @@ bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { if(!flipper_format_read_hex( file, "Password EAS", data->key_eas, sizeof(data->key_eas))) break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlix2Data* data = &dev->dev_data.nfcv_data.sub_data.slix2; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) break; + if(!flipper_format_write_hex(file, "Password Write", data->key_write, sizeof(data->key_write))) break; + if(!flipper_format_write_hex(file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) break; + if(!flipper_format_write_hex(file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlix2Data* data = &dev->dev_data.nfcv_data.sub_data.slix2; + memset(data, 0, sizeof(NfcVData)); + + do { + if(!flipper_format_read_hex( + file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex( + file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + uint8_t temp_value = 0; + NfcVData* data = &dev->dev_data.nfcv_data; + + do { + if(!flipper_format_write_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_write_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_write_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + if(!flipper_format_write_hex(file, "Block Count", &(data->block_num), 1)) break; + if(!flipper_format_write_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_write_hex(file, "Data Content", data->data, data->block_num * data->block_size)) break; + if(!flipper_format_write_comment_cstr(file, "Subtype of this card (0 = ISO15693, 1 = SLIX, 2 = SLIX-S, 3 = SLIX-L, 4 = SLIX2)")) break; + + temp_value = data->type; + if(!flipper_format_write_hex(file, "Subtype", &temp_value, 1)) break; + + switch(data->type) { + case NfcVTypePlain: + saved = true; + break; + case NfcVTypeSlix: + saved = nfc_device_save_slix_data(file, dev); + break; + case NfcVTypeSlixS: + saved = nfc_device_save_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + saved = nfc_device_save_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + saved = nfc_device_save_slix2_data(file, dev); + break; + } + } while(false); + + return saved; +} + +bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + uint8_t temp_value = 0; + NfcVData* data = &dev->dev_data.nfcv_data; + + memset(data, 0, sizeof(NfcVData)); + + do { if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; @@ -688,8 +860,26 @@ bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { if(!flipper_format_read_hex( file, "Data Content", data->data, data->block_num * data->block_size)) break; + if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; + data->type = temp_value; - parsed = true; + switch(data->type) { + case NfcVTypePlain: + parsed = true; + break; + case NfcVTypeSlix: + parsed = nfc_device_load_slix_data(file, dev); + break; + case NfcVTypeSlixS: + parsed = nfc_device_load_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + parsed = nfc_device_load_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + parsed = nfc_device_load_slix2_data(file, dev); + break; + } } while(false); return parsed; @@ -1127,17 +1317,19 @@ static bool nfc_device_save_file( if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; // Write nfc device type if(!flipper_format_write_comment_cstr( - file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic")) + file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693")) break; nfc_device_prepare_format_string(dev, temp_str); if(!flipper_format_write_string(file, "Device type", temp_str)) break; // Write UID - if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats except ISO15693")) + if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break; if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - if(dev->format != NfcDeviceSaveFormatSlixL) { + if(dev->format != NfcDeviceSaveFormatNfcV) { // Write ATQA, SAK + if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) + break; if(!flipper_format_write_hex(file, "ATQA", data->atqa, 2)) break; if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; } @@ -1146,8 +1338,8 @@ static bool nfc_device_save_file( if(!nfc_device_save_mifare_ul_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_save_mifare_df_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatSlixL) { - if(!nfc_device_save_slix_l_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + if(!nfc_device_save_nfcv_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatBankCard) { if(!nfc_device_save_bank_card_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { @@ -1215,7 +1407,7 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!(data_cnt == 4 || data_cnt == 7 || data_cnt == 8)) break; data->uid_len = data_cnt; if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; - if(dev->format != NfcDeviceSaveFormatSlixL) { + if(dev->format != NfcDeviceSaveFormatNfcV) { if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; } @@ -1233,8 +1425,8 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_load_mifare_classic_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_load_mifare_df_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatSlixL) { - if(!nfc_device_load_slix_l_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + if(!nfc_device_load_nfcv_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatBankCard) { if(!nfc_device_load_bank_card_data(file, dev)) break; } diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index d0299fea2..576280918 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -33,8 +33,7 @@ typedef enum { NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, - NfcDeviceProtocolNfcV, - NfcDeviceProtocolSlixL, + NfcDeviceProtocolNfcV } NfcProtocol; typedef enum { @@ -43,7 +42,7 @@ typedef enum { NfcDeviceSaveFormatMifareUl, NfcDeviceSaveFormatMifareClassic, NfcDeviceSaveFormatMifareDesfire, - NfcDeviceSaveFormatSlixL, + NfcDeviceSaveFormatNfcV, } NfcDeviceSaveFormat; typedef struct { diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 98f88a0ab..ea1c72514 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -176,11 +176,12 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { if(nfcv_data->auth_method == NfcVAuthMethodManual) { uint32_t key = 0; + uint8_t *key_data = nfc_worker->dev_data->nfcv_data.sub_data.slix_l.key_privacy; - key |= nfc_worker->dev_data->nfcv_data.key_privacy[0] << 24; - key |= nfc_worker->dev_data->nfcv_data.key_privacy[1] << 16; - key |= nfc_worker->dev_data->nfcv_data.key_privacy[2] << 8; - key |= nfc_worker->dev_data->nfcv_data.key_privacy[3] << 0; + key |= key_data[0] << 24; + key |= key_data[1] << 16; + key |= key_data[2] << 8; + key |= key_data[3] << 0; ret = slix_l_unlock(4, rand, key); } else { ret = slix_l_unlock(4, rand, 0x7FFD6E5B); @@ -493,17 +494,31 @@ static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t furi_assert(tx_rx); FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; bool card_read = false; furi_hal_nfc_sleep(); - if(slix_l_check_card_type(nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5])) { + + /* until here the UID field is reversed from the reader IC. + we will read it here again and it will get placed in the right order. */ + card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx); + + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; + + if(slix_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX detected"); + nfcv_data->type = NfcVTypeSlix; + } else if(slix2_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX2 detected"); + nfcv_data->type = NfcVTypeSlix2; + } else if(slix_s_check_card_type(nfc_data)) { FURI_LOG_I(TAG, "NXP SLIX-L detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolSlixL; - card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx); + nfcv_data->type = NfcVTypeSlixS; + } else if(slix_l_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-L detected"); + nfcv_data->type = NfcVTypeSlixL; } else { - FURI_LOG_I(TAG, "unknown detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; - card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx); + nfcv_data->type = NfcVTypePlain; } return card_read; diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index d50d2a274..67fdcdc3d 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include struct NfcWorker { diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 11604afef..1f94211fc 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -8,6 +8,10 @@ #include #include #include +#include +#include +#include +#include #include "nfcv.h" #include "nfc_util.h" @@ -75,7 +79,7 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data) { if(ret == ERR_NONE) { nfc_data->type = FuriHalNfcTypeV; nfc_data->uid_len = 8; - /* UID is stored reversed in this structure */ + /* UID is stored reversed in this response */ for(int pos = 0; pos < nfc_data->uid_len; pos++) { nfc_data->uid[pos] = rxBuf[2 + (7 - pos)]; } @@ -316,41 +320,62 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui } uint8_t response_buffer[32]; + + switch(nfcv_data->type) { + case NfcVTypeSlixL: + if(nfcv_data->sub_data.slix_l.privacy && + command != ISO15693_CMD_NXP_GET_RANDOM_NUMBER && + command != ISO15693_CMD_NXP_SET_PASSWORD) { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "(command ignored, privacy)"); + } + break; + + default: + break; + } switch(command) { - case ISO15693_GET_SYSTEM_INFO: - { - if(nfcv_data->privacy) { - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO (ignored, privacy)"); - } else { - response_buffer[0] = ISO15693_NOERROR; - response_buffer[1] = 0x0F; - nfcv_uidcpy(&response_buffer[2], nfc_data->uid); - response_buffer[10] = nfcv_data->dsfid; /* DSFID */ - response_buffer[11] = nfcv_data->afi; /* AFI */ - response_buffer[12] = nfcv_data->block_num - 1; /* number of blocks */ - response_buffer[13] = nfcv_data->block_size - 1; /* block size */ - response_buffer[14] = nfcv_data->ic_ref; /* IC reference */ - - nfcv_emu_send(response_buffer, 15); - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO"); - } - - break; - } case ISO15693_INVENTORY: { - if(nfcv_data->privacy) { - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY (ignored, privacy)"); - } else { - response_buffer[0] = ISO15693_NOERROR; - response_buffer[1] = nfcv_data->dsfid; /* DSFID */ - nfcv_uidcpy(&response_buffer[2], nfc_data->uid); + response_buffer[0] = ISO15693_NOERROR; + response_buffer[1] = nfcv_data->dsfid; /* DSFID */ + nfcv_uidcpy(&response_buffer[2], nfc_data->uid); - nfcv_emu_send(response_buffer, 10); - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); - } + nfcv_emu_send(response_buffer, 10); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + break; + } + + case ISO15693_STAYQUIET: + { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "STAYQUIET"); + break; + } + + case ISO15693_LOCKBLOCK: + { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCKBLOCK"); + break; + } + + case ISO15693_READ_MULTI_BLOCK: + { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ_MULTI_BLOCK"); + break; + } + + case ISO15693_WRITE_MULTI_BLOCK: + { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_MULTI_BLOCK"); + break; + } + + case ISO15693_SELECT: + { + response_buffer[0] = ISO15693_NOERROR; + nfcv_emu_send(response_buffer, 1); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SELECT"); break; } @@ -386,11 +411,31 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui break; } + case ISO15693_GET_SYSTEM_INFO: + { + response_buffer[0] = ISO15693_NOERROR; + response_buffer[1] = 0x0F; + nfcv_uidcpy(&response_buffer[2], nfc_data->uid); + response_buffer[10] = nfcv_data->dsfid; /* DSFID */ + response_buffer[11] = nfcv_data->afi; /* AFI */ + response_buffer[12] = nfcv_data->block_num - 1; /* number of blocks */ + response_buffer[13] = nfcv_data->block_size - 1; /* block size */ + response_buffer[14] = nfcv_data->ic_ref; /* IC reference */ + + nfcv_emu_send(response_buffer, 15); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO"); + + break; + } + case ISO15693_CMD_NXP_GET_RANDOM_NUMBER: { + nfcv_data->sub_data.slix_l.rand[0] = 0x00; + nfcv_data->sub_data.slix_l.rand[1] = 0x00; + response_buffer[0] = ISO15693_NOERROR; - response_buffer[1] = 0x00; /* set number to 0x0000 so we get the key in plaintext */ - response_buffer[2] = 0x00; + response_buffer[1] = nfcv_data->sub_data.slix_l.rand[0]; + response_buffer[2] = nfcv_data->sub_data.slix_l.rand[1]; nfcv_emu_send(response_buffer, 3); snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "GET_RANDOM_NUMBER"); @@ -399,17 +444,43 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui case ISO15693_CMD_NXP_SET_PASSWORD: { - uint32_t pass = (payload[payload_offset + 1] << 24) - | (payload[payload_offset + 2] << 16) - | (payload[payload_offset + 3] << 8) - | (payload[payload_offset + 4] << 0); + uint8_t password_id = payload[payload_offset]; + uint8_t *password_xored = &payload[payload_offset + 1]; + uint8_t *rand = nfcv_data->sub_data.slix_l.rand; + uint8_t status = ISO15693_ERROR_GENERIC; + uint8_t *password = NULL; + uint8_t password_rcv[4]; - response_buffer[0] = ISO15693_NOERROR; + switch(password_id) { + case 4: + password = nfcv_data->sub_data.slix_l.key_privacy; + break; + case 8: + password = nfcv_data->sub_data.slix_l.key_destroy; + break; + case 10: + password = nfcv_data->sub_data.slix_l.key_eas; + break; + default: + break; + } + + for(int pos = 0; pos < 4; pos++) { + password_rcv[pos] = password_xored[pos] ^ rand[pos % 2]; + } + + if(!memcmp(password, password_rcv, 4)) { + status = ISO15693_NOERROR; + nfcv_data->sub_data.slix_l.privacy = false; + } else { + printf("pass mismatch: %08lX %08lX %02X", *((uint32_t *)password), *((uint32_t *)password_xored), *rand); + } + + response_buffer[0] = status; nfcv_emu_send(response_buffer, 1); - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X: %08lX", payload[payload_offset + 0], pass); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X", password_id); - nfcv_data->privacy = false; break; } @@ -420,7 +491,7 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui nfcv_emu_send(response_buffer, 1); snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ISO15693_CMD_NXP_ENABLE_PRIVACY"); - nfcv_data->privacy = true; + nfcv_data->sub_data.slix_l.privacy = true; break; } @@ -430,6 +501,10 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui } } +#define COUNT(x) ((sizeof(x))/(sizeof(x[0]))) +uint32_t nfcv_timer_buffer_src[32]; +uint32_t nfcv_timer_buffer[1024]; + void nfcv_emu_init() { nfcv_emu_alloc(); rfal_platform_spi_acquire(); @@ -440,6 +515,53 @@ void nfcv_emu_init() { st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + +#if 0 + memset(nfcv_timer_buffer_src, 0xEE, sizeof(nfcv_timer_buffer_src)); + memset(nfcv_timer_buffer, 0xFA, sizeof(nfcv_timer_buffer)); + + /* configure DMA to read from a timer peripheral */ + LL_DMA_InitTypeDef dma_config = {}; + dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->CNT); + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstAddress = (uint32_t) nfcv_timer_buffer; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.NbData = 32; /* executes LL_DMA_SetDataLength */ + dma_config.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + + /* now set up DMA with these settings */ + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_4); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_4, &dma_config); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4); + + /* make some noise on the counter */ + LL_TIM_DisableCounter(TIM2); + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetPrescaler(TIM2, 0); + LL_TIM_SetAutoReload(TIM2, 0xFFF); + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); + + /* make sure request generation is disabled before modifying registers */ + LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); + /* generator 0 gets fed by EXTI_LINE4 */ + LL_DMAMUX_SetRequestSignalID(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_EXTI_LINE4); + /* trigger on any edge - for now. should be only one? */ + LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); + /* now enable request generation again */ + LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); + + /* configure PB4 as source for EXTI_LINE4. shall it rain. */ + LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTB, LL_GPIO_PIN_4); + +#endif + } void nfcv_emu_deinit() { @@ -469,6 +591,7 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti bool in_critical = false; FURI_CRITICAL_DEFINE(); + while(true) { bool state = furi_hal_gpio_read(&gpio_spi_r_miso); @@ -633,6 +756,7 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti if(timeout && cur_time > timeout) { break; } + /* might exit early on overflows. guess thats okay. */ if(cur_time > next_sleep) { break; @@ -659,8 +783,16 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos); ret = true; } - - furi_delay_ms(0); +/* + printf("nfcv_timer_buffer: %ld", LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_4)); + for(uint32_t pos = 0; pos < 64; pos++) { + if((pos % 4) == 0) { + printf("\r\n"); + } + printf(" 0x%08lX ", nfcv_timer_buffer[pos]); + } + furi_delay_ms(100); + */ return ret; } diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index c2efcd1a1..e8188cb3f 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -81,31 +81,71 @@ typedef enum { } NfcVAuthMethod; typedef enum { - NfcVTypeSlix, - NfcVTypeSlixS, - NfcVTypeSlixL, - NfcVTypeSlix2, + NfcVTypePlain = 0, + NfcVTypeSlix = 1, + NfcVTypeSlixS = 2, + NfcVTypeSlixL = 3, + NfcVTypeSlix2 = 4, } NfcVType; typedef struct { - NfcVType type; - NfcVAuthMethod auth_method; - bool auth_success; + uint8_t key_eas[4]; + uint8_t rand[2]; +} NfcVSlixData; +typedef struct { + uint8_t key_read[4]; + uint8_t key_write[4]; uint8_t key_privacy[4]; uint8_t key_destroy[4]; uint8_t key_eas[4]; + uint8_t rand[2]; + bool privacy; +} NfcVSlix2Data; +typedef struct { + uint8_t key_read[4]; + uint8_t key_write[4]; + uint8_t key_privacy[4]; + uint8_t key_destroy[4]; + uint8_t key_eas[4]; + uint8_t rand[2]; + bool privacy; +} NfcVSlixSData; + +typedef struct { + uint8_t key_privacy[4]; + uint8_t key_destroy[4]; + uint8_t key_eas[4]; + uint8_t rand[2]; + bool privacy; +} NfcVSlixLData; + +typedef union { + NfcVSlixData slix; + NfcVSlix2Data slix2; + NfcVSlixSData slix_s; + NfcVSlixLData slix_l; +} NfcVSubtypeData; + +typedef struct { + /* common ISO15693 fields */ uint8_t dsfid; uint8_t afi; uint8_t ic_ref; uint8_t block_num; uint8_t block_size; uint8_t data[NFCV_MAX_DUMP_SIZE]; - bool privacy; - char error[32]; + /* specfic variant infos */ + NfcVType type; + NfcVSubtypeData sub_data; + + /* runtime data */ char last_command[128]; + char error[32]; + NfcVAuthMethod auth_method; + bool auth_success; } NfcVData; typedef struct { diff --git a/lib/nfc/protocols/slix_l.c b/lib/nfc/protocols/slix.c similarity index 52% rename from lib/nfc/protocols/slix_l.c rename to lib/nfc/protocols/slix.c index d22a8470e..e7dfe44be 100644 --- a/lib/nfc/protocols/slix_l.c +++ b/lib/nfc/protocols/slix.c @@ -1,14 +1,45 @@ #include #include "nfcv.h" -#include "slix_l.h" +#include "slix.h" #include "nfc_util.h" #include #include "furi_hal_nfc.h" -bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2) { - if((UID0 == 0xE0) && (UID1 == 0x04) && (UID2 == 0x03)) { +bool slix_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) + && (nfc_data->uid[1] == 0x04) + && (nfc_data->uid[2] == 0x01) + && (((nfc_data->uid[3] >> 4) & 3) == 2)) { + return true; + } + return false; +} + +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) + && (nfc_data->uid[1] == 0x04) + && (nfc_data->uid[2] == 0x01) + && (((nfc_data->uid[3] >> 4) & 3) == 1)) { + return true; + } + return false; +} + +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) + && (nfc_data->uid[1] == 0x04) + && (nfc_data->uid[2] == 0x02)) { + return true; + } + return false; +} + +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) + && (nfc_data->uid[1] == 0x04) + && (nfc_data->uid[2] == 0x03)) { return true; } return false; @@ -49,10 +80,10 @@ ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password) { uint8_t rxBuf[32]; uint8_t cmd_set_pass[] = { id, - rand[0] ^ ((password>>0) & 0xFF), - rand[1] ^ ((password>>8) & 0xFF), - rand[0] ^ ((password>>16) & 0xFF), - rand[1] ^ ((password>>24) & 0xFF) + rand[0] ^ ((password >> 0) & 0xFF), + rand[1] ^ ((password >> 8) & 0xFF), + rand[0] ^ ((password >> 16) & 0xFF), + rand[1] ^ ((password >> 24) & 0xFF) }; ReturnCode ret = rfalNfcvPollerTransceiveReq( diff --git a/lib/nfc/protocols/slix_l.h b/lib/nfc/protocols/slix.h similarity index 63% rename from lib/nfc/protocols/slix_l.h rename to lib/nfc/protocols/slix.h index fb7a9c4c0..0bcfe702a 100644 --- a/lib/nfc/protocols/slix_l.h +++ b/lib/nfc/protocols/slix.h @@ -10,8 +10,10 @@ #define ISO15693_MANUFACTURER_NXP 0x04 -bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2); - +bool slix_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); ReturnCode slix_l_get_random(uint8_t* rand); ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password); From b4802247dfe97dbfb3e4028de5d95c219d8c6a85 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 17 Nov 2022 01:45:22 +0100 Subject: [PATCH 07/34] always save file even when tag was not locked --- .../main/nfc/scenes/nfc_scene_nfcv_unlock.c | 2 +- lib/nfc/nfc_worker.c | 46 +++++++++---------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c index 4fabd2d99..96293a7c6 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -43,7 +43,7 @@ void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { popup_reset(popup); if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { - nfc_text_store_set(nfc, "SLIX-L_%02X%02X%02X%02X%02X%02X%02X%02X", + nfc_text_store_set(nfc, "SLIX_%02X%02X%02X%02X%02X%02X%02X%02X", nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3], nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index ea1c72514..6533cc94c 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -164,7 +164,27 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { ret = nfcv_inventory(NULL); if(ret == ERR_NONE) { - /* chip is also visible, so no action required */ + /* chip is also visible, so no action required, just save */ + if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + NfcVReader reader; + + if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { + furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); + snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + } else { + furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + } else { + furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + + while(slix_l_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + furi_hal_console_printf(" => chip is already visible, wait for chip to disappear.\r\n"); nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); while(slix_l_get_random(NULL) == ERR_NONE) { @@ -200,29 +220,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { ret = slix_l_unlock(4, rand, 0x0F0F0F0F); } } - if(ret == ERR_NONE) { - /* unlock succesful */ - if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { - NfcVReader reader; - - if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { - furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); - snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); - nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); - } else { - furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); - nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); - } - } else { - furi_hal_console_printf(" => success, wait for chip to disappear.\r\n"); - nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); - } - - while(slix_l_get_random(NULL) == ERR_NONE) { - furi_delay_ms(100); - } - - } else { + if(ret != ERR_NONE) { /* unlock failed */ furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Passwords not\naccepted"); From 971e0093c40c4bfb5be9742770a4083edda83380 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 17 Nov 2022 21:49:29 +0100 Subject: [PATCH 08/34] cleaned up SLIX code fixed byte order mixup read larger cards --- applications/main/nfc/nfc_cli.c | 4 +- .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 59 +++++++- lib/nfc/nfc_device.c | 20 ++- lib/nfc/nfc_worker.c | 117 ++++++++-------- lib/nfc/protocols/nfcv.c | 127 ++++++++++++++---- lib/nfc/protocols/nfcv.h | 6 +- lib/nfc/protocols/slix.c | 42 ++++-- lib/nfc/protocols/slix.h | 4 +- 8 files changed, 271 insertions(+), 108 deletions(-) diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 180c7ba8f..80d8c6984 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -276,7 +276,7 @@ static void nfc_cli_st25r_trans(Cli* cli, FuriString* args) { printf("ISO15693 emulator...\r\nPress Ctrl+C to abort\r\n"); FuriHalNfcDevData nfc_data = { - .uid = { 0x36, 0x78, 0x45, 0x0E, 0x50, 0x03, 0x04, 0xE0 }, + .uid = { 0xE0, 0x04, 0x45, 0x03, 0x50, 0x0E, 0x78, 0x36 }, .uid_len = 8, .type = FuriHalNfcTypeV, }; @@ -295,7 +295,7 @@ static void nfc_cli_st25r_trans(Cli* cli, FuriString* args) { memset(nfcv_data.data, 0xAE, 4 * 8); - nfcv_emu_init(); + nfcv_emu_init(&nfc_data, &nfcv_data); while(!cli_cmd_interrupt_received(cli)) { if(nfcv_emu_loop(&nfc_data, &nfcv_data, 1000)) { printf("[NfcV-Emu] %s\r\n", nfcv_data.last_command); diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index bee3cc36d..a7ffccdb8 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -7,6 +7,17 @@ void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType typ } } +uint32_t nfc_scene_nfc_data_info_get_key(uint8_t *data) { + uint32_t value = 0; + + for(uint32_t pos = 0; pos < 4; pos++) { + value <<= 8; + value |= data[pos]; + } + + return value; +} + void nfc_scene_nfc_data_info_on_enter(void* context) { Nfc* nfc = context; Widget* widget = nfc->widget; @@ -85,13 +96,59 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { furi_string_cat_printf(temp_str, "Data (%d byte)\n", nfcv_data->block_num * nfcv_data->block_size); - for(int block = 0; block < nfcv_data->block_num; block++) { + int maxBlocks = nfcv_data->block_num; + if(maxBlocks > 32) { + maxBlocks = 32; + furi_string_cat_printf(temp_str, "(truncated to %d blocks)\n", maxBlocks); + } + + for(int block = 0; block < maxBlocks; block++) { for(int pos = 0; pos < nfcv_data->block_size; pos++) { furi_string_cat_printf(temp_str, " %02X", nfcv_data->data[block * nfcv_data->block_size + pos]); } furi_string_cat_printf(temp_str, "\n"); } furi_string_cat_printf(temp_str, "\n"); + + switch (dev_data->nfcv_data.type) + { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "Type: Plain\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "Type: SLIX\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf(temp_str, " EAS %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix.key_eas)); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "Type: SLIX-S\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf(temp_str, " Read %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix_s.key_read)); + furi_string_cat_printf(temp_str, " Write %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix_s.key_write)); + furi_string_cat_printf(temp_str, " Privacy %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix_s.key_privacy)); + furi_string_cat_printf(temp_str, " Destroy %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix_s.key_destroy)); + furi_string_cat_printf(temp_str, " EAS %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix_s.key_eas)); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "Type: SLIX-L\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf(temp_str, " Privacy %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix_l.key_privacy)); + furi_string_cat_printf(temp_str, " Destroy %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix_l.key_destroy)); + furi_string_cat_printf(temp_str, " EAS %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix_l.key_eas)); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "Type: SLIX2\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf(temp_str, " Read %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix2.key_read)); + furi_string_cat_printf(temp_str, " Write %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix2.key_write)); + furi_string_cat_printf(temp_str, " Privacy %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix2.key_privacy)); + furi_string_cat_printf(temp_str, " Destroy %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix2.key_destroy)); + furi_string_cat_printf(temp_str, " EAS %08lX\n", nfc_scene_nfc_data_info_get_key(nfcv_data->sub_data.slix2.key_eas)); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } } else { char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 886944fb1..9ff19bc1e 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -807,23 +807,26 @@ bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { bool saved = false; - uint8_t temp_value = 0; NfcVData* data = &dev->dev_data.nfcv_data; do { + uint32_t temp_uint32 = 0; + uint8_t temp_uint8 = 0; + if(!flipper_format_write_hex(file, "DSFID", &(data->dsfid), 1)) break; if(!flipper_format_write_hex(file, "AFI", &(data->afi), 1)) break; if(!flipper_format_write_hex(file, "IC Reference", &(data->ic_ref), 1)) break; - if(!flipper_format_write_hex(file, "Block Count", &(data->block_num), 1)) break; + temp_uint32 = data->block_num; + if(!flipper_format_write_uint32(file, "Block Count", &temp_uint32, 1)) break; if(!flipper_format_write_hex(file, "Block Size", &(data->block_size), 1)) break; if(!flipper_format_write_hex(file, "Data Content", data->data, data->block_num * data->block_size)) break; if(!flipper_format_write_comment_cstr(file, "Subtype of this card (0 = ISO15693, 1 = SLIX, 2 = SLIX-S, 3 = SLIX-L, 4 = SLIX2)")) break; - - temp_value = data->type; - if(!flipper_format_write_hex(file, "Subtype", &temp_value, 1)) break; + temp_uint8 = (uint8_t)data->type; + if(!flipper_format_write_hex(file, "Subtype", &temp_uint8, 1)) break; switch(data->type) { case NfcVTypePlain: + if(!flipper_format_write_comment_cstr(file, "End of ISO15693 parameters")) break; saved = true; break; case NfcVTypeSlix: @@ -846,16 +849,19 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; - uint8_t temp_value = 0; NfcVData* data = &dev->dev_data.nfcv_data; memset(data, 0, sizeof(NfcVData)); do { + uint32_t temp_uint32 = 0; + uint8_t temp_value = 0; + if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; - if(!flipper_format_read_hex(file, "Block Count", &(data->block_num), 1)) break; + if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break; + data->block_num = temp_uint32; if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break; if(!flipper_format_read_hex( file, "Data Content", data->data, data->block_num * data->block_size)) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 6533cc94c..805a34e0d 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -6,6 +6,7 @@ #define TAG "NfcWorker" + /***************************** NFC Worker API *******************************/ NfcWorker* nfc_worker_alloc() { @@ -122,6 +123,31 @@ int32_t nfc_worker_task(void* context) { return 0; } +static bool nfc_worker_read_nfcv_content(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + NfcVReader reader = {}; + + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + do { + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; + if(!nfcv_read_card(&reader, nfc_data, nfcv_data)) break; + + read_success = true; + } while(false); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + + return read_success; +} void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { furi_assert(nfc_worker); @@ -129,6 +155,8 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; FuriHalNfcTxRxContext tx_rx = {}; + uint8_t *key_data = nfcv_data->sub_data.slix_l.key_privacy; + uint32_t key = 0; if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); @@ -153,21 +181,20 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCV); - uint8_t rand[2]; - furi_hal_console_printf("Detect presence\r\n"); - ReturnCode ret = slix_l_get_random(rand); + ReturnCode ret = slix_l_get_random(nfcv_data); if(ret == ERR_NONE) { /* there is some chip, responding with a RAND */ + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; furi_hal_console_printf(" Chip detected. In privacy?\r\n"); ret = nfcv_inventory(NULL); if(ret == ERR_NONE) { /* chip is also visible, so no action required, just save */ if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { - NfcVReader reader; - + NfcVReader reader = {}; + if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n"); snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); @@ -190,21 +217,32 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { while(slix_l_get_random(NULL) == ERR_NONE) { furi_delay_ms(100); } + + key_data[0] = 0; + key_data[1] = 0; + key_data[2] = 0; + key_data[3] = 0; + } else { /* chip is invisible, try to unlock */ furi_hal_console_printf(" chip is invisible, unlocking\r\n"); if(nfcv_data->auth_method == NfcVAuthMethodManual) { - uint32_t key = 0; - uint8_t *key_data = nfc_worker->dev_data->nfcv_data.sub_data.slix_l.key_privacy; key |= key_data[0] << 24; key |= key_data[1] << 16; key |= key_data[2] << 8; key |= key_data[3] << 0; - ret = slix_l_unlock(4, rand, key); + + ret = slix_l_unlock(nfcv_data, 4); } else { - ret = slix_l_unlock(4, rand, 0x7FFD6E5B); + key = 0x7FFD6E5B; + key_data[0] = key >> 24; + key_data[1] = key >> 16; + key_data[2] = key >> 8; + key_data[3] = key >> 0; + ret = slix_l_unlock(nfcv_data, 4); + if(ret != ERR_NONE) { /* main key failed, trying second one */ furi_hal_console_printf(" trying second key after resetting\r\n"); @@ -214,10 +252,16 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { furi_delay_ms(20); furi_hal_nfc_ll_txrx_on(); - if(slix_l_get_random(rand) != ERR_NONE) { + if(slix_l_get_random(nfcv_data) != ERR_NONE) { furi_hal_console_printf(" reset failed\r\n"); } - ret = slix_l_unlock(4, rand, 0x0F0F0F0F); + + key = 0x0F0F0F0F; + key_data[0] = key >> 24; + key_data[1] = key >> 16; + key_data[2] = key >> 8; + key_data[3] = key >> 0; + ret = slix_l_unlock(nfcv_data, 4); } } if(ret != ERR_NONE) { @@ -251,27 +295,6 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { } } -static bool nfc_worker_read_nfcv_content(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { - bool read_success = false; - NfcVReader reader = {}; - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); - reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); - } - - do { - if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; - if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, &nfc_worker->dev_data->nfcv_data)) break; - read_success = true; - } while(false); - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - reader_analyzer_stop(nfc_worker->reader_analyzer); - } - - return read_success; -} static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { @@ -491,9 +514,6 @@ static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t furi_assert(nfc_worker); furi_assert(tx_rx); - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; - bool card_read = false; furi_hal_nfc_sleep(); @@ -501,24 +521,6 @@ static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t we will read it here again and it will get placed in the right order. */ card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx); - nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; - - if(slix_check_card_type(nfc_data)) { - FURI_LOG_I(TAG, "NXP SLIX detected"); - nfcv_data->type = NfcVTypeSlix; - } else if(slix2_check_card_type(nfc_data)) { - FURI_LOG_I(TAG, "NXP SLIX2 detected"); - nfcv_data->type = NfcVTypeSlix2; - } else if(slix_s_check_card_type(nfc_data)) { - FURI_LOG_I(TAG, "NXP SLIX-L detected"); - nfcv_data->type = NfcVTypeSlixS; - } else if(slix_l_check_card_type(nfc_data)) { - FURI_LOG_I(TAG, "NXP SLIX-L detected"); - nfcv_data->type = NfcVTypeSlixL; - } else { - nfcv_data->type = NfcVTypePlain; - } - return card_read; } @@ -570,12 +572,13 @@ void nfc_worker_read(NfcWorker* nfc_worker) { break; } else if(nfc_data->type == FuriHalNfcTypeV) { FURI_LOG_I(TAG, "NfcV detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) { FURI_LOG_I(TAG, "nfc_worker_read_nfcv success"); - event = NfcWorkerEventReadNfcV; - break; + //event = NfcWorkerEventReadNfcV; + //break; } - event = NfcWorkerEventReadUidNfcV; + event = NfcWorkerEventReadNfcV; break; } } else { @@ -622,7 +625,7 @@ void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker) { FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; - nfcv_emu_init(); + nfcv_emu_init(nfc_data, nfcv_data); while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { if(nfcv_emu_loop(nfc_data, nfcv_data, 1000)) { if(nfc_worker->callback) { diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 1f94211fc..9a06e133f 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -15,15 +15,24 @@ #include "nfcv.h" #include "nfc_util.h" +#include "slix.h" #define TAG "NfcV" ReturnCode nfcv_inventory(uint8_t* uid) { uint16_t received = 0; rfalNfcvInventoryRes res; + ReturnCode ret = ERR_NONE; - /* TODO: needs proper abstraction via fury_hal(_ll)_* */ - ReturnCode ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received); + for(int tries = 0; tries < 5; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerInventory( + RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received); + + if(ret == ERR_NONE) { + break; + } + } if(ret == ERR_NONE) { if(uid != NULL) { @@ -37,18 +46,24 @@ ReturnCode nfcv_inventory(uint8_t* uid) { ReturnCode nfcv_read_blocks( NfcVReader* reader, NfcVData* data) { - - reader->blocks_read = 0; + + UNUSED(reader); uint16_t received = 0; for(size_t block = 0; block < data->block_num; block++) { uint8_t rxBuf[32]; - FURI_LOG_D(TAG, "Reading block %d", block); + FURI_LOG_D(TAG, "Reading block %d/%d", block, (data->block_num - 1)); - ReturnCode ret = rfalNfcvPollerReadSingleBlock( - RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block, - rxBuf, sizeof(rxBuf), &received); + ReturnCode ret = ERR_NONE; + for(int tries = 0; tries < 5; tries++) { + ret = rfalNfcvPollerReadSingleBlock( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block, + rxBuf, sizeof(rxBuf), &received); + if(ret == ERR_NONE) { + break; + } + } if(ret != ERR_NONE) { FURI_LOG_D(TAG, "failed to read: %d", ret); return ret; @@ -57,24 +72,27 @@ ReturnCode nfcv_read_blocks( FURI_LOG_D(TAG, " %02X %02X %02X %02X", data->data[block * data->block_size + 0], data->data[block * data->block_size + 1], data->data[block * data->block_size + 2], data->data[block * data->block_size + 3]); - - reader->blocks_read++; } - FURI_LOG_D(TAG, "Read %d blocks", reader->blocks_read); - return ERR_NONE; } ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data) { uint8_t rxBuf[32]; uint16_t received = 0; + ReturnCode ret = ERR_NONE; - FURI_LOG_D(TAG, "Read SystemInformation..."); + FURI_LOG_D(TAG, "Read SYSTEM INFORMATION..."); - ReturnCode ret = rfalNfcvPollerGetSystemInformation( - RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, - rxBuf, sizeof(rxBuf), &received); + for(int tries = 0; tries < 5; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerGetSystemInformation( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + break; + } + } if(ret == ERR_NONE) { nfc_data->type = FuriHalNfcTypeV; @@ -88,6 +106,9 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data) { data->block_num = rxBuf[12] + 1; data->block_size = rxBuf[13] + 1; data->ic_ref = rxBuf[14]; + FURI_LOG_D(TAG, " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3], + nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]); FURI_LOG_D(TAG, " DSFID %d, AFI %d, Blocks %d, Size %d, IC Ref %d", data->dsfid, data->afi, data->block_num, data->block_size, data->ic_ref); return ret; } @@ -101,14 +122,34 @@ bool nfcv_read_card( FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { furi_assert(reader); + furi_assert(nfc_data); furi_assert(nfcv_data); if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) { return false; } - reader->blocks_to_read = nfcv_data->block_num; - return (nfcv_read_blocks(reader, nfcv_data) == ERR_NONE); + if(nfcv_read_blocks(reader, nfcv_data) != ERR_NONE) { + return false; + } + + if(slix_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX detected"); + nfcv_data->type = NfcVTypeSlix; + } else if(slix2_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX2 detected"); + nfcv_data->type = NfcVTypeSlix2; + } else if(slix_s_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-S detected"); + nfcv_data->type = NfcVTypeSlixS; + } else if(slix_l_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-L detected"); + nfcv_data->type = NfcVTypeSlixL; + } else { + nfcv_data->type = NfcVTypePlain; + } + + return true; } /* emulation part */ @@ -296,7 +337,26 @@ int nfcv_uidcmp(uint8_t *dst, uint8_t *src) { return 0; } +uint32_t nfcv_read_le(uint8_t *data, uint32_t length) { + uint32_t value = 0; + for(uint32_t pos = 0; pos < length; pos++) { + value |= data[pos] << ((int)pos * 8); + } + + return value; +} + +uint32_t nfcv_read_be(uint8_t *data, uint32_t length) { + uint32_t value = 0; + + for(uint32_t pos = 0; pos < length; pos++) { + value <<= 8; + value |= data[pos]; + } + + return value; +} void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint8_t* payload, uint32_t payload_length) { @@ -307,7 +367,7 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui uint8_t flags = payload[0]; uint8_t command = payload[1]; bool addressed = !(flags & RFAL_NFCV_REQ_FLAG_INVENTORY) && (flags & RFAL_NFCV_REQ_FLAG_ADDRESS); - bool advanced = addressed && (command >= 0xA0); + bool advanced = (command >= 0xA0); uint8_t address_offset = 2 + (advanced ? 1 : 0); uint8_t payload_offset = address_offset + (addressed ? 8 : 0); uint8_t *address = &payload[address_offset]; @@ -315,7 +375,7 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui if(addressed && nfcv_uidcmp(address, nfc_data->uid)) { printf("addressed packet, but not for us:\r\n"); printf(" destination: %02X%02X%02X%02X%02X%02X%02X%02X\r\n", address[7], address[6], address[5], address[4], address[3], address[2], address[1], address[0]); - printf(" our UID: %02X%02X%02X%02X%02X%02X%02X%02X\r\n", nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5], nfc_data->uid[4], nfc_data->uid[3], nfc_data->uid[2], nfc_data->uid[1], nfc_data->uid[0]); + printf(" our UID: %02X%02X%02X%02X%02X%02X%02X%02X\r\n", nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3], nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]); return; } @@ -326,7 +386,9 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui if(nfcv_data->sub_data.slix_l.privacy && command != ISO15693_CMD_NXP_GET_RANDOM_NUMBER && command != ISO15693_CMD_NXP_SET_PASSWORD) { - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "(command ignored, privacy)"); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "command 0x%02X ignored, privacy mode", command); + FURI_LOG_D(TAG, "%s", nfcv_data->last_command); + return; } break; @@ -434,8 +496,8 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui nfcv_data->sub_data.slix_l.rand[1] = 0x00; response_buffer[0] = ISO15693_NOERROR; - response_buffer[1] = nfcv_data->sub_data.slix_l.rand[0]; - response_buffer[2] = nfcv_data->sub_data.slix_l.rand[1]; + response_buffer[1] = nfcv_data->sub_data.slix_l.rand[1]; + response_buffer[2] = nfcv_data->sub_data.slix_l.rand[0]; nfcv_emu_send(response_buffer, 3); snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "GET_RANDOM_NUMBER"); @@ -466,14 +528,16 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui } for(int pos = 0; pos < 4; pos++) { - password_rcv[pos] = password_xored[pos] ^ rand[pos % 2]; + password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; } + uint32_t pass_expect = nfcv_read_be(password, 4); + uint32_t pass_received = nfcv_read_be(password_rcv, 4); - if(!memcmp(password, password_rcv, 4)) { + if(pass_expect == pass_received) { status = ISO15693_NOERROR; nfcv_data->sub_data.slix_l.privacy = false; } else { - printf("pass mismatch: %08lX %08lX %02X", *((uint32_t *)password), *((uint32_t *)password_xored), *rand); + FURI_LOG_D(TAG, "Password #%d mismatch. Expected 0x%08lX, got 0x%08lX", password_id, pass_expect, pass_received); } response_buffer[0] = status; @@ -505,7 +569,7 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui uint32_t nfcv_timer_buffer_src[32]; uint32_t nfcv_timer_buffer[1024]; -void nfcv_emu_init() { +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { nfcv_emu_alloc(); rfal_platform_spi_acquire(); @@ -516,6 +580,15 @@ void nfcv_emu_init() { furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + + FURI_LOG_D(TAG, "Starting NfcV emulation"); + FURI_LOG_D(TAG, " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3], + nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]); + FURI_LOG_D(TAG, " Card type: %d", nfcv_data->type); + FURI_LOG_D(TAG, " Privacy pass: 0x%08lX", nfcv_read_be(nfcv_data->sub_data.slix_l.key_privacy, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix_l.privacy ? "ON" : "OFF"); + #if 0 memset(nfcv_timer_buffer_src, 0xEE, sizeof(nfcv_timer_buffer_src)); memset(nfcv_timer_buffer, 0xFA, sizeof(nfcv_timer_buffer)); diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index e8188cb3f..be5b090f4 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -15,7 +15,7 @@ #define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) #define DIGITAL_SIGNAL_UNIT_US (100000.0f) -#define NFCV_TOTAL_BLOCKS_MAX 32 +#define NFCV_TOTAL_BLOCKS_MAX 256 #define NFCV_BLOCK_SIZE 4 #define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE*NFCV_TOTAL_BLOCKS_MAX) @@ -133,7 +133,7 @@ typedef struct { uint8_t dsfid; uint8_t afi; uint8_t ic_ref; - uint8_t block_num; + uint16_t block_num; uint8_t block_size; uint8_t data[NFCV_MAX_DUMP_SIZE]; @@ -158,6 +158,6 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data); ReturnCode nfcv_inventory(uint8_t* uid); bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data); -void nfcv_emu_init(); +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); void nfcv_emu_deinit(); bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t timeout_ms); diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c index e7dfe44be..ca3ebcd67 100644 --- a/lib/nfc/protocols/slix.c +++ b/lib/nfc/protocols/slix.c @@ -46,7 +46,7 @@ bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data) { } -ReturnCode slix_l_get_random(uint8_t* rand) { +ReturnCode slix_l_get_random(NfcVData* data) { uint16_t received = 0; uint8_t rxBuf[32]; @@ -65,26 +65,50 @@ ReturnCode slix_l_get_random(uint8_t* rand) { if(received != 3) { return ERR_PROTO; } - if(rand != NULL) { - memcpy(rand, &rxBuf[1], 2); + if(data != NULL) { + data->sub_data.slix_l.rand[0] = rxBuf[2]; + data->sub_data.slix_l.rand[1] = rxBuf[1]; } } return ret; } -ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password) { +ReturnCode slix_l_unlock(NfcVData* data, uint32_t password_id) { furi_assert(rand); uint16_t received = 0; uint8_t rxBuf[32]; uint8_t cmd_set_pass[] = { - id, - rand[0] ^ ((password >> 0) & 0xFF), - rand[1] ^ ((password >> 8) & 0xFF), - rand[0] ^ ((password >> 16) & 0xFF), - rand[1] ^ ((password >> 24) & 0xFF) + password_id, + data->sub_data.slix_l.rand[1], + data->sub_data.slix_l.rand[0], + data->sub_data.slix_l.rand[1], + data->sub_data.slix_l.rand[0] }; + uint8_t *password = NULL; + + switch(password_id) { + case 4: + password = data->sub_data.slix_l.key_privacy; + break; + case 8: + password = data->sub_data.slix_l.key_destroy; + break; + case 10: + password = data->sub_data.slix_l.key_eas; + break; + default: + break; + } + + if(!password) { + return ERR_NOTSUPP; + } + + for(int pos = 0; pos < 4; pos++) { + cmd_set_pass[1 + pos] ^= password[3 - pos]; + } ReturnCode ret = rfalNfcvPollerTransceiveReq( ISO15693_CMD_NXP_SET_PASSWORD, diff --git a/lib/nfc/protocols/slix.h b/lib/nfc/protocols/slix.h index 0bcfe702a..b95a27abd 100644 --- a/lib/nfc/protocols/slix.h +++ b/lib/nfc/protocols/slix.h @@ -14,6 +14,6 @@ bool slix_check_card_type(FuriHalNfcDevData* nfc_data); bool slix2_check_card_type(FuriHalNfcDevData* nfc_data); bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); -ReturnCode slix_l_get_random(uint8_t* rand); -ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password); +ReturnCode slix_l_get_random(NfcVData* data); +ReturnCode slix_l_unlock(NfcVData* data, uint32_t password_id); From d6903a4a066827bb4afcaab4200faea206bb2744 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 17 Nov 2022 21:58:17 +0100 Subject: [PATCH 09/34] some more comments updated API file --- firmware/targets/f7/api_symbols.csv | 4 ++-- lib/nfc/nfc_device.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 2aaf7c8dc..4022a1d25 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.12,, +Version,+,7.13,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1960,7 +1960,7 @@ Function,-,nfca_signal_alloc,NfcaSignal*, Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" Function,-,nfca_signal_free,void,NfcaSignal* Function,-,nfcv_emu_deinit,void, -Function,-,nfcv_emu_init,void, +Function,-,nfcv_emu_init,void,"FuriHalNfcDevData*, NfcVData*" Function,-,nfcv_emu_loop,_Bool,"FuriHalNfcDevData*, NfcVData*, uint32_t" Function,-,nfcv_inventory,ReturnCode,uint8_t* Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*" diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 9ff19bc1e..b773be90b 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -813,11 +813,15 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { uint32_t temp_uint32 = 0; uint8_t temp_uint8 = 0; + if(!flipper_format_write_comment_cstr(file, "Data Storage Format Identifier")) break; if(!flipper_format_write_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_write_comment_cstr(file, "Application Family Identifier")) break; if(!flipper_format_write_hex(file, "AFI", &(data->afi), 1)) break; if(!flipper_format_write_hex(file, "IC Reference", &(data->ic_ref), 1)) break; temp_uint32 = data->block_num; + if(!flipper_format_write_comment_cstr(file, "Number of memory blocks, usually 0 to 256")) break; if(!flipper_format_write_uint32(file, "Block Count", &temp_uint32, 1)) break; + if(!flipper_format_write_comment_cstr(file, "Size of a single memory block, usually 4")) break; if(!flipper_format_write_hex(file, "Block Size", &(data->block_size), 1)) break; if(!flipper_format_write_hex(file, "Data Content", data->data, data->block_num * data->block_size)) break; if(!flipper_format_write_comment_cstr(file, "Subtype of this card (0 = ISO15693, 1 = SLIX, 2 = SLIX-S, 3 = SLIX-L, 4 = SLIX2)")) break; From 24f8db3c27d488a4c5d7fde33bb37153e938c595 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Sat, 19 Nov 2022 00:22:41 +0100 Subject: [PATCH 10/34] temporary DMA based timer capturing --- lib/nfc/protocols/nfcv.c | 43 +++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 9a06e133f..9ef0172a9 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -568,6 +568,13 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui #define COUNT(x) ((sizeof(x))/(sizeof(x[0]))) uint32_t nfcv_timer_buffer_src[32]; uint32_t nfcv_timer_buffer[1024]; +volatile uint32_t nfcv_gpio_calls = 0; + +void nfcv_gpio_cb(void* ctx) { + UNUSED(ctx); + + nfcv_gpio_calls++; +} void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { nfcv_emu_alloc(); @@ -589,7 +596,6 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { FURI_LOG_D(TAG, " Privacy pass: 0x%08lX", nfcv_read_be(nfcv_data->sub_data.slix_l.key_privacy, 4)); FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix_l.privacy ? "ON" : "OFF"); -#if 0 memset(nfcv_timer_buffer_src, 0xEE, sizeof(nfcv_timer_buffer_src)); memset(nfcv_timer_buffer, 0xFA, sizeof(nfcv_timer_buffer)); @@ -602,8 +608,8 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { dma_config.MemoryOrM2MDstAddress = (uint32_t) nfcv_timer_buffer; dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.Mode = LL_DMA_MODE_NORMAL; - dma_config.NbData = 32; /* executes LL_DMA_SetDataLength */ + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.NbData = COUNT(nfcv_timer_buffer); /* executes LL_DMA_SetDataLength */ dma_config.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; @@ -617,7 +623,7 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); LL_TIM_SetPrescaler(TIM2, 0); - LL_TIM_SetAutoReload(TIM2, 0xFFF); + LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); @@ -625,15 +631,13 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); /* generator 0 gets fed by EXTI_LINE4 */ LL_DMAMUX_SetRequestSignalID(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_EXTI_LINE4); - /* trigger on any edge - for now. should be only one? */ + /* trigger on any edge */ LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); /* now enable request generation again */ LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); - /* configure PB4 as source for EXTI_LINE4. shall it rain. */ - LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTB, LL_GPIO_PIN_4); - -#endif + /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */ + furi_hal_gpio_init_ex(&gpio_spi_r_miso, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused); } @@ -679,7 +683,7 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti next_sleep = cur_time + (timeout_ms * clocks_in_ms); /* start edge counting on first rising edge */ - if(state || edges_received) { + if(0 && (state || edges_received)) { edges_received++; /* ignore periods which are too long, might happen on field start */ @@ -693,8 +697,8 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti if(state) { if(periods == 1) { FURI_CRITICAL_ENTER_ADV(); - timeout = cur_time + bit_time * 16; in_critical = true; + timeout = cur_time + bit_time * 16; frame_state = NFCV_FRAME_STATE_SOF2; } else { snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 1 period, got %lu", periods); @@ -856,16 +860,23 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos); ret = true; } -/* + + printf("edges_received: %lu\r\n", edges_received); + printf("nfcv_gpio_calls: %lu\r\n", nfcv_gpio_calls); printf("nfcv_timer_buffer: %ld", LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_4)); - for(uint32_t pos = 0; pos < 64; pos++) { + + uint32_t prev_timer = 0; + for(uint32_t pos = 0; pos < COUNT(nfcv_timer_buffer) - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_4); pos++) { if((pos % 4) == 0) { printf("\r\n"); } - printf(" 0x%08lX ", nfcv_timer_buffer[pos]); + printf(" 0x%08lX ", (nfcv_timer_buffer[pos] - prev_timer)); + + prev_timer = nfcv_timer_buffer[pos]; } - furi_delay_ms(100); - */ + printf("\r\n"); + //furi_delay_ms(100); + return ret; } From 01137a5b6eaa86d8ad3e2b1b30ce47ad23bbc9c2 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Sat, 19 Nov 2022 00:38:42 +0100 Subject: [PATCH 11/34] added pulse_reader for DMA based NFC signal reading updated nfcv to use pulse_reader instead of bitbanging --- firmware/targets/f7/api_symbols.csv | 10 +- lib/SConscript | 1 + lib/misc.scons | 2 + lib/nfc/nfc_worker.c | 2 +- lib/nfc/protocols/nfcv.c | 392 +++++++++------------------- lib/nfc/protocols/nfcv.h | 1 + lib/pulse_reader/pulse_reader.c | 161 ++++++++++++ lib/pulse_reader/pulse_reader.h | 127 +++++++++ 8 files changed, 432 insertions(+), 264 deletions(-) create mode 100644 lib/pulse_reader/pulse_reader.c create mode 100644 lib/pulse_reader/pulse_reader.h diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 4022a1d25..a3ad98dad 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.13,, +Version,+,7.20,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2066,6 +2066,14 @@ Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, siz Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*" +Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" +Function,-,pulse_reader_free,void,PulseReader* +Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int" +Function,-,pulse_reader_samples,uint32_t,PulseReader* +Function,-,pulse_reader_set_bittime,void,"PulseReader*, uint32_t" +Function,-,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit" +Function,-,pulse_reader_start,void,PulseReader* +Function,-,pulse_reader_stop,void,PulseReader* Function,-,putc,int,"int, FILE*" Function,-,putc_unlocked,int,"int, FILE*" Function,-,putchar,int,int diff --git a/lib/SConscript b/lib/SConscript index 60ffabfa9..44e79a653 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -4,6 +4,7 @@ env.Append( LINT_SOURCES=[ "lib/app-scened-template", "lib/digital_signal", + "lib/pulse_reader", "lib/drivers", "lib/flipper_format", "lib/infrared", diff --git a/lib/misc.scons b/lib/misc.scons index 91ad276a0..1f951000e 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -3,6 +3,7 @@ Import("env") env.Append( CPPPATH=[ "#/lib/digital_signal", + "#/lib/pulse_reader", "#/lib/fnv1a_hash", "#/lib/heatshrink", "#/lib/micro-ecc", @@ -25,6 +26,7 @@ sources = [] libs_recurse = [ "digital_signal", + "pulse_reader", "micro-ecc", "one_wire", "u8g2", diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 805a34e0d..eb7329f05 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -627,7 +627,7 @@ void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker) { nfcv_emu_init(nfc_data, nfcv_data); while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { - if(nfcv_emu_loop(nfc_data, nfcv_data, 1000)) { + if(nfcv_emu_loop(nfc_data, nfcv_data, 50)) { if(nfc_worker->callback) { nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); } diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 9ef0172a9..2e35a5a98 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -8,10 +8,6 @@ #include #include #include -#include -#include -#include -#include #include "nfcv.h" #include "nfc_util.h" @@ -154,8 +150,11 @@ bool nfcv_read_card( /* emulation part */ -static const uint32_t clocks_in_ms = 64 * 1000; -static const uint32_t bit_time = 64 * 9.44f; +#define F_SC 13560000 /* MHz */ +#define PULSE_DURATION_PS (128*1000000000000/F_SC) /* ps */ + + +PulseReader *reader_signal = NULL; DigitalSignal* nfcv_resp_pulse_32 = NULL; DigitalSignal* nfcv_resp_unmod = NULL; @@ -565,17 +564,6 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui } } -#define COUNT(x) ((sizeof(x))/(sizeof(x[0]))) -uint32_t nfcv_timer_buffer_src[32]; -uint32_t nfcv_timer_buffer[1024]; -volatile uint32_t nfcv_gpio_calls = 0; - -void nfcv_gpio_cb(void* ctx) { - UNUSED(ctx); - - nfcv_gpio_calls++; -} - void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { nfcv_emu_alloc(); rfal_platform_spi_acquire(); @@ -596,65 +584,26 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { FURI_LOG_D(TAG, " Privacy pass: 0x%08lX", nfcv_read_be(nfcv_data->sub_data.slix_l.key_privacy, 4)); FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix_l.privacy ? "ON" : "OFF"); - memset(nfcv_timer_buffer_src, 0xEE, sizeof(nfcv_timer_buffer_src)); - memset(nfcv_timer_buffer, 0xFA, sizeof(nfcv_timer_buffer)); - - /* configure DMA to read from a timer peripheral */ - LL_DMA_InitTypeDef dma_config = {}; - dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->CNT); - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstAddress = (uint32_t) nfcv_timer_buffer; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.NbData = COUNT(nfcv_timer_buffer); /* executes LL_DMA_SetDataLength */ - dma_config.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - - /* now set up DMA with these settings */ - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_4); - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_4, &dma_config); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4); - - /* make some noise on the counter */ - LL_TIM_DisableCounter(TIM2); - LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); - LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); - LL_TIM_SetPrescaler(TIM2, 0); - LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); - LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableCounter(TIM2); - - /* make sure request generation is disabled before modifying registers */ - LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); - /* generator 0 gets fed by EXTI_LINE4 */ - LL_DMAMUX_SetRequestSignalID(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_EXTI_LINE4); - /* trigger on any edge */ - LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); - /* now enable request generation again */ - LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); - - /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */ - furi_hal_gpio_init_ex(&gpio_spi_r_miso, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused); - + /* allocate a 512 edge buffer, more than enough */ + reader_signal = pulse_reader_alloc(&gpio_spi_r_miso, 512); + /* timebase shall be 1ps */ + pulse_reader_set_timebase(reader_signal, PulseReaderUnitPicosecond); + /* and configure to already calculate the number of bits */ + pulse_reader_set_bittime(reader_signal, PULSE_DURATION_PS); + pulse_reader_start(reader_signal); } void nfcv_emu_deinit() { furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); rfal_platform_spi_release(); nfcv_emu_free(); + + pulse_reader_free(reader_signal); } bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t timeout_ms) { bool ret = false; - uint32_t next_sleep = DWT->CYCCNT + (timeout_ms * clocks_in_ms); - uint32_t timeout = 0; - uint32_t last_change = 0; - uint32_t edges_received = 0; - uint32_t frame_state = NFCV_FRAME_STATE_SOF1; uint32_t periods_previous = 0; uint8_t frame_payload[128]; @@ -662,221 +611,140 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti uint32_t byte_value = 0; uint32_t bits_received = 0; char reset_reason[128]; - - bool prev = furi_hal_gpio_read(&gpio_spi_r_miso); - - bool in_critical = false; - FURI_CRITICAL_DEFINE(); - + bool wait_for_pulse = false; while(true) { - bool state = furi_hal_gpio_read(&gpio_spi_r_miso); - uint32_t cur_time = DWT->CYCCNT; + uint32_t periods = pulse_reader_receive(reader_signal, timeout_ms * 1000); - if(state != prev) { - uint32_t delta = cur_time - last_change; - uint32_t periods = (delta + bit_time/2) / bit_time; - - last_change = cur_time; - prev = state; - next_sleep = cur_time + (timeout_ms * clocks_in_ms); - - /* start edge counting on first rising edge */ - if(0 && (state || edges_received)) { - edges_received++; - - /* ignore periods which are too long, might happen on field start */ - if(periods > 1024) { - continue; - } - - switch(frame_state) { - case NFCV_FRAME_STATE_SOF1: - /* got a rising edge, was it one period? */ - if(state) { - if(periods == 1) { - FURI_CRITICAL_ENTER_ADV(); - in_critical = true; - timeout = cur_time + bit_time * 16; - frame_state = NFCV_FRAME_STATE_SOF2; - } else { - snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 1 period, got %lu", periods); - frame_state = NFCV_FRAME_STATE_RESET; - break; - } - } - break; - - case NFCV_FRAME_STATE_SOF2: - /* waiting for the second low period, telling us about coding */ - if(!state) { - timeout = cur_time + bit_time * 16; - if(periods == 6) { - frame_state = NFCV_FRAME_STATE_CODING_256; - periods_previous = 0; - } else if(periods == 4) { - frame_state = NFCV_FRAME_STATE_CODING_4; - periods_previous = 2; - } else { - snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 4/6 periods, got %lu", periods); - frame_state = NFCV_FRAME_STATE_RESET; - break; - } - } - break; - - case NFCV_FRAME_STATE_CODING_256: - if(!state) { - timeout = cur_time + bit_time * 1024; - if(periods_previous > periods) { - snprintf(reset_reason, sizeof(reset_reason), "1oo256: Missing %lu periods from previous symbol, got %lu", periods_previous, periods); - frame_state = NFCV_FRAME_STATE_RESET; - break; - } - /* previous symbol left us with some pulse periods */ - periods -= periods_previous; - - if(periods > 512) { - snprintf(reset_reason, sizeof(reset_reason), "1oo256: %lu periods is too much", periods); - frame_state = NFCV_FRAME_STATE_RESET; - break; - } - - if(periods == 2) { - frame_state = NFCV_FRAME_STATE_EOF; - break; - } - - periods_previous = 512 - (periods + 1); - byte_value = (periods - 1) / 2; - frame_payload[frame_pos++] = (uint8_t)byte_value; - - } else { - if(periods != 1) { - snprintf(reset_reason, sizeof(reset_reason), "1oo256: Expected a single low pulse"); - frame_state = NFCV_FRAME_STATE_RESET; - break; - } - } - break; - - case NFCV_FRAME_STATE_CODING_4: - /* evaluate high periods on falling edge */ - if(!state) { - timeout = cur_time + bit_time * 16; - if(periods_previous > periods) { - snprintf(reset_reason, sizeof(reset_reason), "1oo4: Missing %lu periods from previous symbol, got %lu", periods_previous, periods); - frame_state = NFCV_FRAME_STATE_RESET; - break; - } - /* previous symbol left us with some pulse periods */ - periods -= periods_previous; - periods_previous = 0; - - byte_value >>= 2; - bits_received += 2; - - if(periods == 1) { - byte_value |= 0x00 << 6; - periods_previous = 6; - } else if(periods == 3) { - byte_value |= 0x01 << 6; - periods_previous = 4; - } else if(periods == 5) { - byte_value |= 0x02 << 6; - periods_previous = 2; - } else if(periods == 7) { - byte_value |= 0x03 << 6; - periods_previous = 0; - } else if(periods == 2) { - frame_state = NFCV_FRAME_STATE_EOF; - break; - } else { - snprintf(reset_reason, sizeof(reset_reason), "1oo4: Expected 1/3/5/7 low pulses, but got %lu", periods); - frame_state = NFCV_FRAME_STATE_RESET; - break; - } - - if(bits_received >= 8) { - frame_payload[frame_pos++] = (uint8_t)byte_value; - bits_received = 0; - } - } else { - if(periods != 1) { - snprintf(reset_reason, sizeof(reset_reason), "1oo4: Expected a single low pulse"); - frame_state = NFCV_FRAME_STATE_RESET; - break; - } - } - break; - } - } + if(periods == PULSE_READER_NO_EDGE) { + break; } + if(wait_for_pulse) { + wait_for_pulse = false; + if(periods != 1) { + snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected a single low pulse in state %lu, but got %lu", frame_state, periods); + frame_state = NFCV_FRAME_STATE_RESET; + } + continue; + } + + switch(frame_state) { + case NFCV_FRAME_STATE_SOF1: + if(periods == 1) { + frame_state = NFCV_FRAME_STATE_SOF2; + } else { + frame_state = NFCV_FRAME_STATE_SOF1; + break; + } + break; + + case NFCV_FRAME_STATE_SOF2: + /* waiting for the second low period, telling us about coding */ + if(periods == 6) { + frame_state = NFCV_FRAME_STATE_CODING_256; + periods_previous = 0; + wait_for_pulse = true; + } else if(periods == 4) { + frame_state = NFCV_FRAME_STATE_CODING_4; + periods_previous = 2; + wait_for_pulse = true; + } else { + //snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 4/6 periods, got %lu", periods); + frame_state = NFCV_FRAME_STATE_SOF1; + } + break; + + case NFCV_FRAME_STATE_CODING_256: + + if(periods_previous > periods) { + snprintf(reset_reason, sizeof(reset_reason), "1oo256: Missing %lu periods from previous symbol, got %lu", periods_previous, periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + + if(periods > 512) { + snprintf(reset_reason, sizeof(reset_reason), "1oo256: %lu periods is too much", periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } + + periods_previous = 512 - (periods + 1); + byte_value = (periods - 1) / 2; + frame_payload[frame_pos++] = (uint8_t)byte_value; + + wait_for_pulse = true; + + break; + + case NFCV_FRAME_STATE_CODING_4: + if(periods_previous > periods) { + snprintf(reset_reason, sizeof(reset_reason), "1oo4: Missing %lu periods from previous symbol, got %lu", periods_previous, periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + periods_previous = 0; + + byte_value >>= 2; + bits_received += 2; + + if(periods == 1) { + byte_value |= 0x00 << 6; + periods_previous = 6; + } else if(periods == 3) { + byte_value |= 0x01 << 6; + periods_previous = 4; + } else if(periods == 5) { + byte_value |= 0x02 << 6; + periods_previous = 2; + } else if(periods == 7) { + byte_value |= 0x03 << 6; + periods_previous = 0; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } else { + snprintf(reset_reason, sizeof(reset_reason), "1oo4: Expected 1/3/5/7 low pulses, but got %lu", periods); + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(bits_received >= 8) { + frame_payload[frame_pos++] = (uint8_t)byte_value; + bits_received = 0; + } + wait_for_pulse = true; + break; + } + /* post-state-machine cleanup and reset */ if(frame_state == NFCV_FRAME_STATE_RESET) { - timeout = 0; - edges_received = 0; frame_state = NFCV_FRAME_STATE_SOF1; - FURI_CRITICAL_EXIT(); - printf("Reset state machine, reason: %s\r\n", reset_reason); - in_critical = false; - furi_delay_ms(50); + + FURI_LOG_D(TAG, "Resetting state machine, reason: '%s'", reset_reason); } else if(frame_state == NFCV_FRAME_STATE_EOF) { - FURI_CRITICAL_EXIT(); - in_critical = false; break; } - - /* no edges detected */ - if(timeout && cur_time > timeout) { - break; - } - - /* might exit early on overflows. guess thats okay. */ - if(cur_time > next_sleep) { - break; - } - } - - if(in_critical) { - FURI_CRITICAL_EXIT(); - in_critical = false; } if(frame_state == NFCV_FRAME_STATE_EOF) { - - furi_hal_gpio_write(&gpio_spi_r_mosi, false); - furi_delay_us(10); - furi_hal_gpio_write(&gpio_spi_r_mosi, true); - furi_delay_us(10); - furi_hal_gpio_write(&gpio_spi_r_mosi, false); - furi_delay_us(10); - furi_hal_gpio_write(&gpio_spi_r_mosi, true); - furi_delay_us(10); - furi_hal_gpio_write(&gpio_spi_r_mosi, false); - + /* we know that this code uses TIM2, so stop pulse reader */ + pulse_reader_stop(reader_signal); nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos); + pulse_reader_start(reader_signal); ret = true; } - printf("edges_received: %lu\r\n", edges_received); - printf("nfcv_gpio_calls: %lu\r\n", nfcv_gpio_calls); - printf("nfcv_timer_buffer: %ld", LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_4)); - - uint32_t prev_timer = 0; - for(uint32_t pos = 0; pos < COUNT(nfcv_timer_buffer) - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_4); pos++) { - if((pos % 4) == 0) { - printf("\r\n"); - } - printf(" 0x%08lX ", (nfcv_timer_buffer[pos] - prev_timer)); - - prev_timer = nfcv_timer_buffer[pos]; - } - printf("\r\n"); - //furi_delay_ms(100); - - return ret; } diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index be5b090f4..25a8550bd 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -4,6 +4,7 @@ #include #include +#include #include "nfc_util.h" #include diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c new file mode 100644 index 000000000..25add2caa --- /dev/null +++ b/lib/pulse_reader/pulse_reader.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pulse_reader.h" + + +#define GPIO_PIN_MAP(pin, prefix) \ + (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ + ((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \ + ((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \ + ((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \ + ((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \ + ((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \ + ((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \ + ((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \ + ((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \ + ((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \ + ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \ + ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \ + ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \ + ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \ + ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \ + prefix##15) + +#define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE) + + + +PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { + PulseReader* signal = malloc(sizeof(PulseReader)); + + signal->timer_buffer = malloc(size * sizeof(uint32_t)); + signal->dma_channel = LL_DMA_CHANNEL_4; + signal->gpio = gpio; + signal->size = size; + signal->timer_value = 0; + signal->pos = 0; + + signal->unit = PulseReaderUnitPicosecond; + signal->bit_time = 1; + + return signal; +} + +void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit) { + signal->unit = unit; +} + +void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) { + signal->bit_time = bit_time; +} + +void pulse_reader_free(PulseReader* signal) { + free(signal->timer_buffer); + free(signal); +} + +uint32_t pulse_reader_samples(PulseReader* signal) { + uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); + + return ((signal->pos + signal->size) - dma_pos) % signal->size; +} + +void pulse_reader_stop(PulseReader* signal) { + + LL_DMA_DisableChannel(DMA1, signal->dma_channel); + LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); + LL_TIM_DisableCounter(TIM2); +} + +void pulse_reader_start(PulseReader* signal) { + + memset(signal->timer_buffer, 0x00, signal->size * sizeof(uint32_t)); + + /* configure DMA to read from a timer peripheral */ + LL_DMA_InitTypeDef dma_config = {}; + dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->CNT); + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstAddress = (uint32_t) signal->timer_buffer; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.NbData = signal->size; /* executes LL_DMA_SetDataLength */ + dma_config.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + + /* start counter */ + LL_TIM_DisableCounter(TIM2); + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetPrescaler(TIM2, 0); + LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); + + /* make sure request generation is disabled before modifying registers */ + LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); + /* generator 0 gets fed by EXTI_LINEn */ + LL_DMAMUX_SetRequestSignalID(NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin)); + /* trigger on rising edge of the interrupt */ + LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); + /* now enable request generation again */ + LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); + + /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */ + furi_hal_gpio_init_ex(signal->gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh, GpioAltFnUnused); + + /* capture current timer */ + signal->pos = 0; + signal->start_level = furi_hal_gpio_read(signal->gpio); + signal->timer_value = TIM2->CNT; + + /* now set up DMA with these settings */ + LL_DMA_DisableChannel(DMA1, signal->dma_channel); + LL_DMA_Init(DMA1, signal->dma_channel, &dma_config); + LL_DMA_EnableChannel(DMA1, signal->dma_channel); +} + +uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { + + uint32_t start_time = DWT->CYCCNT; + uint32_t timeout_ticks = timeout_us * (F_CPU/1000000); + + do { + /* get the DMA's next write position by reading "remaining length" register */ + uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); + + /* the DMA has advanced in the ringbuffer */ + if(dma_pos != signal->pos) { + + uint32_t delta = signal->timer_buffer[signal->pos] - signal->timer_value; + + signal->timer_value = signal->timer_buffer[signal->pos]; + signal->pos++; + signal->pos %= signal->size; + + uint32_t delta_unit = delta * signal->unit; + uint32_t bits = (delta_unit + signal->bit_time / 2) / signal->bit_time; + + return bits; + } + + /* check for timeout */ + uint32_t elapsed = DWT->CYCCNT - start_time; + + if(elapsed > timeout_ticks) { + return PULSE_READER_NO_EDGE; + } + + furi_delay_ms(0); + + } while(true); +} diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h new file mode 100644 index 000000000..1042fab89 --- /dev/null +++ b/lib/pulse_reader/pulse_reader.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PULSE_READER_NO_EDGE 0xFFFFFFFFUL +#define F_CPU 64000000UL + +/** + * unit of the edge durations to return + */ +typedef enum { + PulseReaderUnit64MHz = 1, + PulseReaderUnitPicosecond = 15625 +} PulseReaderUnit; + + +typedef struct { + bool start_level; + uint32_t* timer_buffer; + uint32_t size; + uint32_t pos; + uint32_t timer_value; + PulseReaderUnit unit; + uint32_t bit_time; + uint32_t dma_channel; + const GpioPin* gpio; +} PulseReader; + + +/** Allocate a PulseReader object + * + * Allocates memory for a ringbuffer and initalizes the object + * + * @param[in] gpio the GPIO to use. will get configured as input. + * @param[in] size number of edges to buffer + */ +PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size); + + +/** Free a PulseReader object + * + * Frees all memory of the given object + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_free(PulseReader* signal); + + +/** Start signal capturing + * + * Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_start(PulseReader* signal); + +/** Stop signal capturing + * + * Frees DMA1, TIM2 and DMAMUX_REQ_GEN_0 + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_stop(PulseReader* signal); + + +/** Recevie a sample from ringbuffer + * + * Waits for the specified time until a new edge gets detected. + * If not configured otherwise, the pulse duration will be in picosecond resolution. + * If a bittime was configured, the return value will contain the properly rounded + * number of bit times measured. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] timeout_us time to wait for a signal [µs] + * + * @returns the scaled value of the pulse duration + */ +uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us); + + +/** Get available samples + * + * Get the number of available samples in the ringbuffer + * + * @param[in] signal previously allocated PulseReader object. + * + * @returns the number of samples in buffer + */ +uint32_t pulse_reader_samples(PulseReader* signal); + + +/** Set timebase + * + * Set the timebase to be used when returning pulse duration. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] unit PulseReaderUnit64MHz or PulseReaderUnitPicosecond + */ +void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit); + + + +/** Set bit time + * + * Set the number of timebase units per bit. + * When set, the pulse_reader_receive() will return an already rounded + * bit count value instead of the raw duration. + * + * Set to 1 to return duration again. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] bit_time + */ +void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time); + + +#ifdef __cplusplus +} +#endif From 08a5fee128ac1cb8e4025dbee3c996a11e4f4bbb Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Sat, 19 Nov 2022 12:28:58 +0100 Subject: [PATCH 12/34] log code updates, showing the last entries only --- .../main/nfc/scenes/nfc_scene_emulate_nfcv.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c index 70f0c8ba0..2cc0fe46c 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" -#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200) +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (100) enum { NfcSceneEmulateNfcVStateWidget, @@ -95,11 +95,17 @@ bool nfc_scene_emulate_nfcv_on_event(void* context, SceneManagerEvent event) { if(!furi_string_size(nfc->text_box_store)) { nfc_scene_emulate_nfcv_widget_config(nfc, true); } - // Update TextBox data - if(furi_string_size(nfc->text_box_store) < NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX) { + if(strlen(nfcv_data->last_command) > 0) { + /* use the last n bytes from the log so there's enough space for the new log entry */ + size_t maxSize = NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 3); + if(furi_string_size(nfc->text_box_store) >= maxSize) { + furi_string_right(nfc->text_box_store, maxSize); + } furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); furi_string_push_back(nfc->text_box_store, '\n'); text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + + /* clear previously logged command */ strcpy(nfcv_data->last_command, ""); } consumed = true; From 1344f8f6676c0bf4640a5976661b1f905d85fb23 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Sat, 19 Nov 2022 13:01:04 +0100 Subject: [PATCH 13/34] fix log truncation --- applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c index 2cc0fe46c..0b09bb412 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c @@ -97,9 +97,9 @@ bool nfc_scene_emulate_nfcv_on_event(void* context, SceneManagerEvent event) { } if(strlen(nfcv_data->last_command) > 0) { /* use the last n bytes from the log so there's enough space for the new log entry */ - size_t maxSize = NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 3); + size_t maxSize = NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); if(furi_string_size(nfc->text_box_store) >= maxSize) { - furi_string_right(nfc->text_box_store, maxSize); + furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); } furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); furi_string_push_back(nfc->text_box_store, '\n'); From f2457b6ee586a7fbe24efaaaf224254bd06f1f59 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Sat, 19 Nov 2022 17:03:55 +0100 Subject: [PATCH 14/34] switch to scalable timebase for digital reader to prevent overflows added yielding to emulation loop --- lib/nfc/nfc_worker.c | 1 + lib/nfc/protocols/nfcv.c | 38 +++++++++++++++------------- lib/nfc/protocols/nfcv.h | 12 ++++++--- lib/pulse_reader/pulse_reader.c | 44 +++++++++++++++++++++++++++------ lib/pulse_reader/pulse_reader.h | 11 ++++++--- 5 files changed, 73 insertions(+), 33 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index eb7329f05..1538b2177 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -632,6 +632,7 @@ void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker) { nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); } } + furi_delay_ms(0); } nfcv_emu_deinit(); } diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 2e35a5a98..b14baa2d2 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -150,10 +150,6 @@ bool nfcv_read_card( /* emulation part */ -#define F_SC 13560000 /* MHz */ -#define PULSE_DURATION_PS (128*1000000000000/F_SC) /* ps */ - - PulseReader *reader_signal = NULL; DigitalSignal* nfcv_resp_pulse_32 = NULL; @@ -372,9 +368,9 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui uint8_t *address = &payload[address_offset]; if(addressed && nfcv_uidcmp(address, nfc_data->uid)) { - printf("addressed packet, but not for us:\r\n"); - printf(" destination: %02X%02X%02X%02X%02X%02X%02X%02X\r\n", address[7], address[6], address[5], address[4], address[3], address[2], address[1], address[0]); - printf(" our UID: %02X%02X%02X%02X%02X%02X%02X%02X\r\n", nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3], nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]); + FURI_LOG_D(TAG, "addressed command 0x%02X, but not for us:", command); + FURI_LOG_D(TAG, " dest: %02X%02X%02X%02X%02X%02X%02X%02X", address[7], address[6], address[5], address[4], address[3], address[2], address[1], address[0]); + FURI_LOG_D(TAG, " our UID: %02X%02X%02X%02X%02X%02X%02X%02X", nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3], nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]); return; } @@ -400,7 +396,7 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui case ISO15693_INVENTORY: { response_buffer[0] = ISO15693_NOERROR; - response_buffer[1] = nfcv_data->dsfid; /* DSFID */ + response_buffer[1] = nfcv_data->dsfid; nfcv_uidcpy(&response_buffer[2], nfc_data->uid); nfcv_emu_send(response_buffer, 10); @@ -491,15 +487,18 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui case ISO15693_CMD_NXP_GET_RANDOM_NUMBER: { - nfcv_data->sub_data.slix_l.rand[0] = 0x00; - nfcv_data->sub_data.slix_l.rand[1] = 0x00; + nfcv_data->sub_data.slix_l.rand[0] = furi_hal_random_get(); + nfcv_data->sub_data.slix_l.rand[1] = furi_hal_random_get(); response_buffer[0] = ISO15693_NOERROR; response_buffer[1] = nfcv_data->sub_data.slix_l.rand[1]; response_buffer[2] = nfcv_data->sub_data.slix_l.rand[0]; nfcv_emu_send(response_buffer, 3); - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "GET_RANDOM_NUMBER"); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), + "GET_RANDOM_NUMBER -> 0x%02X%02X", + nfcv_data->sub_data.slix_l.rand[0], + nfcv_data->sub_data.slix_l.rand[1]); break; } @@ -535,14 +534,15 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui if(pass_expect == pass_received) { status = ISO15693_NOERROR; nfcv_data->sub_data.slix_l.privacy = false; + FURI_LOG_D(TAG, "SET_PASSWORD #%d received correct password 0x%08lX", password_id, pass_received); } else { - FURI_LOG_D(TAG, "Password #%d mismatch. Expected 0x%08lX, got 0x%08lX", password_id, pass_expect, pass_received); + FURI_LOG_D(TAG, "SET_PASSWORD #%d mismatch. Expected password 0x%08lX, got 0x%08lX", password_id, pass_expect, pass_received); } response_buffer[0] = status; nfcv_emu_send(response_buffer, 1); - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X", password_id); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X %s", password_id, (status == ISO15693_NOERROR) ? "SUCCESS" : "FAIL"); break; } @@ -562,6 +562,10 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "unsupported: %02X", command); break; } + + if(strlen(nfcv_data->last_command) > 0) { + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); + } } void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { @@ -586,10 +590,10 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { /* allocate a 512 edge buffer, more than enough */ reader_signal = pulse_reader_alloc(&gpio_spi_r_miso, 512); - /* timebase shall be 1ps */ - pulse_reader_set_timebase(reader_signal, PulseReaderUnitPicosecond); + /* timebase shall be 1 ns */ + pulse_reader_set_timebase(reader_signal, PulseReaderUnitNanosecond); /* and configure to already calculate the number of bits */ - pulse_reader_set_bittime(reader_signal, PULSE_DURATION_PS); + pulse_reader_set_bittime(reader_signal, PULSE_DURATION_NS); pulse_reader_start(reader_signal); } @@ -651,7 +655,7 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti periods_previous = 2; wait_for_pulse = true; } else { - //snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 4/6 periods, got %lu", periods); + snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 4/6 periods, got %lu", periods); frame_state = NFCV_FRAME_STATE_SOF1; } break; diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index 25a8550bd..6253fa1a6 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -9,16 +9,20 @@ #include -#define NFCV_FC (13560000.0f) + + +#define NFCV_FC (13560000.0f) /* MHz */ #define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC/32) / 2.0f) /* 1.1799 µs */ #define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */ +#define PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) /* ns */ + #define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) #define DIGITAL_SIGNAL_UNIT_US (100000.0f) -#define NFCV_TOTAL_BLOCKS_MAX 256 -#define NFCV_BLOCK_SIZE 4 -#define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE*NFCV_TOTAL_BLOCKS_MAX) +#define NFCV_TOTAL_BLOCKS_MAX 256 +#define NFCV_BLOCK_SIZE 4 +#define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE*NFCV_TOTAL_BLOCKS_MAX) #define NFCV_FRAME_STATE_SOF1 0 diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index 25add2caa..c5729c42a 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -33,8 +33,8 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { - PulseReader* signal = malloc(sizeof(PulseReader)); + PulseReader* signal = malloc(sizeof(PulseReader)); signal->timer_buffer = malloc(size * sizeof(uint32_t)); signal->dma_channel = LL_DMA_CHANNEL_4; signal->gpio = gpio; @@ -42,14 +42,31 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { signal->timer_value = 0; signal->pos = 0; - signal->unit = PulseReaderUnitPicosecond; - signal->bit_time = 1; + pulse_reader_set_timebase(signal, PulseReaderUnit64MHz); + pulse_reader_set_bittime(signal, 1); return signal; } void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit) { - signal->unit = unit; + switch(unit) { + case PulseReaderUnit64MHz: + signal->unit_multiplier = 1; + signal->unit_divider = 1; + break; + case PulseReaderUnitPicosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1; + break; + case PulseReaderUnitNanosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000; + break; + case PulseReaderUnitMicrosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000000; + break; + } } void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) { @@ -127,7 +144,7 @@ void pulse_reader_start(PulseReader* signal) { uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { uint32_t start_time = DWT->CYCCNT; - uint32_t timeout_ticks = timeout_us * (F_CPU/1000000); + uint32_t timeout_ticks = timeout_us * (F_TIM2/1000000); do { /* get the DMA's next write position by reading "remaining length" register */ @@ -142,10 +159,21 @@ uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { signal->pos++; signal->pos %= signal->size; - uint32_t delta_unit = delta * signal->unit; - uint32_t bits = (delta_unit + signal->bit_time / 2) / signal->bit_time; + uint32_t delta_unit = 0; - return bits; + /* probably larger values, so choose a wider data type */ + if(signal->unit_divider > 1) { + delta_unit = (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider); + } else { + delta_unit = delta * signal->unit_multiplier; + } + + /* if to be scaled to bit times, save a few instructions. should be faster */ + if(signal->bit_time > 1) { + return (delta_unit + signal->bit_time / 2) / signal->bit_time; + } + + return delta_unit; } /* check for timeout */ diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h index 1042fab89..a53e666b0 100644 --- a/lib/pulse_reader/pulse_reader.h +++ b/lib/pulse_reader/pulse_reader.h @@ -11,14 +11,16 @@ extern "C" { #endif #define PULSE_READER_NO_EDGE 0xFFFFFFFFUL -#define F_CPU 64000000UL +#define F_TIM2 64000000UL /** * unit of the edge durations to return */ typedef enum { - PulseReaderUnit64MHz = 1, - PulseReaderUnitPicosecond = 15625 + PulseReaderUnit64MHz, + PulseReaderUnitPicosecond, + PulseReaderUnitNanosecond, + PulseReaderUnitMicrosecond, } PulseReaderUnit; @@ -28,7 +30,8 @@ typedef struct { uint32_t size; uint32_t pos; uint32_t timer_value; - PulseReaderUnit unit; + uint32_t unit_multiplier; + uint32_t unit_divider; uint32_t bit_time; uint32_t dma_channel; const GpioPin* gpio; From 820ccb9775cd1349761caf2099faaef78fcc0f5f Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 21 Nov 2022 01:21:15 +0100 Subject: [PATCH 15/34] switch to signal sequence (nonfunc) --- firmware/targets/f7/api_symbols.csv | 12 +- lib/digital_signal/digital_signal.c | 314 ++++++++++++++++++++++------ lib/digital_signal/digital_signal.h | 41 +++- lib/nfc/protocols/nfcv.c | 43 +++- 4 files changed, 320 insertions(+), 90 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a3ad98dad..9623807ce 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.20,, +Version,+,7.26,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -707,13 +707,21 @@ Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, u Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*" Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" Function,-,difftime,double,"time_t, time_t" +Function,-,digital_sequence_add,void,"DigitalSequence*, uint8_t" +Function,-,digital_sequence_alloc,DigitalSequence*,"uint32_t, const GpioPin*" +Function,-,digital_sequence_clear,void,DigitalSequence* +Function,-,digital_sequence_free,void,DigitalSequence* +Function,-,digital_sequence_send_signal,_Bool,DigitalSignal* +Function,-,digital_sequence_set_signal,void,"DigitalSequence*, uint8_t, DigitalSignal*" +Function,-,digital_sequence_send,_Bool,DigitalSequence* +Function,-,digital_signal_add,void,"DigitalSignal*, uint32_t" Function,-,digital_signal_alloc,DigitalSignal*,uint32_t Function,-,digital_signal_append,_Bool,"DigitalSignal*, DigitalSignal*" Function,-,digital_signal_free,void,DigitalSignal* Function,-,digital_signal_get_edge,uint32_t,"DigitalSignal*, uint32_t" Function,-,digital_signal_get_edges_cnt,uint32_t,DigitalSignal* Function,-,digital_signal_get_start_level,_Bool,DigitalSignal* -Function,-,digital_signal_prepare_arr,void,DigitalSignal* +Function,-,digital_signal_prepare,void,DigitalSignal* Function,-,digital_signal_send,void,"DigitalSignal*, const GpioPin*" Function,-,diprintf,int,"int, const char*, ..." Function,+,dir_walk_alloc,DirWalk*,Storage* diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index c855f188b..76cfe9c39 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -5,6 +5,8 @@ #include #include +#define TAG "[DigitalSignal]" + #pragma GCC optimize("O3,unroll-loops,Ofast") #define F_TIM (64000000.0) @@ -13,11 +15,13 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { DigitalSignal* signal = malloc(sizeof(DigitalSignal)); + signal->prepared = false; signal->start_level = true; signal->edges_max_cnt = max_edges_cnt; - signal->edge_timings = malloc(max_edges_cnt * sizeof(uint32_t)); - signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); + signal->edge_timings = malloc(signal->edges_max_cnt * sizeof(uint32_t)); signal->edge_cnt = 0; + signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); + signal->reload_reg_entries = 0; return signal; } @@ -60,6 +64,8 @@ bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) { } signal_a->edge_cnt += signal_b->edge_cnt - start_copy; + signal_a->prepared = false; + return true; } @@ -75,6 +81,13 @@ uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal) { return signal->edge_cnt; } +void digital_signal_add(DigitalSignal* signal, uint32_t ticks) { + furi_assert(signal); + furi_assert(signal->edge_cnt < signal->edges_max_cnt); + + signal->edge_timings[signal->edge_cnt++] = ticks; +} + uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { furi_assert(signal); furi_assert(edge_num < signal->edge_cnt); @@ -82,77 +95,122 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { return signal->edge_timings[edge_num]; } -void digital_signal_prepare_arr(DigitalSignal* signal) { - uint32_t t_signal_rest = signal->edge_timings[0]; - uint32_t r_count_tick_arr = 0; - uint32_t r_rest_div = 0; +void digital_signal_prepare(DigitalSignal* signal) { + furi_assert(signal); + + if(signal->prepared) { + return; + } + /* set up signal polarities */ + uint32_t bit_set = signal->gpio->pin; + uint32_t bit_reset = signal->gpio->pin << 16; - for(size_t i = 0; i < signal->edge_cnt - 1; i++) { - r_count_tick_arr = t_signal_rest / T_TIM; - r_rest_div = t_signal_rest % T_TIM; - t_signal_rest = signal->edge_timings[i + 1] + r_rest_div; + if(signal->start_level) { + signal->gpio_buff[0] = bit_set; + signal->gpio_buff[1] = bit_reset; + } else { + signal->gpio_buff[0] = bit_reset; + signal->gpio_buff[1] = bit_set; + } - if(r_rest_div < T_TIM_DIV2) { - signal->reload_reg_buff[i] = r_count_tick_arr - 1; - } else { - signal->reload_reg_buff[i] = r_count_tick_arr; - t_signal_rest -= T_TIM; + /* set up edge timings */ + uint32_t remainder = 0; + signal->reload_reg_entries = 0; + + for(size_t pos = 0; pos < signal->edge_cnt; pos++) { + uint32_t pulse_duration = signal->edge_timings[pos] + remainder; + uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; + remainder = pulse_duration - (pulse_ticks * T_TIM); + + if(pulse_ticks > 0) { + signal->reload_reg_buff[signal->reload_reg_entries++] = pulse_ticks; } } + + signal->prepared = true; } -void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { +bool digital_signal_setup_dma(DigitalSignal* signal) { furi_assert(signal); - furi_assert(gpio); - // Configure gpio as output - furi_hal_gpio_init(gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - - // Init gpio buffer and DMA channel - uint16_t gpio_reg = gpio->port->ODR; - uint16_t gpio_buff[2]; - if(signal->start_level) { - gpio_buff[0] = gpio_reg | gpio->pin; - gpio_buff[1] = gpio_reg & ~(gpio->pin); - } else { - gpio_buff[0] = gpio_reg & ~(gpio->pin); - gpio_buff[1] = gpio_reg | gpio->pin; + if(!signal->reload_reg_entries) { + return false; } - LL_DMA_InitTypeDef dma_config = {}; - dma_config.MemoryOrM2MDstAddress = (uint32_t)gpio_buff; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->ODR); - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; - dma_config.NbData = 2; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + + LL_DMA_InitTypeDef dma_config_gpio = {}; + LL_DMA_InitTypeDef dma_config_timer = {}; + + dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buff; + dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->BSRR); + dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config_gpio.NbData = 2; + dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; // Init timer arr register buffer and DMA channel - digital_signal_prepare_arr(signal); - dma_config.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_NORMAL; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = signal->edge_cnt - 2; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_HIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->edge_cnt - 2); + dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; + dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->ARR); + dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config_timer.Mode = LL_DMA_MODE_NORMAL; + dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config_timer.NbData = signal->reload_reg_entries; + dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; + + /* set up DMA channel 1 and 2 for GPIO and timer copy operations */ + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config_gpio); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config_timer); + + /* enable both DMA channels */ + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - // Set up timer + return true; +} + + +void digital_signal_update_dma(DigitalSignal* signal) { + furi_assert(signal); + + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); + + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)signal->gpio_buff); + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->reload_reg_entries); + + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); +} + +void digital_signal_stop_dma() { + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); +} + +void digital_signal_stop_timer() { + LL_TIM_DisableCounter(TIM2); + LL_TIM_SetCounter(TIM2, 0); +} + +void digital_signal_setup_timer() { + + digital_signal_stop_timer(); + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); LL_TIM_SetPrescaler(TIM2, 0); @@ -160,19 +218,137 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableUpdateEvent(TIM2); LL_TIM_EnableDMAReq_UPDATE(TIM2); +} - // Start transactions - LL_TIM_GenerateEvent_UPDATE(TIM2); // Do we really need it? +void digital_signal_start_timer() { + LL_TIM_GenerateEvent_UPDATE(TIM2); LL_TIM_EnableCounter(TIM2); +} - while(!LL_DMA_IsActiveFlag_TC2(DMA1)) - { +void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { + furi_assert(signal); + + /* if selected GPIO changed, force reconfiguration of buffers */ + if(gpio && (signal->gpio != gpio)) { + signal->gpio = gpio; + signal->prepared = false; } - LL_DMA_ClearFlag_TC1(DMA1); - LL_DMA_ClearFlag_TC2(DMA1); - LL_TIM_DisableCounter(TIM2); - LL_TIM_SetCounter(TIM2, 0); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + /* Configure gpio as output */ + furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + digital_signal_prepare(signal); + + digital_signal_setup_dma(signal); + digital_signal_setup_timer(); + digital_signal_start_timer(); + + while(!LL_DMA_IsActiveFlag_TC2(DMA1)) { + } + + digital_signal_stop_timer(); + digital_signal_stop_dma(); +} + + +DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { + + DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); + + sequence->gpio = gpio; + sequence->signals_size = 32; + sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*)); + + sequence->sequence_used = 0; + sequence->sequence_size = size; + sequence->sequence = malloc(sequence->sequence_size); + + return sequence; +} + +void digital_sequence_free(DigitalSequence* sequence) { + furi_assert(sequence); + + free(sequence->signals); + free(sequence->sequence); + free(sequence); +} + +void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index, DigitalSignal* signal) { + furi_assert(sequence); + furi_assert(signal); + furi_assert(signal_index < sequence->signals_size); + + sequence->signals[signal_index] = signal; + + /* all signals will use the sequence's GPIO */ + signal->gpio = sequence->gpio; + signal->prepared = false; + + digital_signal_prepare(signal); +} + +void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { + furi_assert(sequence); + furi_assert(signal_index < sequence->signals_size); + + if(sequence->sequence_used >= sequence->sequence_size) { + sequence->sequence_size += 256; + sequence->sequence = realloc(sequence->sequence, sequence->sequence_size); + } + + sequence->sequence[sequence->sequence_used++] = signal_index; +} + +bool digital_sequence_send_signal(DigitalSignal* signal) { + furi_assert(signal); + + /* the first iteration has to set up the whol machinery */ + if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { + furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + if(!digital_signal_setup_dma(signal)) { + FURI_LOG_D(TAG, "Signal has no entries, aborting"); + return false; + } + digital_signal_setup_timer(); + digital_signal_start_timer(); + } else { + /* it was already active, wait till DMA is done and the last timer ticks are running */ + while(!LL_DMA_IsActiveFlag_TC2(DMA1)) { + } + + /* configure next polarities and timings */ + digital_signal_update_dma(signal); + } + + return true; +} + +bool digital_sequence_send(DigitalSequence* sequence) { + furi_assert(sequence); + + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { + DigitalSignal *sig = sequence->signals[sequence->sequence[pos]]; + + if(!digital_sequence_send_signal(sig)) { + digital_signal_stop_timer(); + digital_signal_stop_dma(); + return false; + } + } + + while(!LL_DMA_IsActiveFlag_TC2(DMA1)) { + } + + digital_signal_stop_timer(); + digital_signal_stop_dma(); + + return true; +} + +void digital_sequence_clear(DigitalSequence* sequence) { + furi_assert(sequence); + + sequence->sequence_used = 0; } diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 90905d74b..00376466e 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -10,30 +10,55 @@ extern "C" { #endif +/* helper for easier signal generation */ +#define DIGITAL_SIGNAL_MS(x) (x*100000000UL) +#define DIGITAL_SIGNAL_US(x) (x*100000UL) +#define DIGITAL_SIGNAL_NS(x) (x*100UL) + + typedef struct { + bool prepared; bool start_level; uint32_t edge_cnt; uint32_t edges_max_cnt; uint32_t* edge_timings; uint32_t* reload_reg_buff; + uint32_t reload_reg_entries; + uint32_t gpio_buff[2]; + const GpioPin* gpio; } DigitalSignal; +typedef struct { + uint8_t signals_size; + uint32_t sequence_used; + uint32_t sequence_size; + const GpioPin* gpio; + DigitalSignal** signals; + uint8_t* sequence; +} DigitalSequence; + + DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); - void digital_signal_free(DigitalSignal* signal); - +void digital_signal_add(DigitalSignal* signal, uint32_t ticks); bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b); - -void digital_signal_prepare_arr(DigitalSignal* signal); - +void digital_signal_prepare(DigitalSignal* signal); bool digital_signal_get_start_level(DigitalSignal* signal); - uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal); - uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num); - void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); + +DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio); +void digital_sequence_free(DigitalSequence* sequence); +void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index, DigitalSignal* signal); +void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index); +bool digital_sequence_send_signal(DigitalSignal* signal); +bool digital_sequence_send(DigitalSequence* sequence); +void digital_sequence_clear(DigitalSequence* sequence); + + + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index b14baa2d2..bce179dba 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -161,7 +161,12 @@ DigitalSignal* nfcv_resp_eof = NULL; DigitalSignal* nfcv_resp_unmod_256 = NULL; DigitalSignal* nfcv_resp_unmod_768 = NULL; -DigitalSignal* nfcv_signal = NULL; +DigitalSequence* nfcv_signal = NULL; + +#define SIG_SOF 0 +#define SIG_BIT0 1 +#define SIG_BIT1 2 +#define SIG_EOF 3 void nfcv_crc(uint8_t* data, uint32_t length, uint8_t* out) { @@ -187,7 +192,7 @@ void nfcv_crc(uint8_t* data, uint32_t length, uint8_t* out) { } void nfcv_emu_free() { - digital_signal_free(nfcv_signal); + digital_sequence_free(nfcv_signal); digital_signal_free(nfcv_resp_unmod_256); digital_signal_free(nfcv_resp_pulse_32); digital_signal_free(nfcv_resp_one); @@ -207,7 +212,8 @@ void nfcv_emu_free() { void nfcv_emu_alloc() { if(!nfcv_signal) { - nfcv_signal = digital_signal_alloc(8192); + /* assuming max frame length is 255 bytes */ + nfcv_signal = digital_sequence_alloc(8 * 255 + 2, &gpio_spi_r_mosi); } if(!nfcv_resp_unmod_256) { @@ -265,6 +271,11 @@ void nfcv_emu_alloc() { /* add extra silence */ digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256); } + + digital_sequence_set_signal(nfcv_signal, SIG_SOF, nfcv_resp_sof); + digital_sequence_set_signal(nfcv_signal, SIG_BIT0, nfcv_resp_zero); + digital_sequence_set_signal(nfcv_signal, SIG_BIT1, nfcv_resp_one); + digital_sequence_set_signal(nfcv_signal, SIG_EOF, nfcv_resp_eof); } @@ -272,10 +283,14 @@ void nfcv_emu_send_raw(uint8_t* data, uint8_t length) { int bits = length * 8; - nfcv_signal->start_level = false; - nfcv_signal->edge_cnt = 0; + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, true); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); - digital_signal_append(nfcv_signal, nfcv_resp_sof); + digital_sequence_clear(nfcv_signal); + digital_sequence_add(nfcv_signal, SIG_SOF); for(int bit_total = 0; bit_total < bits; bit_total++) { uint32_t byte_pos = bit_total / 8; @@ -283,13 +298,13 @@ void nfcv_emu_send_raw(uint8_t* data, uint8_t length) { uint8_t bit_val = 0x01 << bit_pos; if(data[byte_pos] & bit_val) { - digital_signal_append(nfcv_signal, nfcv_resp_one); + digital_sequence_add(nfcv_signal, SIG_BIT1); } else { - digital_signal_append(nfcv_signal, nfcv_resp_zero); + digital_sequence_add(nfcv_signal, SIG_BIT0); } } - digital_signal_append(nfcv_signal, nfcv_resp_eof); + digital_sequence_add(nfcv_signal, SIG_EOF); /* digital signal setup will take some time. win some time by tricking the VCD into thinking that something happens */ furi_hal_gpio_write(&gpio_spi_r_mosi, false); @@ -299,7 +314,7 @@ void nfcv_emu_send_raw(uint8_t* data, uint8_t length) { furi_hal_gpio_write(&gpio_spi_r_mosi, false); FURI_CRITICAL_ENTER(); - digital_signal_send(nfcv_signal, &gpio_spi_r_mosi); + digital_sequence_send(nfcv_signal); FURI_CRITICAL_EXIT(); furi_hal_gpio_write(&gpio_spi_r_mosi, false); } @@ -564,7 +579,7 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui } if(strlen(nfcv_data->last_command) > 0) { - FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); + //FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); } } @@ -744,6 +759,12 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti if(frame_state == NFCV_FRAME_STATE_EOF) { /* we know that this code uses TIM2, so stop pulse reader */ + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, true); + furi_delay_us(10); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + pulse_reader_stop(reader_signal); nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos); pulse_reader_start(reader_signal); From f0feb8921f7a51b54f0526a7c6839257b3bd3d41 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 21 Nov 2022 01:57:16 +0100 Subject: [PATCH 16/34] fix an off-by-one error --- lib/digital_signal/digital_signal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 76cfe9c39..0687c1343 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -123,8 +123,8 @@ void digital_signal_prepare(DigitalSignal* signal) { uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; remainder = pulse_duration - (pulse_ticks * T_TIM); - if(pulse_ticks > 0) { - signal->reload_reg_buff[signal->reload_reg_entries++] = pulse_ticks; + if(pulse_ticks > 1) { + signal->reload_reg_buff[signal->reload_reg_entries++] = pulse_ticks - 1; } } From cd241a29013fcc1929bdcc59de20ff95f4238d67 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 21 Nov 2022 13:36:16 +0100 Subject: [PATCH 17/34] switch to DigitalSequence --- applications/main/nfc/nfc_cli.c | 220 +--------------------------- lib/digital_signal/digital_signal.c | 64 ++++---- lib/digital_signal/digital_signal.h | 1 + lib/nfc/protocols/nfcv.c | 49 +++---- 4 files changed, 60 insertions(+), 274 deletions(-) diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 80d8c6984..676e61b0c 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -14,6 +14,9 @@ #include +#include +#include + static void nfc_cli_print_usage() { printf("Usage:\r\n"); @@ -23,10 +26,6 @@ static void nfc_cli_print_usage() { printf("\temulate\t - emulate predefined nfca card\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("\tfield\t - turn field on\r\n"); - printf("\tst25r_read\t - read ST25R3916 register\r\n"); - printf("\tst25r_write\t - write ST25R3916 register\r\n"); - printf("\tst25r_cmd\t - execute ST25R3916 command\r\n"); - printf("\tst25r_fifo\t - wait for FIFO data\r\n"); } } @@ -111,199 +110,6 @@ static void nfc_cli_field(Cli* cli, FuriString* args) { furi_hal_nfc_sleep(); } -static uint8_t hexdigit(char hex) { - return (hex <= '9') ? hex - '0' : toupper(hex) - 'A' + 10 ; -} - -static uint8_t hexbyte(const char* hex) { - return (hexdigit(*hex) << 4) | hexdigit(*(hex+1)); -} - -static void nfc_cli_st25r_write(Cli* cli, FuriString* args) { - UNUSED(cli); - FuriString* reg = furi_string_alloc(); - FuriString* value = furi_string_alloc(); - - // Check if nfc worker is not busy - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; - } - - if(!args_read_string_and_trim(args, reg) || !args_read_string_and_trim(args, value)) { - printf("You didn't specify and \r\n"); - nfc_cli_print_usage(); - return; - } - - if(furi_string_utf8_length(reg) != 2 || furi_string_utf8_length(value) != 2) { - printf("You didn't specify and as 2-character hex values\r\n"); - nfc_cli_print_usage(); - return; - } - - uint8_t reg_val = hexbyte(furi_string_get_cstr(reg)); - uint8_t value_val = hexbyte(furi_string_get_cstr(value)); - - printf("W %02X <- %02X\r\n", reg_val, value_val); - if(st25r3916WriteRegister(reg_val, value_val) != ERR_NONE) { - printf("Failed to write register\r\n"); - return; - } -} - -static void nfc_cli_st25r_read(Cli* cli, FuriString* args) { - UNUSED(cli); - FuriString* reg = furi_string_alloc(); - - // Check if nfc worker is not busy - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; - } - - if(!args_read_string_and_trim(args, reg)) { - printf("You didn't specify \r\n"); - nfc_cli_print_usage(); - return; - } - - if(furi_string_utf8_length(reg) != 2) { - printf("You didn't specify as 2-character hex value\r\n"); - nfc_cli_print_usage(); - return; - } - - uint8_t reg_val = hexbyte(furi_string_get_cstr(reg)); - uint8_t value_val = 0; - - if(st25r3916ReadRegister(reg_val, &value_val) != ERR_NONE) { - printf("Failed to read register\r\n"); - return; - } - - printf("R %02X -> %02X\r\n", reg_val, value_val); -} - -static void nfc_cli_st25r_cmd(Cli* cli, FuriString* args) { - UNUSED(cli); - FuriString* cmd = furi_string_alloc(); - - // Check if nfc worker is not busy - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; - } - - if(!args_read_string_and_trim(args, cmd)) { - printf("You didn't specify \r\n"); - nfc_cli_print_usage(); - return; - } - - if(furi_string_utf8_length(cmd) != 2) { - printf("You didn't specify as 2-character hex value\r\n"); - nfc_cli_print_usage(); - return; - } - - uint8_t cmd_val = hexbyte(furi_string_get_cstr(cmd)); - - if(st25r3916ExecuteCommand(cmd_val) != ERR_NONE) { - printf("Failed to execute\r\n"); - return; - } - - printf("X %02X\r\n", cmd_val); -} - -static FuriHalNfcTxRxContext tx_rx = {}; - -static void nfc_cli_st25r_fifo(Cli* cli, FuriString* args) { - UNUSED(args); - - // Check if nfc worker is not busy - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; - } - - bool field = false; - - tx_rx.sniff_tx = NULL; - tx_rx.sniff_rx = NULL; - tx_rx.tx_bits = 0; - tx_rx.tx_rx_type = FuriHalNfcTxRxTypeRxRaw; - - rfal_platform_spi_acquire(); - - furi_hal_nfcv_listen_start(); - - printf("Reading FIFO...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - - if(st25r3916IsExtFieldOn() && !field) { - printf("Field entered\r\n"); - field = true; - } - if(!st25r3916IsExtFieldOn() && field) { - printf("Field left\r\n"); - field = false; - } - - if(furi_hal_nfc_listen_rx(&tx_rx, 50)) { - for(int pos = 0; pos < tx_rx.rx_bits / 8; pos++) { - printf(" %02X", tx_rx.rx_data[pos]); - } - printf("\r\n"); - } - - furi_delay_ms(50); - } - rfal_platform_spi_release(); -} - - -static void nfc_cli_st25r_trans(Cli* cli, FuriString* args) { - UNUSED(args); - - // Check if nfc worker is not busy - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; - } - - printf("ISO15693 emulator...\r\nPress Ctrl+C to abort\r\n"); - - FuriHalNfcDevData nfc_data = { - .uid = { 0xE0, 0x04, 0x45, 0x03, 0x50, 0x0E, 0x78, 0x36 }, - .uid_len = 8, - .type = FuriHalNfcTypeV, - }; - NfcVData nfcv_data = { - .afi = 0, - .dsfid = 0, - .block_num = 8, - .block_size = 4, - .ic_ref = 3, - .type = NfcVTypeSlixL, - .sub_data.slix_l = { - .key_privacy = { 0x0F, 0x0F, 0x0F, 0x0F }, - .privacy = false - } - }; - - memset(nfcv_data.data, 0xAE, 4 * 8); - - nfcv_emu_init(&nfc_data, &nfcv_data); - while(!cli_cmd_interrupt_received(cli)) { - if(nfcv_emu_loop(&nfc_data, &nfcv_data, 1000)) { - printf("[NfcV-Emu] %s\r\n", nfcv_data.last_command); - } - } - - nfcv_emu_deinit(); -} static void nfc_cli(Cli* cli, FuriString* args, void* context) { UNUSED(context); @@ -329,26 +135,6 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) { nfc_cli_field(cli, args); break; } - if(furi_string_cmp_str(cmd, "st25r_read") == 0) { - nfc_cli_st25r_read(cli, args); - break; - } - if(furi_string_cmp_str(cmd, "st25r_write") == 0) { - nfc_cli_st25r_write(cli, args); - break; - } - if(furi_string_cmp_str(cmd, "st25r_cmd") == 0) { - nfc_cli_st25r_cmd(cli, args); - break; - } - if(furi_string_cmp_str(cmd, "st25r_fifo") == 0) { - nfc_cli_st25r_fifo(cli, args); - break; - } - if(furi_string_cmp_str(cmd, "st25r_trans") == 0) { - nfc_cli_st25r_trans(cli, args); - break; - } } nfc_cli_print_usage(); diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 0687c1343..6dd29eff5 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -22,6 +22,7 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->edge_cnt = 0; signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); signal->reload_reg_entries = 0; + signal->reload_reg_remainder = 0; return signal; } @@ -98,10 +99,6 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { void digital_signal_prepare(DigitalSignal* signal) { furi_assert(signal); - if(signal->prepared) { - return; - } - /* set up signal polarities */ uint32_t bit_set = signal->gpio->pin; uint32_t bit_reset = signal->gpio->pin << 16; @@ -115,20 +112,30 @@ void digital_signal_prepare(DigitalSignal* signal) { } /* set up edge timings */ - uint32_t remainder = 0; signal->reload_reg_entries = 0; for(size_t pos = 0; pos < signal->edge_cnt; pos++) { - uint32_t pulse_duration = signal->edge_timings[pos] + remainder; + uint32_t pulse_duration = signal->edge_timings[pos] + signal->reload_reg_remainder; uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; - remainder = pulse_duration - (pulse_ticks * T_TIM); + signal->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM); if(pulse_ticks > 1) { signal->reload_reg_buff[signal->reload_reg_entries++] = pulse_ticks - 1; } } +} - signal->prepared = true; + +void digital_signal_stop_dma() { + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); +} + +void digital_signal_stop_timer() { + LL_TIM_DisableCounter(TIM2); + LL_TIM_SetCounter(TIM2, 0); } bool digital_signal_setup_dma(DigitalSignal* signal) { @@ -166,6 +173,8 @@ bool digital_signal_setup_dma(DigitalSignal* signal) { dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; + digital_signal_stop_dma(); + /* set up DMA channel 1 and 2 for GPIO and timer copy operations */ LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config_gpio); LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config_timer); @@ -178,13 +187,14 @@ bool digital_signal_setup_dma(DigitalSignal* signal) { } -void digital_signal_update_dma(DigitalSignal* signal) { +bool digital_signal_update_dma(DigitalSignal* signal) { furi_assert(signal); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_ClearFlag_TC1(DMA1); - LL_DMA_ClearFlag_TC2(DMA1); + if(!signal->reload_reg_entries) { + return false; + } + + digital_signal_stop_dma(); LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)signal->gpio_buff); LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); @@ -193,18 +203,8 @@ void digital_signal_update_dma(DigitalSignal* signal) { LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); -} -void digital_signal_stop_dma() { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_ClearFlag_TC1(DMA1); - LL_DMA_ClearFlag_TC2(DMA1); -} - -void digital_signal_stop_timer() { - LL_TIM_DisableCounter(TIM2); - LL_TIM_SetCounter(TIM2, 0); + return true; } void digital_signal_setup_timer() { @@ -231,7 +231,6 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { /* if selected GPIO changed, force reconfiguration of buffers */ if(gpio && (signal->gpio != gpio)) { signal->gpio = gpio; - signal->prepared = false; } /* Configure gpio as output */ @@ -303,7 +302,7 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { bool digital_sequence_send_signal(DigitalSignal* signal) { furi_assert(signal); - /* the first iteration has to set up the whol machinery */ + /* the first iteration has to set up the whole machinery */ if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); @@ -314,12 +313,15 @@ bool digital_sequence_send_signal(DigitalSignal* signal) { digital_signal_setup_timer(); digital_signal_start_timer(); } else { - /* it was already active, wait till DMA is done and the last timer ticks are running */ + /* transfer was already active, wait till DMA is done and the last timer ticks are running */ while(!LL_DMA_IsActiveFlag_TC2(DMA1)) { } /* configure next polarities and timings */ - digital_signal_update_dma(signal); + if(!digital_signal_update_dma(signal)) { + FURI_LOG_D(TAG, "Signal has no entries, aborting"); + return false; + } } return true; @@ -328,9 +330,15 @@ bool digital_sequence_send_signal(DigitalSignal* signal) { bool digital_sequence_send(DigitalSequence* sequence) { furi_assert(sequence); + uint32_t remainder = 0; + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { DigitalSignal *sig = sequence->signals[sequence->sequence[pos]]; + /* take over previous remainder */ + sig->reload_reg_remainder = remainder; + digital_signal_prepare(sig); + if(!digital_sequence_send_signal(sig)) { digital_signal_stop_timer(); digital_signal_stop_dma(); diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 00376466e..583105e47 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -24,6 +24,7 @@ typedef struct { uint32_t* edge_timings; uint32_t* reload_reg_buff; uint32_t reload_reg_entries; + uint32_t reload_reg_remainder; uint32_t gpio_buff[2]; const GpioPin* gpio; } DigitalSignal; diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index bce179dba..a806d547c 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -161,6 +161,11 @@ DigitalSignal* nfcv_resp_eof = NULL; DigitalSignal* nfcv_resp_unmod_256 = NULL; DigitalSignal* nfcv_resp_unmod_768 = NULL; +//DigitalSignal* nfcv_signal = NULL; + +//const GpioPin* nfcv_out_io = &gpio_ext_pb2; +const GpioPin* nfcv_out_io = &gpio_spi_r_mosi; + DigitalSequence* nfcv_signal = NULL; #define SIG_SOF 0 @@ -192,6 +197,7 @@ void nfcv_crc(uint8_t* data, uint32_t length, uint8_t* out) { } void nfcv_emu_free() { + //digital_signal_free(nfcv_signal); digital_sequence_free(nfcv_signal); digital_signal_free(nfcv_resp_unmod_256); digital_signal_free(nfcv_resp_pulse_32); @@ -212,8 +218,9 @@ void nfcv_emu_free() { void nfcv_emu_alloc() { if(!nfcv_signal) { + //nfcv_signal = digital_signal_alloc(8192); /* assuming max frame length is 255 bytes */ - nfcv_signal = digital_sequence_alloc(8 * 255 + 2, &gpio_spi_r_mosi); + nfcv_signal = digital_sequence_alloc(8 * 255 + 2, nfcv_out_io); } if(!nfcv_resp_unmod_256) { @@ -283,12 +290,11 @@ void nfcv_emu_send_raw(uint8_t* data, uint8_t length) { int bits = length * 8; - furi_hal_gpio_write(&gpio_spi_r_mosi, false); - furi_delay_us(10); - furi_hal_gpio_write(&gpio_spi_r_mosi, true); - furi_delay_us(10); - furi_hal_gpio_write(&gpio_spi_r_mosi, false); + //nfcv_signal->start_level = false; + //nfcv_signal->edge_cnt = 0; + //digital_signal_append(nfcv_signal, nfcv_resp_sof); + digital_sequence_clear(nfcv_signal); digital_sequence_add(nfcv_signal, SIG_SOF); @@ -297,26 +303,17 @@ void nfcv_emu_send_raw(uint8_t* data, uint8_t length) { uint32_t bit_pos = bit_total % 8; uint8_t bit_val = 0x01 << bit_pos; - if(data[byte_pos] & bit_val) { - digital_sequence_add(nfcv_signal, SIG_BIT1); - } else { - digital_sequence_add(nfcv_signal, SIG_BIT0); - } + //digital_signal_append(nfcv_signal, nfcv_resp_one); + digital_sequence_add(nfcv_signal, (data[byte_pos] & bit_val) ? SIG_BIT1 : SIG_BIT0); } + //digital_signal_append(nfcv_signal, nfcv_resp_eof); digital_sequence_add(nfcv_signal, SIG_EOF); - - /* digital signal setup will take some time. win some time by tricking the VCD into thinking that something happens */ - furi_hal_gpio_write(&gpio_spi_r_mosi, false); - furi_delay_us(10); - furi_hal_gpio_write(&gpio_spi_r_mosi, true); - furi_delay_us(10); - furi_hal_gpio_write(&gpio_spi_r_mosi, false); - FURI_CRITICAL_ENTER(); + //digital_signal_send(nfcv_signal, nfcv_out_io); digital_sequence_send(nfcv_signal); FURI_CRITICAL_EXIT(); - furi_hal_gpio_write(&gpio_spi_r_mosi, false); + furi_hal_gpio_write(nfcv_out_io, false); } void nfcv_emu_send(uint8_t* data, uint8_t length) { @@ -579,7 +576,7 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui } if(strlen(nfcv_data->last_command) > 0) { - //FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); } } @@ -594,7 +591,6 @@ void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); - FURI_LOG_D(TAG, "Starting NfcV emulation"); FURI_LOG_D(TAG, " UID: %02X %02X %02X %02X %02X %02X %02X %02X", nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3], @@ -676,7 +672,6 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti break; case NFCV_FRAME_STATE_CODING_256: - if(periods_previous > periods) { snprintf(reset_reason, sizeof(reset_reason), "1oo256: Missing %lu periods from previous symbol, got %lu", periods_previous, periods); frame_state = NFCV_FRAME_STATE_RESET; @@ -758,13 +753,9 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti } if(frame_state == NFCV_FRAME_STATE_EOF) { + FURI_LOG_D(TAG, "Received valid frame"); + /* we know that this code uses TIM2, so stop pulse reader */ - furi_hal_gpio_write(&gpio_spi_r_mosi, false); - furi_delay_us(10); - furi_hal_gpio_write(&gpio_spi_r_mosi, true); - furi_delay_us(10); - furi_hal_gpio_write(&gpio_spi_r_mosi, false); - pulse_reader_stop(reader_signal); nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos); pulse_reader_start(reader_signal); From aad093a4b58ad4ab258a7ad5b770cb464acb7365 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 21 Nov 2022 13:49:19 +0100 Subject: [PATCH 18/34] smaller code cleanups --- lib/nfc/protocols/nfcv.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index a806d547c..aed0023cd 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -149,7 +149,6 @@ bool nfcv_read_card( } /* emulation part */ - PulseReader *reader_signal = NULL; DigitalSignal* nfcv_resp_pulse_32 = NULL; @@ -161,8 +160,6 @@ DigitalSignal* nfcv_resp_eof = NULL; DigitalSignal* nfcv_resp_unmod_256 = NULL; DigitalSignal* nfcv_resp_unmod_768 = NULL; -//DigitalSignal* nfcv_signal = NULL; - //const GpioPin* nfcv_out_io = &gpio_ext_pb2; const GpioPin* nfcv_out_io = &gpio_spi_r_mosi; @@ -197,7 +194,6 @@ void nfcv_crc(uint8_t* data, uint32_t length, uint8_t* out) { } void nfcv_emu_free() { - //digital_signal_free(nfcv_signal); digital_sequence_free(nfcv_signal); digital_signal_free(nfcv_resp_unmod_256); digital_signal_free(nfcv_resp_pulse_32); @@ -218,7 +214,6 @@ void nfcv_emu_free() { void nfcv_emu_alloc() { if(!nfcv_signal) { - //nfcv_signal = digital_signal_alloc(8192); /* assuming max frame length is 255 bytes */ nfcv_signal = digital_sequence_alloc(8 * 255 + 2, nfcv_out_io); } @@ -287,30 +282,21 @@ void nfcv_emu_alloc() { void nfcv_emu_send_raw(uint8_t* data, uint8_t length) { - - int bits = length * 8; - //nfcv_signal->start_level = false; - //nfcv_signal->edge_cnt = 0; - - //digital_signal_append(nfcv_signal, nfcv_resp_sof); - digital_sequence_clear(nfcv_signal); digital_sequence_add(nfcv_signal, SIG_SOF); - for(int bit_total = 0; bit_total < bits; bit_total++) { + for(int bit_total = 0; bit_total < length * 8; bit_total++) { uint32_t byte_pos = bit_total / 8; uint32_t bit_pos = bit_total % 8; uint8_t bit_val = 0x01 << bit_pos; - //digital_signal_append(nfcv_signal, nfcv_resp_one); digital_sequence_add(nfcv_signal, (data[byte_pos] & bit_val) ? SIG_BIT1 : SIG_BIT0); } - //digital_signal_append(nfcv_signal, nfcv_resp_eof); digital_sequence_add(nfcv_signal, SIG_EOF); + FURI_CRITICAL_ENTER(); - //digital_signal_send(nfcv_signal, nfcv_out_io); digital_sequence_send(nfcv_signal); FURI_CRITICAL_EXIT(); furi_hal_gpio_write(nfcv_out_io, false); From a1ae1ed515293b33d87d845acf6cbe5d898b6659 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 21 Nov 2022 15:25:12 +0100 Subject: [PATCH 19/34] switch NfcA to digital sequence also updated DigitalSequence API --- firmware/targets/f7/api_symbols.csv | 6 +-- firmware/targets/f7/furi_hal/furi_hal_nfc.c | 2 +- lib/digital_signal/digital_signal.c | 15 +++--- lib/digital_signal/digital_signal.h | 6 +-- lib/nfc/protocols/nfca.c | 53 +++++++++------------ lib/nfc/protocols/nfca.h | 2 +- lib/nfc/protocols/nfcv.c | 4 +- 7 files changed, 40 insertions(+), 48 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 9623807ce..ea96eac8c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.26,, +Version,+,7.28,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -708,12 +708,12 @@ Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMes Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" Function,-,difftime,double,"time_t, time_t" Function,-,digital_sequence_add,void,"DigitalSequence*, uint8_t" -Function,-,digital_sequence_alloc,DigitalSequence*,"uint32_t, const GpioPin*" +Function,-,digital_sequence_alloc,DigitalSequence*,uint32_t Function,-,digital_sequence_clear,void,DigitalSequence* Function,-,digital_sequence_free,void,DigitalSequence* +Function,-,digital_sequence_send,_Bool,"DigitalSequence*, const GpioPin*" Function,-,digital_sequence_send_signal,_Bool,DigitalSignal* Function,-,digital_sequence_set_signal,void,"DigitalSequence*, uint8_t, DigitalSignal*" -Function,-,digital_sequence_send,_Bool,DigitalSequence* Function,-,digital_signal_add,void,"DigitalSignal*, uint32_t" Function,-,digital_signal_alloc,DigitalSignal*,uint32_t Function,-,digital_signal_append,_Bool,"DigitalSignal*, DigitalSignal*" diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index b95d490af..c14016fc2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -536,7 +536,7 @@ static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_ // Send signal FURI_CRITICAL_ENTER(); nfca_signal_encode(tx_rx->nfca_signal, tx_rx->tx_data, tx_rx->tx_bits, tx_rx->tx_parity); - digital_signal_send(tx_rx->nfca_signal->tx_signal, &gpio_spi_r_mosi); + digital_sequence_send(tx_rx->nfca_signal->tx_signal, &gpio_spi_r_mosi); FURI_CRITICAL_EXIT(); furi_hal_gpio_write(&gpio_spi_r_mosi, false); diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 6dd29eff5..de79c2962 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -15,7 +15,6 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { DigitalSignal* signal = malloc(sizeof(DigitalSignal)); - signal->prepared = false; signal->start_level = true; signal->edges_max_cnt = max_edges_cnt; signal->edge_timings = malloc(signal->edges_max_cnt * sizeof(uint32_t)); @@ -65,8 +64,6 @@ bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) { } signal_a->edge_cnt += signal_b->edge_cnt - start_copy; - signal_a->prepared = false; - return true; } @@ -236,6 +233,8 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { /* Configure gpio as output */ furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + /* single signal, add a temporary, terminating edge at the end */ + signal->edge_timings[signal->edge_cnt++] = 10; digital_signal_prepare(signal); digital_signal_setup_dma(signal); @@ -247,14 +246,15 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { digital_signal_stop_timer(); digital_signal_stop_dma(); + + signal->edge_cnt--; } -DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { +DigitalSequence* digital_sequence_alloc(uint32_t size) { DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); - sequence->gpio = gpio; sequence->signals_size = 32; sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*)); @@ -282,7 +282,6 @@ void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index /* all signals will use the sequence's GPIO */ signal->gpio = sequence->gpio; - signal->prepared = false; digital_signal_prepare(signal); } @@ -327,11 +326,13 @@ bool digital_sequence_send_signal(DigitalSignal* signal) { return true; } -bool digital_sequence_send(DigitalSequence* sequence) { +bool digital_sequence_send(DigitalSequence* sequence, const GpioPin* gpio) { furi_assert(sequence); uint32_t remainder = 0; + sequence->gpio = gpio; + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { DigitalSignal *sig = sequence->signals[sequence->sequence[pos]]; diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 583105e47..83ccbad30 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -14,10 +14,10 @@ extern "C" { #define DIGITAL_SIGNAL_MS(x) (x*100000000UL) #define DIGITAL_SIGNAL_US(x) (x*100000UL) #define DIGITAL_SIGNAL_NS(x) (x*100UL) +#define DIGITAL_SIGNAL_PS(x) (x/10UL) typedef struct { - bool prepared; bool start_level; uint32_t edge_cnt; uint32_t edges_max_cnt; @@ -50,12 +50,12 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num); void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); -DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio); +DigitalSequence* digital_sequence_alloc(uint32_t size); void digital_sequence_free(DigitalSequence* sequence); void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index, DigitalSignal* signal); void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index); bool digital_sequence_send_signal(DigitalSignal* signal); -bool digital_sequence_send(DigitalSequence* sequence); +bool digital_sequence_send(DigitalSequence* sequence, const GpioPin* gpio); void digital_sequence_clear(DigitalSequence* sequence); diff --git a/lib/nfc/protocols/nfca.c b/lib/nfc/protocols/nfca.c index c401f8cc5..94f6a9e70 100644 --- a/lib/nfc/protocols/nfca.c +++ b/lib/nfc/protocols/nfca.c @@ -7,11 +7,10 @@ #define NFCA_CRC_INIT (0x6363) -#define NFCA_F_SIG (13560000.0) -#define T_SIG 7374 //73.746ns*100 -#define T_SIG_x8 58992 //T_SIG*8 -#define T_SIG_x8_x8 471936 //T_SIG*8*8 -#define T_SIG_x8_x9 530928 //T_SIG*8*9 +#define NFCA_F_SIG (13560000.0) /* [Hz] NFC frequency */ +#define NFCA_F_SUB (NFCA_F_SIG/16) /* [Hz] NFC subcarrier frequency fs/16 */ +#define T_SUB (1000000000000.0f / NFCA_F_SUB) /* [ps] subcarrier period = 1/NFCA_F_SUB */ +#define T_SUB_PHASE (T_SUB/2) /* [ps] a single subcarrier phase */ #define NFCA_SIGNAL_MAX_EDGES (1350) @@ -64,35 +63,25 @@ bool nfca_emulation_handler( } static void nfca_add_bit(DigitalSignal* signal, bool bit) { - if(bit) { - signal->start_level = true; - for(size_t i = 0; i < 7; i++) { - signal->edge_timings[i] = T_SIG_x8; - } - signal->edge_timings[7] = T_SIG_x8_x9; - signal->edge_cnt = 8; - } else { - signal->start_level = false; - signal->edge_timings[0] = T_SIG_x8_x8; - for(size_t i = 1; i < 9; i++) { - signal->edge_timings[i] = T_SIG_x8; - } - signal->edge_cnt = 9; + + signal->start_level = bit; + for(size_t i = 0; i < 16; i++) { + signal->edge_timings[signal->edge_cnt++] = T_SUB_PHASE; } } static void nfca_add_byte(NfcaSignal* nfca_signal, uint8_t byte, bool parity) { for(uint8_t i = 0; i < 8; i++) { if(byte & (1 << i)) { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); + digital_sequence_add(nfca_signal->tx_signal, 1); } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); + digital_sequence_add(nfca_signal->tx_signal, 0); } } if(parity) { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); + digital_sequence_add(nfca_signal->tx_signal, 1); } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); + digital_sequence_add(nfca_signal->tx_signal, 0); } } @@ -102,7 +91,7 @@ NfcaSignal* nfca_signal_alloc() { nfca_signal->zero = digital_signal_alloc(10); nfca_add_bit(nfca_signal->one, true); nfca_add_bit(nfca_signal->zero, false); - nfca_signal->tx_signal = digital_signal_alloc(NFCA_SIGNAL_MAX_EDGES); + nfca_signal->tx_signal = digital_sequence_alloc(NFCA_SIGNAL_MAX_EDGES); return nfca_signal; } @@ -112,7 +101,7 @@ void nfca_signal_free(NfcaSignal* nfca_signal) { digital_signal_free(nfca_signal->one); digital_signal_free(nfca_signal->zero); - digital_signal_free(nfca_signal->tx_signal); + digital_sequence_free(nfca_signal->tx_signal); free(nfca_signal); } @@ -121,17 +110,19 @@ void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, u furi_assert(data); furi_assert(parity); - nfca_signal->tx_signal->edge_cnt = 0; - nfca_signal->tx_signal->start_level = true; - // Start of frame - digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); + digital_sequence_clear(nfca_signal->tx_signal); + + /* add a >80/fs phase with no transition, which is >10 1-bits */ + for(int tr1_bit = 0; tr1_bit < 11; tr1_bit++) { + digital_sequence_add(nfca_signal->tx_signal, 1); + } if(bits < 8) { for(size_t i = 0; i < bits; i++) { if(FURI_BIT(data[0], i)) { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); + digital_sequence_add(nfca_signal->tx_signal, 1); } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); + digital_sequence_add(nfca_signal->tx_signal, 0); } } } else { diff --git a/lib/nfc/protocols/nfca.h b/lib/nfc/protocols/nfca.h index 498ef2843..aad1a4fd8 100644 --- a/lib/nfc/protocols/nfca.h +++ b/lib/nfc/protocols/nfca.h @@ -8,7 +8,7 @@ typedef struct { DigitalSignal* one; DigitalSignal* zero; - DigitalSignal* tx_signal; + DigitalSequence* tx_signal; } NfcaSignal; uint16_t nfca_get_crc16(uint8_t* buff, uint16_t len); diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index aed0023cd..42dbd0ded 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -215,7 +215,7 @@ void nfcv_emu_alloc() { if(!nfcv_signal) { /* assuming max frame length is 255 bytes */ - nfcv_signal = digital_sequence_alloc(8 * 255 + 2, nfcv_out_io); + nfcv_signal = digital_sequence_alloc(8 * 255 + 2); } if(!nfcv_resp_unmod_256) { @@ -297,7 +297,7 @@ void nfcv_emu_send_raw(uint8_t* data, uint8_t length) { digital_sequence_add(nfcv_signal, SIG_EOF); FURI_CRITICAL_ENTER(); - digital_sequence_send(nfcv_signal); + digital_sequence_send(nfcv_signal, nfcv_out_io); FURI_CRITICAL_EXIT(); furi_hal_gpio_write(nfcv_out_io, false); } From 8b4e6de56e3e647d8538b271738d6dce11d36453 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 21 Nov 2022 17:38:13 +0100 Subject: [PATCH 20/34] fix NfcA --- lib/nfc/protocols/nfca.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/nfc/protocols/nfca.c b/lib/nfc/protocols/nfca.c index 94f6a9e70..01a0ea740 100644 --- a/lib/nfc/protocols/nfca.c +++ b/lib/nfc/protocols/nfca.c @@ -7,10 +7,10 @@ #define NFCA_CRC_INIT (0x6363) -#define NFCA_F_SIG (13560000.0) /* [Hz] NFC frequency */ -#define NFCA_F_SUB (NFCA_F_SIG/16) /* [Hz] NFC subcarrier frequency fs/16 */ -#define T_SUB (1000000000000.0f / NFCA_F_SUB) /* [ps] subcarrier period = 1/NFCA_F_SUB */ -#define T_SUB_PHASE (T_SUB/2) /* [ps] a single subcarrier phase */ +#define NFCA_F_SIG (13560000.0) /* [Hz] NFC frequency */ +#define NFCA_F_SUB (NFCA_F_SIG/16) /* [Hz] NFC subcarrier frequency fs/16 (847500 Hz) */ +#define T_SUB (1000000000000.0f / NFCA_F_SUB) /* [ps] subcarrier period = 1/NFCA_F_SUB (1.18 µs) */ +#define T_SUB_PHASE (T_SUB/2) /* [ps] a single subcarrier phase (590 µs) */ #define NFCA_SIGNAL_MAX_EDGES (1350) @@ -65,8 +65,18 @@ bool nfca_emulation_handler( static void nfca_add_bit(DigitalSignal* signal, bool bit) { signal->start_level = bit; - for(size_t i = 0; i < 16; i++) { - signal->edge_timings[signal->edge_cnt++] = T_SUB_PHASE; + signal->edge_cnt = 0; + + if(bit) { + for(size_t i = 0; i < 7; i++) { + signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE); + } + signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE * 9); + } else { + signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE * 8); + for(size_t i = 0; i < 8; i++) { + signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE); + } } } @@ -112,10 +122,7 @@ void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, u digital_sequence_clear(nfca_signal->tx_signal); - /* add a >80/fs phase with no transition, which is >10 1-bits */ - for(int tr1_bit = 0; tr1_bit < 11; tr1_bit++) { - digital_sequence_add(nfca_signal->tx_signal, 1); - } + digital_sequence_add(nfca_signal->tx_signal, 1); if(bits < 8) { for(size_t i = 0; i < bits; i++) { From fe9bc3e581beddda18d14c31f51fcc6c7224f343 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 21 Nov 2022 18:02:34 +0100 Subject: [PATCH 21/34] clean up digital_signal.c and fix remainder calculation --- lib/digital_signal/digital_signal.c | 12 +++--------- lib/digital_signal/digital_signal.h | 1 - 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index de79c2962..92e70ddb9 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -225,16 +225,13 @@ void digital_signal_start_timer() { void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { furi_assert(signal); - /* if selected GPIO changed, force reconfiguration of buffers */ - if(gpio && (signal->gpio != gpio)) { - signal->gpio = gpio; - } /* Configure gpio as output */ furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); /* single signal, add a temporary, terminating edge at the end */ signal->edge_timings[signal->edge_cnt++] = 10; + signal->gpio = gpio; digital_signal_prepare(signal); digital_signal_setup_dma(signal); @@ -280,9 +277,6 @@ void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index sequence->signals[signal_index] = signal; - /* all signals will use the sequence's GPIO */ - signal->gpio = sequence->gpio; - digital_signal_prepare(signal); } @@ -331,14 +325,14 @@ bool digital_sequence_send(DigitalSequence* sequence, const GpioPin* gpio) { uint32_t remainder = 0; - sequence->gpio = gpio; - for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { DigitalSignal *sig = sequence->signals[sequence->sequence[pos]]; /* take over previous remainder */ sig->reload_reg_remainder = remainder; + sig->gpio = gpio; digital_signal_prepare(sig); + remainder = sig->reload_reg_remainder; if(!digital_sequence_send_signal(sig)) { digital_signal_stop_timer(); diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 83ccbad30..88079c8f7 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -33,7 +33,6 @@ typedef struct { uint8_t signals_size; uint32_t sequence_used; uint32_t sequence_size; - const GpioPin* gpio; DigitalSignal** signals; uint8_t* sequence; } DigitalSequence; From 3165daf5a61304cc19d4bf4c24cfd071266b064c Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 21 Nov 2022 18:23:20 +0100 Subject: [PATCH 22/34] fix crash situation --- lib/digital_signal/digital_signal.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 92e70ddb9..d05594c18 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -95,6 +95,8 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { void digital_signal_prepare(DigitalSignal* signal) { furi_assert(signal); + furi_assert(signal->gpio); + furi_assert(signal->gpio->pin); /* set up signal polarities */ uint32_t bit_set = signal->gpio->pin; @@ -225,7 +227,6 @@ void digital_signal_start_timer() { void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { furi_assert(signal); - /* Configure gpio as output */ furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); @@ -276,8 +277,6 @@ void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index furi_assert(signal_index < sequence->signals_size); sequence->signals[signal_index] = signal; - - digital_signal_prepare(signal); } void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { @@ -326,7 +325,15 @@ bool digital_sequence_send(DigitalSequence* sequence, const GpioPin* gpio) { uint32_t remainder = 0; for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { - DigitalSignal *sig = sequence->signals[sequence->sequence[pos]]; + uint8_t signal_index = sequence->sequence[pos]; + DigitalSignal *sig = sequence->signals[signal_index]; + + if(!sig) { + FURI_LOG_D(TAG, "Signal at index %u, used at pos %lu is NULL, aborting", signal_index, pos); + digital_signal_stop_timer(); + digital_signal_stop_dma(); + return false; + } /* take over previous remainder */ sig->reload_reg_remainder = remainder; From ae80d06e9346f2113953cb5a35972d52af18220c Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 21 Nov 2022 18:40:54 +0100 Subject: [PATCH 23/34] properly initialize DigitalSequence for NfcA --- lib/digital_signal/digital_signal.c | 8 ++++---- lib/nfc/protocols/nfca.c | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index d05594c18..7aed70a6e 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -5,7 +5,7 @@ #include #include -#define TAG "[DigitalSignal]" +#define TAG "DigitalSignal" #pragma GCC optimize("O3,unroll-loops,Ofast") @@ -299,7 +299,7 @@ bool digital_sequence_send_signal(DigitalSignal* signal) { furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); if(!digital_signal_setup_dma(signal)) { - FURI_LOG_D(TAG, "Signal has no entries, aborting"); + FURI_LOG_D(TAG, "digital_sequence_send_signal: Signal has no entries, aborting"); return false; } digital_signal_setup_timer(); @@ -311,7 +311,7 @@ bool digital_sequence_send_signal(DigitalSignal* signal) { /* configure next polarities and timings */ if(!digital_signal_update_dma(signal)) { - FURI_LOG_D(TAG, "Signal has no entries, aborting"); + FURI_LOG_D(TAG, "digital_sequence_send_signal: Signal has no entries, aborting"); return false; } } @@ -329,7 +329,7 @@ bool digital_sequence_send(DigitalSequence* sequence, const GpioPin* gpio) { DigitalSignal *sig = sequence->signals[signal_index]; if(!sig) { - FURI_LOG_D(TAG, "Signal at index %u, used at pos %lu is NULL, aborting", signal_index, pos); + FURI_LOG_D(TAG, "digital_sequence_send: Signal at index %u, used at pos %lu is NULL, aborting", signal_index, pos); digital_signal_stop_timer(); digital_signal_stop_dma(); return false; diff --git a/lib/nfc/protocols/nfca.c b/lib/nfc/protocols/nfca.c index 01a0ea740..833022b92 100644 --- a/lib/nfc/protocols/nfca.c +++ b/lib/nfc/protocols/nfca.c @@ -103,6 +103,9 @@ NfcaSignal* nfca_signal_alloc() { nfca_add_bit(nfca_signal->zero, false); nfca_signal->tx_signal = digital_sequence_alloc(NFCA_SIGNAL_MAX_EDGES); + digital_sequence_set_signal(nfca_signal->tx_signal, 0, nfca_signal->zero); + digital_sequence_set_signal(nfca_signal->tx_signal, 1, nfca_signal->one); + return nfca_signal; } From 60b2c5cf67a80fe48478d6f1175738414f3c54f4 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Mon, 21 Nov 2022 22:20:55 +0100 Subject: [PATCH 24/34] code cleanups --- lib/digital_signal/digital_signal.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 7aed70a6e..3cae43ed4 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -9,9 +9,9 @@ #pragma GCC optimize("O3,unroll-loops,Ofast") -#define F_TIM (64000000.0) -#define T_TIM 1562 //15.625 ns *100 -#define T_TIM_DIV2 781 //15.625 ns / 2 *100 +#define F_TIM (64000000.0) +#define T_TIM 1562 /* 15.625 ns *100 */ +#define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { DigitalSignal* signal = malloc(sizeof(DigitalSignal)); @@ -227,12 +227,16 @@ void digital_signal_start_timer() { void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { furi_assert(signal); + if(!signal->edge_cnt) { + return; + } + /* Configure gpio as output */ + signal->gpio = gpio; furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); /* single signal, add a temporary, terminating edge at the end */ signal->edge_timings[signal->edge_cnt++] = 10; - signal->gpio = gpio; digital_signal_prepare(signal); digital_signal_setup_dma(signal); From eb98c7b246d939471bd8919e0f270d44d4e3b4ce Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Tue, 22 Nov 2022 00:34:09 +0100 Subject: [PATCH 25/34] rework NfcA and simplify a bit digital_sequence_bake added for testing purpose --- lib/digital_signal/digital_signal.c | 36 ++++++++++- lib/nfc/protocols/nfca.c | 94 +++++++++++++++++++---------- lib/nfc/protocols/nfca.h | 5 +- 3 files changed, 99 insertions(+), 36 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 3cae43ed4..2f27c0fb1 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -1,6 +1,7 @@ #include "digital_signal.h" #include +#include #include #include #include @@ -300,7 +301,6 @@ bool digital_sequence_send_signal(DigitalSignal* signal) { /* the first iteration has to set up the whole machinery */ if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { - furi_hal_gpio_init(signal->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); if(!digital_signal_setup_dma(signal)) { FURI_LOG_D(TAG, "digital_sequence_send_signal: Signal has no entries, aborting"); @@ -323,9 +323,43 @@ bool digital_sequence_send_signal(DigitalSignal* signal) { return true; } +DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { + + uint32_t edges = 0; + + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { + uint8_t signal_index = sequence->sequence[pos]; + DigitalSignal *sig = sequence->signals[signal_index]; + + edges += sig->edge_cnt; + } + + DigitalSignal* ret = digital_signal_alloc(edges); + + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { + uint8_t signal_index = sequence->sequence[pos]; + DigitalSignal *sig = sequence->signals[signal_index]; + + digital_signal_append(ret, sig); + } + + return ret; +} + bool digital_sequence_send(DigitalSequence* sequence, const GpioPin* gpio) { furi_assert(sequence); + //gpio = &gpio_ext_pb2; + furi_hal_gpio_init(gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + if(true) { + DigitalSignal* sig = digital_sequence_bake(sequence); + + digital_signal_send(sig, gpio); + digital_signal_free(sig); + return true; + } + uint32_t remainder = 0; for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { diff --git a/lib/nfc/protocols/nfca.c b/lib/nfc/protocols/nfca.c index 833022b92..609c85d4c 100644 --- a/lib/nfc/protocols/nfca.c +++ b/lib/nfc/protocols/nfca.c @@ -14,6 +14,13 @@ #define NFCA_SIGNAL_MAX_EDGES (1350) +#define SEQ_SOF 0 +#define SEQ_BIT0 1 +#define SEQ_BIT1 2 +#define SEQ_EOF 3 +#define SEQ_IDLE 4 + + typedef struct { uint8_t cmd; uint8_t param; @@ -62,49 +69,63 @@ bool nfca_emulation_handler( return sleep; } -static void nfca_add_bit(DigitalSignal* signal, bool bit) { - - signal->start_level = bit; - signal->edge_cnt = 0; - - if(bit) { - for(size_t i = 0; i < 7; i++) { - signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE); - } - signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE * 9); - } else { - signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE * 8); - for(size_t i = 0; i < 8; i++) { - signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE); - } - } -} - static void nfca_add_byte(NfcaSignal* nfca_signal, uint8_t byte, bool parity) { for(uint8_t i = 0; i < 8; i++) { if(byte & (1 << i)) { - digital_sequence_add(nfca_signal->tx_signal, 1); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1); } else { - digital_sequence_add(nfca_signal->tx_signal, 0); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0); } } if(parity) { - digital_sequence_add(nfca_signal->tx_signal, 1); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1); } else { - digital_sequence_add(nfca_signal->tx_signal, 0); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0); } } +static void nfca_add_modulation(DigitalSignal* signal, size_t phases) { + for(size_t i = 0; i < phases; i++) { + signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE); + } +} + +static void nfca_add_silence(DigitalSignal* signal, size_t phases) { + signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(phases * T_SUB_PHASE); +} + NfcaSignal* nfca_signal_alloc() { NfcaSignal* nfca_signal = malloc(sizeof(NfcaSignal)); - nfca_signal->one = digital_signal_alloc(10); - nfca_signal->zero = digital_signal_alloc(10); - nfca_add_bit(nfca_signal->one, true); - nfca_add_bit(nfca_signal->zero, false); + + /* ISO14443-2 defines 3 sequences for type A communication */ + nfca_signal->seq_d = digital_signal_alloc(10); + nfca_signal->seq_e = digital_signal_alloc(10); + nfca_signal->seq_f = digital_signal_alloc(10); + + /* start all sequences without load. polarity is not defined by standard, + so pick the polarity the best for us */ + nfca_signal->seq_d->start_level = false; + nfca_signal->seq_e->start_level = false; + nfca_signal->seq_f->start_level = false; + + /* SEQ D has the first half modulated, used as SOF */ + nfca_add_modulation(nfca_signal->seq_d, 4); + nfca_add_silence(nfca_signal->seq_d, 4); + + /* SEQ E has the second half modulated */ + nfca_add_silence(nfca_signal->seq_e, 4); + nfca_add_modulation(nfca_signal->seq_e, 4); + + /* SEQ F is just no modulation, used as EOF */ + nfca_add_silence(nfca_signal->seq_f, 8); + nfca_signal->tx_signal = digital_sequence_alloc(NFCA_SIGNAL_MAX_EDGES); - digital_sequence_set_signal(nfca_signal->tx_signal, 0, nfca_signal->zero); - digital_sequence_set_signal(nfca_signal->tx_signal, 1, nfca_signal->one); + digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_SOF, nfca_signal->seq_d); + digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_BIT0, nfca_signal->seq_e); + digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_BIT1, nfca_signal->seq_d); + digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_EOF, nfca_signal->seq_f); + digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_IDLE, nfca_signal->seq_f); return nfca_signal; } @@ -112,8 +133,9 @@ NfcaSignal* nfca_signal_alloc() { void nfca_signal_free(NfcaSignal* nfca_signal) { furi_assert(nfca_signal); - digital_signal_free(nfca_signal->one); - digital_signal_free(nfca_signal->zero); + digital_signal_free(nfca_signal->seq_d); + digital_signal_free(nfca_signal->seq_e); + digital_signal_free(nfca_signal->seq_f); digital_sequence_free(nfca_signal->tx_signal); free(nfca_signal); } @@ -125,14 +147,18 @@ void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, u digital_sequence_clear(nfca_signal->tx_signal); - digital_sequence_add(nfca_signal->tx_signal, 1); + /* add some idle bit times before SOF */ + for(int pos = 0; pos < 4; pos++) { + digital_sequence_add(nfca_signal->tx_signal, SEQ_IDLE); + } + digital_sequence_add(nfca_signal->tx_signal, SEQ_SOF); if(bits < 8) { for(size_t i = 0; i < bits; i++) { if(FURI_BIT(data[0], i)) { - digital_sequence_add(nfca_signal->tx_signal, 1); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1); } else { - digital_sequence_add(nfca_signal->tx_signal, 0); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0); } } } else { @@ -140,4 +166,6 @@ void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, u nfca_add_byte(nfca_signal, data[i], parity[i / 8] & (1 << (7 - (i & 0x07)))); } } + + digital_sequence_add(nfca_signal->tx_signal, SEQ_EOF); } diff --git a/lib/nfc/protocols/nfca.h b/lib/nfc/protocols/nfca.h index aad1a4fd8..82fb3e334 100644 --- a/lib/nfc/protocols/nfca.h +++ b/lib/nfc/protocols/nfca.h @@ -6,8 +6,9 @@ #include typedef struct { - DigitalSignal* one; - DigitalSignal* zero; + DigitalSignal* seq_d; /* sequence D, modulation with subcarrier during first half */ + DigitalSignal* seq_e; /* sequence E, modulation with subcarrier during second half */ + DigitalSignal* seq_f; /* sequence F, no modulation at all */ DigitalSequence* tx_signal; } NfcaSignal; From 116528f8717bae82d6ea6b04b87b5cc9a1806ebb Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Tue, 22 Nov 2022 01:39:46 +0100 Subject: [PATCH 26/34] sync bit phases --- lib/nfc/protocols/nfca.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/nfc/protocols/nfca.c b/lib/nfc/protocols/nfca.c index 609c85d4c..c0b7d3fd0 100644 --- a/lib/nfc/protocols/nfca.c +++ b/lib/nfc/protocols/nfca.c @@ -91,7 +91,13 @@ static void nfca_add_modulation(DigitalSignal* signal, size_t phases) { } static void nfca_add_silence(DigitalSignal* signal, size_t phases) { - signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(phases * T_SUB_PHASE); + bool end_level = signal->start_level ^ (signal->edge_cnt % 2); + + if((signal->edge_cnt == 0) || end_level) { + signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(phases * T_SUB_PHASE); + } else { + signal->edge_timings[signal->edge_cnt - 1] += DIGITAL_SIGNAL_PS(phases * T_SUB_PHASE); + } } NfcaSignal* nfca_signal_alloc() { @@ -102,22 +108,19 @@ NfcaSignal* nfca_signal_alloc() { nfca_signal->seq_e = digital_signal_alloc(10); nfca_signal->seq_f = digital_signal_alloc(10); - /* start all sequences without load. polarity is not defined by standard, - so pick the polarity the best for us */ - nfca_signal->seq_d->start_level = false; - nfca_signal->seq_e->start_level = false; - nfca_signal->seq_f->start_level = false; - /* SEQ D has the first half modulated, used as SOF */ - nfca_add_modulation(nfca_signal->seq_d, 4); - nfca_add_silence(nfca_signal->seq_d, 4); + nfca_signal->seq_d->start_level = true; + nfca_add_modulation(nfca_signal->seq_d, 8); + nfca_add_silence(nfca_signal->seq_d, 8); /* SEQ E has the second half modulated */ - nfca_add_silence(nfca_signal->seq_e, 4); - nfca_add_modulation(nfca_signal->seq_e, 4); + nfca_signal->seq_e->start_level = false; + nfca_add_silence(nfca_signal->seq_e, 8); + nfca_add_modulation(nfca_signal->seq_e, 8); /* SEQ F is just no modulation, used as EOF */ - nfca_add_silence(nfca_signal->seq_f, 8); + nfca_signal->seq_f->start_level = false; + nfca_add_silence(nfca_signal->seq_f, 16); nfca_signal->tx_signal = digital_sequence_alloc(NFCA_SIGNAL_MAX_EDGES); @@ -147,10 +150,8 @@ void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, u digital_sequence_clear(nfca_signal->tx_signal); - /* add some idle bit times before SOF */ - for(int pos = 0; pos < 4; pos++) { - digital_sequence_add(nfca_signal->tx_signal, SEQ_IDLE); - } + /* add some idle bit times before SOF in case the GPIO was active */ + digital_sequence_add(nfca_signal->tx_signal, SEQ_IDLE); digital_sequence_add(nfca_signal->tx_signal, SEQ_SOF); if(bits < 8) { From 021695b2a3dccf7d185ba2c0ebccef9f391379b6 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Tue, 22 Nov 2022 02:11:09 +0100 Subject: [PATCH 27/34] use sequences, but bake them before sending. required for shorter signal sequences, causing higher setup time --- lib/digital_signal/digital_signal.c | 2 +- lib/digital_signal/digital_signal.h | 1 + lib/nfc/protocols/nfca.c | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 2f27c0fb1..ddf0b34f4 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -352,7 +352,7 @@ bool digital_sequence_send(DigitalSequence* sequence, const GpioPin* gpio) { //gpio = &gpio_ext_pb2; furi_hal_gpio_init(gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - if(true) { + if(sequence->bake) { DigitalSignal* sig = digital_sequence_bake(sequence); digital_signal_send(sig, gpio); diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 88079c8f7..8a48507cf 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -31,6 +31,7 @@ typedef struct { typedef struct { uint8_t signals_size; + bool bake; uint32_t sequence_used; uint32_t sequence_size; DigitalSignal** signals; diff --git a/lib/nfc/protocols/nfca.c b/lib/nfc/protocols/nfca.c index c0b7d3fd0..9c3a74575 100644 --- a/lib/nfc/protocols/nfca.c +++ b/lib/nfc/protocols/nfca.c @@ -91,7 +91,7 @@ static void nfca_add_modulation(DigitalSignal* signal, size_t phases) { } static void nfca_add_silence(DigitalSignal* signal, size_t phases) { - bool end_level = signal->start_level ^ (signal->edge_cnt % 2); + bool end_level = signal->start_level ^ ((signal->edge_cnt % 2) == 0); if((signal->edge_cnt == 0) || end_level) { signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(phases * T_SUB_PHASE); @@ -124,6 +124,9 @@ NfcaSignal* nfca_signal_alloc() { nfca_signal->tx_signal = digital_sequence_alloc(NFCA_SIGNAL_MAX_EDGES); + /* we are dealing with shorter sequences, enable bake-before-sending */ + nfca_signal->tx_signal->bake = true; + digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_SOF, nfca_signal->seq_d); digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_BIT0, nfca_signal->seq_e); digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_BIT1, nfca_signal->seq_d); From 9d07f8db290c5e84c22e0aa0260f6768ee66a41e Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Wed, 23 Nov 2022 02:16:52 +0100 Subject: [PATCH 28/34] minimal speedups for mifare_classic.c included missing nfcv.c API update --- lib/nfc/protocols/crypto1.c | 15 ++++++++++++--- lib/nfc/protocols/crypto1.h | 2 +- lib/nfc/protocols/mifare_classic.c | 26 +++++++++++++++++++------- lib/nfc/protocols/nfcv.c | 4 ++-- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/lib/nfc/protocols/crypto1.c b/lib/nfc/protocols/crypto1.c index 2ac0ff081..453b08a22 100644 --- a/lib/nfc/protocols/crypto1.c +++ b/lib/nfc/protocols/crypto1.c @@ -36,7 +36,7 @@ uint32_t crypto1_filter(uint32_t in) { return FURI_BIT(0xEC57E80A, out); } -uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) { +static inline uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) { furi_assert(crypto1); uint8_t out = crypto1_filter(crypto1->odd); uint32_t feed = out & (!!is_encrypted); @@ -58,6 +58,15 @@ uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) { return out; } +static inline uint8_t crypto1_byte_inline(Crypto1* crypto1, uint8_t in, int is_encrypted) { + furi_assert(crypto1); + uint8_t out = 0; + for(uint8_t i = 0; i < 8; i++) { + out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i; + } + return out; +} + uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) { furi_assert(crypto1); uint32_t out = 0; @@ -92,7 +101,7 @@ void crypto1_decrypt( decrypted_data[0] = decrypted_byte; } else { for(size_t i = 0; i < encrypted_data_bits / 8; i++) { - decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; + decrypted_data[i] = crypto1_byte_inline(crypto, 0, 0) ^ encrypted_data[i]; } } } @@ -117,7 +126,7 @@ void crypto1_encrypt( } else { memset(encrypted_parity, 0, plain_data_bits / 8 + 1); for(uint8_t i = 0; i < plain_data_bits / 8; i++) { - encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ + encrypted_data[i] = crypto1_byte_inline(crypto, keystream ? keystream[i] : 0, 0) ^ plain_data[i]; encrypted_parity[i / 8] |= (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01) diff --git a/lib/nfc/protocols/crypto1.h b/lib/nfc/protocols/crypto1.h index 450d1534e..5b1402549 100644 --- a/lib/nfc/protocols/crypto1.h +++ b/lib/nfc/protocols/crypto1.h @@ -12,7 +12,7 @@ void crypto1_reset(Crypto1* crypto1); void crypto1_init(Crypto1* crypto1, uint64_t key); -uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted); +//uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted); uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted); diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 7b0e17975..10a22b033 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -769,19 +769,28 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ crypto1_reset(&emulator->crypto); memcpy(plain_data, tx_rx->rx_data, tx_rx->rx_bits / 8); } else { + tx_rx->rx_bits = 0; if(!furi_hal_nfc_tx_rx(tx_rx, 300)) { FURI_LOG_D( TAG, - "Error in tx rx. Tx :%d bits, Rx: %d bits", + "Error in tx rx. Tx :%d bits, Rx: %d bits. Received:", tx_rx->tx_bits, tx_rx->rx_bits); + + FURI_LOG_D(TAG,"Sent:"); + for(int pos = 0; pos < tx_rx->tx_bits/8; pos++) { + FURI_LOG_D(TAG," %02X", tx_rx->tx_data[pos]); + } + FURI_LOG_D(TAG,"Received:"); + for(int pos = 0; pos < tx_rx->rx_bits/8; pos++) { + FURI_LOG_D(TAG," %02X", tx_rx->rx_data[pos]); + } break; } crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); } if(plain_data[0] == 0x50 && plain_data[1] == 0x00) { - FURI_LOG_T(TAG, "Halt received"); furi_hal_nfc_listen_sleep(); command_processed = true; break; @@ -799,7 +808,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ access_key = MfClassicKeyB; } - uint32_t nonce = prng_successor(DWT->CYCCNT, 32) ^ 0xAA; + uint32_t nonce = prng_successor(DWT->CYCCNT, 2) ^ 0xAA; uint8_t nt[4]; uint8_t nt_keystream[4]; nfc_util_num2bytes(nonce, 4, nt); @@ -807,7 +816,9 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ crypto1_init(&emulator->crypto, key); if(!is_encrypted) { crypto1_word(&emulator->crypto, emulator->cuid ^ nonce, 0); - memcpy(tx_rx->tx_data, nt, sizeof(nt)); + for(size_t pos = 0; pos < sizeof(nt); pos++) { + tx_rx->tx_data[pos] = nt[pos]; + } tx_rx->tx_parity[0] = 0; for(size_t i = 0; i < sizeof(nt); i++) { tx_rx->tx_parity[0] |= nfc_util_odd_parity8(nt[i]) << (7 - i); @@ -826,7 +837,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; } if(!furi_hal_nfc_tx_rx(tx_rx, 500)) { - FURI_LOG_E(TAG, "Error in NT exchange"); + FURI_LOG_E(TAG, "Error in NT exchange?"); command_processed = true; break; } @@ -839,7 +850,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint32_t nr = nfc_util_bytes2num(tx_rx->rx_data, 4); uint32_t ar = nfc_util_bytes2num(&tx_rx->rx_data[4], 4); - +/* FURI_LOG_D( TAG, "%08lx key%c block %d nt/nr/ar: %08lx %08lx %08lx", @@ -849,7 +860,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ nonce, nr, ar); - +*/ crypto1_word(&emulator->crypto, nr, 1); uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0); if(cardRr != prng_successor(nonce, 64)) { @@ -964,6 +975,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; } else { + FURI_LOG_T(TAG, "%02X unknown received", plain_data[0]); // Unknown command break; } diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 42dbd0ded..aed0023cd 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -215,7 +215,7 @@ void nfcv_emu_alloc() { if(!nfcv_signal) { /* assuming max frame length is 255 bytes */ - nfcv_signal = digital_sequence_alloc(8 * 255 + 2); + nfcv_signal = digital_sequence_alloc(8 * 255 + 2, nfcv_out_io); } if(!nfcv_resp_unmod_256) { @@ -297,7 +297,7 @@ void nfcv_emu_send_raw(uint8_t* data, uint8_t length) { digital_sequence_add(nfcv_signal, SIG_EOF); FURI_CRITICAL_ENTER(); - digital_sequence_send(nfcv_signal, nfcv_out_io); + digital_sequence_send(nfcv_signal); FURI_CRITICAL_EXIT(); furi_hal_gpio_write(nfcv_out_io, false); } From b1cd358bfb7af7583c0130ef1bb1bc0b151dd819 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Wed, 23 Nov 2022 02:17:18 +0100 Subject: [PATCH 29/34] reworked nfca signal generation, added idle time before sending response --- lib/nfc/protocols/nfca.c | 65 +++++++++++++--------------------------- lib/nfc/protocols/nfca.h | 2 +- 2 files changed, 22 insertions(+), 45 deletions(-) diff --git a/lib/nfc/protocols/nfca.c b/lib/nfc/protocols/nfca.c index 9c3a74575..6d0fc20b3 100644 --- a/lib/nfc/protocols/nfca.c +++ b/lib/nfc/protocols/nfca.c @@ -8,19 +8,12 @@ #define NFCA_CRC_INIT (0x6363) #define NFCA_F_SIG (13560000.0) /* [Hz] NFC frequency */ -#define NFCA_F_SUB (NFCA_F_SIG/16) /* [Hz] NFC subcarrier frequency fs/16 (847500 Hz) */ +#define NFCA_F_SUB (NFCA_F_SIG / 16) /* [Hz] NFC subcarrier frequency fs/16 (847500 Hz) */ #define T_SUB (1000000000000.0f / NFCA_F_SUB) /* [ps] subcarrier period = 1/NFCA_F_SUB (1.18 µs) */ -#define T_SUB_PHASE (T_SUB/2) /* [ps] a single subcarrier phase (590 µs) */ +#define T_SUB_PHASE (T_SUB / 2) /* [ps] a single subcarrier phase (590 µs) */ #define NFCA_SIGNAL_MAX_EDGES (1350) -#define SEQ_SOF 0 -#define SEQ_BIT0 1 -#define SEQ_BIT1 2 -#define SEQ_EOF 3 -#define SEQ_IDLE 4 - - typedef struct { uint8_t cmd; uint8_t param; @@ -72,32 +65,27 @@ bool nfca_emulation_handler( static void nfca_add_byte(NfcaSignal* nfca_signal, uint8_t byte, bool parity) { for(uint8_t i = 0; i < 8; i++) { if(byte & (1 << i)) { - digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1); + digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_d); } else { - digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0); + digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_e); } } if(parity) { - digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1); + digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_d); } else { - digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0); + digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_e); } } static void nfca_add_modulation(DigitalSignal* signal, size_t phases) { for(size_t i = 0; i < phases; i++) { - signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE); + bool modulated = (i % 2 == 0); + digital_signal_add_pulse(signal, DIGITAL_SIGNAL_PS(T_SUB_PHASE), modulated); } } static void nfca_add_silence(DigitalSignal* signal, size_t phases) { - bool end_level = signal->start_level ^ ((signal->edge_cnt % 2) == 0); - - if((signal->edge_cnt == 0) || end_level) { - signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(phases * T_SUB_PHASE); - } else { - signal->edge_timings[signal->edge_cnt - 1] += DIGITAL_SIGNAL_PS(phases * T_SUB_PHASE); - } + digital_signal_add_pulse(signal, DIGITAL_SIGNAL_PS(phases*T_SUB_PHASE), false); } NfcaSignal* nfca_signal_alloc() { @@ -109,29 +97,16 @@ NfcaSignal* nfca_signal_alloc() { nfca_signal->seq_f = digital_signal_alloc(10); /* SEQ D has the first half modulated, used as SOF */ - nfca_signal->seq_d->start_level = true; nfca_add_modulation(nfca_signal->seq_d, 8); nfca_add_silence(nfca_signal->seq_d, 8); /* SEQ E has the second half modulated */ - nfca_signal->seq_e->start_level = false; nfca_add_silence(nfca_signal->seq_e, 8); nfca_add_modulation(nfca_signal->seq_e, 8); /* SEQ F is just no modulation, used as EOF */ - nfca_signal->seq_f->start_level = false; nfca_add_silence(nfca_signal->seq_f, 16); - - nfca_signal->tx_signal = digital_sequence_alloc(NFCA_SIGNAL_MAX_EDGES); - - /* we are dealing with shorter sequences, enable bake-before-sending */ - nfca_signal->tx_signal->bake = true; - - digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_SOF, nfca_signal->seq_d); - digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_BIT0, nfca_signal->seq_e); - digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_BIT1, nfca_signal->seq_d); - digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_EOF, nfca_signal->seq_f); - digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_IDLE, nfca_signal->seq_f); + nfca_signal->tx_signal = digital_signal_alloc(NFCA_SIGNAL_MAX_EDGES); return nfca_signal; } @@ -142,7 +117,7 @@ void nfca_signal_free(NfcaSignal* nfca_signal) { digital_signal_free(nfca_signal->seq_d); digital_signal_free(nfca_signal->seq_e); digital_signal_free(nfca_signal->seq_f); - digital_sequence_free(nfca_signal->tx_signal); + digital_signal_free(nfca_signal->tx_signal); free(nfca_signal); } @@ -151,18 +126,21 @@ void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, u furi_assert(data); furi_assert(parity); - digital_sequence_clear(nfca_signal->tx_signal); + nfca_signal->tx_signal->edge_cnt = 0; + nfca_signal->tx_signal->start_level = true; - /* add some idle bit times before SOF in case the GPIO was active */ - digital_sequence_add(nfca_signal->tx_signal, SEQ_IDLE); - digital_sequence_add(nfca_signal->tx_signal, SEQ_SOF); + // Start of frame + digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_f); + digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_f); + digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_f); + digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_d); if(bits < 8) { for(size_t i = 0; i < bits; i++) { if(FURI_BIT(data[0], i)) { - digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1); + digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_d); } else { - digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0); + digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_e); } } } else { @@ -170,6 +148,5 @@ void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, u nfca_add_byte(nfca_signal, data[i], parity[i / 8] & (1 << (7 - (i & 0x07)))); } } - - digital_sequence_add(nfca_signal->tx_signal, SEQ_EOF); + digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_f); } diff --git a/lib/nfc/protocols/nfca.h b/lib/nfc/protocols/nfca.h index 82fb3e334..7acd1fb95 100644 --- a/lib/nfc/protocols/nfca.h +++ b/lib/nfc/protocols/nfca.h @@ -9,7 +9,7 @@ typedef struct { DigitalSignal* seq_d; /* sequence D, modulation with subcarrier during first half */ DigitalSignal* seq_e; /* sequence E, modulation with subcarrier during second half */ DigitalSignal* seq_f; /* sequence F, no modulation at all */ - DigitalSequence* tx_signal; + DigitalSignal* tx_signal; } NfcaSignal; uint16_t nfca_get_crc16(uint8_t* buff, uint16_t len); From 31efe21412dffe50dc95e437ada87411cf1ea2fd Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Wed, 23 Nov 2022 21:24:53 +0100 Subject: [PATCH 30/34] timing optimizations for DigitalSequence --- lib/digital_signal/digital_signal.c | 191 ++++++++++++++++++++-------- lib/digital_signal/digital_signal.h | 8 +- 2 files changed, 146 insertions(+), 53 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index ddf0b34f4..8e6cbae62 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -87,6 +87,25 @@ void digital_signal_add(DigitalSignal* signal, uint32_t ticks) { signal->edge_timings[signal->edge_cnt++] = ticks; } +void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level) { + furi_assert(signal); + furi_assert(signal->edge_cnt < signal->edges_max_cnt); + + /* virgin signal? add it as the only level */ + if(signal->edge_cnt == 0) { + signal->start_level = level; + signal->edge_timings[signal->edge_cnt++] = ticks; + } else { + bool end_level = signal->start_level ^ !(signal->edge_cnt % 2); + + if(level != end_level) { + signal->edge_timings[signal->edge_cnt++] = ticks; + } else { + signal->edge_timings[signal->edge_cnt - 1] += ticks; + } + } +} + uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { furi_assert(signal); furi_assert(edge_num < signal->edge_cnt); @@ -126,19 +145,19 @@ void digital_signal_prepare(DigitalSignal* signal) { } -void digital_signal_stop_dma() { +static void digital_signal_stop_dma() { LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); LL_DMA_ClearFlag_TC1(DMA1); LL_DMA_ClearFlag_TC2(DMA1); } -void digital_signal_stop_timer() { +static void digital_signal_stop_timer() { LL_TIM_DisableCounter(TIM2); LL_TIM_SetCounter(TIM2, 0); } -bool digital_signal_setup_dma(DigitalSignal* signal) { +static bool digital_signal_setup_dma(DigitalSignal* signal) { furi_assert(signal); if(!signal->reload_reg_entries) { @@ -186,28 +205,7 @@ bool digital_signal_setup_dma(DigitalSignal* signal) { return true; } - -bool digital_signal_update_dma(DigitalSignal* signal) { - furi_assert(signal); - - if(!signal->reload_reg_entries) { - return false; - } - - digital_signal_stop_dma(); - - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)signal->gpio_buff); - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)signal->reload_reg_buff); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->reload_reg_entries); - - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - - return true; -} - -void digital_signal_setup_timer() { +static void digital_signal_setup_timer() { digital_signal_stop_timer(); @@ -220,7 +218,7 @@ void digital_signal_setup_timer() { LL_TIM_EnableDMAReq_UPDATE(TIM2); } -void digital_signal_start_timer() { +static void digital_signal_start_timer() { LL_TIM_GenerateEvent_UPDATE(TIM2); LL_TIM_EnableCounter(TIM2); } @@ -254,16 +252,34 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { } -DigitalSequence* digital_sequence_alloc(uint32_t size) { - DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); - sequence->signals_size = 32; +void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) { + sequence->signals_size = size; sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*)); + sequence->signals_prolonged = malloc(sequence->signals_size * sizeof(bool)); +} +void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) { sequence->sequence_used = 0; sequence->sequence_size = size; sequence->sequence = malloc(sequence->sequence_size); +} + +DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { + + DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); + + //gpio = &gpio_ext_pb2; + //furi_hal_gpio_init(&gpio_ext_pb2, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + //furi_hal_gpio_init(&gpio_ext_pc3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + //furi_hal_gpio_write(&gpio_ext_pb2, false); + //furi_hal_gpio_write(&gpio_ext_pc3, false); + + sequence->gpio = gpio; + + digital_sequence_alloc_signals(sequence, 32); + digital_sequence_alloc_sequence(sequence, size); return sequence; } @@ -282,6 +298,10 @@ void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index furi_assert(signal_index < sequence->signals_size); sequence->signals[signal_index] = signal; + signal->gpio = sequence->gpio; + signal->reload_reg_remainder = 0; + + digital_signal_prepare(signal); } void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { @@ -296,12 +316,63 @@ void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) { sequence->sequence[sequence->sequence_used++] = signal_index; } -bool digital_sequence_send_signal(DigitalSignal* signal) { +void digital_signal_update_dma(DigitalSignal* signal) { + + volatile uint32_t dma1_data[] = { + /* R6 */ (uint32_t)&(DMA1_Channel1->CCR), + /* R7 */ DMA1_Channel1->CCR & ~DMA_CCR_EN, + /* R8 */ 2, + /* R9 */ (uint32_t)&(signal->gpio->port->BSRR), + /* R10 */ (uint32_t)signal->gpio_buff, + /* R11 */ DMA1_Channel1->CCR | DMA_CCR_EN }; + + volatile uint32_t dma2_data[] = { + /* R0 */ (uint32_t)&(DMA1_Channel2->CCR), + /* R1 */ DMA1_Channel2->CCR & ~DMA_CCR_EN, + /* R2 */ (uint32_t)signal->reload_reg_entries, + /* R3 */ (uint32_t)&(TIM2->ARR), + /* R4 */ (uint32_t)signal->reload_reg_buff, + /* R5 */ DMA1_Channel2->CCR | DMA_CCR_EN }; + + + /* hurry when setting up next transfer */ + asm volatile("\t" + "MOV r6, %[data1]\n\t" + "MOV r7, %[data2]\n\t" + + "PUSH {r0-r12}\n\t" + + "LDM r7, {r0-r5}\n\t" + "LDM r6, {r6-r11}\n\t" + + "loop:\n\t" + "LDR r12, [r0, #4]\n\t" + "CMP r12, #0\n\t" + "BNE loop\n\t" + + "STM r6, {r7-r10}\n\t" /* disable channel and set up new parameters */ + "STR r11, [r6, #0]\n\t" /* enable channel again */ + "STM r0, {r1-r4}\n\t" /* disable channel and set up new parameters */ + "STR r5, [r0, #0]\n\t" /* enable channel again */ + + "POP {r0-r12}\n\t" + + : /* no outputs*/ + : /* inputs */ + [data1] "r" (dma1_data), + [data2] "r" (dma2_data) + : "r6", "r7" ); + + + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); +} + +static bool digital_sequence_send_signal(DigitalSignal* signal) { furi_assert(signal); /* the first iteration has to set up the whole machinery */ if(!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)) { - if(!digital_signal_setup_dma(signal)) { FURI_LOG_D(TAG, "digital_sequence_send_signal: Signal has no entries, aborting"); return false; @@ -309,15 +380,9 @@ bool digital_sequence_send_signal(DigitalSignal* signal) { digital_signal_setup_timer(); digital_signal_start_timer(); } else { - /* transfer was already active, wait till DMA is done and the last timer ticks are running */ - while(!LL_DMA_IsActiveFlag_TC2(DMA1)) { - } /* configure next polarities and timings */ - if(!digital_signal_update_dma(signal)) { - FURI_LOG_D(TAG, "digital_sequence_send_signal: Signal has no entries, aborting"); - return false; - } + digital_signal_update_dma(signal); } return true; @@ -346,21 +411,20 @@ DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { return ret; } -bool digital_sequence_send(DigitalSequence* sequence, const GpioPin* gpio) { +bool digital_sequence_send(DigitalSequence* sequence) { furi_assert(sequence); - //gpio = &gpio_ext_pb2; - furi_hal_gpio_init(gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); if(sequence->bake) { DigitalSignal* sig = digital_sequence_bake(sequence); - digital_signal_send(sig, gpio); + digital_signal_send(sig, sequence->gpio); digital_signal_free(sig); return true; } - uint32_t remainder = 0; + int32_t remainder = 0; for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { uint8_t signal_index = sequence->sequence[pos]; @@ -373,24 +437,51 @@ bool digital_sequence_send(DigitalSequence* sequence, const GpioPin* gpio) { return false; } - /* take over previous remainder */ - sig->reload_reg_remainder = remainder; - sig->gpio = gpio; - digital_signal_prepare(sig); - remainder = sig->reload_reg_remainder; + /* when we are too late more than half a tick, make the first edge temporarily longer */ + bool needs_prolongation = false; - if(!digital_sequence_send_signal(sig)) { + if(remainder >= T_TIM_DIV2) { + remainder -= T_TIM; + needs_prolongation = true; + } + + /* update the total remainder */ + remainder += sig->reload_reg_remainder; + + /* do we need to update the prolongation? */ + if(needs_prolongation != sequence->signals_prolonged[signal_index]) { + if(needs_prolongation) { + sig->edge_timings[0]++; + } else { + sig->edge_timings[0]--; + } + sequence->signals_prolonged[signal_index] = needs_prolongation; + } + + bool success = digital_sequence_send_signal(sig); + + if(!success) { digital_signal_stop_timer(); digital_signal_stop_dma(); return false; } } - while(!LL_DMA_IsActiveFlag_TC2(DMA1)) { + while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { } digital_signal_stop_timer(); digital_signal_stop_dma(); + + /* undo previously prolonged edges */ + for(uint32_t pos = 0; pos < sequence->signals_size; pos++) { + DigitalSignal *sig = sequence->signals[pos]; + + if(sig && sequence->signals_prolonged[pos]) { + sig->edge_timings[0]--; + sequence->signals_prolonged[pos] = false; + } + } return true; } diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 8a48507cf..189ad9198 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -35,13 +35,16 @@ typedef struct { uint32_t sequence_used; uint32_t sequence_size; DigitalSignal** signals; + bool* signals_prolonged; uint8_t* sequence; + const GpioPin* gpio; } DigitalSequence; DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); void digital_signal_free(DigitalSignal* signal); void digital_signal_add(DigitalSignal* signal, uint32_t ticks); +void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level); bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b); void digital_signal_prepare(DigitalSignal* signal); bool digital_signal_get_start_level(DigitalSignal* signal); @@ -50,12 +53,11 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num); void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); -DigitalSequence* digital_sequence_alloc(uint32_t size); +DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio); void digital_sequence_free(DigitalSequence* sequence); void digital_sequence_set_signal(DigitalSequence* sequence, uint8_t signal_index, DigitalSignal* signal); void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index); -bool digital_sequence_send_signal(DigitalSignal* signal); -bool digital_sequence_send(DigitalSequence* sequence, const GpioPin* gpio); +bool digital_sequence_send(DigitalSequence* sequence); void digital_sequence_clear(DigitalSequence* sequence); From 73111c4ac1de969a4525b54e73ce600543ce6a7f Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Wed, 23 Nov 2022 21:25:36 +0100 Subject: [PATCH 31/34] optimized ISO15693 code --- lib/nfc/protocols/nfcv.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index aed0023cd..ead20cba3 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -389,6 +389,9 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui break; } + /* unfortunately the response is quicker than the original NFC tag which causes frame misses */ + furi_delay_us(270); + switch(command) { case ISO15693_INVENTORY: @@ -505,7 +508,6 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui uint8_t password_id = payload[payload_offset]; uint8_t *password_xored = &payload[payload_offset + 1]; uint8_t *rand = nfcv_data->sub_data.slix_l.rand; - uint8_t status = ISO15693_ERROR_GENERIC; uint8_t *password = NULL; uint8_t password_rcv[4]; @@ -530,18 +532,13 @@ void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, ui uint32_t pass_received = nfcv_read_be(password_rcv, 4); if(pass_expect == pass_received) { - status = ISO15693_NOERROR; nfcv_data->sub_data.slix_l.privacy = false; - FURI_LOG_D(TAG, "SET_PASSWORD #%d received correct password 0x%08lX", password_id, pass_received); + response_buffer[0] = ISO15693_NOERROR; + nfcv_emu_send(response_buffer, 1); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X 0x%08lX OK", password_id, pass_received); } else { - FURI_LOG_D(TAG, "SET_PASSWORD #%d mismatch. Expected password 0x%08lX, got 0x%08lX", password_id, pass_expect, pass_received); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X 0x%08lX/%08lX FAIL", password_id, pass_received, pass_expect); } - - response_buffer[0] = status; - - nfcv_emu_send(response_buffer, 1); - snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X %s", password_id, (status == ISO15693_NOERROR) ? "SUCCESS" : "FAIL"); - break; } @@ -739,8 +736,6 @@ bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t ti } if(frame_state == NFCV_FRAME_STATE_EOF) { - FURI_LOG_D(TAG, "Received valid frame"); - /* we know that this code uses TIM2, so stop pulse reader */ pulse_reader_stop(reader_signal); nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos); From 1dc4069756de4b9bde82f55a8a3d698968b21ee6 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 24 Nov 2022 01:42:42 +0100 Subject: [PATCH 32/34] use digital_signal_send in furi_hal_nfc.c --- firmware/targets/f7/furi_hal/furi_hal_nfc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index c14016fc2..54a48b429 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -536,9 +536,8 @@ static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_ // Send signal FURI_CRITICAL_ENTER(); nfca_signal_encode(tx_rx->nfca_signal, tx_rx->tx_data, tx_rx->tx_bits, tx_rx->tx_parity); - digital_sequence_send(tx_rx->nfca_signal->tx_signal, &gpio_spi_r_mosi); + digital_signal_send(tx_rx->nfca_signal->tx_signal, &gpio_spi_r_mosi); FURI_CRITICAL_EXIT(); - furi_hal_gpio_write(&gpio_spi_r_mosi, false); // Configure gpio back to SPI and exit transparent furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); From 674a5c6f48793737dc3cb2e1e638b9128298b715 Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Thu, 24 Nov 2022 01:43:23 +0100 Subject: [PATCH 33/34] added ISO14443 tag emulator code, receiving only yet --- lib/nfc/protocols/nfca_emu.c | 167 +++++++++++++++++++++++++++++++++++ lib/nfc/protocols/nfca_emu.h | 47 ++++++++++ 2 files changed, 214 insertions(+) create mode 100644 lib/nfc/protocols/nfca_emu.c create mode 100644 lib/nfc/protocols/nfca_emu.h diff --git a/lib/nfc/protocols/nfca_emu.c b/lib/nfc/protocols/nfca_emu.c new file mode 100644 index 000000000..6040bad14 --- /dev/null +++ b/lib/nfc/protocols/nfca_emu.c @@ -0,0 +1,167 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nfca_emu.h" + +#define TAG "NfcA-emu" + + + + +void nfca_emu_init(NfcaEmuState *state, FuriHalNfcDevData* nfc_data) { + //nfca_emu_alloc(); + rfal_platform_spi_acquire(); + + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); + st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3); + st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88); + st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + + FURI_LOG_D(TAG, "Starting NfcA emulation"); + FURI_LOG_D(TAG, " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3], + nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]); + + /* allocate a 512 edge buffer, more than enough */ + state->reader_signal = pulse_reader_alloc(&gpio_spi_r_miso, 512); + /* timebase shall be 1 ns */ + pulse_reader_set_timebase(state->reader_signal, PulseReaderUnitNanosecond); + + pulse_reader_start(state->reader_signal); + + /* set start values */ + state->bits_received = 0; + state->have_sof = false; + state->valid_frame = false; +} + +void nfca_emu_deinit(NfcaEmuState *state) { + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + rfal_platform_spi_release(); + pulse_reader_free(state->reader_signal); +} + + +void nfca_bit_received(NfcaEmuState *state, uint8_t bit) { + + /* According to ISO14443-3 short frames have 7 bits and standard 9 bits per byte, + where the 9th bit is odd parity. Data is transmitted LSB first. */ + uint32_t byte_num = (state->bits_received / 9); + uint32_t bit_num = (state->bits_received % 9); + + if(bit_num == 8) { + uint32_t parity_value = 1 << (state->bits_received / 9); + state->parity_bits &= ~parity_value; + state->parity_bits |= bit ? parity_value : 0; + } else { + uint32_t bit_value = 1 << bit_num; + state->frame_data[byte_num] &= ~bit_value; + state->frame_data[byte_num] |= bit ? bit_value : 0; + } + + state->bits_received++; +} + + +bool nfca_emu_loop(NfcaEmuState *state, FuriHalNfcDevData* nfc_data, uint32_t timeout_ms) { + furi_assert(nfc_data); + + bool ret = false; + + while(!ret) { + uint32_t timeout_ns = timeout_ms * 1000; + uint32_t timeout = state->have_sof ? (4 * NFCA_TB) : timeout_ns; + uint32_t nsec = pulse_reader_receive(state->reader_signal, timeout); + + + bool eof = state->have_sof && nsec > 2*NFCA_TB; + + if(nsec != PULSE_READER_NO_EDGE) { + FURI_LOG_T(TAG, "pulse: %lu ns, frametime now: %ld", nsec, state->frame_time); + } + + if(IS_T1(nsec) || eof) { + FURI_LOG_T(TAG, " T1 pulse"); + + if(!state->have_sof) { + FURI_LOG_T(TAG, " SOF"); + state->frame_time = -(NFCA_TB - nsec); + state->have_sof = true; + state->valid_frame = false; + state->bits_received = 0; + continue; + } + + if(state->frame_time > NFCA_TB_MIN) { + FURI_LOG_T(TAG, " Y"); + state->frame_time -= NFCA_TB; + nfca_bit_received(state, 0); + } + + if(IS_ZERO(state->frame_time)) { + FURI_LOG_T(TAG, " Z"); + state->frame_time = -(NFCA_TB - nsec); + nfca_bit_received(state, 0); + } else if(IS_TX(state->frame_time)) { + FURI_LOG_T(TAG, " X"); + state->frame_time = -(NFCA_TX - nsec); + nfca_bit_received(state, 1); + } else { + if(eof) { + FURI_LOG_T(TAG, " EOF"); + state->have_sof = false; + state->valid_frame = true; + ret = true; + } else { + FURI_LOG_D(TAG, " unexpected: %ld", state->frame_time); + } + } + + + } else { + if(!state->have_sof) { + state->frame_time = 0; + } else { + state->frame_time += nsec; + FURI_LOG_T(TAG, " non-T1 pulse"); + } + } + } + + if(ret) { + + if(state->bits_received > 7) { + /* a last 0-bit will look like a missing bit */ + if((state->bits_received % 9) == 8) { + nfca_bit_received(state, 0); + state->bits_received++; + } + FURI_LOG_D(TAG, "Received standard frame: %d bits (%d byte)", state->bits_received, state->bits_received/9); + + for(size_t pos = 0; pos < state->bits_received / 9; pos++) { + FURI_LOG_D(TAG, " 0x%02X", state->frame_data[pos]); + } + } else { + FURI_LOG_D(TAG, "Received short frame: %d bits", state->bits_received); + FURI_LOG_D(TAG, " 0x%02X", state->frame_data[0] & 0x7F); + } + + /* we know that this code uses TIM2, so stop pulse reader */ + //pulse_reader_stop(state->reader_signal); + //nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos); + //pulse_reader_start(state->reader_signal); + } + + return ret; +} diff --git a/lib/nfc/protocols/nfca_emu.h b/lib/nfc/protocols/nfca_emu.h new file mode 100644 index 000000000..8fc769da5 --- /dev/null +++ b/lib/nfc/protocols/nfca_emu.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include "nfc_util.h" + +/* assume fc/128 */ +#define NFCA_FC (13560000.0f) /* MHz */ +#define NFCA_FC_K ((uint32_t)(NFCA_FC/1000)) /* kHz */ +#define NFCA_T1_MIN (24.0f / NFCA_FC * 1000000000) +#define NFCA_T1_MAX (41.0f / NFCA_FC * 1000000000) +#define NFCA_TX (64.0f / NFCA_FC * 1000000000) /* 4.7198 µs */ +#define NFCA_TX_MIN (0.90f * NFCA_TX) +#define NFCA_TX_MAX (1.10f * NFCA_TX) +#define NFCA_TB (128.0f / NFCA_FC * 1000000000) /* 9.4395 µs */ +#define NFCA_TB_MIN (0.80f * NFCA_TB) +#define NFCA_TB_MAX (1.20f * NFCA_TB) + +#define IS_T1(x) ((x)>=NFCA_T1_MIN && (x)<=NFCA_T1_MAX) +#define IS_TX(x) ((x)>=NFCA_TX_MIN && (x)<=NFCA_TX_MAX) +#define IS_TB(x) ((x)>=NFCA_TB_MIN && (x)<=NFCA_TB_MAX) +#define IS_ZERO(x) ((x)>=-NFCA_T1_MIN/2 && (x)<=NFCA_T1_MIN/2) + +#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) +#define DIGITAL_SIGNAL_UNIT_US (100000.0f) + +#define NFCA_FRAME_LENGTH 32 + + +typedef struct { + bool have_sof; + bool valid_frame; + int32_t frame_time; + size_t bits_received; + uint8_t frame_data[NFCA_FRAME_LENGTH]; + uint32_t parity_bits; + PulseReader *reader_signal; +} NfcaEmuState; + +bool nfca_emu_loop(NfcaEmuState *state, FuriHalNfcDevData* nfc_data, uint32_t timeout_ms); +void nfca_emu_deinit(NfcaEmuState *state); +void nfca_emu_init(NfcaEmuState *state, FuriHalNfcDevData* nfc_data); From 013e88154f57c7f847a962e38c7d3f63941f4f4b Mon Sep 17 00:00:00 2001 From: g3gg0 Date: Fri, 25 Nov 2022 02:46:49 +0100 Subject: [PATCH 34/34] implement ISO14433-A fully in software for pointless experimenting reasons and improving the DigitalReader / SignalSequence code in performance --- firmware/targets/f7/api_symbols.csv | 15 ++- firmware/targets/f7/furi_hal/furi_hal_nfc.c | 125 +++++++++++++++++- .../targets/furi_hal_include/furi_hal_nfc.h | 8 ++ lib/digital_signal/digital_signal.c | 83 +++++------- lib/digital_signal/digital_signal.h | 4 + lib/nfc/nfc_worker.c | 64 ++++++++- lib/nfc/nfc_worker_i.h | 1 + lib/nfc/protocols/mifare_classic.c | 22 +-- lib/nfc/protocols/nfca.c | 67 +++++++--- lib/nfc/protocols/nfca.h | 3 +- .../protocols/{nfca_emu.c => nfca_trans_rx.c} | 113 ++++++++-------- .../protocols/{nfca_emu.h => nfca_trans_rx.h} | 23 ++-- lib/pulse_reader/pulse_reader.c | 68 ++++++---- lib/pulse_reader/pulse_reader.h | 14 +- 14 files changed, 427 insertions(+), 183 deletions(-) rename lib/nfc/protocols/{nfca_emu.c => nfca_trans_rx.c} (58%) rename lib/nfc/protocols/{nfca_emu.h => nfca_trans_rx.h} (61%) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index ea96eac8c..bb092b7ff 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,7.28,, +Version,+,7.33,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -669,7 +669,6 @@ Function,-,coshl,long double,long double Function,-,cosl,long double,long double Function,+,crc32_calc_buffer,uint32_t,"uint32_t, const void*, size_t" Function,+,crc32_calc_file,uint32_t,"File*, const FileCrcProgressCb, void*" -Function,-,crypto1_bit,uint8_t,"Crypto1*, uint8_t, int" Function,-,crypto1_byte,uint8_t,"Crypto1*, uint8_t, int" Function,-,crypto1_decrypt,void,"Crypto1*, uint8_t*, uint16_t, uint8_t*" Function,-,crypto1_encrypt,void,"Crypto1*, uint8_t*, uint8_t*, uint16_t, uint8_t*, uint8_t*" @@ -708,13 +707,13 @@ Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMes Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" Function,-,difftime,double,"time_t, time_t" Function,-,digital_sequence_add,void,"DigitalSequence*, uint8_t" -Function,-,digital_sequence_alloc,DigitalSequence*,uint32_t +Function,-,digital_sequence_alloc,DigitalSequence*,"uint32_t, const GpioPin*" Function,-,digital_sequence_clear,void,DigitalSequence* Function,-,digital_sequence_free,void,DigitalSequence* -Function,-,digital_sequence_send,_Bool,"DigitalSequence*, const GpioPin*" -Function,-,digital_sequence_send_signal,_Bool,DigitalSignal* +Function,-,digital_sequence_send,_Bool,DigitalSequence* Function,-,digital_sequence_set_signal,void,"DigitalSequence*, uint8_t, DigitalSignal*" Function,-,digital_signal_add,void,"DigitalSignal*, uint32_t" +Function,-,digital_signal_add_pulse,void,"DigitalSignal*, uint32_t, _Bool" Function,-,digital_signal_alloc,DigitalSignal*,uint32_t Function,-,digital_signal_append,_Bool,"DigitalSignal*, DigitalSignal*" Function,-,digital_signal_free,void,DigitalSignal* @@ -1166,6 +1165,7 @@ Function,+,furi_hal_nfc_emulate_nfca,_Bool,"uint8_t*, uint8_t, uint8_t*, uint8_t Function,+,furi_hal_nfc_exit_sleep,void, Function,+,furi_hal_nfc_field_off,void, Function,+,furi_hal_nfc_field_on,void, +Function,-,furi_hal_nfc_gen_bitstream,void,"FuriHalNfcTxRxContext*, uint8_t*, size_t" Function,-,furi_hal_nfc_init,void, Function,+,furi_hal_nfc_is_busy,_Bool, Function,+,furi_hal_nfc_is_init,_Bool, @@ -1967,6 +1967,11 @@ Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" Function,-,nfca_signal_alloc,NfcaSignal*, Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" Function,-,nfca_signal_free,void,NfcaSignal* +Function,-,nfca_trans_rx_continue,void,NfcaTransRxState* +Function,-,nfca_trans_rx_deinit,void,NfcaTransRxState* +Function,-,nfca_trans_rx_init,void,NfcaTransRxState* +Function,-,nfca_trans_rx_loop,_Bool,"NfcaTransRxState*, uint32_t" +Function,-,nfca_trans_rx_pause,void,NfcaTransRxState* Function,-,nfcv_emu_deinit,void, Function,-,nfcv_emu_init,void,"FuriHalNfcDevData*, NfcVData*" Function,-,nfcv_emu_loop,_Bool,"FuriHalNfcDevData*, NfcVData*, uint32_t" diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 54a48b429..e18fa3819 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -523,6 +523,7 @@ bool furi_hal_nfc_emulate_nfca( return true; } + static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { furi_assert(tx_rx->nfca_signal); @@ -536,7 +537,7 @@ static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_ // Send signal FURI_CRITICAL_ENTER(); nfca_signal_encode(tx_rx->nfca_signal, tx_rx->tx_data, tx_rx->tx_bits, tx_rx->tx_parity); - digital_signal_send(tx_rx->nfca_signal->tx_signal, &gpio_spi_r_mosi); + digital_sequence_send(tx_rx->nfca_signal->tx_signal); FURI_CRITICAL_EXIT(); // Configure gpio back to SPI and exit transparent @@ -601,6 +602,94 @@ static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_ return ret; } +static bool furi_hal_nfc_fully_transparent_raw_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { + furi_assert(tx_rx); + + bool received = false; + + tx_rx->rx_bits = 0; + + if(tx_rx->tx_bits) { + nfca_trans_rx_pause(&tx_rx->nfca_trans_state); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + digital_sequence_send(tx_rx->nfca_signal->tx_signal); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + nfca_trans_rx_continue(&tx_rx->nfca_trans_state); + + if(tx_rx->sniff_tx) { + tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, false, tx_rx->sniff_context); + } + } + + if(timeout_ms) { + tx_rx->nfca_trans_state.bits_received = 0; + received = nfca_trans_rx_loop(&tx_rx->nfca_trans_state, timeout_ms); + + if(received) { + if(tx_rx->nfca_trans_state.bits_received > 7) { + tx_rx->rx_bits = tx_rx->nfca_trans_state.bits_received/9 * 8; + for(size_t pos = 0; pos < tx_rx->rx_bits/8; pos++) { + tx_rx->rx_data[pos] = tx_rx->nfca_trans_state.frame_data[pos]; + } + } else { + tx_rx->rx_bits = tx_rx->nfca_trans_state.bits_received; + tx_rx->rx_data[0] = tx_rx->nfca_trans_state.frame_data[0] & ~(0xFF << tx_rx->rx_bits); + } + + if(tx_rx->sniff_rx) { + tx_rx->sniff_rx(tx_rx->rx_data, tx_rx->rx_bits, false, tx_rx->sniff_context); + } + } + } + + return received; +} + +static bool furi_hal_nfc_fully_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { + furi_assert(tx_rx); + + bool received = false; + + tx_rx->rx_bits = 0; + + if(tx_rx->tx_bits) { + nfca_trans_rx_pause(&tx_rx->nfca_trans_state); + FURI_CRITICAL_ENTER(); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + nfca_signal_encode(tx_rx->nfca_signal, tx_rx->tx_data, tx_rx->tx_bits, tx_rx->tx_parity); + digital_sequence_send(tx_rx->nfca_signal->tx_signal); + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + FURI_CRITICAL_EXIT(); + nfca_trans_rx_continue(&tx_rx->nfca_trans_state); + + if(tx_rx->sniff_tx) { + tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, false, tx_rx->sniff_context); + } + } + + if(timeout_ms) { + tx_rx->nfca_trans_state.bits_received = 0; + received = nfca_trans_rx_loop(&tx_rx->nfca_trans_state, timeout_ms); + + if(received) { + if(tx_rx->nfca_trans_state.bits_received > 7) { + tx_rx->rx_bits = tx_rx->nfca_trans_state.bits_received/9 * 8; + memcpy(tx_rx->rx_data, tx_rx->nfca_trans_state.frame_data, tx_rx->nfca_trans_state.bits_received/9); + } else { + tx_rx->rx_bits = tx_rx->nfca_trans_state.bits_received; + tx_rx->rx_data[0] = tx_rx->nfca_trans_state.frame_data[0] & ~(0xFF << tx_rx->rx_bits); + } + + if(tx_rx->sniff_rx) { + tx_rx->sniff_rx(tx_rx->rx_data, tx_rx->rx_bits, false, tx_rx->sniff_context); + } + } + } + + return received; +} + + static uint32_t furi_hal_nfc_tx_rx_get_flag(FuriHalNfcTxRxType type) { uint32_t flags = 0; @@ -674,9 +763,39 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity( return curr_byte * 8; } +static uint8_t furi_hal_nfc_gen_parity(uint8_t value) { + value ^= (value >> 4); + value ^= (value >> 2); + value ^= (value >> 1); + + return (value ^ 1) & 1; +} + +void furi_hal_nfc_gen_bitstream(FuriHalNfcTxRxContext* tx_rx, uint8_t *buffer, size_t len) { + for(size_t pos = 0; pos < len; pos++) { + uint32_t parity_bit_num = pos % 8; + uint8_t bit = furi_hal_nfc_gen_parity(buffer[pos]); + + tx_rx->tx_data[pos] = buffer[pos]; + tx_rx->tx_parity[pos / 8] &= ~(1 << (7 - parity_bit_num)); + tx_rx->tx_parity[pos / 8] |= bit << (7 - parity_bit_num); + } + tx_rx->tx_bits = len * 8; +} + bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { furi_assert(tx_rx); + if(tx_rx->tx_rx_type == FuriHalNfcTxRxFullyRawTransparent) { + return furi_hal_nfc_fully_transparent_raw_tx_rx(tx_rx, timeout_ms); + } + if(tx_rx->tx_rx_type == FuriHalNfcTxRxFullyTransparent) { + return furi_hal_nfc_fully_transparent_tx_rx(tx_rx, timeout_ms); + } + if(tx_rx->tx_rx_type == FuriHalNfcTxRxTransparent) { + return furi_hal_nfc_transparent_tx_rx(tx_rx, timeout_ms); + } + ReturnCode ret; rfalNfcState state = RFAL_NFC_STATE_ACTIVATED; uint8_t temp_tx_buff[FURI_HAL_NFC_DATA_BUFF_SIZE] = {}; @@ -684,9 +803,7 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { uint8_t* temp_rx_buff = NULL; uint16_t* temp_rx_bits = NULL; - if(tx_rx->tx_rx_type == FuriHalNfcTxRxTransparent) { - return furi_hal_nfc_transparent_tx_rx(tx_rx, timeout_ms); - } + //FURI_LOG_D(TAG, "furi_hal_nfc_tx_rx %u", tx_rx->tx_rx_type); // Prepare data for FIFO if necessary uint32_t flags = furi_hal_nfc_tx_rx_get_flag(tx_rx->tx_rx_type); diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index 1363a1572..12ce91523 100644 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -14,6 +14,7 @@ extern "C" { #endif #include #include +#include #define FURI_HAL_NFC_UID_MAX_LEN 10 #define FURI_HAL_NFC_DATA_BUFF_SIZE (512) @@ -46,6 +47,8 @@ typedef enum { FuriHalNfcTxRxTypeRaw, FuriHalNfcTxRxTypeRxRaw, FuriHalNfcTxRxTransparent, + FuriHalNfcTxRxFullyRawTransparent, + FuriHalNfcTxRxFullyTransparent } FuriHalNfcTxRxType; typedef bool (*FuriHalNfcEmulateCallback)( @@ -91,6 +94,8 @@ typedef struct { uint16_t rx_bits; FuriHalNfcTxRxType tx_rx_type; NfcaSignal* nfca_signal; + NfcaTransRxState nfca_trans_state; + bool nfca_trans_initialized; FuriHalNfcTxRxSniffCallback sniff_tx; FuriHalNfcTxRxSniffCallback sniff_rx; @@ -425,6 +430,9 @@ FuriHalNfcReturn furi_hal_nfc_ll_txrx_bits( void furi_hal_nfc_ll_poll(); + +void furi_hal_nfc_gen_bitstream(FuriHalNfcTxRxContext* tx_rx, uint8_t *buffer, size_t len); + #ifdef __cplusplus } #endif diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 8e6cbae62..c7e8bbca5 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -2,18 +2,16 @@ #include #include -#include -#include #include #define TAG "DigitalSignal" -#pragma GCC optimize("O3,unroll-loops,Ofast") #define F_TIM (64000000.0) #define T_TIM 1562 /* 15.625 ns *100 */ #define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ + DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { DigitalSignal* signal = malloc(sizeof(DigitalSignal)); signal->start_level = true; @@ -23,6 +21,26 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t)); signal->reload_reg_entries = 0; signal->reload_reg_remainder = 0; + + signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_gpio.NbData = 2; + signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + + signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->ARR); + signal->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + signal->dma_config_timer.Mode = LL_DMA_MODE_NORMAL; + signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + signal->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; return signal; } @@ -144,7 +162,6 @@ void digital_signal_prepare(DigitalSignal* signal) { } } - static void digital_signal_stop_dma() { LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); @@ -164,39 +181,14 @@ static bool digital_signal_setup_dma(DigitalSignal* signal) { return false; } - LL_DMA_InitTypeDef dma_config_gpio = {}; - LL_DMA_InitTypeDef dma_config_timer = {}; - - dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buff; - dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->BSRR); - dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; - dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config_gpio.NbData = 2; - dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; - - // Init timer arr register buffer and DMA channel - dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; - dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->ARR); - dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config_timer.Mode = LL_DMA_MODE_NORMAL; - dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config_timer.NbData = signal->reload_reg_entries; - dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; - - digital_signal_stop_dma(); + signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buff; + signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->BSRR); + signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; + signal->dma_config_timer.NbData = signal->reload_reg_entries; /* set up DMA channel 1 and 2 for GPIO and timer copy operations */ - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config_gpio); - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config_timer); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &signal->dma_config_gpio); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &signal->dma_config_timer); /* enable both DMA channels */ LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); @@ -251,9 +243,6 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { signal->edge_cnt--; } - - - void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) { sequence->signals_size = size; sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*)); @@ -270,13 +259,8 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); - //gpio = &gpio_ext_pb2; - //furi_hal_gpio_init(&gpio_ext_pb2, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - //furi_hal_gpio_init(&gpio_ext_pc3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - //furi_hal_gpio_write(&gpio_ext_pb2, false); - //furi_hal_gpio_write(&gpio_ext_pc3, false); - sequence->gpio = gpio; + sequence->bake = false; digital_sequence_alloc_signals(sequence, 32); digital_sequence_alloc_sequence(sequence, size); @@ -380,7 +364,6 @@ static bool digital_sequence_send_signal(DigitalSignal* signal) { digital_signal_setup_timer(); digital_signal_start_timer(); } else { - /* configure next polarities and timings */ digital_signal_update_dma(signal); } @@ -425,6 +408,7 @@ bool digital_sequence_send(DigitalSequence* sequence) { } int32_t remainder = 0; + FURI_CRITICAL_ENTER(); for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { uint8_t signal_index = sequence->sequence[pos]; @@ -432,9 +416,7 @@ bool digital_sequence_send(DigitalSequence* sequence) { if(!sig) { FURI_LOG_D(TAG, "digital_sequence_send: Signal at index %u, used at pos %lu is NULL, aborting", signal_index, pos); - digital_signal_stop_timer(); - digital_signal_stop_dma(); - return false; + break; } /* when we are too late more than half a tick, make the first edge temporarily longer */ @@ -461,11 +443,10 @@ bool digital_sequence_send(DigitalSequence* sequence) { bool success = digital_sequence_send_signal(sig); if(!success) { - digital_signal_stop_timer(); - digital_signal_stop_dma(); - return false; + break; } } + FURI_CRITICAL_EXIT(); while(LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2)) { } diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index 189ad9198..4d25b90c2 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -5,6 +5,8 @@ #include #include +#include +#include #ifdef __cplusplus extern "C" { @@ -27,6 +29,8 @@ typedef struct { uint32_t reload_reg_remainder; uint32_t gpio_buff[2]; const GpioPin* gpio; + LL_DMA_InitTypeDef dma_config_gpio; + LL_DMA_InitTypeDef dma_config_timer; } DigitalSignal; typedef struct { diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 1538b2177..eff4d8d39 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -857,6 +857,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { } void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; MfClassicEmulator emulator = { @@ -870,11 +871,67 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { rfal_platform_spi_acquire(); furi_hal_nfc_listen_start(nfc_data); + nfca_trans_rx_init(&tx_rx.nfca_trans_state); + + tx_rx.tx_rx_type = FuriHalNfcTxRxFullyTransparent; + + uint8_t tx_buffer_aticoll[32]; + memcpy(tx_buffer_aticoll, &nfc_data->uid, 4); + nfca_append_crc16(tx_buffer_aticoll, 4); + + uint8_t tx_buffer_ack[8]; + tx_buffer_ack[0] = nfc_data->sak; + nfca_append_crc16(tx_buffer_ack, 1); + while(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { - if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { + tx_rx.tx_bits = 0; + tx_rx.rx_bits = 0; + if(furi_hal_nfc_tx_rx(&tx_rx, 300)) { + FURI_LOG_D(TAG, "Command: %02X", tx_rx.rx_data[0]); + + if(tx_rx.rx_bits == 7) { + switch(tx_rx.rx_data[0]) { + /* MAGIC WUPC1 */ + case 0x40: + continue; + + /* WUPA */ + case 0x52: + furi_hal_nfc_gen_bitstream(&tx_rx, nfc_data->atqa, 2); + furi_hal_nfc_tx_rx(&tx_rx, 0); + continue; + } + } + + if(tx_rx.rx_bits >= 16) { + switch(tx_rx.rx_data[0]) { + /* SELECT */ + case 0x93: + switch(tx_rx.rx_data[1]) { + /* ANTICOLL */ + case 0x20: + furi_hal_nfc_gen_bitstream(&tx_rx, tx_buffer_aticoll, 6); + furi_hal_nfc_tx_rx(&tx_rx, 0); + continue; + + /* SELECT UID */ + case 0x70: + furi_hal_nfc_gen_bitstream(&tx_rx, tx_buffer_ack, 3); + furi_hal_nfc_tx_rx(&tx_rx, 0); + continue; + } + break; + + /* HALS */ + case 0x50: + continue; + } + } + mf_classic_emulator(&emulator, &tx_rx); } } + if(emulator.data_changed) { nfc_worker->dev_data->mf_classic_data = emulator.data; if(nfc_worker->callback) { @@ -883,9 +940,12 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { emulator.data_changed = false; } - nfca_signal_free(nfca_signal); + nfca_trans_rx_deinit(&tx_rx.nfca_trans_state); rfal_platform_spi_release(); + + + nfca_signal_free(nfca_signal); } void nfc_worker_write_mf_classic(NfcWorker* nfc_worker) { diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 67fdcdc3d..ef1983e81 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 10a22b033..094ea7507 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -763,6 +763,10 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint8_t plain_data[MF_CLASSIC_MAX_DATA_SIZE]; MfClassicKey access_key = MfClassicKeyA; + tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent; + + FURI_LOG_D(TAG, "Starting mf_classic_emulator"); + // Read command while(!command_processed) { if(!is_encrypted) { @@ -791,7 +795,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } if(plain_data[0] == 0x50 && plain_data[1] == 0x00) { - furi_hal_nfc_listen_sleep(); + //furi_hal_nfc_listen_sleep(); command_processed = true; break; } else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) { @@ -824,7 +828,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->tx_parity[0] |= nfc_util_odd_parity8(nt[i]) << (7 - i); } tx_rx->tx_bits = sizeof(nt) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent; } else { crypto1_encrypt( &emulator->crypto, @@ -834,7 +838,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = sizeof(nt) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent; } if(!furi_hal_nfc_tx_rx(tx_rx, 500)) { FURI_LOG_E(TAG, "Error in NT exchange?"); @@ -881,7 +885,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = sizeof(responce) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent; is_encrypted = true; } else if(is_encrypted && plain_data[0] == 0x30) { @@ -912,7 +916,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } else { tx_rx->tx_data[0] = nack; } - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent; tx_rx->tx_bits = 4; furi_hal_nfc_tx_rx(tx_rx, 300); break; @@ -928,7 +932,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = 18 * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent; } else if(is_encrypted && plain_data[0] == 0xA0) { uint8_t block = plain_data[1]; if(block > mf_classic_get_total_block_num(emulator->data.type)) { @@ -937,7 +941,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Send ACK uint8_t ack = 0x0A; crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent; tx_rx->tx_bits = 4; if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; @@ -972,7 +976,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Send ACK ack = 0x0A; crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent; tx_rx->tx_bits = 4; } else { FURI_LOG_T(TAG, "%02X unknown received", plain_data[0]); @@ -989,7 +993,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } else { tx_rx->tx_data[0] = nack; } - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent; tx_rx->tx_bits = 4; furi_hal_nfc_tx_rx(tx_rx, 300); } diff --git a/lib/nfc/protocols/nfca.c b/lib/nfc/protocols/nfca.c index 6d0fc20b3..328db012b 100644 --- a/lib/nfc/protocols/nfca.c +++ b/lib/nfc/protocols/nfca.c @@ -2,18 +2,27 @@ #include #include #include +#include +#include #define NFCA_CMD_RATS (0xE0U) #define NFCA_CRC_INIT (0x6363) #define NFCA_F_SIG (13560000.0) /* [Hz] NFC frequency */ -#define NFCA_F_SUB (NFCA_F_SIG / 16) /* [Hz] NFC subcarrier frequency fs/16 (847500 Hz) */ +#define NFCA_F_SUB (NFCA_F_SIG/16) /* [Hz] NFC subcarrier frequency fs/16 (847500 Hz) */ #define T_SUB (1000000000000.0f / NFCA_F_SUB) /* [ps] subcarrier period = 1/NFCA_F_SUB (1.18 µs) */ -#define T_SUB_PHASE (T_SUB / 2) /* [ps] a single subcarrier phase (590 µs) */ +#define T_SUB_PHASE (T_SUB/2) /* [ps] a single subcarrier phase (590 µs) */ #define NFCA_SIGNAL_MAX_EDGES (1350) +#define SEQ_SOF 0 +#define SEQ_BIT0 1 +#define SEQ_BIT1 2 +#define SEQ_EOF 3 +#define SEQ_IDLE 4 + + typedef struct { uint8_t cmd; uint8_t param; @@ -65,27 +74,32 @@ bool nfca_emulation_handler( static void nfca_add_byte(NfcaSignal* nfca_signal, uint8_t byte, bool parity) { for(uint8_t i = 0; i < 8; i++) { if(byte & (1 << i)) { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_d); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1); } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_e); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0); } } if(parity) { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_d); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1); } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_e); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0); } } static void nfca_add_modulation(DigitalSignal* signal, size_t phases) { for(size_t i = 0; i < phases; i++) { - bool modulated = (i % 2 == 0); - digital_signal_add_pulse(signal, DIGITAL_SIGNAL_PS(T_SUB_PHASE), modulated); + signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(T_SUB_PHASE); } } static void nfca_add_silence(DigitalSignal* signal, size_t phases) { - digital_signal_add_pulse(signal, DIGITAL_SIGNAL_PS(phases*T_SUB_PHASE), false); + bool end_level = signal->start_level ^ ((signal->edge_cnt % 2) == 0); + + if((signal->edge_cnt == 0) || end_level) { + signal->edge_timings[signal->edge_cnt++] = DIGITAL_SIGNAL_PS(phases * T_SUB_PHASE); + } else { + signal->edge_timings[signal->edge_cnt - 1] += DIGITAL_SIGNAL_PS(phases * T_SUB_PHASE); + } } NfcaSignal* nfca_signal_alloc() { @@ -97,16 +111,29 @@ NfcaSignal* nfca_signal_alloc() { nfca_signal->seq_f = digital_signal_alloc(10); /* SEQ D has the first half modulated, used as SOF */ + nfca_signal->seq_d->start_level = true; nfca_add_modulation(nfca_signal->seq_d, 8); nfca_add_silence(nfca_signal->seq_d, 8); /* SEQ E has the second half modulated */ + nfca_signal->seq_e->start_level = false; nfca_add_silence(nfca_signal->seq_e, 8); nfca_add_modulation(nfca_signal->seq_e, 8); /* SEQ F is just no modulation, used as EOF */ + nfca_signal->seq_f->start_level = false; nfca_add_silence(nfca_signal->seq_f, 16); - nfca_signal->tx_signal = digital_signal_alloc(NFCA_SIGNAL_MAX_EDGES); + + nfca_signal->tx_signal = digital_sequence_alloc(NFCA_SIGNAL_MAX_EDGES, &gpio_spi_r_mosi); + + /* we are dealing with shorter sequences, enable bake-before-sending */ + //nfca_signal->tx_signal->bake = true; + + digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_SOF, nfca_signal->seq_d); + digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_BIT0, nfca_signal->seq_e); + digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_BIT1, nfca_signal->seq_d); + digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_EOF, nfca_signal->seq_f); + digital_sequence_set_signal(nfca_signal->tx_signal, SEQ_IDLE, nfca_signal->seq_f); return nfca_signal; } @@ -117,7 +144,7 @@ void nfca_signal_free(NfcaSignal* nfca_signal) { digital_signal_free(nfca_signal->seq_d); digital_signal_free(nfca_signal->seq_e); digital_signal_free(nfca_signal->seq_f); - digital_signal_free(nfca_signal->tx_signal); + digital_sequence_free(nfca_signal->tx_signal); free(nfca_signal); } @@ -126,21 +153,18 @@ void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, u furi_assert(data); furi_assert(parity); - nfca_signal->tx_signal->edge_cnt = 0; - nfca_signal->tx_signal->start_level = true; + digital_sequence_clear(nfca_signal->tx_signal); - // Start of frame - digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_f); - digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_f); - digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_f); - digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_d); + /* add some idle bit times before SOF in case the GPIO was active */ + digital_sequence_add(nfca_signal->tx_signal, SEQ_IDLE); + digital_sequence_add(nfca_signal->tx_signal, SEQ_SOF); if(bits < 8) { for(size_t i = 0; i < bits; i++) { if(FURI_BIT(data[0], i)) { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_d); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1); } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_e); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0); } } } else { @@ -148,5 +172,6 @@ void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, u nfca_add_byte(nfca_signal, data[i], parity[i / 8] & (1 << (7 - (i & 0x07)))); } } - digital_signal_append(nfca_signal->tx_signal, nfca_signal->seq_f); + + digital_sequence_add(nfca_signal->tx_signal, SEQ_EOF); } diff --git a/lib/nfc/protocols/nfca.h b/lib/nfc/protocols/nfca.h index 7acd1fb95..a4928a3eb 100644 --- a/lib/nfc/protocols/nfca.h +++ b/lib/nfc/protocols/nfca.h @@ -9,7 +9,7 @@ typedef struct { DigitalSignal* seq_d; /* sequence D, modulation with subcarrier during first half */ DigitalSignal* seq_e; /* sequence E, modulation with subcarrier during second half */ DigitalSignal* seq_f; /* sequence F, no modulation at all */ - DigitalSignal* tx_signal; + DigitalSequence* tx_signal; } NfcaSignal; uint16_t nfca_get_crc16(uint8_t* buff, uint16_t len); @@ -27,3 +27,4 @@ NfcaSignal* nfca_signal_alloc(); void nfca_signal_free(NfcaSignal* nfca_signal); void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, uint8_t* parity); + diff --git a/lib/nfc/protocols/nfca_emu.c b/lib/nfc/protocols/nfca_trans_rx.c similarity index 58% rename from lib/nfc/protocols/nfca_emu.c rename to lib/nfc/protocols/nfca_trans_rx.c index 6040bad14..3f8047272 100644 --- a/lib/nfc/protocols/nfca_emu.c +++ b/lib/nfc/protocols/nfca_trans_rx.c @@ -10,16 +10,13 @@ #include #include -#include "nfca_emu.h" +#include "nfca_trans_rx.h" -#define TAG "NfcA-emu" +#define TAG "NfcA-trans-rx" - - -void nfca_emu_init(NfcaEmuState *state, FuriHalNfcDevData* nfc_data) { - //nfca_emu_alloc(); - rfal_platform_spi_acquire(); +void nfca_trans_rx_init(NfcaTransRxState *state) { + FURI_LOG_D(TAG, "Starting NfcA transparent rx"); st25r3916ExecuteCommand(ST25R3916_CMD_STOP); st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3); @@ -28,11 +25,6 @@ void nfca_emu_init(NfcaEmuState *state, FuriHalNfcDevData* nfc_data) { furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); - FURI_LOG_D(TAG, "Starting NfcA emulation"); - FURI_LOG_D(TAG, " UID: %02X %02X %02X %02X %02X %02X %02X %02X", - nfc_data->uid[0], nfc_data->uid[1], nfc_data->uid[2], nfc_data->uid[3], - nfc_data->uid[4], nfc_data->uid[5], nfc_data->uid[6], nfc_data->uid[7]); - /* allocate a 512 edge buffer, more than enough */ state->reader_signal = pulse_reader_alloc(&gpio_spi_r_miso, 512); /* timebase shall be 1 ns */ @@ -46,20 +38,30 @@ void nfca_emu_init(NfcaEmuState *state, FuriHalNfcDevData* nfc_data) { state->valid_frame = false; } -void nfca_emu_deinit(NfcaEmuState *state) { +void nfca_trans_rx_deinit(NfcaTransRxState *state) { furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); - rfal_platform_spi_release(); pulse_reader_free(state->reader_signal); } +void nfca_trans_rx_pause(NfcaTransRxState *state) { + pulse_reader_stop(state->reader_signal); +} -void nfca_bit_received(NfcaEmuState *state, uint8_t bit) { +void nfca_trans_rx_continue(NfcaTransRxState *state) { + pulse_reader_start(state->reader_signal); +} + +static void nfca_bit_received(NfcaTransRxState *state, uint8_t bit) { /* According to ISO14443-3 short frames have 7 bits and standard 9 bits per byte, where the 9th bit is odd parity. Data is transmitted LSB first. */ uint32_t byte_num = (state->bits_received / 9); uint32_t bit_num = (state->bits_received % 9); + if(byte_num >= NFCA_FRAME_LENGTH) { + return; + } + if(bit_num == 8) { uint32_t parity_value = 1 << (state->bits_received / 9); state->parity_bits &= ~parity_value; @@ -74,94 +76,97 @@ void nfca_bit_received(NfcaEmuState *state, uint8_t bit) { } -bool nfca_emu_loop(NfcaEmuState *state, FuriHalNfcDevData* nfc_data, uint32_t timeout_ms) { - furi_assert(nfc_data); +bool nfca_trans_rx_loop(NfcaTransRxState *state, uint32_t timeout_ms) { + furi_assert(state); - bool ret = false; + state->valid_frame = false; + state->have_sof = false; + state->bits_received = 0; - while(!ret) { - uint32_t timeout_ns = timeout_ms * 1000; - uint32_t timeout = state->have_sof ? (4 * NFCA_TB) : timeout_ns; - uint32_t nsec = pulse_reader_receive(state->reader_signal, timeout); + bool done = false; + uint32_t timeout_us = timeout_ms * 1000; - bool eof = state->have_sof && nsec > 2*NFCA_TB; + while(!done) { + uint32_t nsec = pulse_reader_receive(state->reader_signal, timeout_us); - if(nsec != PULSE_READER_NO_EDGE) { - FURI_LOG_T(TAG, "pulse: %lu ns, frametime now: %ld", nsec, state->frame_time); + bool eof = state->have_sof && (nsec >= (2 * NFCA_TB)); + bool lost_pulse = false; + + if(state->have_sof && nsec == PULSE_READER_LOST_EDGE) { + nsec = NFCA_T1; + lost_pulse = true; + } else if(nsec == PULSE_READER_NO_EDGE) { + done = true; } if(IS_T1(nsec) || eof) { - FURI_LOG_T(TAG, " T1 pulse"); - + timeout_us = (3 * NFCA_TB) / 1000; if(!state->have_sof) { - FURI_LOG_T(TAG, " SOF"); state->frame_time = -(NFCA_TB - nsec); state->have_sof = true; state->valid_frame = false; state->bits_received = 0; + state->debug_pos = 0; + if(lost_pulse) { + state->frame_time -= nsec; + } continue; } if(state->frame_time > NFCA_TB_MIN) { - FURI_LOG_T(TAG, " Y"); state->frame_time -= NFCA_TB; nfca_bit_received(state, 0); } if(IS_ZERO(state->frame_time)) { - FURI_LOG_T(TAG, " Z"); state->frame_time = -(NFCA_TB - nsec); nfca_bit_received(state, 0); } else if(IS_TX(state->frame_time)) { - FURI_LOG_T(TAG, " X"); state->frame_time = -(NFCA_TX - nsec); nfca_bit_received(state, 1); } else { if(eof) { - FURI_LOG_T(TAG, " EOF"); state->have_sof = false; state->valid_frame = true; - ret = true; + done = true; } else { - FURI_LOG_D(TAG, " unexpected: %ld", state->frame_time); } } - - } else { if(!state->have_sof) { - state->frame_time = 0; + if(IS_TB(nsec)) { + state->frame_time = 0; + state->have_sof = true; + state->valid_frame = false; + state->bits_received = 0; + state->debug_pos = 0; + if(lost_pulse) { + state->frame_time -= nsec; + } + continue; + } else { + state->frame_time = 0; + } } else { state->frame_time += nsec; - FURI_LOG_T(TAG, " non-T1 pulse"); } } + + if(lost_pulse) { + state->frame_time -= nsec; + } } - if(ret) { - + if(state->valid_frame) { if(state->bits_received > 7) { /* a last 0-bit will look like a missing bit */ if((state->bits_received % 9) == 8) { nfca_bit_received(state, 0); state->bits_received++; } - FURI_LOG_D(TAG, "Received standard frame: %d bits (%d byte)", state->bits_received, state->bits_received/9); - - for(size_t pos = 0; pos < state->bits_received / 9; pos++) { - FURI_LOG_D(TAG, " 0x%02X", state->frame_data[pos]); - } - } else { - FURI_LOG_D(TAG, "Received short frame: %d bits", state->bits_received); - FURI_LOG_D(TAG, " 0x%02X", state->frame_data[0] & 0x7F); } - - /* we know that this code uses TIM2, so stop pulse reader */ - //pulse_reader_stop(state->reader_signal); - //nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos); - //pulse_reader_start(state->reader_signal); } - return ret; + return state->valid_frame; } diff --git a/lib/nfc/protocols/nfca_emu.h b/lib/nfc/protocols/nfca_trans_rx.h similarity index 61% rename from lib/nfc/protocols/nfca_emu.h rename to lib/nfc/protocols/nfca_trans_rx.h index 8fc769da5..d9c5f9513 100644 --- a/lib/nfc/protocols/nfca_emu.h +++ b/lib/nfc/protocols/nfca_trans_rx.h @@ -12,6 +12,7 @@ /* assume fc/128 */ #define NFCA_FC (13560000.0f) /* MHz */ #define NFCA_FC_K ((uint32_t)(NFCA_FC/1000)) /* kHz */ +#define NFCA_T1 (28.0f / NFCA_FC * 1000000000) #define NFCA_T1_MIN (24.0f / NFCA_FC * 1000000000) #define NFCA_T1_MAX (41.0f / NFCA_FC * 1000000000) #define NFCA_TX (64.0f / NFCA_FC * 1000000000) /* 4.7198 µs */ @@ -21,15 +22,16 @@ #define NFCA_TB_MIN (0.80f * NFCA_TB) #define NFCA_TB_MAX (1.20f * NFCA_TB) -#define IS_T1(x) ((x)>=NFCA_T1_MIN && (x)<=NFCA_T1_MAX) -#define IS_TX(x) ((x)>=NFCA_TX_MIN && (x)<=NFCA_TX_MAX) -#define IS_TB(x) ((x)>=NFCA_TB_MIN && (x)<=NFCA_TB_MAX) -#define IS_ZERO(x) ((x)>=-NFCA_T1_MIN/2 && (x)<=NFCA_T1_MIN/2) +#define IS_T1(x) ((x)>=NFCA_T1_MIN && (x)<=NFCA_T1_MAX) +#define IS_TX(x) ((x)>=NFCA_TX_MIN && (x)<=NFCA_TX_MAX) +#define IS_TB(x) ((x)>=NFCA_TB_MIN && (x)<=NFCA_TB_MAX) +#define IS_ZERO(x) ((x)>=-NFCA_T1_MIN/2 && (x)<=NFCA_T1_MIN/2) #define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) #define DIGITAL_SIGNAL_UNIT_US (100000.0f) #define NFCA_FRAME_LENGTH 32 +#define NFCA_DEBUG_LENGTH 128 typedef struct { @@ -38,10 +40,15 @@ typedef struct { int32_t frame_time; size_t bits_received; uint8_t frame_data[NFCA_FRAME_LENGTH]; + uint32_t debug_buffer[NFCA_DEBUG_LENGTH]; + size_t debug_pos; uint32_t parity_bits; PulseReader *reader_signal; -} NfcaEmuState; +} NfcaTransRxState; -bool nfca_emu_loop(NfcaEmuState *state, FuriHalNfcDevData* nfc_data, uint32_t timeout_ms); -void nfca_emu_deinit(NfcaEmuState *state); -void nfca_emu_init(NfcaEmuState *state, FuriHalNfcDevData* nfc_data); +bool nfca_trans_rx_loop(NfcaTransRxState *state, uint32_t timeout_ms); +void nfca_trans_rx_deinit(NfcaTransRxState *state); +void nfca_trans_rx_init(NfcaTransRxState *state); + +void nfca_trans_rx_pause(NfcaTransRxState *state); +void nfca_trans_rx_continue(NfcaTransRxState *state); \ No newline at end of file diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c index c5729c42a..f829bf831 100644 --- a/lib/pulse_reader/pulse_reader.c +++ b/lib/pulse_reader/pulse_reader.c @@ -2,10 +2,6 @@ #include #include #include -#include -#include -#include -#include #include "pulse_reader.h" @@ -36,6 +32,7 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { PulseReader* signal = malloc(sizeof(PulseReader)); signal->timer_buffer = malloc(size * sizeof(uint32_t)); + signal->gpio_buffer = malloc(size * sizeof(uint32_t)); signal->dma_channel = LL_DMA_CHANNEL_4; signal->gpio = gpio; signal->size = size; @@ -45,6 +42,26 @@ PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { pulse_reader_set_timebase(signal, PulseReaderUnit64MHz); pulse_reader_set_bittime(signal, 1); + signal->dma_config_timer.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->CNT); + signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t) signal->timer_buffer; + signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR; + signal->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_timer.Priority = LL_DMA_PRIORITY_VERYHIGH; + + signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + signal->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + return signal; } @@ -75,6 +92,7 @@ void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) { void pulse_reader_free(PulseReader* signal) { free(signal->timer_buffer); + free(signal->gpio_buffer); free(signal); } @@ -85,32 +103,21 @@ uint32_t pulse_reader_samples(PulseReader* signal) { } void pulse_reader_stop(PulseReader* signal) { - LL_DMA_DisableChannel(DMA1, signal->dma_channel); + LL_DMA_DisableChannel(DMA1, signal->dma_channel+1); LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); LL_TIM_DisableCounter(TIM2); } void pulse_reader_start(PulseReader* signal) { - - memset(signal->timer_buffer, 0x00, signal->size * sizeof(uint32_t)); - /* configure DMA to read from a timer peripheral */ - LL_DMA_InitTypeDef dma_config = {}; - dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) &(TIM2->CNT); - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstAddress = (uint32_t) signal->timer_buffer; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.NbData = signal->size; /* executes LL_DMA_SetDataLength */ - dma_config.PeriphRequest = LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + signal->dma_config_timer.NbData = signal->size; + + signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) &(signal->gpio->port->IDR); + signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t) signal->gpio_buffer; + signal->dma_config_gpio.NbData = signal->size; /* start counter */ - LL_TIM_DisableCounter(TIM2); LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); LL_TIM_SetPrescaler(TIM2, 0); @@ -118,8 +125,6 @@ void pulse_reader_start(PulseReader* signal) { LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); - /* make sure request generation is disabled before modifying registers */ - LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); /* generator 0 gets fed by EXTI_LINEn */ LL_DMAMUX_SetRequestSignalID(NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin)); /* trigger on rising edge of the interrupt */ @@ -134,11 +139,13 @@ void pulse_reader_start(PulseReader* signal) { signal->pos = 0; signal->start_level = furi_hal_gpio_read(signal->gpio); signal->timer_value = TIM2->CNT; + signal->gpio_mask = signal->gpio->pin; /* now set up DMA with these settings */ - LL_DMA_DisableChannel(DMA1, signal->dma_channel); - LL_DMA_Init(DMA1, signal->dma_channel, &dma_config); + LL_DMA_Init(DMA1, signal->dma_channel, &signal->dma_config_timer); + LL_DMA_Init(DMA1, signal->dma_channel+1, &signal->dma_config_gpio); LL_DMA_EnableChannel(DMA1, signal->dma_channel); + LL_DMA_EnableChannel(DMA1, signal->dma_channel+1); } uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { @@ -154,8 +161,17 @@ uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { if(dma_pos != signal->pos) { uint32_t delta = signal->timer_buffer[signal->pos] - signal->timer_value; + uint32_t last_gpio_value = signal->gpio_value; + signal->gpio_value = signal->gpio_buffer[signal->pos]; + + /* check if the GPIO really toggled. if not, we lost an edge :( */ + if(((last_gpio_value ^ signal->gpio_value) & signal->gpio_mask) != signal->gpio_mask) { + signal->gpio_value ^= signal->gpio_mask; + return PULSE_READER_LOST_EDGE; + } signal->timer_value = signal->timer_buffer[signal->pos]; + signal->pos++; signal->pos %= signal->size; @@ -183,7 +199,7 @@ uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { return PULSE_READER_NO_EDGE; } - furi_delay_ms(0); + //furi_delay_ms(0); } while(true); } diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h index a53e666b0..886c9d9e7 100644 --- a/lib/pulse_reader/pulse_reader.h +++ b/lib/pulse_reader/pulse_reader.h @@ -3,6 +3,10 @@ #include #include #include +#include +#include +#include +#include #include @@ -10,8 +14,9 @@ extern "C" { #endif -#define PULSE_READER_NO_EDGE 0xFFFFFFFFUL -#define F_TIM2 64000000UL +#define PULSE_READER_NO_EDGE 0xFFFFFFFFUL +#define PULSE_READER_LOST_EDGE 0xFFFFFFFEUL +#define F_TIM2 64000000UL /** * unit of the edge durations to return @@ -27,14 +32,19 @@ typedef enum { typedef struct { bool start_level; uint32_t* timer_buffer; + uint32_t* gpio_buffer; uint32_t size; uint32_t pos; uint32_t timer_value; + uint32_t gpio_value; + uint32_t gpio_mask; uint32_t unit_multiplier; uint32_t unit_divider; uint32_t bit_time; uint32_t dma_channel; const GpioPin* gpio; + LL_DMA_InitTypeDef dma_config_timer; + LL_DMA_InitTypeDef dma_config_gpio; } PulseReader;