mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-16 00:18:11 -07:00
NFC: Implement Type 4 Tag reading
This commit is contained in:
@@ -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 */
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_protocol_support_base.h"
|
||||
|
||||
extern const NfcProtocolSupportBase nfc_protocol_support_type_4_tag;
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -26,6 +26,7 @@ env.Append(
|
||||
File("protocols/slix/slix.h"),
|
||||
File("protocols/st25tb/st25tb.h"),
|
||||
File("protocols/felica/felica.h"),
|
||||
File("protocols/type_4_tag/type_4_tag.h"),
|
||||
# Pollers
|
||||
File("protocols/iso14443_3a/iso14443_3a_poller.h"),
|
||||
File("protocols/iso14443_3b/iso14443_3b_poller.h"),
|
||||
@@ -38,6 +39,7 @@ env.Append(
|
||||
File("protocols/slix/slix_poller.h"),
|
||||
File("protocols/st25tb/st25tb_poller.h"),
|
||||
File("protocols/felica/felica_poller.h"),
|
||||
File("protocols/type_4_tag/type_4_tag_poller.h"),
|
||||
# Listeners
|
||||
File("protocols/iso14443_3a/iso14443_3a_listener.h"),
|
||||
File("protocols/iso14443_4a/iso14443_4a_listener.h"),
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <nfc/protocols/mf_desfire/mf_desfire.h>
|
||||
#include <nfc/protocols/slix/slix_device_defs.h>
|
||||
#include <nfc/protocols/st25tb/st25tb.h>
|
||||
#include <nfc/protocols/type_4_tag/type_4_tag.h>
|
||||
|
||||
/**
|
||||
* @brief List of registered NFC device implementations.
|
||||
@@ -44,5 +45,6 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = {
|
||||
[NfcProtocolMfDesfire] = &nfc_device_mf_desfire,
|
||||
[NfcProtocolSlix] = &nfc_device_slix,
|
||||
[NfcProtocolSt25tb] = &nfc_device_st25tb,
|
||||
[NfcProtocolType4Tag] = &nfc_device_type_4_tag,
|
||||
/* Add new protocols here */
|
||||
};
|
||||
|
||||
@@ -14,10 +14,11 @@ const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = {
|
||||
[NfcProtocolIso14443_4a] = &nfc_listener_iso14443_4a,
|
||||
[NfcProtocolIso14443_4b] = NULL,
|
||||
[NfcProtocolIso15693_3] = &nfc_listener_iso15693_3,
|
||||
[NfcProtocolFelica] = &nfc_listener_felica,
|
||||
[NfcProtocolMfUltralight] = &mf_ultralight_listener,
|
||||
[NfcProtocolMfClassic] = &mf_classic_listener,
|
||||
[NfcProtocolMfDesfire] = NULL,
|
||||
[NfcProtocolSlix] = &nfc_listener_slix,
|
||||
[NfcProtocolSt25tb] = NULL,
|
||||
[NfcProtocolFelica] = &nfc_listener_felica,
|
||||
[NfcProtocolType4Tag] = NULL,
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <nfc/protocols/mf_desfire/mf_desfire_poller_defs.h>
|
||||
#include <nfc/protocols/slix/slix_poller_defs.h>
|
||||
#include <nfc/protocols/st25tb/st25tb_poller_defs.h>
|
||||
#include <nfc/protocols/type_4_tag/type_4_tag_poller_defs.h>
|
||||
|
||||
const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = {
|
||||
[NfcProtocolIso14443_3a] = &nfc_poller_iso14443_3a,
|
||||
@@ -25,6 +26,7 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = {
|
||||
[NfcProtocolMfPlus] = &mf_plus_poller,
|
||||
[NfcProtocolMfDesfire] = &mf_desfire_poller,
|
||||
[NfcProtocolSlix] = &nfc_poller_slix,
|
||||
/* Add new pollers here */
|
||||
[NfcProtocolSt25tb] = &nfc_poller_st25tb,
|
||||
[NfcProtocolType4Tag] = &type_4_tag_poller,
|
||||
/* Add new pollers here */
|
||||
};
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
* | | |
|
||||
* ISO14443-4A Mf Ultralight Mf Classic
|
||||
* |
|
||||
* Mf Desfire
|
||||
* +-----+------+----------+
|
||||
* | | |
|
||||
* Mf Desfire Type 4 Tag Mf Plus
|
||||
* ```
|
||||
*
|
||||
* When implementing a new protocol, its place in the tree must be determined first.
|
||||
@@ -62,6 +64,7 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = {
|
||||
static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = {
|
||||
NfcProtocolMfDesfire,
|
||||
NfcProtocolMfPlus,
|
||||
NfcProtocolType4Tag,
|
||||
};
|
||||
|
||||
/** List of ISO115693-3 child protocols. */
|
||||
@@ -153,6 +156,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = {
|
||||
.children_num = 0,
|
||||
.children_protocol = NULL,
|
||||
},
|
||||
[NfcProtocolType4Tag] =
|
||||
{
|
||||
.parent_protocol = NfcProtocolIso14443_4a,
|
||||
.children_num = 0,
|
||||
.children_protocol = NULL,
|
||||
},
|
||||
/* Add new protocols here */
|
||||
};
|
||||
|
||||
|
||||
@@ -188,6 +188,7 @@ typedef enum {
|
||||
NfcProtocolMfDesfire,
|
||||
NfcProtocolSlix,
|
||||
NfcProtocolSt25tb,
|
||||
NfcProtocolType4Tag,
|
||||
/* Add new protocols here */
|
||||
|
||||
NfcProtocolNum, /**< Special value representing the number of available protocols. */
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "type_4_tag_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TYPE_4_TAG_PROTOCOL_NAME "Type 4 Tag"
|
||||
|
||||
const NfcDeviceBase nfc_device_type_4_tag = {
|
||||
.protocol_name = TYPE_4_TAG_PROTOCOL_NAME,
|
||||
.alloc = (NfcDeviceAlloc)type_4_tag_alloc,
|
||||
.free = (NfcDeviceFree)type_4_tag_free,
|
||||
.reset = (NfcDeviceReset)type_4_tag_reset,
|
||||
.copy = (NfcDeviceCopy)type_4_tag_copy,
|
||||
.verify = (NfcDeviceVerify)type_4_tag_verify,
|
||||
.load = (NfcDeviceLoad)type_4_tag_load,
|
||||
.save = (NfcDeviceSave)type_4_tag_save,
|
||||
.is_equal = (NfcDeviceEqual)type_4_tag_is_equal,
|
||||
.get_name = (NfcDeviceGetName)type_4_tag_get_device_name,
|
||||
.get_uid = (NfcDeviceGetUid)type_4_tag_get_uid,
|
||||
.set_uid = (NfcDeviceSetUid)type_4_tag_set_uid,
|
||||
.get_base_data = (NfcDeviceGetBaseData)type_4_tag_get_base_data,
|
||||
};
|
||||
|
||||
Type4TagData* type_4_tag_alloc(void) {
|
||||
Type4TagData* data = malloc(sizeof(Type4TagData));
|
||||
data->iso14443_4a_data = iso14443_4a_alloc();
|
||||
data->ndef_data = simple_array_alloc(&simple_array_config_uint8_t);
|
||||
return data;
|
||||
}
|
||||
|
||||
void type_4_tag_free(Type4TagData* data) {
|
||||
furi_check(data);
|
||||
|
||||
type_4_tag_reset(data);
|
||||
simple_array_free(data->ndef_data);
|
||||
iso14443_4a_free(data->iso14443_4a_data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void type_4_tag_reset(Type4TagData* data) {
|
||||
furi_check(data);
|
||||
|
||||
iso14443_4a_reset(data->iso14443_4a_data);
|
||||
|
||||
data->t4t_version.value = 0;
|
||||
data->chunk_max_read = 0;
|
||||
data->chunk_max_write = 0;
|
||||
data->ndef_file_id = 0;
|
||||
data->ndef_max_len = 0;
|
||||
data->ndef_read_lock = 0;
|
||||
data->ndef_write_lock = 0;
|
||||
|
||||
simple_array_reset(data->ndef_data);
|
||||
}
|
||||
|
||||
void type_4_tag_copy(Type4TagData* data, const Type4TagData* other) {
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
type_4_tag_reset(data);
|
||||
|
||||
iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data);
|
||||
|
||||
data->t4t_version.value = other->t4t_version.value;
|
||||
data->chunk_max_read = other->chunk_max_read;
|
||||
data->chunk_max_write = other->chunk_max_write;
|
||||
data->ndef_file_id = other->ndef_file_id;
|
||||
data->ndef_max_len = other->ndef_max_len;
|
||||
data->ndef_read_lock = other->ndef_read_lock;
|
||||
data->ndef_write_lock = other->ndef_write_lock;
|
||||
|
||||
simple_array_copy(data->ndef_data, other->ndef_data);
|
||||
}
|
||||
|
||||
bool type_4_tag_verify(Type4TagData* data, const FuriString* device_type) {
|
||||
UNUSED(data);
|
||||
UNUSED(device_type);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool type_4_tag_load(Type4TagData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
FuriString* prefix = furi_string_alloc();
|
||||
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break;
|
||||
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(prefix);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool type_4_tag_save(const Type4TagData* data, FlipperFormat* ff) {
|
||||
furi_check(data);
|
||||
furi_check(ff);
|
||||
|
||||
FuriString* prefix = furi_string_alloc();
|
||||
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break;
|
||||
|
||||
if(!flipper_format_write_comment_cstr(ff, TYPE_4_TAG_PROTOCOL_NAME " specific data"))
|
||||
break;
|
||||
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(prefix);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool type_4_tag_is_equal(const Type4TagData* data, const Type4TagData* other) {
|
||||
furi_check(data);
|
||||
furi_check(other);
|
||||
|
||||
return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) &&
|
||||
data->t4t_version.value == other->t4t_version.value &&
|
||||
data->chunk_max_read == other->chunk_max_read &&
|
||||
data->chunk_max_write == other->chunk_max_write &&
|
||||
data->ndef_file_id == other->ndef_file_id &&
|
||||
data->ndef_max_len == other->ndef_max_len &&
|
||||
data->ndef_read_lock == other->ndef_read_lock &&
|
||||
data->ndef_write_lock == other->ndef_write_lock &&
|
||||
simple_array_is_equal(data->ndef_data, other->ndef_data);
|
||||
}
|
||||
|
||||
const char* type_4_tag_get_device_name(const Type4TagData* data, NfcDeviceNameType name_type) {
|
||||
UNUSED(data);
|
||||
UNUSED(name_type);
|
||||
return TYPE_4_TAG_PROTOCOL_NAME;
|
||||
}
|
||||
|
||||
const uint8_t* type_4_tag_get_uid(const Type4TagData* data, size_t* uid_len) {
|
||||
furi_check(data);
|
||||
furi_check(uid_len);
|
||||
|
||||
return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len);
|
||||
}
|
||||
|
||||
bool type_4_tag_set_uid(Type4TagData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_check(data);
|
||||
|
||||
return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len);
|
||||
}
|
||||
|
||||
Iso14443_4aData* type_4_tag_get_base_data(const Type4TagData* data) {
|
||||
furi_check(data);
|
||||
|
||||
return data->iso14443_4a_data;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>
|
||||
|
||||
#include <lib/toolbox/simple_array.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
Type4TagErrorNone,
|
||||
Type4TagErrorNotPresent,
|
||||
Type4TagErrorProtocol,
|
||||
Type4TagErrorTimeout,
|
||||
} Type4TagError;
|
||||
|
||||
typedef struct {
|
||||
Iso14443_4aData* iso14443_4a_data;
|
||||
// Tag specific
|
||||
union {
|
||||
struct {
|
||||
uint8_t minor : 4;
|
||||
uint8_t major : 4;
|
||||
};
|
||||
uint8_t value;
|
||||
} t4t_version;
|
||||
uint16_t chunk_max_read;
|
||||
uint16_t chunk_max_write;
|
||||
uint16_t ndef_file_id;
|
||||
uint16_t ndef_max_len;
|
||||
uint8_t ndef_read_lock;
|
||||
uint8_t ndef_write_lock;
|
||||
// Data contained, not tag specific
|
||||
SimpleArray* ndef_data;
|
||||
} Type4TagData;
|
||||
|
||||
extern const NfcDeviceBase nfc_device_type_4_tag;
|
||||
|
||||
// Virtual methods
|
||||
|
||||
Type4TagData* type_4_tag_alloc(void);
|
||||
|
||||
void type_4_tag_free(Type4TagData* data);
|
||||
|
||||
void type_4_tag_reset(Type4TagData* data);
|
||||
|
||||
void type_4_tag_copy(Type4TagData* data, const Type4TagData* other);
|
||||
|
||||
bool type_4_tag_verify(Type4TagData* data, const FuriString* device_type);
|
||||
|
||||
bool type_4_tag_load(Type4TagData* data, FlipperFormat* ff, uint32_t version);
|
||||
|
||||
bool type_4_tag_save(const Type4TagData* data, FlipperFormat* ff);
|
||||
|
||||
bool type_4_tag_is_equal(const Type4TagData* data, const Type4TagData* other);
|
||||
|
||||
const char* type_4_tag_get_device_name(const Type4TagData* data, NfcDeviceNameType name_type);
|
||||
|
||||
const uint8_t* type_4_tag_get_uid(const Type4TagData* data, size_t* uid_len);
|
||||
|
||||
bool type_4_tag_set_uid(Type4TagData* data, const uint8_t* uid, size_t uid_len);
|
||||
|
||||
Iso14443_4aData* type_4_tag_get_base_data(const Type4TagData* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "type_4_tag_i.h"
|
||||
|
||||
#define TAG "Type4Tag"
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "type_4_tag.h"
|
||||
|
||||
#define TYPE_4_TAG_ISO_SELECT_CMD 0x00, 0xA4
|
||||
#define TYPE_4_TAG_ISO_SELECT_P1_BY_NAME (0x04)
|
||||
#define TYPE_4_TAG_ISO_SELECT_P1_BY_ID (0x00)
|
||||
#define TYPE_4_TAG_ISO_SELECT_P2_EMPTY (0x0C)
|
||||
#define TYPE_4_TAG_ISO_SELECT_LE_EMPTY (0x00)
|
||||
|
||||
#define TYPE_4_TAG_ISO_READ_CMD 0x00, 0xB0
|
||||
#define TYPE_4_TAG_ISO_READ_P1_EMPTY (0x00)
|
||||
#define TYPE_4_TAG_ISO_READ_P2_BEGINNING (0x00)
|
||||
#define TYPE_4_TAG_ISO_READ_LE_FULL (0x00)
|
||||
|
||||
#define TYPE_4_TAG_ISO_STATUS_LEN (2)
|
||||
#define TYPE_4_TAG_ISO_STATUS_SUCCESS 0x90, 0x00
|
||||
#define TYPE_4_TAG_ISO_RW_CHUNK_LEN (255)
|
||||
#define TYPE_4_TAG_ISO_APP_NAME_LEN (7)
|
||||
#define TYPE_4_TAG_ISO_APP_NAME 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01
|
||||
#define TYPE_4_TAG_T4T_CC_FILE_ID_LEN (2)
|
||||
#define TYPE_4_TAG_T4T_CC_FILE_ID 0xE1, 0x03
|
||||
|
||||
#define TYPE_4_TAG_T4T_CC_VNO 0x20
|
||||
|
||||
typedef enum FURI_PACKED {
|
||||
Type4TagCcTlvTypeNdefFileCtrl = 0x04,
|
||||
Type4TagCcTlvTypeProprietaryFileCtrl = 0x05,
|
||||
} Type4TagCcTlvType;
|
||||
|
||||
typedef struct FURI_PACKED {
|
||||
uint16_t file_id;
|
||||
uint16_t max_len;
|
||||
uint8_t read_perm;
|
||||
uint8_t write_perm;
|
||||
} Type4TagCcTlvNdefFileCtrl;
|
||||
|
||||
typedef union FURI_PACKED {
|
||||
Type4TagCcTlvNdefFileCtrl ndef_file_ctrl;
|
||||
} Type4TagCcTlvValue;
|
||||
|
||||
typedef struct FURI_PACKED {
|
||||
Type4TagCcTlvType type;
|
||||
uint8_t len;
|
||||
Type4TagCcTlvValue value;
|
||||
} Type4TagCcTlv;
|
||||
|
||||
typedef struct FURI_PACKED {
|
||||
uint16_t len;
|
||||
uint8_t t4t_vno;
|
||||
uint16_t mle;
|
||||
uint16_t mlc;
|
||||
Type4TagCcTlv tlv[];
|
||||
} Type4TagCc;
|
||||
@@ -0,0 +1,187 @@
|
||||
#include "type_4_tag_poller_i.h"
|
||||
#include "type_4_tag_i.h"
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "Type4TagPoller"
|
||||
|
||||
// Read returns 2 byte status trailer, write sends 5 byte command header
|
||||
#define TYPE_4_TAG_BUF_SIZE (TYPE_4_TAG_ISO_RW_CHUNK_LEN + 5)
|
||||
|
||||
typedef NfcCommand (*Type4TagPollerReadHandler)(Type4TagPoller* instance);
|
||||
|
||||
const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->data;
|
||||
}
|
||||
|
||||
static Type4TagPoller* type_4_tag_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) {
|
||||
Type4TagPoller* instance = malloc(sizeof(Type4TagPoller));
|
||||
instance->iso14443_4a_poller = iso14443_4a_poller;
|
||||
instance->data = type_4_tag_alloc();
|
||||
instance->tx_buffer = bit_buffer_alloc(TYPE_4_TAG_BUF_SIZE);
|
||||
instance->rx_buffer = bit_buffer_alloc(TYPE_4_TAG_BUF_SIZE);
|
||||
|
||||
instance->type_4_tag_event.data = &instance->type_4_tag_event_data;
|
||||
|
||||
instance->general_event.protocol = NfcProtocolType4Tag;
|
||||
instance->general_event.event_data = &instance->type_4_tag_event;
|
||||
instance->general_event.instance = instance;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void type_4_tag_poller_free(Type4TagPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
type_4_tag_free(instance->data);
|
||||
bit_buffer_free(instance->tx_buffer);
|
||||
bit_buffer_free(instance->rx_buffer);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static NfcCommand type_4_tag_poller_handler_idle(Type4TagPoller* instance) {
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
iso14443_4a_copy(
|
||||
instance->data->iso14443_4a_data,
|
||||
iso14443_4a_poller_get_data(instance->iso14443_4a_poller));
|
||||
|
||||
instance->state = Type4TagPollerStateSelectApplication;
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand type_4_tag_poller_handler_select_app(Type4TagPoller* instance) {
|
||||
instance->error = type_4_tag_poller_select_app(instance);
|
||||
if(instance->error == Type4TagErrorNone) {
|
||||
FURI_LOG_D(TAG, "Select application success");
|
||||
instance->state = Type4TagPollerStateReadCapabilityContainer;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to select application");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
instance->state = Type4TagPollerStateReadFailed;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand type_4_tag_poller_handler_read_cc(Type4TagPoller* instance) {
|
||||
instance->error = type_4_tag_poller_read_cc(instance);
|
||||
if(instance->error == Type4TagErrorNone) {
|
||||
FURI_LOG_D(TAG, "Read CC success");
|
||||
instance->state = Type4TagPollerStateReadNdefMessage;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to read CC");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
instance->state = Type4TagPollerStateReadFailed;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand type_4_tag_poller_handler_read_ndef(Type4TagPoller* instance) {
|
||||
instance->error = type_4_tag_poller_read_ndef(instance);
|
||||
if(instance->error == Type4TagErrorNone) {
|
||||
FURI_LOG_D(TAG, "Read NDEF success");
|
||||
instance->state = Type4TagPollerStateReadSuccess;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to read NDEF");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
instance->state = Type4TagPollerStateReadFailed;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand type_4_tag_poller_handler_read_fail(Type4TagPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Read Failed");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
instance->type_4_tag_event.data->error = instance->error;
|
||||
NfcCommand command = instance->callback(instance->general_event, instance->context);
|
||||
instance->state = Type4TagPollerStateIdle;
|
||||
return command;
|
||||
}
|
||||
|
||||
static NfcCommand type_4_tag_poller_handler_read_success(Type4TagPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Read success.");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
instance->type_4_tag_event.type = Type4TagPollerEventTypeReadSuccess;
|
||||
NfcCommand command = instance->callback(instance->general_event, instance->context);
|
||||
return command;
|
||||
}
|
||||
|
||||
static const Type4TagPollerReadHandler type_4_tag_poller_read_handler[Type4TagPollerStateNum] = {
|
||||
[Type4TagPollerStateIdle] = type_4_tag_poller_handler_idle,
|
||||
[Type4TagPollerStateSelectApplication] = type_4_tag_poller_handler_select_app,
|
||||
[Type4TagPollerStateReadCapabilityContainer] = type_4_tag_poller_handler_read_cc,
|
||||
[Type4TagPollerStateReadNdefMessage] = type_4_tag_poller_handler_read_ndef,
|
||||
[Type4TagPollerStateReadFailed] = type_4_tag_poller_handler_read_fail,
|
||||
[Type4TagPollerStateReadSuccess] = type_4_tag_poller_handler_read_success,
|
||||
};
|
||||
|
||||
static void type_4_tag_poller_set_callback(
|
||||
Type4TagPoller* instance,
|
||||
NfcGenericCallback callback,
|
||||
void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
static NfcCommand type_4_tag_poller_run(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolIso14443_4a);
|
||||
|
||||
Type4TagPoller* instance = context;
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->callback);
|
||||
|
||||
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
|
||||
furi_assert(iso14443_4a_event);
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
|
||||
command = type_4_tag_poller_read_handler[instance->state](instance);
|
||||
} else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {
|
||||
instance->type_4_tag_event.type = Type4TagPollerEventTypeReadFailed;
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static bool type_4_tag_poller_detect(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolIso14443_4a);
|
||||
|
||||
Type4TagPoller* instance = context;
|
||||
furi_assert(instance);
|
||||
|
||||
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
|
||||
furi_assert(iso14443_4a_event);
|
||||
|
||||
bool protocol_detected = false;
|
||||
|
||||
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
|
||||
Type4TagError error = type_4_tag_poller_select_app(instance);
|
||||
if(error == Type4TagErrorNone) {
|
||||
protocol_detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
return protocol_detected;
|
||||
}
|
||||
|
||||
const NfcPollerBase type_4_tag_poller = {
|
||||
.alloc = (NfcPollerAlloc)type_4_tag_poller_alloc,
|
||||
.free = (NfcPollerFree)type_4_tag_poller_free,
|
||||
.set_callback = (NfcPollerSetCallback)type_4_tag_poller_set_callback,
|
||||
.run = (NfcPollerRun)type_4_tag_poller_run,
|
||||
.detect = (NfcPollerDetect)type_4_tag_poller_detect,
|
||||
.get_data = (NfcPollerGetData)type_4_tag_poller_get_data,
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "type_4_tag.h"
|
||||
|
||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type4TagPoller opaque type definition.
|
||||
*/
|
||||
typedef struct Type4TagPoller Type4TagPoller;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of possible Type4Tag poller event types.
|
||||
*/
|
||||
typedef enum {
|
||||
Type4TagPollerEventTypeReadSuccess, /**< Card was read successfully. */
|
||||
Type4TagPollerEventTypeReadFailed, /**< Poller failed to read card. */
|
||||
} Type4TagPollerEventType;
|
||||
|
||||
/**
|
||||
* @brief Type4Tag poller event data.
|
||||
*/
|
||||
typedef union {
|
||||
Type4TagError error; /**< Error code indicating card reading fail reason. */
|
||||
} Type4TagPollerEventData;
|
||||
|
||||
/**
|
||||
* @brief Type4Tag poller event structure.
|
||||
*
|
||||
* Upon emission of an event, an instance of this struct will be passed to the callback.
|
||||
*/
|
||||
typedef struct {
|
||||
Type4TagPollerEventType type; /**< Type of emmitted event. */
|
||||
Type4TagPollerEventData* data; /**< Pointer to event specific data. */
|
||||
} Type4TagPollerEvent;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
extern const NfcPollerBase type_4_tag_poller;
|
||||
@@ -0,0 +1,265 @@
|
||||
#include "type_4_tag_poller_i.h"
|
||||
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include <furi.h>
|
||||
|
||||
#include "type_4_tag_i.h"
|
||||
|
||||
#define TAG "Type4TagPoller"
|
||||
|
||||
Type4TagError type_4_tag_process_error(Iso14443_4aError error) {
|
||||
switch(error) {
|
||||
case Iso14443_4aErrorNone:
|
||||
return Type4TagErrorNone;
|
||||
case Iso14443_4aErrorNotPresent:
|
||||
return Type4TagErrorNotPresent;
|
||||
case Iso14443_4aErrorTimeout:
|
||||
return Type4TagErrorTimeout;
|
||||
default:
|
||||
return Type4TagErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
Type4TagError type_4_tag_apdu_trx(Type4TagPoller* instance, BitBuffer* tx_buf, BitBuffer* rx_buf) {
|
||||
furi_check(instance);
|
||||
|
||||
bit_buffer_reset(rx_buf);
|
||||
|
||||
Iso14443_4aError iso14443_4a_error =
|
||||
iso14443_4a_poller_send_block(instance->iso14443_4a_poller, tx_buf, rx_buf);
|
||||
|
||||
bit_buffer_reset(tx_buf);
|
||||
|
||||
if(iso14443_4a_error != Iso14443_4aErrorNone) {
|
||||
return type_4_tag_process_error(iso14443_4a_error);
|
||||
}
|
||||
|
||||
size_t response_len = bit_buffer_get_size_bytes(rx_buf);
|
||||
if(response_len < TYPE_4_TAG_ISO_STATUS_LEN) {
|
||||
return Type4TagErrorProtocol;
|
||||
}
|
||||
|
||||
const uint8_t success[TYPE_4_TAG_ISO_STATUS_LEN] = {TYPE_4_TAG_ISO_STATUS_SUCCESS};
|
||||
uint8_t status[TYPE_4_TAG_ISO_STATUS_LEN] = {
|
||||
bit_buffer_get_byte(rx_buf, response_len - 2),
|
||||
bit_buffer_get_byte(rx_buf, response_len - 1),
|
||||
};
|
||||
bit_buffer_set_size_bytes(rx_buf, response_len - 2);
|
||||
|
||||
if(memcmp(status, success, sizeof(status)) == 0) {
|
||||
return Type4TagErrorNone;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "APDU failed: 0x%02X%02X", status[0], status[1]);
|
||||
return Type4TagErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
FURI_LOG_D(TAG, "Select application");
|
||||
const uint8_t type_4_tag_select_app_apdu[] = {
|
||||
TYPE_4_TAG_ISO_SELECT_CMD,
|
||||
TYPE_4_TAG_ISO_SELECT_P1_BY_NAME,
|
||||
TYPE_4_TAG_ISO_SELECT_P2_EMPTY,
|
||||
TYPE_4_TAG_ISO_APP_NAME_LEN,
|
||||
TYPE_4_TAG_ISO_APP_NAME,
|
||||
TYPE_4_TAG_ISO_SELECT_LE_EMPTY,
|
||||
};
|
||||
|
||||
bit_buffer_append_bytes(
|
||||
instance->tx_buffer, type_4_tag_select_app_apdu, sizeof(type_4_tag_select_app_apdu));
|
||||
|
||||
return type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
|
||||
}
|
||||
|
||||
Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
Type4TagError error;
|
||||
|
||||
do {
|
||||
FURI_LOG_D(TAG, "Select CC");
|
||||
const uint8_t type_4_tag_select_cc_apdu[] = {
|
||||
TYPE_4_TAG_ISO_SELECT_CMD,
|
||||
TYPE_4_TAG_ISO_SELECT_P1_BY_ID,
|
||||
TYPE_4_TAG_ISO_SELECT_P2_EMPTY,
|
||||
TYPE_4_TAG_T4T_CC_FILE_ID_LEN,
|
||||
TYPE_4_TAG_T4T_CC_FILE_ID,
|
||||
TYPE_4_TAG_ISO_SELECT_LE_EMPTY,
|
||||
};
|
||||
|
||||
bit_buffer_append_bytes(
|
||||
instance->tx_buffer, type_4_tag_select_cc_apdu, sizeof(type_4_tag_select_cc_apdu));
|
||||
|
||||
error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
|
||||
if(error != Type4TagErrorNone) break;
|
||||
|
||||
FURI_LOG_D(TAG, "Read CC");
|
||||
const uint8_t type_4_tag_read_cc_apdu[] = {
|
||||
TYPE_4_TAG_ISO_READ_CMD,
|
||||
TYPE_4_TAG_ISO_READ_P1_EMPTY,
|
||||
TYPE_4_TAG_ISO_READ_P2_BEGINNING,
|
||||
TYPE_4_TAG_ISO_READ_LE_FULL,
|
||||
};
|
||||
|
||||
bit_buffer_append_bytes(
|
||||
instance->tx_buffer, type_4_tag_read_cc_apdu, sizeof(type_4_tag_read_cc_apdu));
|
||||
|
||||
error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
|
||||
if(error != Type4TagErrorNone) break;
|
||||
|
||||
const Type4TagCc* cc = (const Type4TagCc*)bit_buffer_get_data(instance->rx_buffer);
|
||||
if(cc->t4t_vno != TYPE_4_TAG_T4T_CC_VNO) {
|
||||
FURI_LOG_E(TAG, "Unsupported T4T version");
|
||||
error = Type4TagErrorProtocol;
|
||||
break;
|
||||
}
|
||||
|
||||
const Type4TagCcTlv* tlv = cc->tlv;
|
||||
const Type4TagCcTlvNdefFileCtrl* ndef_file_ctrl = NULL;
|
||||
while((void*)tlv < (void*)cc + cc->len) {
|
||||
if(tlv->type == Type4TagCcTlvTypeNdefFileCtrl) {
|
||||
ndef_file_ctrl = &tlv->value.ndef_file_ctrl;
|
||||
break;
|
||||
}
|
||||
|
||||
if(tlv->len < 0xFF) {
|
||||
tlv = (void*)&tlv->value + tlv->len;
|
||||
} else {
|
||||
uint16_t len = bit_lib_bytes_to_num_be((void*)&tlv->len + 1, sizeof(uint16_t));
|
||||
tlv = (void*)&tlv->value + sizeof(len) + len;
|
||||
}
|
||||
}
|
||||
if(!ndef_file_ctrl) {
|
||||
FURI_LOG_E(TAG, "No NDEF file ctrl TLV");
|
||||
error = Type4TagErrorProtocol;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->data->t4t_version.value = cc->t4t_vno;
|
||||
instance->data->chunk_max_read = bit_lib_bytes_to_num_be((void*)&cc->mle, sizeof(cc->mle));
|
||||
instance->data->chunk_max_write =
|
||||
bit_lib_bytes_to_num_be((void*)&cc->mlc, sizeof(cc->mlc));
|
||||
instance->data->ndef_file_id = bit_lib_bytes_to_num_be(
|
||||
(void*)&ndef_file_ctrl->file_id, sizeof(ndef_file_ctrl->file_id));
|
||||
instance->data->ndef_max_len =
|
||||
bit_lib_bytes_to_num_be(
|
||||
(void*)&ndef_file_ctrl->max_len, sizeof(ndef_file_ctrl->max_len)) -
|
||||
sizeof(uint16_t);
|
||||
instance->data->ndef_read_lock = ndef_file_ctrl->read_perm;
|
||||
instance->data->ndef_write_lock = ndef_file_ctrl->write_perm;
|
||||
|
||||
FURI_LOG_D(TAG, "Detected NDEF file ID 0x%04X", instance->data->ndef_file_id);
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
Type4TagError error;
|
||||
|
||||
do {
|
||||
FURI_LOG_D(TAG, "Select NDEF");
|
||||
const uint8_t type_4_tag_select_ndef_apdu_1[] = {
|
||||
TYPE_4_TAG_ISO_SELECT_CMD,
|
||||
TYPE_4_TAG_ISO_SELECT_P1_BY_ID,
|
||||
TYPE_4_TAG_ISO_SELECT_P2_EMPTY,
|
||||
sizeof(instance->data->ndef_file_id),
|
||||
};
|
||||
uint8_t ndef_file_id_be[sizeof(instance->data->ndef_file_id)];
|
||||
bit_lib_num_to_bytes_be(
|
||||
instance->data->ndef_file_id, sizeof(instance->data->ndef_file_id), ndef_file_id_be);
|
||||
const uint8_t type_4_tag_select_ndef_apdu_2[] = {
|
||||
TYPE_4_TAG_ISO_SELECT_LE_EMPTY,
|
||||
};
|
||||
|
||||
bit_buffer_append_bytes(
|
||||
instance->tx_buffer,
|
||||
type_4_tag_select_ndef_apdu_1,
|
||||
sizeof(type_4_tag_select_ndef_apdu_1));
|
||||
bit_buffer_append_bytes(instance->tx_buffer, ndef_file_id_be, sizeof(ndef_file_id_be));
|
||||
bit_buffer_append_bytes(
|
||||
instance->tx_buffer,
|
||||
type_4_tag_select_ndef_apdu_2,
|
||||
sizeof(type_4_tag_select_ndef_apdu_2));
|
||||
|
||||
error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
|
||||
if(error != Type4TagErrorNone) break;
|
||||
|
||||
FURI_LOG_D(TAG, "Read NDEF len");
|
||||
uint16_t ndef_len;
|
||||
const uint8_t type_4_tag_read_ndef_len_apdu[] = {
|
||||
TYPE_4_TAG_ISO_READ_CMD,
|
||||
TYPE_4_TAG_ISO_READ_P1_EMPTY,
|
||||
TYPE_4_TAG_ISO_READ_P2_BEGINNING,
|
||||
sizeof(ndef_len),
|
||||
};
|
||||
|
||||
bit_buffer_append_bytes(
|
||||
instance->tx_buffer,
|
||||
type_4_tag_read_ndef_len_apdu,
|
||||
sizeof(type_4_tag_read_ndef_len_apdu));
|
||||
|
||||
error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
|
||||
if(error != Type4TagErrorNone) break;
|
||||
|
||||
ndef_len =
|
||||
bit_lib_bytes_to_num_be(bit_buffer_get_data(instance->rx_buffer), sizeof(ndef_len));
|
||||
simple_array_init(instance->data->ndef_data, ndef_len);
|
||||
if(ndef_len > 510) {
|
||||
// Both size and offset for READ BINARY are 1 byte uint
|
||||
// So the furthest we can read is 255 bytes at offset 255
|
||||
// AKA 2 * 255 byte chunks = 510 bytes
|
||||
// TODO: Surely there has to be another way?
|
||||
FURI_LOG_E(TAG, "NDEF file too long: %zu bytes", ndef_len);
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Read NDEF");
|
||||
const uint8_t type_4_tag_read_ndef_apdu_1[] = {
|
||||
TYPE_4_TAG_ISO_READ_CMD,
|
||||
TYPE_4_TAG_ISO_READ_P1_EMPTY,
|
||||
};
|
||||
|
||||
uint16_t ndef_pos = 0;
|
||||
uint8_t* ndef_data = simple_array_get_data(instance->data->ndef_data);
|
||||
uint8_t chunk_max = MIN(instance->data->chunk_max_read, TYPE_4_TAG_ISO_RW_CHUNK_LEN);
|
||||
while(ndef_len > 0) {
|
||||
uint8_t chunk_len = MIN(ndef_len, chunk_max);
|
||||
|
||||
bit_buffer_append_bytes(
|
||||
instance->tx_buffer,
|
||||
type_4_tag_read_ndef_apdu_1,
|
||||
sizeof(type_4_tag_read_ndef_apdu_1));
|
||||
bit_buffer_append_byte(instance->tx_buffer, (uint8_t)ndef_pos);
|
||||
bit_buffer_append_byte(instance->tx_buffer, chunk_len);
|
||||
|
||||
error = type_4_tag_apdu_trx(instance, instance->tx_buffer, instance->rx_buffer);
|
||||
if(error != Type4TagErrorNone) break;
|
||||
if(bit_buffer_get_size_bytes(instance->rx_buffer) != chunk_len) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Wrong NDEF chunk len: %zu != %zu",
|
||||
bit_buffer_get_size_bytes(instance->rx_buffer),
|
||||
ndef_len);
|
||||
error = Type4TagErrorProtocol;
|
||||
break;
|
||||
}
|
||||
memcpy(&ndef_data[ndef_pos], bit_buffer_get_data(instance->rx_buffer), chunk_len);
|
||||
|
||||
ndef_pos += chunk_len;
|
||||
ndef_len -= chunk_len;
|
||||
}
|
||||
if(error != Type4TagErrorNone) break;
|
||||
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"Read NDEF file 0x%04X of %lu bytes",
|
||||
instance->data->ndef_file_id,
|
||||
simple_array_get_count(instance->data->ndef_data));
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "type_4_tag_poller.h"
|
||||
|
||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
Type4TagPollerStateIdle,
|
||||
Type4TagPollerStateSelectApplication,
|
||||
Type4TagPollerStateReadCapabilityContainer,
|
||||
Type4TagPollerStateReadNdefMessage,
|
||||
Type4TagPollerStateReadFailed,
|
||||
Type4TagPollerStateReadSuccess,
|
||||
|
||||
Type4TagPollerStateNum,
|
||||
} Type4TagPollerState;
|
||||
|
||||
struct Type4TagPoller {
|
||||
Iso14443_4aPoller* iso14443_4a_poller;
|
||||
Type4TagPollerState state;
|
||||
Type4TagError error;
|
||||
Type4TagData* data;
|
||||
BitBuffer* tx_buffer;
|
||||
BitBuffer* rx_buffer;
|
||||
Type4TagPollerEventData type_4_tag_event_data;
|
||||
Type4TagPollerEvent type_4_tag_event;
|
||||
NfcGenericEvent general_event;
|
||||
NfcGenericCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
Type4TagError type_4_tag_process_error(Iso14443_4aError error);
|
||||
|
||||
const Type4TagData* type_4_tag_poller_get_data(Type4TagPoller* instance);
|
||||
|
||||
Type4TagError type_4_tag_poller_select_app(Type4TagPoller* instance);
|
||||
|
||||
Type4TagError type_4_tag_poller_read_cc(Type4TagPoller* instance);
|
||||
|
||||
Type4TagError type_4_tag_poller_read_ndef(Type4TagPoller* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -169,6 +169,8 @@ Header,+,lib/nfc/protocols/slix/slix_poller.h,,
|
||||
Header,+,lib/nfc/protocols/st25tb/st25tb.h,,
|
||||
Header,+,lib/nfc/protocols/st25tb/st25tb_poller.h,,
|
||||
Header,+,lib/nfc/protocols/st25tb/st25tb_poller_sync.h,,
|
||||
Header,+,lib/nfc/protocols/type_4_tag/type_4_tag.h,,
|
||||
Header,+,lib/nfc/protocols/type_4_tag/type_4_tag_poller.h,,
|
||||
Header,+,lib/one_wire/maxim_crc.h,,
|
||||
Header,+,lib/one_wire/one_wire_host.h,,
|
||||
Header,+,lib/one_wire/one_wire_slave.h,,
|
||||
@@ -3625,6 +3627,18 @@ Function,-,toupper_l,int,"int, locale_t"
|
||||
Function,-,trunc,double,double
|
||||
Function,-,truncf,float,float
|
||||
Function,-,truncl,long double,long double
|
||||
Function,+,type_4_tag_alloc,Type4TagData*,
|
||||
Function,+,type_4_tag_copy,void,"Type4TagData*, const Type4TagData*"
|
||||
Function,+,type_4_tag_free,void,Type4TagData*
|
||||
Function,+,type_4_tag_get_base_data,Iso14443_4aData*,const Type4TagData*
|
||||
Function,+,type_4_tag_get_device_name,const char*,"const Type4TagData*, NfcDeviceNameType"
|
||||
Function,+,type_4_tag_get_uid,const uint8_t*,"const Type4TagData*, size_t*"
|
||||
Function,+,type_4_tag_is_equal,_Bool,"const Type4TagData*, const Type4TagData*"
|
||||
Function,+,type_4_tag_load,_Bool,"Type4TagData*, FlipperFormat*, uint32_t"
|
||||
Function,+,type_4_tag_reset,void,Type4TagData*
|
||||
Function,+,type_4_tag_save,_Bool,"const Type4TagData*, FlipperFormat*"
|
||||
Function,+,type_4_tag_set_uid,_Bool,"Type4TagData*, const uint8_t*, size_t"
|
||||
Function,+,type_4_tag_verify,_Bool,"Type4TagData*, const FuriString*"
|
||||
Function,+,uint8_to_hex_chars,void,"const uint8_t*, uint8_t*, int"
|
||||
Function,-,ungetc,int,"int, FILE*"
|
||||
Function,-,unsetenv,int,const char*
|
||||
@@ -4012,6 +4026,7 @@ Variable,-,nfc_device_mf_desfire,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_mf_plus,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_st25tb,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_type_4_tag,const NfcDeviceBase,
|
||||
Variable,+,sequence_audiovisual_alert,const NotificationSequence,
|
||||
Variable,+,sequence_blink_blue_10,const NotificationSequence,
|
||||
Variable,+,sequence_blink_blue_100,const NotificationSequence,
|
||||
|
||||
|
Reference in New Issue
Block a user