MX 1 год назад
Родитель
Сommit
714b1cd4c8
9 измененных файлов с 246 добавлено и 104 удалено
  1. 1 1
      application.fam
  2. 36 26
      helpers/mag_helpers.c
  3. 2 0
      helpers/mag_types.h
  4. 1 0
      mag_i.h
  5. 47 16
      mag_state.c
  6. 3 1
      mag_state.h
  7. 32 11
      scenes/mag_scene_emulate_config.c
  8. 0 16
      scenes/mag_scene_saved_menu.c
  9. 124 33
      scenes/mag_scene_settings.c

+ 1 - 1
application.fam

@@ -17,7 +17,7 @@ App(
     fap_category="GPIO",
     fap_icon_assets="icons",
     fap_icon_assets_symbol="mag",
-    fap_version=(0, 7),  # major, minor
+    fap_version=(0, 9),  # major, minor
     fap_description="Enables wireless transmission of magstripe data",
     fap_author="Zachary Weiss",
     fap_weburl="https://github.com/zacharyweiss/magspoof_flipper",

+ 36 - 26
helpers/mag_helpers.c

@@ -5,6 +5,7 @@
 #define ZERO_PREFIX 25 // n zeros prefix
 #define ZERO_BETWEEN 53 // n zeros between tracks
 #define ZERO_SUFFIX 25 // n zeros suffix
+#define REPEAT_DELAY_MS 50
 
 // bits per char on a given track
 const uint8_t bitlen[] = {7, 5, 5};
@@ -331,40 +332,49 @@ void mag_spoof(Mag* mag) {
 
     if(!tx_init(state)) return;
 
-    FURI_CRITICAL_ENTER();
-    for(uint16_t i = 0; i < (ZERO_PREFIX * 2); i++) {
-        // is this right?
-        if(!!(i % 2)) bit ^= 1;
-        play_halfbit(bit, state);
-        furi_delay_us(state->us_clock);
-    }
+    uint8_t i = 0;
+    do {
+        FURI_CRITICAL_ENTER();
+        for(uint16_t i = 0; i < (ZERO_PREFIX * 2); i++) {
+            // is this right?
+            if(!!(i % 2)) bit ^= 1;
+            play_halfbit(bit, state);
+            furi_delay_us(state->us_clock);
+        }
+
+        if((state->track == MagTrackStateOneAndTwo) || (state->track == MagTrackStateOne))
+            play_track((uint8_t*)bits_t1_manchester, bits_t1_count, state, false);
 
-    if((state->track == MagTrackStateOneAndTwo) || (state->track == MagTrackStateOne))
-        play_track((uint8_t*)bits_t1_manchester, bits_t1_count, state, false);
+        if((state->track == MagTrackStateOneAndTwo))
+            for(uint16_t i = 0; i < (ZERO_BETWEEN * 2); i++) {
+                if(!!(i % 2)) bit ^= 1;
+                play_halfbit(bit, state);
+                furi_delay_us(state->us_clock);
+            }
 
-    if((state->track == MagTrackStateOneAndTwo))
-        for(uint16_t i = 0; i < (ZERO_BETWEEN * 2); i++) {
+        if((state->track == MagTrackStateOneAndTwo) || (state->track == MagTrackStateTwo))
+            play_track(
+                (uint8_t*)bits_t2_manchester,
+                bits_t2_count,
+                state,
+                (state->reverse == MagReverseStateOn));
+
+        if((state->track == MagTrackStateThree))
+            play_track((uint8_t*)bits_t3_manchester, bits_t3_count, state, false);
+
+        for(uint16_t i = 0; i < (ZERO_SUFFIX * 2); i++) {
             if(!!(i % 2)) bit ^= 1;
             play_halfbit(bit, state);
             furi_delay_us(state->us_clock);
         }
+        FURI_CRITICAL_EXIT();
 
-    if((state->track == MagTrackStateOneAndTwo) || (state->track == MagTrackStateTwo))
-        play_track(
-            (uint8_t*)bits_t2_manchester,
-            bits_t2_count,
-            state,
-            (state->reverse == MagReverseStateOn));
-
-    if((state->track == MagTrackStateThree))
-        play_track((uint8_t*)bits_t3_manchester, bits_t3_count, state, false);
+        i++;
+        FURI_LOG_D(
+            TAG, "TX %u (n_repeats: %u, repeat_mode: %u)", i, state->n_repeats, state->repeat_mode);
+        furi_delay_ms(REPEAT_DELAY_MS);
 
-    for(uint16_t i = 0; i < (ZERO_SUFFIX * 2); i++) {
-        if(!!(i % 2)) bit ^= 1;
-        play_halfbit(bit, state);
-        furi_delay_us(state->us_clock);
-    }
-    FURI_CRITICAL_EXIT();
+    } while((i < state->n_repeats) && state->repeat_mode);
 
     free(data1);
     free(data2);

+ 2 - 0
helpers/mag_types.h

@@ -46,6 +46,8 @@ typedef enum {
 #define MAG_STATE_DEFAULT_PIN_OUTPUT MagPinA6
 #define MAG_STATE_DEFAULT_PIN_ENABLE MagPinA4
 #define MAG_STATE_DEFAULT_ALLOW_UART false
+#define MAG_STATE_DEFAULT_N_REPEATS 3
+#define MAG_STATE_DEFAULT_REPEAT_MODE true
 
 typedef enum {
     MagViewSubmenu,

+ 1 - 0
mag_i.h

@@ -53,6 +53,7 @@ enum MagCustomEvent {
     MagEventNext = 100,
     MagEventExit,
     MagEventPopupClosed,
+    MagEventConfirmDialog,
 };
 
 typedef struct {

+ 47 - 16
mag_state.c

@@ -50,15 +50,41 @@ bool mag_state_load(MagState* out_state) {
             if(!flipper_format_file_open_existing(file, MAG_STATE_PATH)) break;
             if(!flipper_format_read_header(file, str, &tmp)) break;
             if(furi_string_cmp_str(str, MAG_STATE_HEADER)) break;
-            if(tmp != MAG_STATE_VER) break;
-
-            if(!flipper_format_read_uint32(file, "pin_input", &tmp, 1)) break;
-            state.pin_input = tmp;
-            if(!flipper_format_read_uint32(file, "pin_output", &tmp, 1)) break;
-            state.pin_output = tmp;
-            if(!flipper_format_read_uint32(file, "pin_enable", &tmp, 1)) break;
-            state.pin_enable = tmp;
-            if(!flipper_format_read_bool(file, "allow_uart", &state.allow_uart, 1)) break;
+            // if(tmp != MAG_STATE_VER) break;
+
+            if(!flipper_format_read_uint32(file, "pin_input", &tmp, 1)) {
+                flipper_format_rewind(file);
+                tmp = MAG_STATE_DEFAULT_PIN_INPUT;
+            }
+            state.pin_input = (MagPin)tmp;
+
+            if(!flipper_format_read_uint32(file, "pin_output", &tmp, 1)) {
+                flipper_format_rewind(file);
+                tmp = MAG_STATE_DEFAULT_PIN_OUTPUT;
+            }
+            state.pin_output = (MagPin)tmp;
+
+            if(!flipper_format_read_uint32(file, "pin_enable", &tmp, 1)) {
+                flipper_format_rewind(file);
+                tmp = MAG_STATE_DEFAULT_PIN_ENABLE;
+            }
+            state.pin_enable = (MagPin)tmp;
+
+            if(!flipper_format_read_bool(file, "allow_uart", &state.allow_uart, 1)) {
+                flipper_format_rewind(file);
+                state.allow_uart = MAG_STATE_DEFAULT_ALLOW_UART;
+            }
+
+            if(!flipper_format_read_uint32(file, "n_repeats", &tmp, 1)) {
+                flipper_format_rewind(file);
+                tmp = MAG_STATE_DEFAULT_N_REPEATS;
+            }
+            state.n_repeats = (uint8_t)tmp;
+
+            if(!flipper_format_read_bool(file, "repeat_mode", &state.repeat_mode, 1)) {
+                flipper_format_rewind(file);
+                state.repeat_mode = MAG_STATE_DEFAULT_REPEAT_MODE;
+            }
 
             loaded_from_file = true;
         } while(0);
@@ -66,16 +92,18 @@ bool mag_state_load(MagState* out_state) {
     }
     furi_record_close(RECORD_STORAGE);
 
-    // If could not be read from file
-    // Or file GPIO config is invalid (pins overlap)
-    // Set defaults
+    // If file's GPIO config is invalid (pins overlap)
+    // Reset to defaults
     // Additionally raise message to user?
-    if(!loaded_from_file || !mag_state_gpio_is_valid(&state)) {
+    if(!mag_state_gpio_is_valid(&state)) {
         mag_state_gpio_reset(&state);
     }
 
     if(!loaded_from_file) {
+        mag_state_gpio_reset(&state);
         state.allow_uart = MAG_STATE_DEFAULT_ALLOW_UART;
+        state.n_repeats = MAG_STATE_DEFAULT_N_REPEATS;
+        state.repeat_mode = MAG_STATE_DEFAULT_REPEAT_MODE;
     }
 
     // set defaults we don't save
@@ -102,13 +130,16 @@ void mag_state_save(MagState* state) {
         if(!flipper_format_file_open_always(file, MAG_STATE_PATH)) break;
         if(!flipper_format_write_header_cstr(file, MAG_STATE_HEADER, MAG_STATE_VER)) break;
 
-        tmp = state->pin_input;
+        tmp = (uint32_t)state->pin_input;
         if(!flipper_format_write_uint32(file, "pin_input", &tmp, 1)) break;
-        tmp = state->pin_output;
+        tmp = (uint32_t)state->pin_output;
         if(!flipper_format_write_uint32(file, "pin_output", &tmp, 1)) break;
-        tmp = state->pin_enable;
+        tmp = (uint32_t)state->pin_enable;
         if(!flipper_format_write_uint32(file, "pin_enable", &tmp, 1)) break;
         if(!flipper_format_write_bool(file, "allow_uart", &state->allow_uart, 1)) break;
+        tmp = (uint32_t)state->n_repeats;
+        if(!flipper_format_write_uint32(file, "n_repeats", &tmp, 1)) break;
+        if(!flipper_format_write_bool(file, "repeat_mode", &state->repeat_mode, 1)) break;
 
     } while(0);
     flipper_format_free(file);

+ 3 - 1
mag_state.h

@@ -14,7 +14,7 @@
 #include "helpers/mag_types.h"
 
 #define MAG_STATE_HEADER "Mag State"
-#define MAG_STATE_VER 1
+#define MAG_STATE_VER 2
 #define MAG_STATE_DIR STORAGE_APP_DATA_PATH_PREFIX
 #define MAG_STATE_PATH MAG_STATE_DIR "/mag_state.txt"
 
@@ -29,6 +29,8 @@ typedef struct {
     MagPin pin_enable;
     bool allow_uart;
     bool is_debug;
+    uint8_t n_repeats;
+    bool repeat_mode;
 } MagState;
 
 const GpioPin* mag_state_enum_to_pin(MagPin pin);

+ 32 - 11
scenes/mag_scene_emulate_config.c

@@ -3,11 +3,12 @@
 #define TAG "MagSceneEmulateConfig"
 
 enum MagEmulateConfigIndex {
-    MagEmulateConfigIndexTx,
+    MagEmulateConfigIndexClock,
     MagEmulateConfigIndexTrack,
     MagEmulateConfigIndexReverse,
-    MagEmulateConfigIndexClock,
-    MagEmulateConfigIndexInterpacket,
+    MagEmulateConfigIndexRepeat,
+    MagEmulateConfigIndexTx,
+    // MagEmulateConfigIndexInterpacket,
 };
 
 #define TX_COUNT 7
@@ -163,6 +164,14 @@ static void mag_scene_emulate_config_set_reverse(VariableItem* item) {
     }
 };
 
+static void mag_scene_emulate_config_set_repeat_mode(VariableItem* item) {
+    Mag* mag = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, reverse_text[index]);
+
+    mag->state.repeat_mode = (bool)index;
+}
+
 static void mag_scene_emulate_config_set_clock(VariableItem* item) {
     Mag* mag = variable_item_get_context(item);
     uint8_t index = variable_item_get_current_value_index(item);
@@ -182,8 +191,6 @@ static void mag_scene_emulate_config_set_interpacket(VariableItem* item) {
 };
 
 void mag_scene_emulate_config_on_enter(void* context) {
-    // TODO: retrieve current values from struct, rather than setting to default on setup
-
     Mag* mag = context;
     VariableItem* item;
     uint8_t value_index;
@@ -192,7 +199,7 @@ void mag_scene_emulate_config_on_enter(void* context) {
     item = variable_item_list_add(
         mag->variable_item_list, "Clock:", CLOCK_COUNT, mag_scene_emulate_config_set_clock, mag);
     value_index = value_index_uint32(mag->state.us_clock, clock_value, CLOCK_COUNT);
-    scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
+    // scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
     variable_item_set_current_value_index(item, value_index);
     variable_item_set_current_value_text(item, clock_text[value_index]);
 
@@ -200,7 +207,7 @@ void mag_scene_emulate_config_on_enter(void* context) {
     item = variable_item_list_add(
         mag->variable_item_list, "Track:", TRACK_COUNT, mag_scene_emulate_config_set_track, mag);
     value_index = value_index_uint32(mag->state.track, track_value, TRACK_COUNT);
-    scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
+    //scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
     variable_item_set_current_value_index(item, value_index);
     variable_item_set_current_value_text(item, track_text[value_index]);
 
@@ -213,7 +220,19 @@ void mag_scene_emulate_config_on_enter(void* context) {
         mag_scene_emulate_config_set_reverse,
         mag);
     value_index = value_index_uint32(mag->state.reverse, reverse_value, REVERSE_COUNT);
-    scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
+    //scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, reverse_text[value_index]);
+
+    // Repeated TX
+    item = variable_item_list_add(
+        mag->variable_item_list,
+        "Repeat:",
+        REVERSE_COUNT,
+        mag_scene_emulate_config_set_repeat_mode,
+        mag);
+    value_index = (uint32_t)mag->state.repeat_mode;
+    //scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
     variable_item_set_current_value_index(item, value_index);
     variable_item_set_current_value_text(item, reverse_text[value_index]);
 
@@ -224,7 +243,7 @@ void mag_scene_emulate_config_on_enter(void* context) {
         item = variable_item_list_add(
             mag->variable_item_list, "TX via:", TX_COUNT, mag_scene_emulate_config_set_tx, mag);
         value_index = value_index_uint32(mag->state.tx, tx_value, TX_COUNT);
-        scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
+        //scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item);
         variable_item_set_current_value_index(item, value_index);
         variable_item_set_current_value_text(item, tx_text[value_index]);
 #ifdef FW_ORIGIN_Official
@@ -247,6 +266,10 @@ void mag_scene_emulate_config_on_enter(void* context) {
     variable_item_set_current_value_text(item, interpacket_text[value_index]);*/
     UNUSED(mag_scene_emulate_config_set_interpacket);
 
+    variable_item_list_set_selected_item(
+        mag->variable_item_list,
+        scene_manager_get_scene_state(mag->scene_manager, MagSceneEmulateConfig));
+
     view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewVariableItemList);
 }
 
@@ -266,6 +289,4 @@ void mag_scene_emulate_config_on_exit(void* context) {
     Mag* mag = context;
     variable_item_list_set_selected_item(mag->variable_item_list, 0);
     variable_item_list_reset(mag->variable_item_list);
-    // mag_last_settings_save?
-    // scene_manager_set_scene_state? Using subghz_scene_reciever_config as framework/inspo
 }

+ 0 - 16
scenes/mag_scene_saved_menu.c

@@ -17,22 +17,6 @@ void mag_scene_saved_menu_on_enter(void* context) {
     Mag* mag = context;
     Submenu* submenu = mag->submenu;
 
-    // messy code to quickly check which tracks are available for emulation/display
-    // there's likely a better spot to do this, but the MagDevice functions don't have access to the full mag struct...
-    bool is_empty_t1 = furi_string_empty(mag->mag_dev->dev_data.track[0].str);
-    bool is_empty_t2 = furi_string_empty(mag->mag_dev->dev_data.track[1].str);
-    bool is_empty_t3 = furi_string_empty(mag->mag_dev->dev_data.track[2].str);
-
-    if(!is_empty_t1 && !is_empty_t2) {
-        mag->state.track = MagTrackStateOneAndTwo;
-    } else if(!is_empty_t1) {
-        mag->state.track = MagTrackStateOne;
-    } else if(!is_empty_t2) {
-        mag->state.track = MagTrackStateTwo;
-    } else if(!is_empty_t3) {
-        mag->state.track = MagTrackStateThree;
-    } // TODO: what happens if no track data present?
-
     submenu_add_item(
         submenu, "Emulate", SubmenuIndexEmulate, mag_scene_saved_menu_submenu_callback, mag);
     //submenu_add_item(

+ 124 - 33
scenes/mag_scene_settings.c

@@ -8,7 +8,11 @@ enum VarItemListIndex {
     VarItemListIndexPinInput,
     VarItemListIndexPinOutput,
     VarItemListIndexPinEnable,
+    VarItemListIndexNRepeats,
+    VarItemListIndexRepeatModeOn,
+#ifndef FW_ORIGIN_Official
     VarItemListIndexAllowUART,
+#endif
 };
 
 static const char* gpio[] = {
@@ -25,6 +29,40 @@ const uint8_t GPIO_COUNT = COUNT_OF(gpio);
 // static const char* uart_pins[] = {[DapUartTypeUSART1] = "13,14", [DapUartTypeLPUART1] = "15,16"};
 // static const char* uart_swap[] = {[DapUartTXRXNormal] = "No", [DapUartTXRXSwap] = "Yes"};
 
+#define N_REPEATS_COUNT 10
+const char* const n_repeats_text[N_REPEATS_COUNT] = {
+    "2",
+    "3",
+    "4",
+    "5",
+    "6",
+    "7",
+    "8",
+    "9",
+    "10",
+    "20",
+};
+const uint32_t n_repeats_value[N_REPEATS_COUNT] = {
+    2,
+    3,
+    4,
+    5,
+    6,
+    7,
+    8,
+    9,
+    10,
+    20,
+};
+
+#define OFF_ON_COUNT 2
+const char* const off_on_text[OFF_ON_COUNT] = {
+    "OFF",
+    "ON",
+};
+
+VariableItem* item_dialog_cb;
+
 void mag_scene_settings_var_item_list_callback(void* context, uint32_t index) {
     Mag* mag = context;
     view_dispatcher_send_custom_event(mag->view_dispatcher, index);
@@ -51,6 +89,39 @@ static void mag_scene_settings_set_gpio_enable(VariableItem* item) {
     mag_scene_settings_set_gpio(item, &mag->state.pin_enable);
 };
 
+static void mag_scene_settings_set_n_repeats(VariableItem* item) {
+    Mag* mag = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, n_repeats_text[index]);
+    mag->state.n_repeats = n_repeats_value[index];
+}
+
+static void mag_scene_settings_set_bool(VariableItem* item, bool* bool_out) {
+    uint8_t index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, off_on_text[index]);
+    *bool_out = (bool)index;
+}
+
+static void mag_scene_settings_set_repeat_mode(VariableItem* item) {
+    Mag* mag = variable_item_get_context(item);
+    mag_scene_settings_set_bool(item, &mag->state.repeat_mode);
+}
+
+static void mag_scene_settings_set_allow_uart(VariableItem* item) {
+    Mag* mag = variable_item_get_context(item);
+
+    // rising change when value index is truth-y, and prior value false
+    bool rising = !mag->state.allow_uart && !!variable_item_get_current_value_index(item);
+    // trigger dialog only on rising change
+    if(rising) {
+        item_dialog_cb = item;
+        view_dispatcher_send_custom_event(mag->view_dispatcher, MagEventConfirmDialog);
+    }
+
+    // set value & text based on current varitem index
+    mag_scene_settings_set_bool(item, &mag->state.allow_uart);
+}
+
 static void mag_pin_variable_item_list_add(
     Mag* mag,
     const char* label,
@@ -62,10 +133,30 @@ static void mag_pin_variable_item_list_add(
     variable_item_set_current_value_text(item, gpio[pin]);
 }
 
+static void mag_bool_variable_item_list_add(
+    Mag* mag,
+    const char* label,
+    bool value,
+    VariableItemChangeCallback change_callback) {
+    VariableItem* item =
+        variable_item_list_add(mag->variable_item_list, label, OFF_ON_COUNT, change_callback, mag);
+    uint32_t value_index = (uint32_t)value;
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, off_on_text[value_index]);
+}
+
 void mag_scene_settings_on_enter(void* context) {
     Mag* mag = context;
-    VariableItem* item;
     VariableItemList* var_item_list = mag->variable_item_list;
+    VariableItem* item;
+    uint32_t value_index;
+
+    // reload state in the event temporary changes have been
+    // made on the emulate config screen
+    // only changes made in this scene should be saved, and this scene
+    // should always represent the saved settings, not the transient ones for
+    // a given emulation.
+    mag_state_load(&mag->state);
 
     mag_pin_variable_item_list_add(
         mag, "Input pin:", mag->state.pin_input, mag_scene_settings_set_gpio_input);
@@ -74,8 +165,17 @@ void mag_scene_settings_on_enter(void* context) {
     mag_pin_variable_item_list_add(
         mag, "Enable pin:", mag->state.pin_enable, mag_scene_settings_set_gpio_enable);
 
-    item = variable_item_list_add(var_item_list, "UART MSR: ", 1, NULL, mag);
-    variable_item_set_current_value_text(item, mag->state.allow_uart ? "ON" : "OFF");
+    mag_bool_variable_item_list_add(
+        mag, "Repeat default:", mag->state.repeat_mode, mag_scene_settings_set_repeat_mode);
+
+    item = variable_item_list_add(
+        var_item_list, "# repeats: ", N_REPEATS_COUNT, mag_scene_settings_set_n_repeats, mag);
+    value_index = value_index_uint32(mag->state.n_repeats, n_repeats_value, N_REPEATS_COUNT);
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, n_repeats_text[value_index]);
+
+    mag_bool_variable_item_list_add(
+        mag, "UART MSR:", mag->state.allow_uart, mag_scene_settings_set_allow_uart);
 
     variable_item_list_set_enter_callback(
         var_item_list, mag_scene_settings_var_item_list_callback, mag);
@@ -109,34 +209,6 @@ void mag_scene_settings_dialog_invalid_pins(Mag* mag) {
     }
 }
 
-void mag_scene_settings_dialog_allow_uart(Mag* mag) {
-    bool change = mag->state.allow_uart;
-    if(!change) {
-        DialogMessage* msg = dialog_message_alloc();
-        dialog_message_set_header(msg, "UART MSR", 64, 0, AlignCenter, AlignTop);
-        dialog_message_set_buttons(msg, "No", NULL, "Yes");
-        dialog_message_set_text(
-            msg,
-            "This option requires a\nUART-compatible mag reader.\nIs it installed?\n",
-            64,
-            32,
-            AlignCenter,
-            AlignCenter);
-        DialogMessageButton res = dialog_message_show(furi_record_open(RECORD_DIALOGS), msg);
-        if(res == DialogMessageButtonRight) {
-            change = true;
-        }
-        dialog_message_free(msg);
-        furi_record_close(RECORD_DIALOGS);
-    }
-    if(change) {
-        mag->state.allow_uart = !mag->state.allow_uart;
-        variable_item_set_current_value_text(
-            variable_item_list_get(mag->variable_item_list, VarItemListIndexAllowUART),
-            mag->state.allow_uart ? "ON" : "OFF");
-    }
-}
-
 bool mag_scene_settings_on_event(void* context, SceneManagerEvent event) {
     Mag* mag = context;
     SceneManager* scene_manager = mag->scene_manager;
@@ -153,12 +225,31 @@ bool mag_scene_settings_on_event(void* context, SceneManagerEvent event) {
         } else {
             scene_manager_previous_scene(scene_manager);
         }
+
         break;
     case SceneManagerEventTypeCustom:
         scene_manager_set_scene_state(mag->scene_manager, MagSceneSettings, event.event);
         consumed = true;
-        if(event.event == VarItemListIndexAllowUART) {
-            mag_scene_settings_dialog_allow_uart(mag);
+        if(event.event == MagEventConfirmDialog) {
+            DialogMessage* msg = dialog_message_alloc();
+            dialog_message_set_header(msg, "UART MSR", 64, 0, AlignCenter, AlignTop);
+            dialog_message_set_buttons(msg, "No", NULL, "Yes");
+            dialog_message_set_text(
+                msg,
+                "This option requires a\nUART-compatible mag reader.\nIs it installed?\n",
+                64,
+                32,
+                AlignCenter,
+                AlignCenter);
+            DialogMessageButton res = dialog_message_show(furi_record_open(RECORD_DIALOGS), msg);
+            if(res != DialogMessageButtonRight) {
+                // if not "Yes", reset to "OFF" (0 / false-y)
+                variable_item_set_current_value_index(item_dialog_cb, 0);
+                mag_scene_settings_set_bool(item_dialog_cb, &mag->state.allow_uart);
+            }
+            dialog_message_free(msg);
+            furi_record_close(RECORD_DIALOGS);
+            item_dialog_cb = NULL;
         }
         break;
     default: