mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-14 20:38:35 -07:00
added NfcV emulation
This commit is contained in:
@@ -1,11 +1,20 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
|
#include <furi_hal_nfc.h>
|
||||||
#include <cli/cli.h>
|
#include <cli/cli.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
|
#include <st25r3916.h>
|
||||||
|
#include <st25r3916_irq.h>
|
||||||
|
#include <furi_hal_spi.h>
|
||||||
|
#include <furi_hal_gpio.h>
|
||||||
|
#include <furi_hal_cortex.h>
|
||||||
|
#include <furi_hal_resources.h>
|
||||||
|
|
||||||
#include <lib/nfc/nfc_types.h>
|
#include <lib/nfc/nfc_types.h>
|
||||||
#include <lib/nfc/nfc_device.h>
|
#include <lib/nfc/nfc_device.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void nfc_cli_print_usage() {
|
static void nfc_cli_print_usage() {
|
||||||
printf("Usage:\r\n");
|
printf("Usage:\r\n");
|
||||||
printf("nfc <cmd>\r\n");
|
printf("nfc <cmd>\r\n");
|
||||||
@@ -14,6 +23,10 @@ static void nfc_cli_print_usage() {
|
|||||||
printf("\temulate\t - emulate predefined nfca card\r\n");
|
printf("\temulate\t - emulate predefined nfca card\r\n");
|
||||||
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
|
||||||
printf("\tfield\t - turn field on\r\n");
|
printf("\tfield\t - turn field on\r\n");
|
||||||
|
printf("\tst25r_read\t - read ST25R3916 register\r\n");
|
||||||
|
printf("\tst25r_write\t - write ST25R3916 register\r\n");
|
||||||
|
printf("\tst25r_cmd\t - execute ST25R3916 command\r\n");
|
||||||
|
printf("\tst25r_fifo\t - wait for FIFO data\r\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +111,197 @@ static void nfc_cli_field(Cli* cli, FuriString* args) {
|
|||||||
furi_hal_nfc_sleep();
|
furi_hal_nfc_sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t hexdigit(char hex) {
|
||||||
|
return (hex <= '9') ? hex - '0' : toupper(hex) - 'A' + 10 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t hexbyte(const char* hex) {
|
||||||
|
return (hexdigit(*hex) << 4) | hexdigit(*(hex+1));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_cli_st25r_write(Cli* cli, FuriString* args) {
|
||||||
|
UNUSED(cli);
|
||||||
|
FuriString* reg = furi_string_alloc();
|
||||||
|
FuriString* value = furi_string_alloc();
|
||||||
|
|
||||||
|
// Check if nfc worker is not busy
|
||||||
|
if(furi_hal_nfc_is_busy()) {
|
||||||
|
printf("Nfc is busy\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!args_read_string_and_trim(args, reg) || !args_read_string_and_trim(args, value)) {
|
||||||
|
printf("You didn't specify <reg> and <value>\r\n");
|
||||||
|
nfc_cli_print_usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(furi_string_utf8_length(reg) != 2 || furi_string_utf8_length(value) != 2) {
|
||||||
|
printf("You didn't specify <reg> and <value> as 2-character hex values\r\n");
|
||||||
|
nfc_cli_print_usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t reg_val = hexbyte(furi_string_get_cstr(reg));
|
||||||
|
uint8_t value_val = hexbyte(furi_string_get_cstr(value));
|
||||||
|
|
||||||
|
printf("W %02X <- %02X\r\n", reg_val, value_val);
|
||||||
|
if(st25r3916WriteRegister(reg_val, value_val) != ERR_NONE) {
|
||||||
|
printf("Failed to write register\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_cli_st25r_read(Cli* cli, FuriString* args) {
|
||||||
|
UNUSED(cli);
|
||||||
|
FuriString* reg = furi_string_alloc();
|
||||||
|
|
||||||
|
// Check if nfc worker is not busy
|
||||||
|
if(furi_hal_nfc_is_busy()) {
|
||||||
|
printf("Nfc is busy\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!args_read_string_and_trim(args, reg)) {
|
||||||
|
printf("You didn't specify <reg>\r\n");
|
||||||
|
nfc_cli_print_usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(furi_string_utf8_length(reg) != 2) {
|
||||||
|
printf("You didn't specify <reg> as 2-character hex value\r\n");
|
||||||
|
nfc_cli_print_usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t reg_val = hexbyte(furi_string_get_cstr(reg));
|
||||||
|
uint8_t value_val = 0;
|
||||||
|
|
||||||
|
if(st25r3916ReadRegister(reg_val, &value_val) != ERR_NONE) {
|
||||||
|
printf("Failed to read register\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("R %02X -> %02X\r\n", reg_val, value_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfc_cli_st25r_cmd(Cli* cli, FuriString* args) {
|
||||||
|
UNUSED(cli);
|
||||||
|
FuriString* cmd = furi_string_alloc();
|
||||||
|
|
||||||
|
// Check if nfc worker is not busy
|
||||||
|
if(furi_hal_nfc_is_busy()) {
|
||||||
|
printf("Nfc is busy\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!args_read_string_and_trim(args, cmd)) {
|
||||||
|
printf("You didn't specify <cmd>\r\n");
|
||||||
|
nfc_cli_print_usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(furi_string_utf8_length(cmd) != 2) {
|
||||||
|
printf("You didn't specify <cmd> as 2-character hex value\r\n");
|
||||||
|
nfc_cli_print_usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t cmd_val = hexbyte(furi_string_get_cstr(cmd));
|
||||||
|
|
||||||
|
if(st25r3916ExecuteCommand(cmd_val) != ERR_NONE) {
|
||||||
|
printf("Failed to execute\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("X %02X\r\n", cmd_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FuriHalNfcTxRxContext tx_rx = {};
|
||||||
|
|
||||||
|
static void nfc_cli_st25r_fifo(Cli* cli, FuriString* args) {
|
||||||
|
UNUSED(args);
|
||||||
|
|
||||||
|
// Check if nfc worker is not busy
|
||||||
|
if(furi_hal_nfc_is_busy()) {
|
||||||
|
printf("Nfc is busy\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool field = false;
|
||||||
|
|
||||||
|
tx_rx.sniff_tx = NULL;
|
||||||
|
tx_rx.sniff_rx = NULL;
|
||||||
|
tx_rx.tx_bits = 0;
|
||||||
|
tx_rx.tx_rx_type = FuriHalNfcTxRxTypeRxRaw;
|
||||||
|
|
||||||
|
rfal_platform_spi_acquire();
|
||||||
|
|
||||||
|
furi_hal_nfcv_listen_start();
|
||||||
|
|
||||||
|
printf("Reading FIFO...\r\nPress Ctrl+C to abort\r\n");
|
||||||
|
while(!cli_cmd_interrupt_received(cli)) {
|
||||||
|
|
||||||
|
if(st25r3916IsExtFieldOn() && !field) {
|
||||||
|
printf("Field entered\r\n");
|
||||||
|
field = true;
|
||||||
|
}
|
||||||
|
if(!st25r3916IsExtFieldOn() && field) {
|
||||||
|
printf("Field left\r\n");
|
||||||
|
field = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(furi_hal_nfc_listen_rx(&tx_rx, 50)) {
|
||||||
|
for(int pos = 0; pos < tx_rx.rx_bits / 8; pos++) {
|
||||||
|
printf(" %02X", tx_rx.rx_data[pos]);
|
||||||
|
}
|
||||||
|
printf("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_delay_ms(50);
|
||||||
|
}
|
||||||
|
rfal_platform_spi_release();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void nfc_cli_st25r_trans(Cli* cli, FuriString* args) {
|
||||||
|
UNUSED(args);
|
||||||
|
|
||||||
|
// Check if nfc worker is not busy
|
||||||
|
if(furi_hal_nfc_is_busy()) {
|
||||||
|
printf("Nfc is busy\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("ISO15693 emulator...\r\nPress Ctrl+C to abort\r\n");
|
||||||
|
|
||||||
|
FuriHalNfcDevData nfc_data = {
|
||||||
|
.uid = { 0x36, 0x78, 0x45, 0x0E, 0x50, 0x03, 0x04, 0xE0 },
|
||||||
|
.uid_len = 8,
|
||||||
|
.type = FuriHalNfcTypeV,
|
||||||
|
};
|
||||||
|
NfcVData nfcv_data = {
|
||||||
|
.afi = 0,
|
||||||
|
.dsfid = 0,
|
||||||
|
.block_num = 8,
|
||||||
|
.block_size = 4,
|
||||||
|
.ic_ref = 3,
|
||||||
|
.key_privacy = { 0x0F, 0x0F, 0x0F, 0x0F },
|
||||||
|
.privacy = false
|
||||||
|
};
|
||||||
|
|
||||||
|
memset(nfcv_data.data, 0xAE, 4 * 8);
|
||||||
|
|
||||||
|
nfcv_emu_init();
|
||||||
|
while(!cli_cmd_interrupt_received(cli)) {
|
||||||
|
if(nfcv_emu_loop(&nfc_data, &nfcv_data, 1000)) {
|
||||||
|
printf("[NfcV-Emu] %s\r\n", nfcv_data.last_command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nfcv_emu_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
static void nfc_cli(Cli* cli, FuriString* args, void* context) {
|
static void nfc_cli(Cli* cli, FuriString* args, void* context) {
|
||||||
UNUSED(context);
|
UNUSED(context);
|
||||||
FuriString* cmd;
|
FuriString* cmd;
|
||||||
@@ -122,6 +326,26 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) {
|
|||||||
nfc_cli_field(cli, args);
|
nfc_cli_field(cli, args);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if(furi_string_cmp_str(cmd, "st25r_read") == 0) {
|
||||||
|
nfc_cli_st25r_read(cli, args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(furi_string_cmp_str(cmd, "st25r_write") == 0) {
|
||||||
|
nfc_cli_st25r_write(cli, args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(furi_string_cmp_str(cmd, "st25r_cmd") == 0) {
|
||||||
|
nfc_cli_st25r_cmd(cli, args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(furi_string_cmp_str(cmd, "st25r_fifo") == 0) {
|
||||||
|
nfc_cli_st25r_fifo(cli, args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(furi_string_cmp_str(cmd, "st25r_trans") == 0) {
|
||||||
|
nfc_cli_st25r_trans(cli, args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nfc_cli_print_usage();
|
nfc_cli_print_usage();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ ADD_SCENE(nfc, save_name, SaveName)
|
|||||||
ADD_SCENE(nfc, save_success, SaveSuccess)
|
ADD_SCENE(nfc, save_success, SaveSuccess)
|
||||||
ADD_SCENE(nfc, file_select, FileSelect)
|
ADD_SCENE(nfc, file_select, FileSelect)
|
||||||
ADD_SCENE(nfc, emulate_uid, EmulateUid)
|
ADD_SCENE(nfc, emulate_uid, EmulateUid)
|
||||||
|
ADD_SCENE(nfc, emulate_nfcv, EmulateNfcV)
|
||||||
ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess)
|
ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess)
|
||||||
ADD_SCENE(nfc, nfca_menu, NfcaMenu)
|
ADD_SCENE(nfc, nfca_menu, NfcaMenu)
|
||||||
ADD_SCENE(nfc, nfcv_menu, NfcVMenu)
|
ADD_SCENE(nfc, nfcv_menu, NfcVMenu)
|
||||||
|
|||||||
141
applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c
Normal file
141
applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#include "../nfc_i.h"
|
||||||
|
|
||||||
|
#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NfcSceneEmulateNfcVStateWidget,
|
||||||
|
NfcSceneEmulateNfcVStateTextBox,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool nfc_emulate_nfcv_worker_callback(NfcWorkerEvent event, void* context) {
|
||||||
|
UNUSED(event);
|
||||||
|
furi_assert(context);
|
||||||
|
Nfc* nfc = context;
|
||||||
|
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_emulate_nfcv_widget_callback(GuiButtonType result, InputType type, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
Nfc* nfc = context;
|
||||||
|
if(type == InputTypeShort) {
|
||||||
|
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_emulate_nfcv_textbox_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
Nfc* nfc = context;
|
||||||
|
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add widget with device name or inform that data received
|
||||||
|
static void nfc_scene_emulate_nfcv_widget_config(Nfc* nfc, bool data_received) {
|
||||||
|
FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
|
||||||
|
Widget* widget = nfc->widget;
|
||||||
|
widget_reset(widget);
|
||||||
|
FuriString* info_str;
|
||||||
|
info_str = furi_string_alloc();
|
||||||
|
|
||||||
|
widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61);
|
||||||
|
widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating NfcV");
|
||||||
|
if(strcmp(nfc->dev->dev_name, "")) {
|
||||||
|
furi_string_printf(info_str, "%s", nfc->dev->dev_name);
|
||||||
|
} else {
|
||||||
|
for(uint8_t i = 0; i < data->uid_len; i++) {
|
||||||
|
furi_string_cat_printf(info_str, "%02X ", data->uid[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
furi_string_trim(info_str);
|
||||||
|
widget_add_text_box_element(
|
||||||
|
widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true);
|
||||||
|
furi_string_free(info_str);
|
||||||
|
if(data_received) {
|
||||||
|
widget_add_button_element(
|
||||||
|
widget, GuiButtonTypeCenter, "Log", nfc_scene_emulate_nfcv_widget_callback, nfc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_emulate_nfcv_on_enter(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
|
||||||
|
// Setup Widget
|
||||||
|
nfc_scene_emulate_nfcv_widget_config(nfc, false);
|
||||||
|
// Setup TextBox
|
||||||
|
TextBox* text_box = nfc->text_box;
|
||||||
|
text_box_set_font(text_box, TextBoxFontHex);
|
||||||
|
text_box_set_focus(text_box, TextBoxFocusEnd);
|
||||||
|
furi_string_reset(nfc->text_box_store);
|
||||||
|
|
||||||
|
// Set Widget state and view
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget);
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||||
|
// Start worker
|
||||||
|
memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData));
|
||||||
|
nfc_worker_start(
|
||||||
|
nfc->worker,
|
||||||
|
NfcWorkerStateNfcVEmulate,
|
||||||
|
&nfc->dev->dev_data,
|
||||||
|
nfc_emulate_nfcv_worker_callback,
|
||||||
|
nfc);
|
||||||
|
|
||||||
|
nfc_blink_emulate_start(nfc);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfc_scene_emulate_nfcv_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
|
||||||
|
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateNfcV);
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == NfcCustomEventWorkerExit) {
|
||||||
|
// Add data button to widget if data is received for the first time
|
||||||
|
if(!furi_string_size(nfc->text_box_store)) {
|
||||||
|
nfc_scene_emulate_nfcv_widget_config(nfc, true);
|
||||||
|
}
|
||||||
|
// Update TextBox data
|
||||||
|
if(furi_string_size(nfc->text_box_store) < NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX) {
|
||||||
|
furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command);
|
||||||
|
furi_string_push_back(nfc->text_box_store, '\n');
|
||||||
|
text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store));
|
||||||
|
strcpy(nfcv_data->last_command, "");
|
||||||
|
}
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == GuiButtonTypeCenter && state == NfcSceneEmulateNfcVStateWidget) {
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateTextBox);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == NfcCustomEventViewExit && state == NfcSceneEmulateNfcVStateTextBox) {
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
if(state == NfcSceneEmulateNfcVStateTextBox) {
|
||||||
|
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
nfc->scene_manager, NfcSceneEmulateNfcV, NfcSceneEmulateNfcVStateWidget);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfc_scene_emulate_nfcv_on_exit(void* context) {
|
||||||
|
Nfc* nfc = context;
|
||||||
|
|
||||||
|
// Stop worker
|
||||||
|
nfc_worker_stop(nfc->worker);
|
||||||
|
|
||||||
|
// Clear view
|
||||||
|
widget_reset(nfc->widget);
|
||||||
|
text_box_reset(nfc->text_box);
|
||||||
|
furi_string_reset(nfc->text_box_store);
|
||||||
|
|
||||||
|
nfc_blink_stop(nfc);
|
||||||
|
}
|
||||||
@@ -14,7 +14,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
|
|||||||
NfcDeviceData* dev_data = &nfc->dev->dev_data;
|
NfcDeviceData* dev_data = &nfc->dev->dev_data;
|
||||||
NfcProtocol protocol = dev_data->protocol;
|
NfcProtocol protocol = dev_data->protocol;
|
||||||
uint8_t text_scroll_height = 0;
|
uint8_t text_scroll_height = 0;
|
||||||
if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)|| (protocol == NfcDeviceProtocolSlixL)) {
|
if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)
|
||||||
|
|| (protocol == NfcDeviceProtocolSlixL) || (protocol == NfcDeviceProtocolNfcV)) {
|
||||||
widget_add_button_element(
|
widget_add_button_element(
|
||||||
widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc);
|
widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc);
|
||||||
text_scroll_height = 52;
|
text_scroll_height = 52;
|
||||||
@@ -40,6 +41,8 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
|
|||||||
temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type));
|
temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type));
|
||||||
} else if(protocol == NfcDeviceProtocolMifareDesfire) {
|
} else if(protocol == NfcDeviceProtocolMifareDesfire) {
|
||||||
furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n");
|
furi_string_cat_printf(temp_str, "\e#MIFARE DESfire\n");
|
||||||
|
} else if(protocol == NfcDeviceProtocolNfcV) {
|
||||||
|
furi_string_cat_printf(temp_str, "\e#ISO15693\n");
|
||||||
} else if(protocol == NfcDeviceProtocolSlixL) {
|
} else if(protocol == NfcDeviceProtocolSlixL) {
|
||||||
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n");
|
furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n");
|
||||||
} else {
|
} else {
|
||||||
@@ -47,7 +50,7 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set tag iso data
|
// Set tag iso data
|
||||||
if(protocol == NfcDeviceProtocolSlixL) {
|
if((protocol == NfcDeviceProtocolSlixL) || (protocol == NfcDeviceProtocolNfcV)) {
|
||||||
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
|
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
|
||||||
|
|
||||||
furi_string_cat_printf(temp_str, "UID:\n");
|
furi_string_cat_printf(temp_str, "UID:\n");
|
||||||
@@ -151,6 +154,9 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
|
|||||||
} else if(protocol == NfcDeviceProtocolSlixL) {
|
} else if(protocol == NfcDeviceProtocolSlixL) {
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(protocol == NfcDeviceProtocolNfcV) {
|
||||||
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu);
|
||||||
|
consumed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
enum SubmenuIndex {
|
enum SubmenuIndex {
|
||||||
SubmenuIndexSave,
|
SubmenuIndexSave,
|
||||||
|
SubmenuIndexEmulate,
|
||||||
};
|
};
|
||||||
|
|
||||||
void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) {
|
void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) {
|
||||||
@@ -17,6 +18,8 @@ void nfc_scene_nfcv_menu_on_enter(void* context) {
|
|||||||
|
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc);
|
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(
|
submenu_set_selected_item(
|
||||||
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu));
|
nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu));
|
||||||
@@ -35,7 +38,15 @@ bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) {
|
|||||||
nfc_device_set_name(nfc->dev, "");
|
nfc_device_set_name(nfc->dev, "");
|
||||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
|
||||||
consumed = true;
|
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);
|
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event);
|
||||||
|
|
||||||
} else if(event.type == SceneManagerEventTypeBack) {
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
|||||||
@@ -96,7 +96,8 @@ void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) {
|
|||||||
popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop);
|
popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
notification_message(nfc->notifications, &sequence_success);
|
notification_message(nfc->notifications, &sequence_single_vibro);
|
||||||
|
//notification_message(nfc->notifications, &sequence_success);
|
||||||
|
|
||||||
popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57);
|
popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57);
|
||||||
popup_set_context(popup, nfc);
|
popup_set_context(popup, nfc);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,7.5,,
|
Version,+,7.12,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
Header,+,applications/services/cli/cli.h,,
|
||||||
Header,+,applications/services/cli/cli_vcp.h,,
|
Header,+,applications/services/cli/cli_vcp.h,,
|
||||||
@@ -1181,6 +1181,7 @@ Function,+,furi_hal_nfc_stop,void,
|
|||||||
Function,+,furi_hal_nfc_stop_cmd,void,
|
Function,+,furi_hal_nfc_stop_cmd,void,
|
||||||
Function,+,furi_hal_nfc_tx_rx,_Bool,"FuriHalNfcTxRxContext*, uint16_t"
|
Function,+,furi_hal_nfc_tx_rx,_Bool,"FuriHalNfcTxRxContext*, uint16_t"
|
||||||
Function,+,furi_hal_nfc_tx_rx_full,_Bool,FuriHalNfcTxRxContext*
|
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_init,void,
|
||||||
Function,+,furi_hal_os_tick,void,
|
Function,+,furi_hal_os_tick,void,
|
||||||
Function,+,furi_hal_power_check_otg_status,void,
|
Function,+,furi_hal_power_check_otg_status,void,
|
||||||
@@ -1958,8 +1959,12 @@ Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t"
|
|||||||
Function,-,nfca_signal_alloc,NfcaSignal*,
|
Function,-,nfca_signal_alloc,NfcaSignal*,
|
||||||
Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*"
|
Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*"
|
||||||
Function,-,nfca_signal_free,void,NfcaSignal*
|
Function,-,nfca_signal_free,void,NfcaSignal*
|
||||||
|
Function,-,nfcv_emu_deinit,void,
|
||||||
|
Function,-,nfcv_emu_init,void,
|
||||||
|
Function,-,nfcv_emu_loop,_Bool,"FuriHalNfcDevData*, NfcVData*, uint32_t"
|
||||||
Function,-,nfcv_inventory,ReturnCode,uint8_t*
|
Function,-,nfcv_inventory,ReturnCode,uint8_t*
|
||||||
Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*"
|
Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*"
|
||||||
|
Function,-,nfcv_read_card,_Bool,"NfcVReader*, FuriHalNfcDevData*, NfcVData*"
|
||||||
Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*"
|
Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*"
|
||||||
Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*"
|
Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*"
|
||||||
Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*"
|
Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*"
|
||||||
|
|||||||
|
@@ -367,6 +367,39 @@ void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data) {
|
|||||||
st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE);
|
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() {
|
void rfal_interrupt_callback_handler() {
|
||||||
furi_event_flag_set(event, EVENT_FLAG_INTERRUPT);
|
furi_event_flag_set(event, EVENT_FLAG_INTERRUPT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,6 +177,12 @@ bool furi_hal_nfc_listen(
|
|||||||
*/
|
*/
|
||||||
void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data);
|
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
|
/** Read data in Target Listen mode
|
||||||
* @note Must be called only after furi_hal_nfc_listen_start()
|
* @note Must be called only after furi_hal_nfc_listen_start()
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -23,18 +23,33 @@ extern "C" {
|
|||||||
#define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED())
|
#define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED())
|
||||||
#endif
|
#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
|
#ifndef FURI_CRITICAL_ENTER
|
||||||
#define FURI_CRITICAL_ENTER() \
|
#define FURI_CRITICAL_ENTER() \
|
||||||
uint32_t __isrm = 0; \
|
FURI_CRITICAL_DEFINE(); \
|
||||||
bool __from_isr = FURI_IS_ISR(); \
|
FURI_CRITICAL_ENTER_ADV();
|
||||||
bool __kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); \
|
|
||||||
if(__from_isr) { \
|
|
||||||
__isrm = taskENTER_CRITICAL_FROM_ISR(); \
|
|
||||||
} else if(__kernel_running) { \
|
|
||||||
taskENTER_CRITICAL(); \
|
|
||||||
} else { \
|
|
||||||
__disable_irq(); \
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FURI_CRITICAL_EXIT
|
#ifndef FURI_CRITICAL_EXIT
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) {
|
|||||||
signal->start_level = true;
|
signal->start_level = true;
|
||||||
signal->edges_max_cnt = max_edges_cnt;
|
signal->edges_max_cnt = max_edges_cnt;
|
||||||
signal->edge_timings = malloc(max_edges_cnt * sizeof(uint32_t));
|
signal->edge_timings = malloc(max_edges_cnt * sizeof(uint32_t));
|
||||||
signal->reload_reg_buff = malloc(max_edges_cnt * sizeof(uint32_t));
|
signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t));
|
||||||
signal->edge_cnt = 0;
|
signal->edge_cnt = 0;
|
||||||
|
|
||||||
return signal;
|
return signal;
|
||||||
@@ -37,7 +37,10 @@ bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) {
|
|||||||
if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) {
|
if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) {
|
||||||
return false;
|
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;
|
bool end_level = signal_a->start_level;
|
||||||
if(signal_a->edge_cnt) {
|
if(signal_a->edge_cnt) {
|
||||||
end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2);
|
end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2);
|
||||||
@@ -84,6 +87,7 @@ void digital_signal_prepare_arr(DigitalSignal* signal) {
|
|||||||
uint32_t r_count_tick_arr = 0;
|
uint32_t r_count_tick_arr = 0;
|
||||||
uint32_t r_rest_div = 0;
|
uint32_t r_rest_div = 0;
|
||||||
|
|
||||||
|
|
||||||
for(size_t i = 0; i < signal->edge_cnt - 1; i++) {
|
for(size_t i = 0; i < signal->edge_cnt - 1; i++) {
|
||||||
r_count_tick_arr = t_signal_rest / T_TIM;
|
r_count_tick_arr = t_signal_rest / T_TIM;
|
||||||
r_rest_div = t_signal_rest % T_TIM;
|
r_rest_div = t_signal_rest % T_TIM;
|
||||||
@@ -162,7 +166,8 @@ void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) {
|
|||||||
LL_TIM_EnableCounter(TIM2);
|
LL_TIM_EnableCounter(TIM2);
|
||||||
|
|
||||||
while(!LL_DMA_IsActiveFlag_TC2(DMA1))
|
while(!LL_DMA_IsActiveFlag_TC2(DMA1))
|
||||||
;
|
{
|
||||||
|
}
|
||||||
|
|
||||||
LL_DMA_ClearFlag_TC1(DMA1);
|
LL_DMA_ClearFlag_TC1(DMA1);
|
||||||
LL_DMA_ClearFlag_TC2(DMA1);
|
LL_DMA_ClearFlag_TC2(DMA1);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ typedef enum {
|
|||||||
NfcDeviceProtocolMifareUl,
|
NfcDeviceProtocolMifareUl,
|
||||||
NfcDeviceProtocolMifareClassic,
|
NfcDeviceProtocolMifareClassic,
|
||||||
NfcDeviceProtocolMifareDesfire,
|
NfcDeviceProtocolMifareDesfire,
|
||||||
|
NfcDeviceProtocolNfcV,
|
||||||
NfcDeviceProtocolSlixL,
|
NfcDeviceProtocolSlixL,
|
||||||
} NfcProtocol;
|
} NfcProtocol;
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ int32_t nfc_worker_task(void* context) {
|
|||||||
nfc_worker_read(nfc_worker);
|
nfc_worker_read(nfc_worker);
|
||||||
} else if(nfc_worker->state == NfcWorkerStateUidEmulate) {
|
} else if(nfc_worker->state == NfcWorkerStateUidEmulate) {
|
||||||
nfc_worker_emulate_uid(nfc_worker);
|
nfc_worker_emulate_uid(nfc_worker);
|
||||||
|
} else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) {
|
||||||
|
nfc_worker_emulate_nfcv(nfc_worker);
|
||||||
} else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
|
} else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
|
||||||
nfc_worker_emulate_apdu(nfc_worker);
|
nfc_worker_emulate_apdu(nfc_worker);
|
||||||
} else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) {
|
} else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) {
|
||||||
@@ -135,6 +137,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) {
|
|||||||
|
|
||||||
furi_hal_nfc_sleep();
|
furi_hal_nfc_sleep();
|
||||||
|
|
||||||
|
|
||||||
while((nfc_worker->state == NfcWorkerStateNfcVUnlock) ||
|
while((nfc_worker->state == NfcWorkerStateNfcVUnlock) ||
|
||||||
(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) {
|
(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) {
|
||||||
|
|
||||||
@@ -201,7 +204,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) {
|
|||||||
if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) {
|
if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) {
|
||||||
NfcVReader reader;
|
NfcVReader reader;
|
||||||
|
|
||||||
if(!slix_l_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) {
|
if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) {
|
||||||
furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n");
|
furi_hal_console_printf(" => failed, wait for chip to disappear.\r\n");
|
||||||
snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed");
|
snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed");
|
||||||
nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context);
|
nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context);
|
||||||
@@ -249,7 +252,7 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
static bool nfc_worker_read_nfcv_content(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||||
bool read_success = false;
|
bool read_success = false;
|
||||||
NfcVReader reader = {};
|
NfcVReader reader = {};
|
||||||
|
|
||||||
@@ -260,7 +263,7 @@ static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext*
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break;
|
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break;
|
||||||
if(!slix_l_read_card(&reader, &nfc_worker->dev_data->nfc_data, &nfc_worker->dev_data->nfcv_data)) break;
|
if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, &nfc_worker->dev_data->nfcv_data)) break;
|
||||||
read_success = true;
|
read_success = true;
|
||||||
} while(false);
|
} while(false);
|
||||||
|
|
||||||
@@ -271,6 +274,7 @@ static bool nfc_worker_read_slix_l(NfcWorker* nfc_worker, FuriHalNfcTxRxContext*
|
|||||||
return read_success;
|
return read_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||||
bool read_success = false;
|
bool read_success = false;
|
||||||
MfUltralightReader reader = {};
|
MfUltralightReader reader = {};
|
||||||
@@ -495,11 +499,11 @@ static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* t
|
|||||||
if(slix_l_check_card_type(nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5])) {
|
if(slix_l_check_card_type(nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5])) {
|
||||||
FURI_LOG_I(TAG, "NXP SLIX-L detected");
|
FURI_LOG_I(TAG, "NXP SLIX-L detected");
|
||||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolSlixL;
|
nfc_worker->dev_data->protocol = NfcDeviceProtocolSlixL;
|
||||||
card_read = nfc_worker_read_slix_l(nfc_worker, tx_rx);
|
card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx);
|
||||||
} else {
|
} else {
|
||||||
FURI_LOG_I(TAG, "unknown detected");
|
FURI_LOG_I(TAG, "unknown detected");
|
||||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown;
|
nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV;
|
||||||
card_read = true;
|
card_read = nfc_worker_read_nfcv_content(nfc_worker, tx_rx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return card_read;
|
return card_read;
|
||||||
@@ -555,10 +559,8 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
|
|||||||
FURI_LOG_I(TAG, "NfcV detected");
|
FURI_LOG_I(TAG, "NfcV detected");
|
||||||
if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) {
|
if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) {
|
||||||
FURI_LOG_I(TAG, "nfc_worker_read_nfcv success");
|
FURI_LOG_I(TAG, "nfc_worker_read_nfcv success");
|
||||||
if(dev_data->protocol == NfcDeviceProtocolSlixL) {
|
event = NfcWorkerEventReadNfcV;
|
||||||
event = NfcWorkerEventReadNfcV;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
event = NfcWorkerEventReadUidNfcV;
|
event = NfcWorkerEventReadUidNfcV;
|
||||||
break;
|
break;
|
||||||
@@ -603,6 +605,21 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker) {
|
||||||
|
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
||||||
|
NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data;
|
||||||
|
|
||||||
|
nfcv_emu_init();
|
||||||
|
while(nfc_worker->state == NfcWorkerStateNfcVEmulate) {
|
||||||
|
if(nfcv_emu_loop(nfc_data, nfcv_data, 1000)) {
|
||||||
|
if(nfc_worker->callback) {
|
||||||
|
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nfcv_emu_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
|
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
|
||||||
FuriHalNfcTxRxContext tx_rx = {};
|
FuriHalNfcTxRxContext tx_rx = {};
|
||||||
FuriHalNfcDevData params = {
|
FuriHalNfcDevData params = {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ typedef enum {
|
|||||||
NfcWorkerStateReadMfUltralightReadAuth,
|
NfcWorkerStateReadMfUltralightReadAuth,
|
||||||
NfcWorkerStateMfClassicDictAttack,
|
NfcWorkerStateMfClassicDictAttack,
|
||||||
NfcWorkerStateAnalyzeReader,
|
NfcWorkerStateAnalyzeReader,
|
||||||
|
NfcWorkerStateNfcVEmulate,
|
||||||
NfcWorkerStateNfcVUnlock,
|
NfcWorkerStateNfcVUnlock,
|
||||||
NfcWorkerStateNfcVUnlockAndSave,
|
NfcWorkerStateNfcVUnlockAndSave,
|
||||||
// Debug
|
// Debug
|
||||||
@@ -91,4 +92,4 @@ void nfc_worker_start(
|
|||||||
|
|
||||||
void nfc_worker_stop(NfcWorker* nfc_worker);
|
void nfc_worker_stop(NfcWorker* nfc_worker);
|
||||||
void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker);
|
void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker);
|
||||||
|
void nfc_worker_emulate_nfcv(NfcWorker* nfc_worker);
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include <furi_hal_nfc.h>
|
||||||
|
#include <furi_hal_spi.h>
|
||||||
|
#include <furi_hal_gpio.h>
|
||||||
|
#include <furi_hal_cortex.h>
|
||||||
|
#include <furi_hal_resources.h>
|
||||||
|
#include <st25r3916.h>
|
||||||
|
#include <st25r3916_irq.h>
|
||||||
|
|
||||||
#include "nfcv.h"
|
#include "nfcv.h"
|
||||||
#include "nfc_util.h"
|
#include "nfc_util.h"
|
||||||
#include <furi.h>
|
|
||||||
#include "furi_hal_nfc.h"
|
|
||||||
|
|
||||||
#define TAG "NfcV"
|
#define TAG "NfcV"
|
||||||
|
|
||||||
@@ -82,4 +90,577 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data) {
|
|||||||
FURI_LOG_D(TAG, "Failed: %d", ret);
|
FURI_LOG_D(TAG, "Failed: %d", ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nfcv_read_card(
|
||||||
|
NfcVReader* reader,
|
||||||
|
FuriHalNfcDevData* nfc_data,
|
||||||
|
NfcVData* nfcv_data) {
|
||||||
|
furi_assert(reader);
|
||||||
|
furi_assert(nfcv_data);
|
||||||
|
|
||||||
|
if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader->blocks_to_read = nfcv_data->block_num;
|
||||||
|
return (nfcv_read_blocks(reader, nfcv_data) == ERR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* emulation part */
|
||||||
|
|
||||||
|
static const uint32_t clocks_in_ms = 64 * 1000;
|
||||||
|
static const uint32_t bit_time = 64 * 9.44f;
|
||||||
|
|
||||||
|
DigitalSignal* nfcv_resp_pulse_32 = NULL;
|
||||||
|
DigitalSignal* nfcv_resp_unmod = NULL;
|
||||||
|
DigitalSignal* nfcv_resp_one = NULL;
|
||||||
|
DigitalSignal* nfcv_resp_zero = NULL;
|
||||||
|
DigitalSignal* nfcv_resp_sof = NULL;
|
||||||
|
DigitalSignal* nfcv_resp_eof = NULL;
|
||||||
|
DigitalSignal* nfcv_resp_unmod_256 = NULL;
|
||||||
|
DigitalSignal* nfcv_resp_unmod_768 = NULL;
|
||||||
|
|
||||||
|
DigitalSignal* nfcv_signal = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
void nfcv_crc(uint8_t* data, uint32_t length, uint8_t* out) {
|
||||||
|
uint32_t reg = 0xFFFF;
|
||||||
|
uint32_t i = 0;
|
||||||
|
uint32_t j = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
reg = reg ^ ((uint32_t)data[i]);
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
if (reg & 0x0001) {
|
||||||
|
reg = (reg >> 1) ^ 0x8408;
|
||||||
|
} else {
|
||||||
|
reg = (reg >> 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t crc = ~(uint16_t)(reg & 0xffff);
|
||||||
|
|
||||||
|
out[0] = crc & 0xFF;
|
||||||
|
out[1] = crc >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfcv_emu_free() {
|
||||||
|
digital_signal_free(nfcv_signal);
|
||||||
|
digital_signal_free(nfcv_resp_unmod_256);
|
||||||
|
digital_signal_free(nfcv_resp_pulse_32);
|
||||||
|
digital_signal_free(nfcv_resp_one);
|
||||||
|
digital_signal_free(nfcv_resp_zero);
|
||||||
|
digital_signal_free(nfcv_resp_sof);
|
||||||
|
digital_signal_free(nfcv_resp_eof);
|
||||||
|
|
||||||
|
nfcv_signal = NULL;
|
||||||
|
nfcv_resp_unmod_256 = NULL;
|
||||||
|
nfcv_resp_pulse_32 = NULL;
|
||||||
|
nfcv_resp_one = NULL;
|
||||||
|
nfcv_resp_zero = NULL;
|
||||||
|
nfcv_resp_sof = NULL;
|
||||||
|
nfcv_resp_eof = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfcv_emu_alloc() {
|
||||||
|
|
||||||
|
if(!nfcv_signal) {
|
||||||
|
nfcv_signal = digital_signal_alloc(8192);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!nfcv_resp_unmod_256) {
|
||||||
|
/* unmodulated 256/fc signal as building block */
|
||||||
|
nfcv_resp_unmod_256 = digital_signal_alloc(4);
|
||||||
|
nfcv_resp_unmod_256->start_level = false;
|
||||||
|
nfcv_resp_unmod_256->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S);
|
||||||
|
nfcv_resp_unmod_256->edge_cnt = 1;
|
||||||
|
}
|
||||||
|
if(!nfcv_resp_pulse_32) {
|
||||||
|
/* modulated fc/32 pulse as building block */
|
||||||
|
nfcv_resp_pulse_32 = digital_signal_alloc(4);
|
||||||
|
nfcv_resp_pulse_32->start_level = true;
|
||||||
|
nfcv_resp_pulse_32->edge_timings[0] = (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S);
|
||||||
|
nfcv_resp_pulse_32->edge_timings[1] = (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S);
|
||||||
|
nfcv_resp_pulse_32->edge_cnt = 2;
|
||||||
|
}
|
||||||
|
if(!nfcv_resp_one) {
|
||||||
|
/* logical one: 256/fc unmodulated then 8 pulses fc/32 */
|
||||||
|
nfcv_resp_one = digital_signal_alloc(24);
|
||||||
|
digital_signal_append(nfcv_resp_one, nfcv_resp_unmod_256);
|
||||||
|
for(size_t i = 0; i < 8; i++) {
|
||||||
|
digital_signal_append(nfcv_resp_one, nfcv_resp_pulse_32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!nfcv_resp_zero) {
|
||||||
|
/* logical zero: 8 pulses fc/32 then 256/fc unmodulated */
|
||||||
|
nfcv_resp_zero = digital_signal_alloc(24);
|
||||||
|
for(size_t i = 0; i < 8; i++) {
|
||||||
|
digital_signal_append(nfcv_resp_zero, nfcv_resp_pulse_32);
|
||||||
|
}
|
||||||
|
digital_signal_append(nfcv_resp_zero, nfcv_resp_unmod_256);
|
||||||
|
}
|
||||||
|
if(!nfcv_resp_sof) {
|
||||||
|
/* SOF: unmodulated 768/fc, 24 pulses fc/32, logic 1 */
|
||||||
|
nfcv_resp_sof = digital_signal_alloc(128);
|
||||||
|
digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256);
|
||||||
|
digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256);
|
||||||
|
digital_signal_append(nfcv_resp_sof, nfcv_resp_unmod_256);
|
||||||
|
for(size_t i = 0; i < 24; i++) {
|
||||||
|
digital_signal_append(nfcv_resp_sof, nfcv_resp_pulse_32);
|
||||||
|
}
|
||||||
|
digital_signal_append(nfcv_resp_sof, nfcv_resp_one);
|
||||||
|
}
|
||||||
|
if(!nfcv_resp_eof) {
|
||||||
|
/* EOF: logic 0, 24 pulses fc/32, unmodulated 768/fc */
|
||||||
|
nfcv_resp_eof = digital_signal_alloc(128);
|
||||||
|
digital_signal_append(nfcv_resp_eof, nfcv_resp_zero);
|
||||||
|
for(size_t i = 0; i < 24; i++) {
|
||||||
|
digital_signal_append(nfcv_resp_eof, nfcv_resp_pulse_32);
|
||||||
|
}
|
||||||
|
digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256);
|
||||||
|
digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256);
|
||||||
|
digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256);
|
||||||
|
/* add extra silence */
|
||||||
|
digital_signal_append(nfcv_resp_eof, nfcv_resp_unmod_256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void nfcv_emu_send_raw(uint8_t* data, uint8_t length) {
|
||||||
|
|
||||||
|
int bits = length * 8;
|
||||||
|
|
||||||
|
nfcv_signal->start_level = false;
|
||||||
|
nfcv_signal->edge_cnt = 0;
|
||||||
|
|
||||||
|
digital_signal_append(nfcv_signal, nfcv_resp_sof);
|
||||||
|
|
||||||
|
for(int bit_total = 0; bit_total < bits; bit_total++) {
|
||||||
|
uint32_t byte_pos = bit_total / 8;
|
||||||
|
uint32_t bit_pos = bit_total % 8;
|
||||||
|
uint8_t bit_val = 0x01 << bit_pos;
|
||||||
|
|
||||||
|
if(data[byte_pos] & bit_val) {
|
||||||
|
digital_signal_append(nfcv_signal, nfcv_resp_one);
|
||||||
|
} else {
|
||||||
|
digital_signal_append(nfcv_signal, nfcv_resp_zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
digital_signal_append(nfcv_signal, nfcv_resp_eof);
|
||||||
|
|
||||||
|
/* digital signal setup will take some time. win some time by tricking the VCD into thinking that something happens */
|
||||||
|
furi_hal_gpio_write(&gpio_spi_r_mosi, false);
|
||||||
|
furi_delay_us(10);
|
||||||
|
furi_hal_gpio_write(&gpio_spi_r_mosi, true);
|
||||||
|
furi_delay_us(10);
|
||||||
|
furi_hal_gpio_write(&gpio_spi_r_mosi, false);
|
||||||
|
|
||||||
|
FURI_CRITICAL_ENTER();
|
||||||
|
digital_signal_send(nfcv_signal, &gpio_spi_r_mosi);
|
||||||
|
FURI_CRITICAL_EXIT();
|
||||||
|
furi_hal_gpio_write(&gpio_spi_r_mosi, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfcv_emu_send(uint8_t* data, uint8_t length) {
|
||||||
|
uint8_t buffer[64];
|
||||||
|
|
||||||
|
if(length + 2 > (uint8_t)sizeof(buffer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buffer, data, length);
|
||||||
|
nfcv_crc(buffer, length, &buffer[length]);
|
||||||
|
nfcv_emu_send_raw(buffer, length + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void nfcv_uidcpy(uint8_t *dst, uint8_t *src) {
|
||||||
|
for(int pos = 0; pos < 8; pos++) {
|
||||||
|
dst[pos] = src[7-pos];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int nfcv_uidcmp(uint8_t *dst, uint8_t *src) {
|
||||||
|
for(int pos = 0; pos < 8; pos++) {
|
||||||
|
if(dst[pos] != src[7-pos]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void nfcv_emu_handle_packet(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint8_t* payload, uint32_t payload_length) {
|
||||||
|
|
||||||
|
if(!payload_length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t flags = payload[0];
|
||||||
|
uint8_t command = payload[1];
|
||||||
|
bool addressed = !(flags & RFAL_NFCV_REQ_FLAG_INVENTORY) && (flags & RFAL_NFCV_REQ_FLAG_ADDRESS);
|
||||||
|
bool advanced = addressed && (command >= 0xA0);
|
||||||
|
uint8_t address_offset = 2 + (advanced ? 1 : 0);
|
||||||
|
uint8_t payload_offset = address_offset + (addressed ? 8 : 0);
|
||||||
|
uint8_t *address = &payload[address_offset];
|
||||||
|
|
||||||
|
if(addressed && nfcv_uidcmp(address, nfc_data->uid)) {
|
||||||
|
printf("addressed packet, but not for us:\r\n");
|
||||||
|
printf(" destination: %02X%02X%02X%02X%02X%02X%02X%02X\r\n", address[7], address[6], address[5], address[4], address[3], address[2], address[1], address[0]);
|
||||||
|
printf(" our UID: %02X%02X%02X%02X%02X%02X%02X%02X\r\n", nfc_data->uid[7], nfc_data->uid[6], nfc_data->uid[5], nfc_data->uid[4], nfc_data->uid[3], nfc_data->uid[2], nfc_data->uid[1], nfc_data->uid[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t response_buffer[32];
|
||||||
|
|
||||||
|
switch(command) {
|
||||||
|
case ISO15693_GET_SYSTEM_INFO:
|
||||||
|
{
|
||||||
|
if(nfcv_data->privacy) {
|
||||||
|
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO (ignored, privacy)");
|
||||||
|
} else {
|
||||||
|
response_buffer[0] = ISO15693_NOERROR;
|
||||||
|
response_buffer[1] = 0x0F;
|
||||||
|
nfcv_uidcpy(&response_buffer[2], nfc_data->uid);
|
||||||
|
response_buffer[10] = nfcv_data->dsfid; /* DSFID */
|
||||||
|
response_buffer[11] = nfcv_data->afi; /* AFI */
|
||||||
|
response_buffer[12] = nfcv_data->block_num - 1; /* number of blocks */
|
||||||
|
response_buffer[13] = nfcv_data->block_size - 1; /* block size */
|
||||||
|
response_buffer[14] = nfcv_data->ic_ref; /* IC reference */
|
||||||
|
|
||||||
|
nfcv_emu_send(response_buffer, 15);
|
||||||
|
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ISO15693_INVENTORY:
|
||||||
|
{
|
||||||
|
if(nfcv_data->privacy) {
|
||||||
|
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY (ignored, privacy)");
|
||||||
|
} else {
|
||||||
|
response_buffer[0] = ISO15693_NOERROR;
|
||||||
|
response_buffer[1] = nfcv_data->dsfid; /* DSFID */
|
||||||
|
nfcv_uidcpy(&response_buffer[2], nfc_data->uid);
|
||||||
|
|
||||||
|
nfcv_emu_send(response_buffer, 10);
|
||||||
|
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ISO15693_READBLOCK:
|
||||||
|
{
|
||||||
|
uint8_t block = payload[payload_offset];
|
||||||
|
|
||||||
|
if(block >= nfcv_data->block_num) {
|
||||||
|
response_buffer[0] = ISO15693_ERROR_BLOCK_WRITE;
|
||||||
|
nfcv_emu_send(response_buffer, 1);
|
||||||
|
} else {
|
||||||
|
response_buffer[0] = ISO15693_NOERROR;
|
||||||
|
memcpy(&response_buffer[1], &nfcv_data->data[nfcv_data->block_size * block], nfcv_data->block_size);
|
||||||
|
nfcv_emu_send(response_buffer, 1 + nfcv_data->block_size);
|
||||||
|
}
|
||||||
|
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ISO15693_WRITEBLOCK:
|
||||||
|
{
|
||||||
|
uint8_t block = payload[payload_offset];
|
||||||
|
uint8_t *data = &payload[payload_offset + 1];
|
||||||
|
|
||||||
|
if(block >= nfcv_data->block_num) {
|
||||||
|
response_buffer[0] = ISO15693_ERROR_BLOCK_WRITE;
|
||||||
|
} else {
|
||||||
|
response_buffer[0] = ISO15693_NOERROR;
|
||||||
|
memcpy(&nfcv_data->data[nfcv_data->block_size * block], &response_buffer[1], nfcv_data->block_size);
|
||||||
|
}
|
||||||
|
nfcv_emu_send(response_buffer, 1);
|
||||||
|
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE BLOCK %d <- %02X %02X %02X %02X", block, data[0], data[1], data[2], data[3]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ISO15693_CMD_NXP_GET_RANDOM_NUMBER:
|
||||||
|
{
|
||||||
|
response_buffer[0] = ISO15693_NOERROR;
|
||||||
|
response_buffer[1] = 0x00; /* set number to 0x0000 so we get the key in plaintext */
|
||||||
|
response_buffer[2] = 0x00;
|
||||||
|
|
||||||
|
nfcv_emu_send(response_buffer, 3);
|
||||||
|
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "GET_RANDOM_NUMBER");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ISO15693_CMD_NXP_SET_PASSWORD:
|
||||||
|
{
|
||||||
|
uint32_t pass = (payload[payload_offset + 1] << 24)
|
||||||
|
| (payload[payload_offset + 2] << 16)
|
||||||
|
| (payload[payload_offset + 3] << 8)
|
||||||
|
| (payload[payload_offset + 4] << 0);
|
||||||
|
|
||||||
|
response_buffer[0] = ISO15693_NOERROR;
|
||||||
|
|
||||||
|
nfcv_emu_send(response_buffer, 1);
|
||||||
|
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SET_PASSWORD #%02X: %08lX", payload[payload_offset + 0], pass);
|
||||||
|
|
||||||
|
nfcv_data->privacy = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ISO15693_CMD_NXP_ENABLE_PRIVACY:
|
||||||
|
{
|
||||||
|
response_buffer[0] = ISO15693_NOERROR;
|
||||||
|
|
||||||
|
nfcv_emu_send(response_buffer, 1);
|
||||||
|
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ISO15693_CMD_NXP_ENABLE_PRIVACY");
|
||||||
|
|
||||||
|
nfcv_data->privacy = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "unsupported: %02X", command);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfcv_emu_init() {
|
||||||
|
nfcv_emu_alloc();
|
||||||
|
rfal_platform_spi_acquire();
|
||||||
|
|
||||||
|
st25r3916ExecuteCommand(ST25R3916_CMD_STOP);
|
||||||
|
st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0xC3);
|
||||||
|
st25r3916WriteRegister(ST25R3916_REG_MODE, 0x88);
|
||||||
|
st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE);
|
||||||
|
|
||||||
|
furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nfcv_emu_deinit() {
|
||||||
|
furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc);
|
||||||
|
rfal_platform_spi_release();
|
||||||
|
nfcv_emu_free();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t timeout_ms) {
|
||||||
|
|
||||||
|
bool ret = false;
|
||||||
|
uint32_t next_sleep = DWT->CYCCNT + (timeout_ms * clocks_in_ms);
|
||||||
|
uint32_t timeout = 0;
|
||||||
|
uint32_t last_change = 0;
|
||||||
|
uint32_t edges_received = 0;
|
||||||
|
|
||||||
|
uint32_t frame_state = NFCV_FRAME_STATE_SOF1;
|
||||||
|
uint32_t periods_previous = 0;
|
||||||
|
uint8_t frame_payload[128];
|
||||||
|
uint32_t frame_pos = 0;
|
||||||
|
uint32_t byte_value = 0;
|
||||||
|
uint32_t bits_received = 0;
|
||||||
|
char reset_reason[128];
|
||||||
|
|
||||||
|
bool prev = furi_hal_gpio_read(&gpio_spi_r_miso);
|
||||||
|
|
||||||
|
bool in_critical = false;
|
||||||
|
FURI_CRITICAL_DEFINE();
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
|
||||||
|
bool state = furi_hal_gpio_read(&gpio_spi_r_miso);
|
||||||
|
uint32_t cur_time = DWT->CYCCNT;
|
||||||
|
|
||||||
|
if(state != prev) {
|
||||||
|
uint32_t delta = cur_time - last_change;
|
||||||
|
uint32_t periods = (delta + bit_time/2) / bit_time;
|
||||||
|
|
||||||
|
last_change = cur_time;
|
||||||
|
prev = state;
|
||||||
|
next_sleep = cur_time + (timeout_ms * clocks_in_ms);
|
||||||
|
|
||||||
|
/* start edge counting on first rising edge */
|
||||||
|
if(state || edges_received) {
|
||||||
|
edges_received++;
|
||||||
|
|
||||||
|
/* ignore periods which are too long, might happen on field start */
|
||||||
|
if(periods > 1024) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(frame_state) {
|
||||||
|
case NFCV_FRAME_STATE_SOF1:
|
||||||
|
/* got a rising edge, was it one period? */
|
||||||
|
if(state) {
|
||||||
|
if(periods == 1) {
|
||||||
|
FURI_CRITICAL_ENTER_ADV();
|
||||||
|
timeout = cur_time + bit_time * 16;
|
||||||
|
in_critical = true;
|
||||||
|
frame_state = NFCV_FRAME_STATE_SOF2;
|
||||||
|
} else {
|
||||||
|
snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 1 period, got %lu", periods);
|
||||||
|
frame_state = NFCV_FRAME_STATE_RESET;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NFCV_FRAME_STATE_SOF2:
|
||||||
|
/* waiting for the second low period, telling us about coding */
|
||||||
|
if(!state) {
|
||||||
|
timeout = cur_time + bit_time * 16;
|
||||||
|
if(periods == 6) {
|
||||||
|
frame_state = NFCV_FRAME_STATE_CODING_256;
|
||||||
|
periods_previous = 0;
|
||||||
|
} else if(periods == 4) {
|
||||||
|
frame_state = NFCV_FRAME_STATE_CODING_4;
|
||||||
|
periods_previous = 2;
|
||||||
|
} else {
|
||||||
|
snprintf(reset_reason, sizeof(reset_reason), "SOF: Expected 4/6 periods, got %lu", periods);
|
||||||
|
frame_state = NFCV_FRAME_STATE_RESET;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NFCV_FRAME_STATE_CODING_256:
|
||||||
|
if(!state) {
|
||||||
|
timeout = cur_time + bit_time * 1024;
|
||||||
|
if(periods_previous > periods) {
|
||||||
|
snprintf(reset_reason, sizeof(reset_reason), "1oo256: Missing %lu periods from previous symbol, got %lu", periods_previous, periods);
|
||||||
|
frame_state = NFCV_FRAME_STATE_RESET;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* previous symbol left us with some pulse periods */
|
||||||
|
periods -= periods_previous;
|
||||||
|
|
||||||
|
if(periods > 512) {
|
||||||
|
snprintf(reset_reason, sizeof(reset_reason), "1oo256: %lu periods is too much", periods);
|
||||||
|
frame_state = NFCV_FRAME_STATE_RESET;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(periods == 2) {
|
||||||
|
frame_state = NFCV_FRAME_STATE_EOF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
periods_previous = 512 - (periods + 1);
|
||||||
|
byte_value = (periods - 1) / 2;
|
||||||
|
frame_payload[frame_pos++] = (uint8_t)byte_value;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if(periods != 1) {
|
||||||
|
snprintf(reset_reason, sizeof(reset_reason), "1oo256: Expected a single low pulse");
|
||||||
|
frame_state = NFCV_FRAME_STATE_RESET;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NFCV_FRAME_STATE_CODING_4:
|
||||||
|
/* evaluate high periods on falling edge */
|
||||||
|
if(!state) {
|
||||||
|
timeout = cur_time + bit_time * 16;
|
||||||
|
if(periods_previous > periods) {
|
||||||
|
snprintf(reset_reason, sizeof(reset_reason), "1oo4: Missing %lu periods from previous symbol, got %lu", periods_previous, periods);
|
||||||
|
frame_state = NFCV_FRAME_STATE_RESET;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* previous symbol left us with some pulse periods */
|
||||||
|
periods -= periods_previous;
|
||||||
|
periods_previous = 0;
|
||||||
|
|
||||||
|
byte_value >>= 2;
|
||||||
|
bits_received += 2;
|
||||||
|
|
||||||
|
if(periods == 1) {
|
||||||
|
byte_value |= 0x00 << 6;
|
||||||
|
periods_previous = 6;
|
||||||
|
} else if(periods == 3) {
|
||||||
|
byte_value |= 0x01 << 6;
|
||||||
|
periods_previous = 4;
|
||||||
|
} else if(periods == 5) {
|
||||||
|
byte_value |= 0x02 << 6;
|
||||||
|
periods_previous = 2;
|
||||||
|
} else if(periods == 7) {
|
||||||
|
byte_value |= 0x03 << 6;
|
||||||
|
periods_previous = 0;
|
||||||
|
} else if(periods == 2) {
|
||||||
|
frame_state = NFCV_FRAME_STATE_EOF;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
snprintf(reset_reason, sizeof(reset_reason), "1oo4: Expected 1/3/5/7 low pulses, but got %lu", periods);
|
||||||
|
frame_state = NFCV_FRAME_STATE_RESET;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bits_received >= 8) {
|
||||||
|
frame_payload[frame_pos++] = (uint8_t)byte_value;
|
||||||
|
bits_received = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(periods != 1) {
|
||||||
|
snprintf(reset_reason, sizeof(reset_reason), "1oo4: Expected a single low pulse");
|
||||||
|
frame_state = NFCV_FRAME_STATE_RESET;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* post-state-machine cleanup and reset */
|
||||||
|
if(frame_state == NFCV_FRAME_STATE_RESET) {
|
||||||
|
timeout = 0;
|
||||||
|
edges_received = 0;
|
||||||
|
frame_state = NFCV_FRAME_STATE_SOF1;
|
||||||
|
FURI_CRITICAL_EXIT();
|
||||||
|
printf("Reset state machine, reason: %s\r\n", reset_reason);
|
||||||
|
in_critical = false;
|
||||||
|
furi_delay_ms(50);
|
||||||
|
} else if(frame_state == NFCV_FRAME_STATE_EOF) {
|
||||||
|
FURI_CRITICAL_EXIT();
|
||||||
|
in_critical = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no edges detected */
|
||||||
|
if(timeout && cur_time > timeout) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* might exit early on overflows. guess thats okay. */
|
||||||
|
if(cur_time > next_sleep) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(in_critical) {
|
||||||
|
FURI_CRITICAL_EXIT();
|
||||||
|
in_critical = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(frame_state == NFCV_FRAME_STATE_EOF) {
|
||||||
|
|
||||||
|
furi_hal_gpio_write(&gpio_spi_r_mosi, false);
|
||||||
|
furi_delay_us(10);
|
||||||
|
furi_hal_gpio_write(&gpio_spi_r_mosi, true);
|
||||||
|
furi_delay_us(10);
|
||||||
|
furi_hal_gpio_write(&gpio_spi_r_mosi, false);
|
||||||
|
furi_delay_us(10);
|
||||||
|
furi_hal_gpio_write(&gpio_spi_r_mosi, true);
|
||||||
|
furi_delay_us(10);
|
||||||
|
furi_hal_gpio_write(&gpio_spi_r_mosi, false);
|
||||||
|
|
||||||
|
nfcv_emu_handle_packet(nfc_data, nfcv_data, frame_payload, frame_pos);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_delay_ms(0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,10 +8,73 @@
|
|||||||
#include <furi_hal_nfc.h>
|
#include <furi_hal_nfc.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define NFCV_FC (13560000.0f)
|
||||||
|
#define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC/32) / 2.0f) /* 1.1799 µs */
|
||||||
|
#define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */
|
||||||
|
|
||||||
|
#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f)
|
||||||
|
#define DIGITAL_SIGNAL_UNIT_US (100000.0f)
|
||||||
|
|
||||||
#define NFCV_TOTAL_BLOCKS_MAX 32
|
#define NFCV_TOTAL_BLOCKS_MAX 32
|
||||||
#define NFCV_BLOCK_SIZE 4
|
#define NFCV_BLOCK_SIZE 4
|
||||||
#define NFCV_MAX_DUMP_SIZE (NFCV_BLOCK_SIZE*NFCV_TOTAL_BLOCKS_MAX)
|
#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 {
|
typedef enum {
|
||||||
NfcVAuthMethodManual,
|
NfcVAuthMethodManual,
|
||||||
NfcVAuthMethodTonieBox,
|
NfcVAuthMethodTonieBox,
|
||||||
@@ -39,8 +102,10 @@ typedef struct {
|
|||||||
uint8_t block_num;
|
uint8_t block_num;
|
||||||
uint8_t block_size;
|
uint8_t block_size;
|
||||||
uint8_t data[NFCV_MAX_DUMP_SIZE];
|
uint8_t data[NFCV_MAX_DUMP_SIZE];
|
||||||
|
bool privacy;
|
||||||
|
|
||||||
char error[32];
|
char error[32];
|
||||||
|
char last_command[128];
|
||||||
} NfcVData;
|
} NfcVData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -51,4 +116,8 @@ typedef struct {
|
|||||||
ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data);
|
ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data);
|
||||||
ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data);
|
ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data);
|
||||||
ReturnCode nfcv_inventory(uint8_t* uid);
|
ReturnCode nfcv_inventory(uint8_t* uid);
|
||||||
|
bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data);
|
||||||
|
|
||||||
|
void nfcv_emu_init();
|
||||||
|
void nfcv_emu_deinit();
|
||||||
|
bool nfcv_emu_loop(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, uint32_t timeout_ms);
|
||||||
|
|||||||
@@ -14,20 +14,6 @@ bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool slix_l_read_card(
|
|
||||||
NfcVReader* reader,
|
|
||||||
FuriHalNfcDevData* nfc_data,
|
|
||||||
NfcVData* nfcv_data) {
|
|
||||||
furi_assert(reader);
|
|
||||||
furi_assert(nfcv_data);
|
|
||||||
|
|
||||||
if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
reader->blocks_to_read = nfcv_data->block_num;
|
|
||||||
return (nfcv_read_blocks(reader, nfcv_data) == ERR_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnCode slix_l_get_random(uint8_t* rand) {
|
ReturnCode slix_l_get_random(uint8_t* rand) {
|
||||||
uint16_t received = 0;
|
uint16_t received = 0;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
|
|
||||||
bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2);
|
bool slix_l_check_card_type(uint8_t UID0, uint8_t UID1, uint8_t UID2);
|
||||||
bool slix_l_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data);
|
|
||||||
|
|
||||||
ReturnCode slix_l_get_random(uint8_t* rand);
|
ReturnCode slix_l_get_random(uint8_t* rand);
|
||||||
ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password);
|
ReturnCode slix_l_unlock(uint32_t id, uint8_t* rand, uint32_t password);
|
||||||
|
|||||||
Reference in New Issue
Block a user