NFC: Implement Type 4 Tag reading

This commit is contained in:
Willy-JL
2025-03-05 08:10:03 +00:00
parent 0d99e54a17
commit 358631ec86
21 changed files with 1157 additions and 3 deletions

View File

@@ -21,6 +21,7 @@
#include "mf_desfire/mf_desfire.h"
#include "slix/slix.h"
#include "st25tb/st25tb.h"
#include "type_4_tag/type_4_tag.h"
/**
* @brief Array of pointers to concrete protocol support implementations.
@@ -43,5 +44,6 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = {
[NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire,
[NfcProtocolSlix] = &nfc_protocol_support_slix,
[NfcProtocolSt25tb] = &nfc_protocol_support_st25tb,
[NfcProtocolType4Tag] = &nfc_protocol_support_type_4_tag,
/* Add new protocol support implementations here */
};

View File

@@ -0,0 +1,220 @@
#include "type_4_tag.h"
#include "type_4_tag_render.h"
#include <nfc/protocols/type_4_tag/type_4_tag_poller.h>
#include <toolbox/pretty_format.h>
#include "nfc/nfc_app_i.h"
#include "../nfc_protocol_support_common.h"
#include "../nfc_protocol_support_gui_common.h"
#include "../iso14443_4a/iso14443_4a_i.h"
enum {
SubmenuIndexWrite = SubmenuIndexCommonMax,
};
enum {
NfcSceneMoreInfoStateASCII,
NfcSceneMoreInfoStateRawData,
};
static void nfc_scene_info_on_enter_type_4_tag(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag);
FuriString* temp_str = furi_string_alloc();
nfc_append_filename_string_when_present(instance, temp_str);
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_type_4_tag_info(data, NfcProtocolFormatTypeFull, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
static void nfc_scene_more_info_on_enter_type_4_tag(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag);
furi_string_reset(instance->text_box_store);
uint32_t scene_state =
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMoreInfo);
if(scene_state == NfcSceneMoreInfoStateASCII) {
pretty_format_bytes_hex_canonical(
instance->text_box_store,
TYPE_4_TAG_RENDER_BYTES_PER_LINE,
PRETTY_FORMAT_FONT_MONOSPACE,
simple_array_cget_data(data->ndef_data),
simple_array_get_count(data->ndef_data));
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store));
widget_add_button_element(
instance->widget,
GuiButtonTypeRight,
"Raw Data",
nfc_protocol_support_common_widget_callback,
instance);
widget_add_button_element(
instance->widget,
GuiButtonTypeLeft,
"Info",
nfc_protocol_support_common_widget_callback,
instance);
} else if(scene_state == NfcSceneMoreInfoStateRawData) {
nfc_render_type_4_tag_dump(data, instance->text_box_store);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store));
widget_add_button_element(
instance->widget,
GuiButtonTypeLeft,
"ASCII",
nfc_protocol_support_common_widget_callback,
instance);
}
}
static bool nfc_scene_more_info_on_event_type_4_tag(NfcApp* instance, SceneManagerEvent event) {
bool consumed = false;
if((event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeLeft) ||
(event.type == SceneManagerEventTypeBack)) {
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateASCII);
scene_manager_previous_scene(instance->scene_manager);
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) {
scene_manager_set_scene_state(
instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateRawData);
scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo);
consumed = true;
}
return consumed;
}
static NfcCommand nfc_scene_read_poller_callback_type_4_tag(NfcGenericEvent event, void* context) {
furi_assert(event.protocol == NfcProtocolType4Tag);
NfcCommand command = NfcCommandContinue;
NfcApp* instance = context;
const Type4TagPollerEvent* type_4_tag_event = event.event_data;
if(type_4_tag_event->type == Type4TagPollerEventTypeReadSuccess) {
nfc_device_set_data(
instance->nfc_device, NfcProtocolType4Tag, nfc_poller_get_data(instance->poller));
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
command = NfcCommandStop;
} else if(type_4_tag_event->type == Type4TagPollerEventTypeReadFailed) {
command = NfcCommandReset;
}
return command;
}
static void nfc_scene_read_on_enter_type_4_tag(NfcApp* instance) {
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_type_4_tag, instance);
}
static void nfc_scene_read_and_saved_menu_on_enter_type_4_tag(NfcApp* instance) {
Submenu* submenu = instance->submenu;
submenu_add_item(
submenu,
"Write (Not Implemented)",
SubmenuIndexWrite,
nfc_protocol_support_common_submenu_callback,
instance);
}
static bool
nfc_scene_read_and_saved_menu_on_event_type_4_tag(NfcApp* instance, SceneManagerEvent event) {
bool consumed = false;
UNUSED(instance);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexWrite) {
// TODO: Implement write
// scene_manager_next_scene(instance->scene_manager, NfcSceneType4TagWrite);
consumed = true;
}
}
return consumed;
}
static void nfc_scene_read_success_on_enter_type_4_tag(NfcApp* instance) {
const NfcDevice* device = instance->nfc_device;
const Type4TagData* data = nfc_device_get_data(device, NfcProtocolType4Tag);
FuriString* temp_str = furi_string_alloc();
furi_string_cat_printf(
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
nfc_render_type_4_tag_info(data, NfcProtocolFormatTypeShort, temp_str);
widget_add_text_scroll_element(
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
}
static void nfc_scene_emulate_on_enter_type_4_tag(NfcApp* instance) {
// TODO: Implement full emulation
const Iso14443_4aData* iso14443_4a_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);
instance->listener =
nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data);
nfc_listener_start(
instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
}
const NfcProtocolSupportBase nfc_protocol_support_type_4_tag = {
.features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo,
.scene_info =
{
.on_enter = nfc_scene_info_on_enter_type_4_tag,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_more_info =
{
.on_enter = nfc_scene_more_info_on_enter_type_4_tag,
.on_event = nfc_scene_more_info_on_event_type_4_tag,
},
.scene_read =
{
.on_enter = nfc_scene_read_on_enter_type_4_tag,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_read_menu =
{
.on_enter = nfc_scene_read_and_saved_menu_on_enter_type_4_tag,
.on_event = nfc_scene_read_and_saved_menu_on_event_type_4_tag,
},
.scene_read_success =
{
.on_enter = nfc_scene_read_success_on_enter_type_4_tag,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_saved_menu =
{
.on_enter = nfc_scene_read_and_saved_menu_on_enter_type_4_tag,
.on_event = nfc_scene_read_and_saved_menu_on_event_type_4_tag,
},
.scene_save_name =
{
.on_enter = nfc_protocol_support_common_on_enter_empty,
.on_event = nfc_protocol_support_common_on_event_empty,
},
.scene_emulate =
{
.on_enter = nfc_scene_emulate_on_enter_type_4_tag,
.on_event = nfc_protocol_support_common_on_event_empty,
},
};

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../nfc_protocol_support_base.h"
extern const NfcProtocolSupportBase nfc_protocol_support_type_4_tag;

View File

@@ -0,0 +1,50 @@
#include "type_4_tag_render.h"
#include "../iso14443_4a/iso14443_4a_render.h"
#define TYPE_4_TAG_RENDER_MAX_RECORD_SIZE (256U)
void nfc_render_type_4_tag_info(
const Type4TagData* data,
NfcProtocolFormatType format_type,
FuriString* str) {
nfc_render_iso14443_4a_brief(type_4_tag_get_base_data(data), str);
furi_string_cat(str, "\n::::::::::::::::::[Tag Specs]::::::::::::::::::\n");
furi_string_cat_printf(
str, "T4T Mapping Version: %u.%u\n", data->t4t_version.major, data->t4t_version.minor);
furi_string_cat_printf(str, "NDEF File ID: 0x%04X\n", data->ndef_file_id);
furi_string_cat_printf(str, "Max NDEF Size: %u\n", data->ndef_max_len);
furi_string_cat_printf(
str, "APDU Sizes: R:%u W:%u\n", data->chunk_max_read, data->chunk_max_write);
furi_string_cat_printf(
str,
"Read Lock: 0x%02X%s\n",
data->ndef_read_lock,
data->ndef_read_lock == 0 ? " (unlocked)" : "");
furi_string_cat_printf(
str,
"Write Lock: 0x%02X%s\n",
data->ndef_write_lock,
data->ndef_write_lock == 0 ? " (unlocked)" : "");
furi_string_cat(str, ":::::::::::::::[Stored NDEF]:::::::::::::::\n");
furi_string_cat_printf(str, "Current NDEF Size: %lu", simple_array_get_count(data->ndef_data));
if(format_type != NfcProtocolFormatTypeFull) return;
furi_string_cat(str, "\n\e#ISO14443-4 data");
nfc_render_iso14443_4a_extra(type_4_tag_get_base_data(data), str);
}
void nfc_render_type_4_tag_dump(const Type4TagData* data, FuriString* str) {
furi_string_cat_printf(str, "\e*");
const uint8_t* ndef_data = simple_array_cget_data(data->ndef_data);
size_t ndef_len = simple_array_get_count(data->ndef_data);
for(size_t i = 0; i < ndef_len; i += TYPE_4_TAG_RENDER_BYTES_PER_LINE) {
const uint8_t* line_data = &ndef_data[i];
for(size_t j = 0; j < TYPE_4_TAG_RENDER_BYTES_PER_LINE; j += 2) {
furi_string_cat_printf(str, " %02X%02X", line_data[j], line_data[j + 1]);
}
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <nfc/protocols/type_4_tag/type_4_tag.h>
#include "../nfc_protocol_support_render_common.h"
#define TYPE_4_TAG_RENDER_BYTES_PER_LINE (4)
void nfc_render_type_4_tag_info(
const Type4TagData* data,
NfcProtocolFormatType format_type,
FuriString* str);
void nfc_render_type_4_tag_dump(const Type4TagData* data, FuriString* str);