|
|
@@ -1,20 +1,183 @@
|
|
|
#include "../mag_i.h"
|
|
|
|
|
|
+#define PIN_A 0
|
|
|
+#define PIN_B 1 // currently unused
|
|
|
+#define CLOCK_US 250 // typically set between 200-500us
|
|
|
+
|
|
|
+uint8_t magspoof_bit_dir = 0;
|
|
|
+
|
|
|
+void mag_scene_emulate_test_dialog_callback(DialogExResult result, void* context) {
|
|
|
+ Mag* mag = context;
|
|
|
+
|
|
|
+ view_dispatcher_send_custom_event(mag->view_dispatcher, result);
|
|
|
+}
|
|
|
+
|
|
|
+void gpio_item_set_rfid_pin(uint8_t index, bool level) {
|
|
|
+ if(index == 0) {
|
|
|
+ furi_hal_gpio_write(&gpio_rfid_carrier_out, level);
|
|
|
+ // A7 GPIO pin for debugging purposes
|
|
|
+ //furi_hal_gpio_write(&gpio_ext_pa7, level);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void play_bit(uint8_t send_bit) {
|
|
|
+ magspoof_bit_dir ^= 1;
|
|
|
+ gpio_item_set_rfid_pin(PIN_A, magspoof_bit_dir);
|
|
|
+ // PIN_B goes unused in current LF modulation.
|
|
|
+ // Leaving legacy here in event we attempt downstream modulation,
|
|
|
+ // rather than just modulating RFID_OUT upstream for signal forming
|
|
|
+ gpio_item_set_rfid_pin(PIN_B, !magspoof_bit_dir);
|
|
|
+ furi_delay_us(CLOCK_US);
|
|
|
+
|
|
|
+ if(send_bit) {
|
|
|
+ magspoof_bit_dir ^= 1;
|
|
|
+ gpio_item_set_rfid_pin(PIN_A, magspoof_bit_dir);
|
|
|
+ gpio_item_set_rfid_pin(PIN_B, !magspoof_bit_dir);
|
|
|
+ }
|
|
|
+ furi_delay_us(CLOCK_US);
|
|
|
+}
|
|
|
+
|
|
|
+static void mag_spoof(FuriString* track_str, uint8_t track) {
|
|
|
+ furi_hal_power_enable_otg();
|
|
|
+
|
|
|
+ size_t from;
|
|
|
+ size_t to;
|
|
|
+
|
|
|
+ // TODO ';' in first track case
|
|
|
+ if(track == 0) {
|
|
|
+ from = furi_string_search_char(track_str, '%');
|
|
|
+ to = furi_string_search_char(track_str, '?', from);
|
|
|
+ } else if(track == 1) {
|
|
|
+ from = furi_string_search_char(track_str, ';');
|
|
|
+ to = furi_string_search_char(track_str, '?', from);
|
|
|
+ } else {
|
|
|
+ from = 0;
|
|
|
+ to = furi_string_size(track_str);
|
|
|
+ }
|
|
|
+ if(from >= to) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ furi_string_mid(track_str, from, to - from + 1);
|
|
|
+
|
|
|
+ const char* data = furi_string_get_cstr(track_str);
|
|
|
+
|
|
|
+ printf("%s", data);
|
|
|
+
|
|
|
+ furi_hal_ibutton_start_drive();
|
|
|
+ furi_hal_ibutton_pin_low();
|
|
|
+
|
|
|
+ // this doesn't seem to make a difference, leaving it in
|
|
|
+ furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
|
|
+ furi_hal_gpio_write(&gpio_rfid_data_in, false);
|
|
|
+
|
|
|
+ // false->ground RFID antenna; true->don't ground
|
|
|
+ // skotopes (RFID dev) say normally you'd want RFID_PULL in high for signal forming, while modulating RFID_OUT
|
|
|
+ // dunaevai135 had it low in their old code. Leaving low, as it doesn't seem to make a difference on my janky antenna
|
|
|
+ furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
|
|
+ furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false);
|
|
|
+
|
|
|
+ furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
|
|
+
|
|
|
+ // A7 GPIO pin for debugging purposes
|
|
|
+ //furi_hal_gpio_init(&gpio_ext_pa7, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
|
|
+
|
|
|
+ furi_delay_ms(300);
|
|
|
+
|
|
|
+ FURI_CRITICAL_ENTER();
|
|
|
+
|
|
|
+ const uint8_t bitlen[] = {7, 5, 5};
|
|
|
+ const int sublen[] = {32, 48, 48};
|
|
|
+ int tmp, crc, lrc = 0;
|
|
|
+ magspoof_bit_dir = 0;
|
|
|
+
|
|
|
+ // First put out a bunch of leading zeros.
|
|
|
+ for(uint8_t i = 0; i < 25; i++) {
|
|
|
+ play_bit(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ for(uint8_t i = 0; data[i] != '\0'; i++) {
|
|
|
+ crc = 1;
|
|
|
+ tmp = data[i] - sublen[track];
|
|
|
+
|
|
|
+ for(uint8_t j = 0; j < bitlen[track] - 1; j++) {
|
|
|
+ crc ^= tmp & 1;
|
|
|
+ lrc ^= (tmp & 1) << j;
|
|
|
+ play_bit(tmp & 1);
|
|
|
+ tmp >>= 1;
|
|
|
+ }
|
|
|
+ play_bit(crc);
|
|
|
+ }
|
|
|
+
|
|
|
+ // finish calculating and send last "byte" (LRC)
|
|
|
+ tmp = lrc;
|
|
|
+ crc = 1;
|
|
|
+ for(uint8_t j = 0; j < bitlen[track] - 1; j++) {
|
|
|
+ crc ^= tmp & 1;
|
|
|
+ play_bit(tmp & 1);
|
|
|
+ tmp >>= 1;
|
|
|
+ }
|
|
|
+ play_bit(crc);
|
|
|
+
|
|
|
+ // finish with 0's
|
|
|
+ for(uint8_t i = 0; i < 5 * 5; i++) {
|
|
|
+ play_bit(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ gpio_item_set_rfid_pin(PIN_A, 0);
|
|
|
+ gpio_item_set_rfid_pin(PIN_B, 0);
|
|
|
+
|
|
|
+ FURI_CRITICAL_EXIT();
|
|
|
+
|
|
|
+ furi_hal_rfid_pins_reset();
|
|
|
+ furi_hal_power_disable_otg();
|
|
|
+}
|
|
|
+
|
|
|
void mag_scene_emulate_test_on_enter(void* context) {
|
|
|
Mag* mag = context;
|
|
|
- UNUSED(mag);
|
|
|
+ Widget* widget = mag->widget;
|
|
|
+
|
|
|
+ FuriString* tmp_string;
|
|
|
+ tmp_string = furi_string_alloc();
|
|
|
+
|
|
|
+ widget_add_button_element(widget, GuiButtonTypeLeft, "Back", mag_widget_callback, mag);
|
|
|
+ widget_add_button_element(widget, GuiButtonTypeCenter, "Emulate", mag_widget_callback, mag);
|
|
|
+
|
|
|
+ furi_string_printf(tmp_string, "Emulate?");
|
|
|
+ widget_add_string_element(
|
|
|
+ widget, 64, 0, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp_string));
|
|
|
+ furi_string_reset(tmp_string);
|
|
|
+
|
|
|
+ view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget);
|
|
|
+ furi_string_free(tmp_string);
|
|
|
}
|
|
|
|
|
|
bool mag_scene_emulate_test_on_event(void* context, SceneManagerEvent event) {
|
|
|
Mag* mag = context;
|
|
|
- UNUSED(mag);
|
|
|
- UNUSED(event);
|
|
|
+ SceneManager* scene_manager = mag->scene_manager;
|
|
|
bool consumed = false;
|
|
|
|
|
|
+ if(event.type == SceneManagerEventTypeCustom) {
|
|
|
+ if(event.event == DialogExResultCenter) {
|
|
|
+ consumed = true;
|
|
|
+
|
|
|
+ // Hardcoding a test string for the time being, while we debug/improve LF RFID TX
|
|
|
+ FuriString* v = furi_string_alloc();
|
|
|
+ furi_string_set_str(
|
|
|
+ v,
|
|
|
+ "%B123456781234567^LASTNAME/FIRST^YYMMSSSDDDDDDDDDDDDDDDDDDDDDDDDD?;1234567812?");
|
|
|
+ mag_spoof(v, 0);
|
|
|
+ furi_string_free(v);
|
|
|
+ } else if(event.event == GuiButtonTypeLeft) {
|
|
|
+ consumed = true;
|
|
|
+
|
|
|
+ scene_manager_previous_scene(scene_manager);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return consumed;
|
|
|
}
|
|
|
|
|
|
void mag_scene_emulate_test_on_exit(void* context) {
|
|
|
Mag* mag = context;
|
|
|
- UNUSED(mag);
|
|
|
+ widget_reset(mag->widget);
|
|
|
}
|