diff --git a/application.fam b/application.fam index 3b2551c2f9b..23b18be4311 100644 --- a/application.fam +++ b/application.fam @@ -1,6 +1,6 @@ App( appid="mag", - name="MagSpoof WIP", + name="MagSpoof", apptype=FlipperAppType.EXTERNAL, entry_point="mag_app", cdefines=["APP_MAG"], @@ -11,10 +11,10 @@ App( "dialogs", ], provides=[], - stack_size=5 * 1024, + stack_size=6 * 1024, order=64, # keep it at the bottom of the list while still WIP fap_icon="icons/mag_10px.png", - fap_category="Tools", + fap_category="GPIO", fap_icon_assets="icons", fap_version=(0, 5), # major, minor fap_description="WIP MagSpoof port using the RFID subsystem", diff --git a/helpers/mag_helpers.c b/helpers/mag_helpers.c index c95d13e7adb..2d5e83080a6 100644 --- a/helpers/mag_helpers.c +++ b/helpers/mag_helpers.c @@ -2,8 +2,9 @@ #define TAG "MagHelpers" -#define GPIO_PIN_A &gpio_ext_pa6 -#define GPIO_PIN_B &gpio_ext_pa7 +// Haviv Board - pins gpio_ext_pa7 & gpio_ext_pa6 was swapped. +#define GPIO_PIN_A &gpio_ext_pa7 +#define GPIO_PIN_B &gpio_ext_pa6 #define GPIO_PIN_ENABLE &gpio_ext_pa4 #define RFID_PIN_OUT &gpio_rfid_carrier_out @@ -126,10 +127,12 @@ void tx_init_rfid() { // initialize RFID system for TX // OTG needed for RFID? Or just legacy from GPIO? - furi_hal_power_enable_otg(); + // furi_hal_power_enable_otg(); + furi_hal_ibutton_pin_configure(); + + // furi_hal_ibutton_start_drive(); + furi_hal_ibutton_pin_write(false); - furi_hal_ibutton_start_drive(); - furi_hal_ibutton_pin_low(); // Initializing at GpioSpeedLow seems sufficient for our needs; no improvements seen by increasing speed setting diff --git a/helpers/mag_types.h b/helpers/mag_types.h index 196b7301691..c4f11549aaa 100644 --- a/helpers/mag_types.h +++ b/helpers/mag_types.h @@ -36,3 +36,10 @@ typedef enum { MagTxCC1101_434, MagTxCC1101_868, } MagTxState; + + +typedef enum { + UART_TerminalEventRefreshConsoleOutput = 0, + UART_TerminalEventStartConsole, + UART_TerminalEventStartKeyboard, +} UART_TerminalCustomEvent; diff --git a/mag_device.c b/mag_device.c index a678cee9070..4e4b8b3b786 100644 --- a/mag_device.c +++ b/mag_device.c @@ -230,6 +230,77 @@ bool mag_device_delete(MagDevice* mag_dev, bool use_load_path) { return deleted; } +bool mag_device_parse_card_string(MagDevice* mag_dev, FuriString* f_card_str) { + furi_assert(mag_dev); + FURI_LOG_D(TAG, "mag_device_parse_card_string"); + + const char* card_str = furi_string_get_cstr(f_card_str); + + FURI_LOG_D(TAG, "Parsing card string: %s", card_str); + + // Track 1 + const char* track1_start = strchr(card_str, '%'); + if(!track1_start) { + FURI_LOG_D(TAG, "Could not find track 1 start"); + return false; + } + track1_start++; + const char* track1_end = strchr(track1_start, '?'); + if(!track1_end) { + FURI_LOG_D(TAG, "Could not find track 1 end"); + return false; + } + size_t track1_len = track1_end - track1_start; + + FURI_LOG_D(TAG, "Track 1: %.*s", track1_len, track1_start); + + mag_dev->dev_data.track[0].len = track1_len; + furi_string_printf(mag_dev->dev_data.track[0].str, "%%%.*s?", track1_len, track1_start); + + // Track 2 + const char* track2_start = strchr(track1_end, ';'); + if (!track2_start) { + FURI_LOG_D(TAG, "Could not find track 2 start"); + return true; + } + + track2_start++; + const char* track2_end = strchr(track2_start, '?'); + if(!track2_end) { + FURI_LOG_D(TAG, "Could not find track 2 end"); + return true; + } + size_t track2_len = track2_end - track2_start; + + FURI_LOG_D(TAG, "Track 2: %.*s", track2_len, track2_start); + + mag_dev->dev_data.track[1].len = track2_len; + furi_string_printf(mag_dev->dev_data.track[1].str, "%%%.*s?", track2_len, track2_start); + + // Track 3 + const char* track3_start = strchr(track2_end, ';'); + if (!track3_start) { + FURI_LOG_D(TAG, "Could not find track 3 start"); + return true; + } + + track3_start++; + const char* track3_end = strchr(track3_start, '?'); + if(!track3_end) { + FURI_LOG_D(TAG, "Could not find track 3 end"); + return true; + } + size_t track3_len = track3_end - track3_start; + + FURI_LOG_D(TAG, "Track 3: %.*s", track3_len, track3_start); + + mag_dev->dev_data.track[2].len = track3_len; + furi_string_printf(mag_dev->dev_data.track[2].str, "%%%.*s?", track3_len, track3_start); + + return true; +} + + void mag_device_set_loading_callback( MagDevice* mag_dev, MagLoadingCallback callback, diff --git a/mag_device.h b/mag_device.h index fac209643ee..591cde94374 100644 --- a/mag_device.h +++ b/mag_device.h @@ -17,6 +17,7 @@ typedef void (*MagLoadingCallback)(void* context, bool state); typedef struct { FuriString* str; + size_t len; } MagTrack; typedef struct { @@ -49,6 +50,8 @@ void mag_device_clear(MagDevice* mag_dev); bool mag_device_delete(MagDevice* mag_dev, bool use_load_path); +bool mag_device_parse_card_string(MagDevice* mag_dev, FuriString* card_str); + void mag_device_set_loading_callback( MagDevice* mag_dev, MagLoadingCallback callback, diff --git a/mag_i.h b/mag_i.h index 05c02559f78..e4e8092e072 100644 --- a/mag_i.h +++ b/mag_i.h @@ -33,6 +33,7 @@ #include #include "scenes/mag_scene.h" +#include "scenes/mag_scene_read.h" #define MAG_TEXT_STORE_SIZE 150 @@ -50,6 +51,7 @@ typedef struct { uint32_t us_interpacket; } MagSetting; + typedef struct { ViewDispatcher* view_dispatcher; Gui* gui; @@ -76,6 +78,17 @@ typedef struct { // Custom views Mag_TextInput* mag_text_input; + + // UART + FuriThread* uart_rx_thread; + FuriStreamBuffer* uart_rx_stream; + uint8_t uart_rx_buf[UART_RX_BUF_SIZE + 1]; + void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context); + + char uart_text_input_store[UART_TERMINAL_TEXT_INPUT_STORE_SIZE + 1]; + FuriString* uart_text_box_store; + size_t uart_text_box_store_strlen; + // UART_TextInput* text_input; } Mag; void mag_text_store_set(Mag* mag, const char* text, ...); diff --git a/scenes/mag_scene_config.h b/scenes/mag_scene_config.h index b5c356ba4e9..7ab276e53bf 100644 --- a/scenes/mag_scene_config.h +++ b/scenes/mag_scene_config.h @@ -12,3 +12,4 @@ ADD_SCENE(mag, delete_success, DeleteSuccess) ADD_SCENE(mag, delete_confirm, DeleteConfirm) ADD_SCENE(mag, exit_confirm, ExitConfirm) ADD_SCENE(mag, under_construction, UnderConstruction) +ADD_SCENE(mag, read, Read) \ No newline at end of file diff --git a/scenes/mag_scene_read.c b/scenes/mag_scene_read.c new file mode 100644 index 00000000000..5d779261649 --- /dev/null +++ b/scenes/mag_scene_read.c @@ -0,0 +1,178 @@ +// Creator: Hummus@FlipperGang + +#include "../mag_i.h" +#include "../helpers/mag_helpers.h" + +#include "mag_scene_read.h" + +#define TAG "MagSceneRead" + +void uart_callback(UartIrqEvent event, uint8_t data, void *context) { + Mag* mag = context; + if(event == UartIrqEventRXNE) { + furi_stream_buffer_send(mag->uart_rx_stream, &data, 1, 0); + furi_thread_flags_set(furi_thread_get_id(mag->uart_rx_thread), WorkerEvtRxDone); + } +} + +static int32_t uart_worker(void* context) { + Mag* mag = context; + mag->uart_rx_stream = furi_stream_buffer_alloc(UART_RX_BUF_SIZE, 1); + mag->uart_text_box_store_strlen = 0; + + while (1) { + uint32_t events = furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); + // furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEvtStop) break; + if(events & WorkerEvtRxDone) { + FURI_LOG_D(TAG, "WorkerEvtRxDone"); + // notification_message(mag->notifications, &sequence_success); + size_t len = furi_stream_buffer_receive(mag->uart_rx_stream, mag->uart_rx_buf, UART_RX_BUF_SIZE, 200); + FURI_LOG_D(TAG, "UART RX len: %d", len); + + if (len > 0) { + // If text box store gets too big, then truncate it + mag->uart_text_box_store_strlen += len; + + if(mag->uart_text_box_store_strlen >= UART_TERMINAL_TEXT_BOX_STORE_SIZE - 1) { + furi_string_right(mag->uart_text_box_store, mag->uart_text_box_store_strlen / 2); + mag->uart_text_box_store_strlen = furi_string_size(mag->uart_text_box_store) + len; + } + + // Add '\0' to the end of the string, and then add the new data + mag->uart_rx_buf[len] = '\0'; + furi_string_cat_printf(mag->uart_text_box_store, "%s", mag->uart_rx_buf); + + FURI_LOG_D(TAG, "UART RX buf: %*.s", len, mag->uart_rx_buf); + FURI_LOG_D(TAG, "UART RX store: %s", furi_string_get_cstr(mag->uart_text_box_store)); + + } + + FURI_LOG_D(TAG, "UARTEventRxData"); + + view_dispatcher_send_custom_event( + mag->view_dispatcher, UARTEventRxData); + + } + } + + furi_stream_buffer_free(mag->uart_rx_stream); + + return 0; +} + +void update_widgets(Mag* mag) { + // Clear widget from all elements + widget_reset(mag->widget); + + // Titlebar + widget_add_icon_element(mag->widget, 38, -1, &I_mag_file_10px); + widget_add_string_element(mag->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "READ"); + widget_add_icon_element(mag->widget, 81, -1, &I_mag_file_10px); + + // Text box + widget_add_text_scroll_element(mag->widget, 0, 10, 128, 40, furi_string_get_cstr(mag->uart_text_box_store)); + + // Buttons + widget_add_button_element(mag->widget, GuiButtonTypeLeft, "Clear", mag_widget_callback, mag); + widget_add_button_element(mag->widget, GuiButtonTypeRight, "Parse", mag_widget_callback, mag); +} + +void mag_scene_read_on_enter(void* context) { + Mag* mag = context; + FuriString* message = furi_string_alloc(); + furi_string_printf(message, "Flipper Elite, swipe a card!\n"); + mag->uart_text_box_store = message; + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget); + + update_widgets(mag); + + + // Initialize UART + // furi_hal_console_disable(); + furi_hal_uart_deinit(FuriHalUartIdUSART1); + furi_hal_uart_init(FuriHalUartIdUSART1, 9600); + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_callback, mag); + FURI_LOG_D(TAG, "UART initialized"); + + mag->uart_rx_thread = furi_thread_alloc(); + furi_thread_set_name(mag->uart_rx_thread, "UartRx"); + furi_thread_set_stack_size(mag->uart_rx_thread, 1024); + furi_thread_set_context(mag->uart_rx_thread, mag); + furi_thread_set_callback(mag->uart_rx_thread, uart_worker); + + furi_thread_start(mag->uart_rx_thread); + FURI_LOG_D(TAG, "UART worker started"); +} + +bool mag_scene_read_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + FURI_LOG_D(TAG, "Custom event: %ld", event.event); + + switch(event.event) { + case GuiButtonTypeLeft: // Clear + consumed = true; + // Clear text box store + furi_string_reset(mag->uart_text_box_store); + mag->uart_text_box_store_strlen = 0; + break; + + case GuiButtonTypeRight: // Parse + consumed = true; + FURI_LOG_D(TAG, "Trying to parse"); + MagDevice* mag_dev = mag->mag_dev; + + bool res = mag_device_parse_card_string(mag_dev, mag->uart_text_box_store); + furi_string_reset(mag->uart_text_box_store); + if(res) { + notification_message(mag->notifications, &sequence_success); + + furi_string_printf(mag->uart_text_box_store, "Track 1: %.*s\nTrack 2: %.*s\nTrack 3: %.*s", + mag_dev->dev_data.track[0].len, furi_string_get_cstr(mag_dev->dev_data.track[0].str), + mag_dev->dev_data.track[1].len, furi_string_get_cstr(mag_dev->dev_data.track[1].str), + mag_dev->dev_data.track[2].len, furi_string_get_cstr(mag_dev->dev_data.track[2].str)); + + // Switch to saved menu scene + scene_manager_next_scene(mag->scene_manager, MagSceneSavedMenu); + + } else { + furi_string_printf(mag->uart_text_box_store, "Failed to parse! Try again\n"); + notification_message(mag->notifications, &sequence_error); + } + + break; + } + + update_widgets(mag); + } + + return consumed; +} + +void mag_scene_read_on_exit(void* context) { + Mag* mag = context; + // notification_message(mag->notifications, &sequence_blink_stop); + widget_reset(mag->widget); + // view_dispatcher_remove_view(mag->view_dispatcher, MagViewWidget); + + // Stop UART worker + FURI_LOG_D(TAG, "Stopping UART worker"); + furi_thread_flags_set(furi_thread_get_id(mag->uart_rx_thread), WorkerEvtStop); + furi_thread_join(mag->uart_rx_thread); + furi_thread_free(mag->uart_rx_thread); + FURI_LOG_D(TAG, "UART worker stopped"); + + furi_string_free(mag->uart_text_box_store); + + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL); + furi_hal_uart_deinit(FuriHalUartIdUSART1); + // furi_hal_console_enable(); + + notification_message(mag->notifications, &sequence_blink_stop); +} \ No newline at end of file diff --git a/scenes/mag_scene_read.h b/scenes/mag_scene_read.h new file mode 100644 index 00000000000..bb76f7edb66 --- /dev/null +++ b/scenes/mag_scene_read.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#define UART_RX_BUF_SIZE (320) +#define UART_TERMINAL_TEXT_BOX_STORE_SIZE (4096) +#define UART_TERMINAL_TEXT_INPUT_STORE_SIZE (512) +#define UART_CH (FuriHalUartIdUSART1) +#define UART_BAUDRATE (9600) + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtRxDone = (1 << 1), +} WorkerEvtFlags; + +typedef enum { + UARTEventRxData = 100, +} UARTEvents; + + +#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone) diff --git a/scenes/mag_scene_saved_menu.c b/scenes/mag_scene_saved_menu.c index f2b66de4191..f9235cd546c 100644 --- a/scenes/mag_scene_saved_menu.c +++ b/scenes/mag_scene_saved_menu.c @@ -17,6 +17,8 @@ void mag_scene_saved_menu_on_enter(void* context) { Mag* mag = context; Submenu* submenu = mag->submenu; + notification_message(mag->notifications, &sequence_blink_cyan_10); + // 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); diff --git a/scenes/mag_scene_start.c b/scenes/mag_scene_start.c index 3a1cd2f903e..5382341891c 100644 --- a/scenes/mag_scene_start.c +++ b/scenes/mag_scene_start.c @@ -2,6 +2,7 @@ typedef enum { SubmenuIndexSaved, + SubmenuIndexRead, //SubmenuIndexAddManually, SubmenuIndexAbout, } SubmenuIndex; @@ -17,6 +18,7 @@ void mag_scene_start_on_enter(void* context) { Submenu* submenu = mag->submenu; submenu_add_item(submenu, "Saved", SubmenuIndexSaved, mag_scene_start_submenu_callback, mag); + submenu_add_item(submenu, "Read", SubmenuIndexRead, mag_scene_start_submenu_callback, mag); //submenu_add_item( // submenu, "Add Manually", SubmenuIndexAddManually, mag_scene_start_submenu_callback, mag); submenu_add_item(submenu, "About", SubmenuIndexAbout, mag_scene_start_submenu_callback, mag); @@ -41,6 +43,11 @@ bool mag_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(mag->scene_manager, MagSceneFileSelect); consumed = true; break; + + case SubmenuIndexRead: + scene_manager_next_scene(mag->scene_manager, MagSceneRead); + consumed = true; + break; //case SubmenuIndexAddManually: // scene_manager_next_scene(mag->scene_manager, MagSceneInputValue); // consumed = true;