diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index cff994baf..76d8f8ef8 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -300,6 +300,11 @@ 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 { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); DOLPHIN_DEED(DolphinDeedNfcEmulate); diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index a6475ca68..676e61b0c 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -1,11 +1,23 @@ #include #include +#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"); @@ -98,6 +110,7 @@ static void nfc_cli_field(Cli* cli, FuriString* args) { furi_hal_nfc_sleep(); } + static void nfc_cli(Cli* cli, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 0bebe35f3..ac6d7f928 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -12,8 +12,13 @@ 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) +ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu) +ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) +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_emulate_nfcv.c b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c new file mode 100644 index 000000000..0b09bb412 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c @@ -0,0 +1,147 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (100) + +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); + } + 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) + 1); + if(furi_string_size(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'); + 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; + } 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_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index fc6021d73..b3eae672f 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -4,6 +4,7 @@ enum SubmenuIndex { SubmenuIndexReadCardType, SubmenuIndexMfClassicKeys, SubmenuIndexMfUltralightUnlock, + SubmenuIndexNfcVUnlock, }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -34,6 +35,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); } @@ -56,6 +63,9 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + consumed = true; } 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 fa73201d0..6d5f7a1ca 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; @@ -14,7 +25,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)) { + if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl) + || (protocol == NfcDeviceProtocolNfcV)) { widget_add_button_element( widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); text_scroll_height = 52; @@ -42,19 +54,113 @@ 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) { + 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 - 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 == NfcDeviceProtocolNfcV) { + 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); + + 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); + 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) { @@ -136,6 +242,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 == 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 new file mode 100644 index 000000000..d0e223c15 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c @@ -0,0 +1,48 @@ +#include "../nfc_i.h" +#include + +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(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, NfcSceneNfcVUnlock); + 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..ee26baf69 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -0,0 +1,64 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexSave, + SubmenuIndexEmulate, +}; + +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_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)); + + 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 = NfcDeviceSaveFormatNfcV; + // Clear device name + 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) { + 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_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c new file mode 100644 index 000000000..96293a7c6 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -0,0 +1,151 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneNfcVUnlockStateIdle, + NfcSceneNfcVUnlockStateDetecting, + NfcSceneNfcVUnlockStateUnlocked, + NfcSceneNfcVUnlockStateAlreadyUnlocked, + NfcSceneNfcVUnlockStateNotSupportedCard, +} NfcSceneNfcVUnlockState; + +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(data->key_privacy, nfc->byte_input_store, 4); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + } + return true; +} + +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_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 = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock); + if(curr_state != state) { + Popup* popup = nfc->popup; + 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 == NfcSceneNfcVUnlockStateUnlocked) { + popup_reset(popup); + + if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { + 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]); + + nfc->dev->format = NfcDeviceSaveFormatNfcV; + + 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_single_vibro); + //notification_message(nfc->notifications, &sequence_success); + + 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); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + } 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_unlock_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + } else if(state == NfcSceneNfcVUnlockStateNotSupportedCard) { + 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, NfcSceneNfcVUnlock, state); + } +} + +void nfc_scene_nfcv_unlock_on_enter(void* context) { + Nfc* nfc = context; + + nfc_device_clear(nfc->dev); + // 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, + NfcWorkerStateNfcVUnlockAndSave, + &nfc->dev->dev_data, + nfc_scene_nfcv_unlock_worker_callback, + nfc); + + nfc_blink_read_start(nfc); +} + +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_unlock_set_state(nfc, NfcSceneNfcVUnlockStateUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventAborted) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateAlreadyUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateNotSupportedCard); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneNfcVUnlockMenu); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_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, 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 new file mode 100644 index 000000000..afa19ee41 --- /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, NfcSceneNfcVUnlock); + 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 7b877d645..4214d9b5e 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); // Set unlock password input to 0xFFFFFFFF only on fresh read 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_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index bd7c9817e..43f2246cd 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -34,6 +34,9 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcScenePassportAuth)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcScenePassportAuth); + } 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/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index c0d3ed5c6..616dc0ef7 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -117,6 +117,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/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3924fb4ba..4eb28720a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -688,7 +688,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*" @@ -726,13 +725,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,_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* 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* @@ -1178,6 +1185,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, @@ -1201,6 +1209,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, @@ -2007,12 +2016,28 @@ 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,-,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" +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*" Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" @@ -2114,6 +2139,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/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 2d27313ae..e18fa3819 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); } @@ -490,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); @@ -503,9 +537,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_signal_send(tx_rx->nfca_signal->tx_signal, &gpio_spi_r_mosi); + digital_sequence_send(tx_rx->nfca_signal->tx_signal); 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); @@ -569,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; @@ -642,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] = {}; @@ -652,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 d3f6de602..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; @@ -177,6 +182,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() * @@ -419,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/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/SConscript b/lib/SConscript index 7cc0acf2d..27979bc5c 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -4,6 +4,7 @@ env.Append( LINT_SOURCES=[ Dir("app-scened-template"), Dir("digital_signal"), + Dir("pulse_reader"), Dir("drivers"), Dir("flipper_format"), Dir("infrared"), diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 46ca307a7..c7e8bbca5 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -1,23 +1,46 @@ #include "digital_signal.h" #include -#include -#include +#include #include -#pragma GCC optimize("O3,unroll-loops,Ofast") +#define TAG "DigitalSignal" + + +#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)); 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->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; + 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; } @@ -37,7 +60,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); @@ -72,6 +98,32 @@ 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; +} + +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); @@ -79,76 +131,76 @@ 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); + furi_assert(signal->gpio); + furi_assert(signal->gpio->pin); + + /* 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 */ + signal->reload_reg_entries = 0; + + for(size_t pos = 0; pos < signal->edge_cnt; pos++) { + uint32_t pulse_duration = signal->edge_timings[pos] + signal->reload_reg_remainder; + uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / 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; } } } -void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { +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); +} + +static void digital_signal_stop_timer() { + LL_TIM_DisableCounter(TIM2); + LL_TIM_SetCounter(TIM2, 0); +} + +static 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); - // 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); + 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, &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); LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - // Set up timer + return true; +} + +static 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); @@ -156,18 +208,267 @@ 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? +static 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(!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; + 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(); + + 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*)); + 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)); + + sequence->gpio = gpio; + sequence->bake = false; + + digital_sequence_alloc_signals(sequence, 32); + digital_sequence_alloc_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; + signal->gpio = sequence->gpio; + signal->reload_reg_remainder = 0; + + 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; +} + +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); - 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); +} + +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; + } + digital_signal_setup_timer(); + digital_signal_start_timer(); + } else { + /* configure next polarities and timings */ + digital_signal_update_dma(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) { + furi_assert(sequence); + + furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + if(sequence->bake) { + DigitalSignal* sig = digital_sequence_bake(sequence); + + digital_signal_send(sig, sequence->gpio); + digital_signal_free(sig); + return true; + } + + int32_t remainder = 0; + FURI_CRITICAL_ENTER(); + + for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) { + uint8_t signal_index = sequence->sequence[pos]; + DigitalSignal *sig = sequence->signals[signal_index]; + + if(!sig) { + FURI_LOG_D(TAG, "digital_sequence_send: Signal at index %u, used at pos %lu is NULL, aborting", signal_index, pos); + break; + } + + /* when we are too late more than half a tick, make the first edge temporarily longer */ + bool needs_prolongation = false; + + 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) { + break; + } + } + FURI_CRITICAL_EXIT(); + + 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; +} + +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..4d25b90c2 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -5,35 +5,67 @@ #include #include +#include +#include #ifdef __cplusplus 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) +#define DIGITAL_SIGNAL_PS(x) (x/10UL) + + typedef struct { 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 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 { + uint8_t signals_size; + bool bake; + 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_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(DigitalSequence* sequence); +void digital_sequence_clear(DigitalSequence* sequence); + + + #ifdef __cplusplus } #endif 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_device.c b/lib/nfc/nfc_device.c index d80187b09..67c0bc072 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -51,6 +51,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 == NfcDeviceSaveFormatNfcV) { + furi_string_set(format_string, "ISO15693"); } else { furi_string_set(format_string, "Unknown"); } @@ -86,6 +88,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, "ISO15693")) { + dev->format = NfcDeviceSaveFormatNfcV; + dev->dev_data.protocol = NfcDeviceProtocolNfcV; + return true; + } return false; } @@ -643,6 +650,258 @@ 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; + 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_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixLData* data = &dev->dev_data.nfcv_data.sub_data.slix_l; + 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_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; + NfcVData* data = &dev->dev_data.nfcv_data; + + do { + 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; + 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: + 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; + 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_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)) + break; + if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; + data->type = temp_value; + + 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; +} + static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { bool saved = false; EmvData* data = &dev->dev_data.emv_data; @@ -1061,21 +1320,29 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { 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, Bank card")) + 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, 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 is common for all formats")) 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 != 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; + } // 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 == 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) { @@ -1146,11 +1413,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 != NfcDeviceSaveFormatNfcV) { + 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) { @@ -1165,6 +1434,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 == 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 f4726fe39..5ad6c4761 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -34,6 +35,7 @@ typedef enum { NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, + NfcDeviceProtocolNfcV } NfcProtocol; typedef enum { @@ -42,6 +44,7 @@ typedef enum { NfcDeviceSaveFormatMifareUl, NfcDeviceSaveFormatMifareClassic, NfcDeviceSaveFormatMifareDesfire, + NfcDeviceSaveFormatNfcV, } NfcDeviceSaveFormat; typedef struct { @@ -77,6 +80,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 3355385b4..a1a85b8eb 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() { @@ -98,6 +99,8 @@ int32_t nfc_worker_task(void* context) { } } 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) { @@ -114,6 +117,10 @@ 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 == 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); @@ -121,6 +128,180 @@ 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); + furi_assert(nfc_worker->callback); + + 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); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + furi_hal_nfc_sleep(); + + + while((nfc_worker->state == NfcWorkerStateNfcVUnlock) || + (nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) { + + 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); + + furi_hal_console_printf("Detect presence\r\n"); + 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 = {}; + + 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) { + 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) { + + key |= key_data[0] << 24; + key |= key_data[1] << 16; + key |= key_data[2] << 8; + key |= key_data[3] << 0; + + ret = slix_l_unlock(nfcv_data, 4); + } else { + 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"); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + if(slix_l_get_random(nfcv_data) != ERR_NONE) { + furi_hal_console_printf(" reset failed\r\n"); + } + + 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) { + /* 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_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MfUltralightReader reader = {}; @@ -417,6 +598,20 @@ static bool nfc_worker_read_nfcb(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); + + bool card_read = false; + furi_hal_nfc_sleep(); + + /* 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); + + return card_read; +} + void nfc_worker_read(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); @@ -474,7 +669,14 @@ void nfc_worker_read(NfcWorker* nfc_worker) { event = NfcWorkerEventReadUidNfcF; break; } else if(nfc_data->type == FuriHalNfcTypeV) { - event = NfcWorkerEventReadUidNfcV; + 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; } } else { @@ -592,6 +794,22 @@ 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(nfc_data, nfcv_data); + while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + if(nfcv_emu_loop(nfc_data, nfcv_data, 50)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + } + } + furi_delay_ms(0); + } + nfcv_emu_deinit(); +} + void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; FuriHalNfcDevData params = { @@ -836,6 +1054,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 = { @@ -849,11 +1068,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) { @@ -862,9 +1137,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.h b/lib/nfc/nfc_worker.h index 7d8264ef5..60f993dd5 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -18,6 +18,9 @@ typedef enum { NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, + NfcWorkerStateNfcVEmulate, + NfcWorkerStateNfcVUnlock, + NfcWorkerStateNfcVUnlockAndSave, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -39,6 +42,7 @@ typedef enum { NfcWorkerEventReadMfClassicDone, NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, + NfcWorkerEventReadNfcV, NfcWorkerEventReadBankCard, NfcWorkerEventReadPassport, @@ -68,6 +72,7 @@ typedef enum { // Mifare Ultralight events NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command + NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key } NfcWorkerEvent; @@ -89,3 +94,5 @@ void nfc_worker_start( void* context); 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/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index f827ae981..a80a6011a 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include #include struct NfcWorker { 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 b7a52bc01..cf4e6be9b 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -763,26 +763,39 @@ 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) { 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(); + //furi_hal_nfc_listen_sleep(); command_processed = true; break; } else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) { @@ -799,7 +812,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,13 +820,15 @@ 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); } tx_rx->tx_bits = sizeof(nt) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_rx_type = FuriHalNfcTxRxFullyTransparent; } else { crypto1_encrypt( &emulator->crypto, @@ -823,10 +838,10 @@ 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"); + FURI_LOG_E(TAG, "Error in NT exchange?"); command_processed = true; break; } @@ -839,7 +854,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 +864,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)) { @@ -870,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) { @@ -901,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; @@ -917,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)) { @@ -926,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; @@ -961,9 +976,10 @@ 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]); // Unknown command break; } @@ -977,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 c401f8cc5..328db012b 100644 --- a/lib/nfc/protocols/nfca.c +++ b/lib/nfc/protocols/nfca.c @@ -2,19 +2,27 @@ #include #include #include +#include +#include #define NFCA_CMD_RATS (0xE0U) #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 (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) +#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; @@ -63,46 +71,69 @@ bool nfca_emulation_handler( return sleep; } -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; - } -} - 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, SEQ_BIT1); } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0); } } if(parity) { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1); } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); + 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) { + 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() { 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); - nfca_signal->tx_signal = digital_signal_alloc(NFCA_SIGNAL_MAX_EDGES); + + /* 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); + + /* 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, &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; } @@ -110,9 +141,10 @@ 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->tx_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); free(nfca_signal); } @@ -121,17 +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; - // Start of frame - digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); + digital_sequence_clear(nfca_signal->tx_signal); + + /* 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->one); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT1); } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); + digital_sequence_add(nfca_signal->tx_signal, SEQ_BIT0); } } } else { @@ -139,4 +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_sequence_add(nfca_signal->tx_signal, SEQ_EOF); } diff --git a/lib/nfc/protocols/nfca.h b/lib/nfc/protocols/nfca.h index 498ef2843..a4928a3eb 100644 --- a/lib/nfc/protocols/nfca.h +++ b/lib/nfc/protocols/nfca.h @@ -6,9 +6,10 @@ #include typedef struct { - DigitalSignal* one; - DigitalSignal* zero; - DigitalSignal* tx_signal; + 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; uint16_t nfca_get_crc16(uint8_t* buff, uint16_t len); @@ -26,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_trans_rx.c b/lib/nfc/protocols/nfca_trans_rx.c new file mode 100644 index 000000000..3f8047272 --- /dev/null +++ b/lib/nfc/protocols/nfca_trans_rx.c @@ -0,0 +1,172 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nfca_trans_rx.h" + +#define TAG "NfcA-trans-rx" + + +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); + st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88); + st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + + /* 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_trans_rx_deinit(NfcaTransRxState *state) { + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + pulse_reader_free(state->reader_signal); +} + +void nfca_trans_rx_pause(NfcaTransRxState *state) { + pulse_reader_stop(state->reader_signal); +} + +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; + 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_trans_rx_loop(NfcaTransRxState *state, uint32_t timeout_ms) { + furi_assert(state); + + state->valid_frame = false; + state->have_sof = false; + state->bits_received = 0; + + bool done = false; + + uint32_t timeout_us = timeout_ms * 1000; + + while(!done) { + uint32_t nsec = pulse_reader_receive(state->reader_signal, timeout_us); + + 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) { + timeout_us = (3 * NFCA_TB) / 1000; + if(!state->have_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) { + state->frame_time -= NFCA_TB; + nfca_bit_received(state, 0); + } + + if(IS_ZERO(state->frame_time)) { + state->frame_time = -(NFCA_TB - nsec); + nfca_bit_received(state, 0); + } else if(IS_TX(state->frame_time)) { + state->frame_time = -(NFCA_TX - nsec); + nfca_bit_received(state, 1); + } else { + if(eof) { + state->have_sof = false; + state->valid_frame = true; + done = true; + } else { + } + } + } else { + if(!state->have_sof) { + 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; + } + } + + if(lost_pulse) { + state->frame_time -= nsec; + } + } + + 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++; + } + } + } + + return state->valid_frame; +} diff --git a/lib/nfc/protocols/nfca_trans_rx.h b/lib/nfc/protocols/nfca_trans_rx.h new file mode 100644 index 000000000..d9c5f9513 --- /dev/null +++ b/lib/nfc/protocols/nfca_trans_rx.h @@ -0,0 +1,54 @@ +#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 (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 */ +#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 +#define NFCA_DEBUG_LENGTH 128 + + +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 debug_buffer[NFCA_DEBUG_LENGTH]; + size_t debug_pos; + uint32_t parity_bits; + PulseReader *reader_signal; +} NfcaTransRxState; + +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/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c new file mode 100644 index 000000000..ead20cba3 --- /dev/null +++ b/lib/nfc/protocols/nfcv.c @@ -0,0 +1,747 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + + 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) { + memcpy(uid, res.UID, 8); + } + } + + return ret; +} + +ReturnCode nfcv_read_blocks( + NfcVReader* reader, + NfcVData* data) { + + 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/%d", block, (data->block_num - 1)); + + 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; + } + 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]); + } + + 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 SYSTEM INFORMATION..."); + + 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; + nfc_data->uid_len = 8; + /* 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)]; + } + 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, " 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; + } + FURI_LOG_D(TAG, "Failed: %d", ret); + + return ret; +} + +bool nfcv_read_card( + NfcVReader* reader, + 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; + } + + 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 */ +PulseReader *reader_signal = NULL; + +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; + +//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 +#define SIG_BIT0 1 +#define SIG_BIT1 2 +#define SIG_EOF 3 + + +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_sequence_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) { + /* assuming max frame length is 255 bytes */ + nfcv_signal = digital_sequence_alloc(8 * 255 + 2, nfcv_out_io); + } + + 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); + } + + 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); +} + + +void nfcv_emu_send_raw(uint8_t* data, uint8_t length) { + + digital_sequence_clear(nfcv_signal); + digital_sequence_add(nfcv_signal, SIG_SOF); + + 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_sequence_add(nfcv_signal, (data[byte_pos] & bit_val) ? SIG_BIT1 : SIG_BIT0); + } + + digital_sequence_add(nfcv_signal, SIG_EOF); + + FURI_CRITICAL_ENTER(); + digital_sequence_send(nfcv_signal); + FURI_CRITICAL_EXIT(); + furi_hal_gpio_write(nfcv_out_io, 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; +} + +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) { + + 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 = (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)) { + 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; + } + + 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 0x%02X ignored, privacy mode", command); + FURI_LOG_D(TAG, "%s", nfcv_data->last_command); + return; + } + break; + + default: + break; + } + + /* unfortunately the response is quicker than the original NFC tag which causes frame misses */ + furi_delay_us(270); + + switch(command) { + + case ISO15693_INVENTORY: + { + response_buffer[0] = ISO15693_NOERROR; + response_buffer[1] = nfcv_data->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_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; + } + + 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_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] = 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 -> 0x%02X%02X", + nfcv_data->sub_data.slix_l.rand[0], + nfcv_data->sub_data.slix_l.rand[1]); + break; + } + + case ISO15693_CMD_NXP_SET_PASSWORD: + { + 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 *password = NULL; + uint8_t password_rcv[4]; + + 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[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(pass_expect == pass_received) { + nfcv_data->sub_data.slix_l.privacy = false; + 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 { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X 0x%08lX/%08lX FAIL", password_id, pass_received, pass_expect); + } + 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->sub_data.slix_l.privacy = true; + break; + } + + default: + 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) { + 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); + + 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"); + + /* allocate a 512 edge buffer, more than enough */ + reader_signal = pulse_reader_alloc(&gpio_spi_r_miso, 512); + /* 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_NS); + 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 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 wait_for_pulse = false; + + while(true) { + + uint32_t periods = pulse_reader_receive(reader_signal, timeout_ms * 1000); + + 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) { + frame_state = NFCV_FRAME_STATE_SOF1; + + FURI_LOG_D(TAG, "Resetting state machine, reason: '%s'", reset_reason); + } else if(frame_state == NFCV_FRAME_STATE_EOF) { + break; + } + } + + if(frame_state == NFCV_FRAME_STATE_EOF) { + /* 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; + } + + return ret; +} diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h new file mode 100644 index 000000000..6253fa1a6 --- /dev/null +++ b/lib/nfc/protocols/nfcv.h @@ -0,0 +1,168 @@ +#pragma once + +#include +#include + +#include +#include +#include "nfc_util.h" +#include + + + + +#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_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, +} NfcVAuthMethod; + +typedef enum { + NfcVTypePlain = 0, + NfcVTypeSlix = 1, + NfcVTypeSlixS = 2, + NfcVTypeSlixL = 3, + NfcVTypeSlix2 = 4, +} NfcVType; + +typedef struct { + 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; + uint16_t block_num; + uint8_t block_size; + uint8_t data[NFCV_MAX_DUMP_SIZE]; + + /* 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 { + uint16_t blocks_to_read; + int16_t blocks_read; +} NfcVReader; + +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(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 new file mode 100644 index 000000000..ca3ebcd67 --- /dev/null +++ b/lib/nfc/protocols/slix.c @@ -0,0 +1,125 @@ + +#include +#include "nfcv.h" +#include "slix.h" +#include "nfc_util.h" +#include +#include "furi_hal_nfc.h" + + +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; +} + + +ReturnCode slix_l_get_random(NfcVData* data) { + 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(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(NfcVData* data, uint32_t password_id) { + furi_assert(rand); + + uint16_t received = 0; + uint8_t rxBuf[32]; + uint8_t cmd_set_pass[] = { + 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, + 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.h b/lib/nfc/protocols/slix.h new file mode 100644 index 000000000..b95a27abd --- /dev/null +++ b/lib/nfc/protocols/slix.h @@ -0,0 +1,19 @@ +#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_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(NfcVData* data); +ReturnCode slix_l_unlock(NfcVData* data, uint32_t password_id); + diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c new file mode 100644 index 000000000..f829bf831 --- /dev/null +++ b/lib/pulse_reader/pulse_reader.c @@ -0,0 +1,205 @@ +#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->gpio_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; + + 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; +} + +void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit 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) { + signal->bit_time = bit_time; +} + +void pulse_reader_free(PulseReader* signal) { + free(signal->timer_buffer); + free(signal->gpio_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_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) { + /* configure DMA to read from a timer peripheral */ + 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_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); + + /* 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; + signal->gpio_mask = signal->gpio->pin; + + /* now set up DMA with these settings */ + 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) { + + uint32_t start_time = DWT->CYCCNT; + uint32_t timeout_ticks = timeout_us * (F_TIM2/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; + 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; + + uint32_t delta_unit = 0; + + /* 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 */ + 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..886c9d9e7 --- /dev/null +++ b/lib/pulse_reader/pulse_reader.h @@ -0,0 +1,140 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PULSE_READER_NO_EDGE 0xFFFFFFFFUL +#define PULSE_READER_LOST_EDGE 0xFFFFFFFEUL +#define F_TIM2 64000000UL + +/** + * unit of the edge durations to return + */ +typedef enum { + PulseReaderUnit64MHz, + PulseReaderUnitPicosecond, + PulseReaderUnitNanosecond, + PulseReaderUnitMicrosecond, +} PulseReaderUnit; + + +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; + + +/** 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