Просмотр исходного кода

Init work on rev3, but this is not working code

derskythe 3 лет назад
Родитель
Сommit
37d56329f8
21 измененных файлов с 785 добавлено и 1135 удалено
  1. 0 346
      applications/plugins/subbrute/helpers/subbrute_worker.c
  2. 0 39
      applications/plugins/subbrute/helpers/subbrute_worker.h
  3. 1 3
      applications/plugins/subbrute/scenes/subbrute_scene_load_file.c
  4. 4 7
      applications/plugins/subbrute/scenes/subbrute_scene_load_select.c
  5. 34 142
      applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c
  6. 11 21
      applications/plugins/subbrute/scenes/subbrute_scene_save_name.c
  7. 1 1
      applications/plugins/subbrute/scenes/subbrute_scene_save_success.c
  8. 51 117
      applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c
  9. 2 3
      applications/plugins/subbrute/scenes/subbrute_scene_start.c
  10. 8 69
      applications/plugins/subbrute/subbrute.c
  11. 1 1
      applications/plugins/subbrute/subbrute_custom_event.h
  12. 379 289
      applications/plugins/subbrute/subbrute_device.c
  13. 29 73
      applications/plugins/subbrute/subbrute_device.h
  14. 64 0
      applications/plugins/subbrute/subbrute_device_i.h
  15. 7 19
      applications/plugins/subbrute/subbrute_i.h
  16. 127 0
      applications/plugins/subbrute/subbrute_protocols.c
  17. 42 0
      applications/plugins/subbrute/subbrute_protocols.h
  18. 18 0
      applications/plugins/subbrute/subbrute_protocols_i.h
  19. 2 1
      applications/plugins/subbrute/views/subbrute_attack_view.c
  20. 1 1
      applications/plugins/subbrute/views/subbrute_attack_view.h
  21. 3 3
      applications/plugins/subbrute/views/subbrute_main_view.c

+ 0 - 346
applications/plugins/subbrute/helpers/subbrute_worker.c

@@ -1,346 +0,0 @@
-#include "subbrute_worker.h"
-
-#include <subghz/environment.h>
-#include <subghz/transmitter.h>
-#include <flipper_format_i.h>
-#include <lib/subghz/subghz_tx_rx_worker.h>
-
-#define TAG "SubBruteWorker"
-
-struct SubBruteWorker {
-    SubGhzTxRxWorker* subghz_txrx;
-    volatile bool worker_running;
-    volatile bool worker_manual_mode;
-    bool is_manual_init;
-    bool is_continuous_worker;
-
-    SubGhzEnvironment* environment;
-    SubGhzTransmitter* transmitter;
-    FlipperFormat* flipper_format;
-
-    uint32_t last_time_tx_data;
-
-    // Preset and frequency needed
-    FuriHalSubGhzPreset preset;
-    uint32_t frequency;
-    string_t protocol_name;
-
-    //SubBruteWorkerCallback callback;
-    //void* context;
-};
-
-/** Taken from subghz_tx_rx_worker.c */
-#define SUBBRUTE_TXRX_WORKER_BUF_SIZE 2048
-#define SUBBRUTE_TXRX_WORKER_MAX_TXRX_SIZE 60
-#define SUBBRUTE_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF 40
-#define SUBBRUTE_TX_TIMEOUT 5
-#define SUBBRUTE_SEND_DELAY 20
-
-SubBruteWorker* subbrute_worker_alloc() {
-    SubBruteWorker* instance = malloc(sizeof(SubBruteWorker));
-
-    //instance->status = SubBruteWorkerStatusIDLE;
-    instance->worker_running = false;
-    instance->worker_manual_mode = false;
-
-    //instance->environment = subghz_environment_alloc();
-    instance->transmitter = NULL;
-
-    instance->flipper_format = flipper_format_string_alloc();
-    string_init(instance->protocol_name);
-
-    // SubGhzTxRxWorker
-    instance->subghz_txrx = subghz_tx_rx_worker_alloc();
-
-    return instance;
-}
-
-void subbrute_worker_free(SubBruteWorker* instance) {
-    furi_assert(instance);
-    furi_assert(!instance->worker_running);
-
-    if(instance->transmitter != NULL) {
-        subghz_transmitter_free(instance->transmitter);
-        instance->transmitter = NULL;
-    }
-
-    /*if(instance->environment != NULL) {
-        subghz_environment_free(instance->environment);
-        instance->environment = NULL;
-    }*/
-
-    flipper_format_free(instance->flipper_format);
-
-    string_clear(instance->protocol_name);
-
-    // SubGhzTxRxWorker
-    subghz_tx_rx_worker_free(instance->subghz_txrx);
-
-    free(instance);
-}
-
-bool subbrute_worker_start(
-    SubBruteWorker* instance,
-    uint32_t frequency,
-    FuriHalSubGhzPreset preset,
-    const char* protocol_name) {
-    furi_assert(instance);
-
-    if(instance->worker_manual_mode) {
-        FURI_LOG_W(TAG, "Invalid mode for starting worker!");
-        return false;
-    }
-
-    instance->frequency = frequency;
-    instance->preset = preset;
-
-    string_clear(instance->protocol_name);
-    string_init_printf(instance->protocol_name, "%s", protocol_name);
-
-    bool res = false;
-
-    furi_hal_subghz_reset();
-    furi_hal_subghz_idle();
-    furi_hal_subghz_load_preset(instance->preset);
-
-    furi_hal_subghz_set_frequency_and_path(instance->frequency);
-    furi_hal_subghz_flush_rx();
-
-    //if(furi_hal_subghz_is_tx_allowed(frequency)) {
-    instance->frequency = frequency;
-    res = true;
-    //}
-    instance->worker_running = res;
-
-#ifdef FURI_DEBUG
-    FURI_LOG_I(TAG, "Frequency: %d", frequency);
-#endif
-    instance->preset = preset;
-    if(res) {
-        instance->worker_running = res =
-            subghz_tx_rx_worker_start(instance->subghz_txrx, frequency);
-    }
-    return res;
-}
-
-void subbrute_worker_stop(SubBruteWorker* instance) {
-    furi_assert(instance);
-
-    instance->worker_running = false;
-
-    if(subghz_tx_rx_worker_is_running(instance->subghz_txrx)) {
-        subghz_tx_rx_worker_stop(instance->subghz_txrx);
-    }
-}
-
-void subbrute_worker_set_continuous_worker(SubBruteWorker* instance, bool is_continuous_worker) {
-    furi_assert(instance);
-
-    instance->is_continuous_worker = is_continuous_worker;
-}
-
-bool subbrute_worker_get_continuous_worker(SubBruteWorker* instance) {
-    furi_assert(instance);
-
-    return instance->is_continuous_worker;
-}
-
-bool subbrute_worker_is_running(SubBruteWorker* instance) {
-    furi_assert(instance);
-
-    return instance->worker_running;
-}
-
-bool subbrute_worker_can_transmit(SubBruteWorker* instance) {
-    furi_assert(instance);
-
-    return (furi_get_tick() - instance->last_time_tx_data) > SUBBRUTE_SEND_DELAY;
-}
-
-bool subbrute_worker_can_manual_transmit(SubBruteWorker* instance, bool is_button_pressed) {
-    furi_assert(instance);
-
-    if(is_button_pressed) {
-        // It's human pressed, trying to reset twice pressing
-        return !instance->worker_manual_mode &&
-               (furi_get_tick() - instance->last_time_tx_data) > 500;
-    } else {
-        return !instance->worker_manual_mode;
-    }
-}
-
-bool subbrute_worker_transmit(SubBruteWorker* instance, const char* payload) {
-    furi_assert(instance);
-    furi_assert(instance->worker_running);
-
-    if(!subbrute_worker_can_transmit(instance)) {
-        FURI_LOG_E(TAG, "Too early to transmit");
-
-        return false;
-    }
-    instance->last_time_tx_data = furi_get_tick();
-
-#ifdef FURI_DEBUG
-    //FURI_LOG_D(TAG, "payload: %s", payload);
-#endif
-
-    while(!subghz_tx_rx_worker_write(instance->subghz_txrx, (uint8_t*)payload, strlen(payload))) {
-        furi_delay_ms(10);
-    }
-
-    furi_hal_subghz_flush_tx();
-    //    Stream* stream = flipper_format_get_raw_stream(instance->flipper_format);
-    //    stream_clean(stream);
-    //    stream_write_cstring(stream, payload);
-    //    subghz_transmitter_deserialize(instance->transmitter, instance->flipper_format);
-
-    return true;
-}
-
-// Init MANUAL
-bool subbrute_worker_init_manual_transmit(
-    SubBruteWorker* instance,
-    uint32_t frequency,
-    FuriHalSubGhzPreset preset,
-    const char* protocol_name) {
-#ifdef FURI_DEBUG
-    FURI_LOG_D(
-        TAG,
-        "subbrute_worker_init_manual_transmit. frequency: %d, protocol: %s",
-        frequency,
-        protocol_name);
-#endif
-    if(instance->worker_manual_mode || !subbrute_worker_can_manual_transmit(instance, false) ||
-       instance->worker_running) {
-#ifdef FURI_DEBUG
-        FURI_LOG_D(TAG, "cannot transmit");
-#endif
-        return false;
-    }
-    if(instance->worker_running) {
-#ifdef FURI_DEBUG
-        FURI_LOG_D(TAG, "subbrute_worker_stop");
-#endif
-        subbrute_worker_stop(instance);
-    }
-
-    // Not transmit at this period
-    instance->worker_manual_mode = true;
-
-    if(instance->is_manual_init) {
-        FURI_LOG_E(TAG, "Trying to setup without normally shutdown prev transmit session!");
-        subbrute_worker_manual_transmit_stop(instance);
-    }
-
-    instance->preset = preset;
-    instance->frequency = frequency;
-
-    string_clear(instance->protocol_name);
-    string_init_printf(instance->protocol_name, "%s", protocol_name);
-
-    furi_hal_subghz_reset();
-    furi_hal_subghz_idle();
-    furi_hal_subghz_load_preset(instance->preset);
-
-    furi_hal_subghz_set_frequency_and_path(instance->frequency);
-    furi_hal_subghz_flush_rx();
-
-    /*if(!furi_hal_subghz_is_tx_allowed(frequency)) {
-        FURI_LOG_E(TAG, "Frequency: %d invalid!", frequency);
-
-        instance->frequency = frequency;
-        instance->worker_manual_mode = false;
-        return false;
-    }*/
-
-#ifdef FURI_DEBUG
-    FURI_LOG_I(TAG, "Frequency: %d", frequency);
-#endif
-
-    instance->transmitter = subghz_transmitter_alloc_init(
-        instance->environment, string_get_cstr(instance->protocol_name));
-
-    furi_hal_subghz_reset();
-    furi_hal_subghz_load_preset(instance->preset);
-    instance->frequency = furi_hal_subghz_set_frequency_and_path(frequency);
-
-    furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
-    furi_hal_subghz_sleep();
-    subghz_transmitter_free(instance->transmitter);
-    instance->transmitter = NULL;
-
-    instance->worker_manual_mode = false;
-    instance->is_manual_init = true;
-
-    return true;
-}
-
-void subbrute_worker_manual_transmit_stop(SubBruteWorker* instance) {
-#ifdef FURI_DEBUG
-    FURI_LOG_D(TAG, "subbrute_worker_manual_transmit_stop");
-#endif
-    if(!instance->is_manual_init) {
-        return;
-    }
-
-    furi_hal_subghz_idle();
-    furi_hal_subghz_sleep();
-
-    if(instance->transmitter != NULL) {
-        subghz_transmitter_free(instance->transmitter);
-        instance->transmitter = NULL;
-    }
-
-    instance->is_manual_init = false;
-}
-
-bool subbrute_worker_manual_transmit(SubBruteWorker* instance, const char* payload) {
-    furi_assert(instance);
-
-    if(instance->worker_manual_mode || !subbrute_worker_can_transmit(instance)) {
-#ifdef FURI_DEBUG
-        FURI_LOG_D(TAG, "cannot transmit");
-#endif
-        return false;
-    }
-    if(instance->worker_running) {
-        FURI_LOG_W(TAG, "Worker was working for manual mode. Shutdown thread");
-        subbrute_worker_stop(instance);
-    }
-    if(!instance->is_manual_init) {
-        FURI_LOG_E(TAG, "Manually transmit doesn't set!");
-        return false;
-    }
-
-    instance->last_time_tx_data = furi_get_tick();
-    instance->worker_manual_mode = true;
-
-    Stream* stream = flipper_format_get_raw_stream(instance->flipper_format);
-    stream_clean(stream);
-    stream_write_cstring(stream, payload);
-
-    instance->transmitter = subghz_transmitter_alloc_init(
-        instance->environment, string_get_cstr(instance->protocol_name));
-    subghz_transmitter_deserialize(instance->transmitter, instance->flipper_format);
-    furi_hal_subghz_reset();
-    furi_hal_subghz_load_preset(instance->preset);
-    instance->frequency = furi_hal_subghz_set_frequency_and_path(instance->frequency);
-
-    furi_hal_subghz_start_async_tx(subghz_transmitter_yield, instance->transmitter);
-
-    while(!furi_hal_subghz_is_async_tx_complete()) {
-        furi_delay_ms(SUBBRUTE_TX_TIMEOUT);
-    }
-    furi_hal_subghz_stop_async_tx();
-
-    furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
-    furi_hal_subghz_sleep();
-    subghz_transmitter_free(instance->transmitter);
-    instance->transmitter = NULL;
-
-    stream_clean(stream);
-
-    instance->worker_manual_mode = false;
-
-    return true;
-}

+ 0 - 39
applications/plugins/subbrute/helpers/subbrute_worker.h

@@ -1,39 +0,0 @@
-#pragma once
-
-#include <furi_hal_subghz.h>
-
-typedef struct SubBruteWorker SubBruteWorker;
-/**
- * Same like SubGhzTxRxWorkerStatus in subghz_tx_rx_worker.h
- * using just to not include that file
-
-typedef enum {
-    SubBruteWorkerStatusIDLE,
-    SubBruteWorkerStatusTx,
-    // SubBruteWorkerStatusRx,
-} SubBruteWorkerStatus;
-
-//typedef void (*SubBruteWorkerCallback)(SubBruteWorkerStatus event, void* context);
-*/
-SubBruteWorker* subbrute_worker_alloc();
-void subbrute_worker_free(SubBruteWorker* instance);
-bool subbrute_worker_start(
-    SubBruteWorker* instance,
-    uint32_t frequency,
-    FuriHalSubGhzPreset preset,
-    const char* protocol_name);
-void subbrute_worker_stop(SubBruteWorker* instance);
-bool subbrute_worker_get_continuous_worker(SubBruteWorker* instance);
-void subbrute_worker_set_continuous_worker(SubBruteWorker* instance, bool is_continuous_worker);
-//bool subbrute_worker_write(SubBruteWorker* instance, uint8_t* data, size_t size);
-bool subbrute_worker_is_running(SubBruteWorker* instance);
-bool subbrute_worker_can_transmit(SubBruteWorker* instance);
-bool subbrute_worker_can_manual_transmit(SubBruteWorker* instance, bool is_button_pressed);
-bool subbrute_worker_transmit(SubBruteWorker* instance, const char* payload);
-bool subbrute_worker_init_manual_transmit(
-    SubBruteWorker* instance,
-    uint32_t frequency,
-    FuriHalSubGhzPreset preset,
-    const char* protocol_name);
-bool subbrute_worker_manual_transmit(SubBruteWorker* instance, const char* payload);
-void subbrute_worker_manual_transmit_stop(SubBruteWorker* instance);

+ 1 - 3
applications/plugins/subbrute/scenes/subbrute_scene_load_file.c

@@ -1,6 +1,5 @@
 #include "../subbrute_i.h"
-#include "../subbrute_custom_event.h"
-#include <lib/subghz/protocols/registry.h>
+#include "subbrute_scene.h"
 
 #define TAG "SubBruteSceneLoadFile"
 
@@ -40,7 +39,6 @@ void subbrute_scene_load_file_on_enter(void* context) {
             load_result = subbrute_device_attack_set(instance->device, SubBruteAttackLoadFile);
             if(load_result == SubBruteFileResultOk) {
                 // Ready to run!
-                instance->device->state = SubBruteDeviceStateReady;
                 FURI_LOG_I(TAG, "Ready to run");
                 res = true;
             }

+ 4 - 7
applications/plugins/subbrute/scenes/subbrute_scene_load_select.c

@@ -1,6 +1,5 @@
 #include "../subbrute_i.h"
-#include "../subbrute_custom_event.h"
-#include "../views/subbrute_main_view.h"
+#include "subbrute_scene.h"
 
 #define TAG "SubBruteSceneStart"
 
@@ -24,7 +23,7 @@ void subbrute_scene_load_select_on_enter(void* context) {
 
     instance->current_view = SubBruteViewMain;
     subbrute_main_view_set_callback(view, subbrute_scene_load_select_callback, instance);
-    subbrute_main_view_set_index(view, 7, true, instance->device->file_key);
+    subbrute_main_view_set_index(view, 7, true, subbrute_device_get_file_key(instance->device));
 
     view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
 }
@@ -42,10 +41,8 @@ bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event)
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SubBruteCustomEventTypeIndexSelected) {
-            instance->device->load_index = subbrute_main_view_get_index(instance->view_main);
-#ifdef FURI_DEBUG
-            FURI_LOG_D(TAG, "load_index: %d", instance->device->load_index);
-#endif
+            subbrute_device_set_load_index(
+                instance->device, subbrute_main_view_get_index(instance->view_main));
             scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
             consumed = true;
         }

+ 34 - 142
applications/plugins/subbrute/scenes/subbrute_scene_run_attack.c

@@ -1,7 +1,7 @@
 #include "../subbrute_i.h"
+#include "subbrute_scene.h"
 #include "../subbrute_custom_event.h"
 #include "../views/subbrute_attack_view.h"
-#include "../helpers/subbrute_worker.h"
 
 #define TAG "SubBruteSceneRunAttack"
 
@@ -12,56 +12,25 @@ static void subbrute_scene_run_attack_callback(SubBruteCustomEvent event, void*
     view_dispatcher_send_custom_event(instance->view_dispatcher, event);
 }
 
-//static void subbrute_scene_run_attack_worker_callback(void* context) {
-//    SubBruteState* instance = (SubBruteState*)context;
-//
-//    if(instance->locked || instance->device->key_index + 1 > instance->device->max_value) {
-//        return;
-//    }
-//    instance->locked = true;
-//
-//    if(subbrute_worker_can_manual_transmit(instance->worker)) {
-//        // Blink
-//        notification_message(instance->notifications, &sequence_blink_yellow_100);
-//        subbrute_device_create_packet_parsed(instance->device, instance->device->key_index, true);
-//
-//#ifdef FURI_DEBUG
-//        FURI_LOG_I(TAG, "subbrute_worker_manual_transmit");
-//#endif
-//        if(subbrute_worker_manual_transmit(instance->worker, instance->device->payload)) {
-//#ifdef FURI_DEBUG
-//            FURI_LOG_I(TAG, "transmit ok");
-//#endif
-//            // Make payload for new iteration or exit
-//            if(instance->device->key_index + 1 <= instance->device->max_value) {
-//                instance->device->key_index++;
-//            } else {
-//                view_dispatcher_send_custom_event(
-//                    instance->view_dispatcher, SubBruteCustomEventTypeTransmitFinished);
-//            }
-//        }
-//
-//        // Stop
-//        notification_message(instance->notifications, &sequence_blink_stop);
-//    }
-//
-//    instance->locked = false;
-//    subbrute_attack_view_set_current_step(instance->view_attack, instance->device->key_index);
-//}
+static void
+    subbrute_scene_run_attack_device_state_changed(void* context, SubBruteDeviceState state) {
+    furi_assert(context);
+
+    SubBruteState* instance = (SubBruteState*)context;
 
+    if(state == SubBruteDeviceStateIDLE) {
+        // Can't be IDLE on this step!
+        view_dispatcher_send_custom_event(instance->view_dispatcher, SubBruteCustomEventTypeError);
+    } else if(state == SubBruteDeviceStateFinished) {
+        view_dispatcher_send_custom_event(
+            instance->view_dispatcher, SubBruteCustomEventTypeTransmitFinished);
+    }
+}
 void subbrute_scene_run_attack_on_exit(void* context) {
     furi_assert(context);
     SubBruteState* instance = (SubBruteState*)context;
-    //    SubBruteAttackState* state = (SubBruteAttackState*)scene_manager_get_scene_state(
-    //        instance->scene_manager, SubBruteSceneRunAttack);
-    //    furi_assert(state);
-    //
-    //    furi_timer_free(state->timer);
-    //    free(state);
-
-    if(subbrute_worker_get_continuous_worker(instance->worker)) {
-        subbrute_worker_stop(instance->worker);
-    }
+
+    subbrute_worker_stop(instance->device);
 
     notification_message(instance->notifications, &sequence_blink_stop);
 }
@@ -70,124 +39,47 @@ void subbrute_scene_run_attack_on_enter(void* context) {
     furi_assert(context);
     SubBruteState* instance = (SubBruteState*)context;
     SubBruteAttackView* view = instance->view_attack;
-    //
-    //    SubBruteAttackState* state = malloc(sizeof(SubBruteAttackState));
-    //    scene_manager_set_scene_state(
-    //        instance->scene_manager, SubBruteSceneRunAttack, (uint32_t)state);
 
     instance->current_view = SubBruteViewAttack;
     subbrute_attack_view_set_callback(view, subbrute_scene_run_attack_callback, instance);
     view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
 
-    subbrute_attack_view_init_values(
-        view,
-        (uint8_t)instance->device->attack,
-        instance->device->max_value,
-        instance->device->key_index,
-        true);
-
-    if(subbrute_worker_get_continuous_worker(instance->worker)) {
-        // Init Continuous worker with values!
-        if(!subbrute_worker_start(
-               instance->worker,
-               instance->device->frequency,
-               instance->device->preset,
-               string_get_cstr(instance->device->protocol_name))) {
-            FURI_LOG_W(TAG, "Worker Continuous init failed!");
-        }
-    } else {
-        // Init worker with values
-        if(!subbrute_worker_init_manual_transmit(
-               instance->worker,
-               instance->device->frequency,
-               instance->device->preset,
-               string_get_cstr(instance->device->protocol_name))) {
-            FURI_LOG_W(TAG, "Worker init failed!");
-        }
+    subbrute_device_set_callback(
+        instance->device, subbrute_scene_run_attack_device_state_changed, instance);
 
-        //    state->timer = furi_timer_alloc(
-        //        subbrute_scene_run_attack_worker_callback, FuriTimerTypePeriodic, instance);
-        //    furi_timer_start(state->timer, pdMS_TO_TICKS(100)); // 20 ms
+    if(!subbrute_device_is_worker_running(instance->device)) {
+        subbrute_worker_start(instance->device);
     }
 }
 
 bool subbrute_scene_run_attack_on_event(void* context, SceneManagerEvent event) {
     SubBruteState* instance = (SubBruteState*)context;
-    //    SubBruteAttackState* state = (SubBruteAttackState*)scene_manager_get_scene_state(
-    //        instance->scene_manager, SubBruteSceneRunAttack);
-    //    furi_assert(state);
+    SubBruteAttackView* view = instance->view_attack;
 
     bool consumed = false;
 
     if(event.type == SceneManagerEventTypeCustom) {
-        SubBruteAttackView* view = instance->view_attack;
+        subbrute_attack_view_set_current_step(view, subbrute_device_get_step(instance->device));
 
-        if(event.event == SubBruteCustomEventTypeTransmitNotStarted ||
-           event.event == SubBruteCustomEventTypeTransmitFinished ||
-           event.event == SubBruteCustomEventTypeBackPressed) {
-            //            furi_timer_stop(state->timer);
-            // Stop transmit
+        if(event.event == SubBruteCustomEventTypeTransmitFinished) {
             notification_message(instance->notifications, &sequence_display_backlight_on);
             notification_message(instance->notifications, &sequence_single_vibro);
-            subbrute_attack_view_set_current_step(view, instance->device->key_index);
+
+            scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
+        } else if(
+            event.event == SubBruteCustomEventTypeTransmitNotStarted ||
+            event.event == SubBruteCustomEventTypeBackPressed) {
+            // Stop transmit
             scene_manager_search_and_switch_to_previous_scene(
                 instance->scene_manager, SubBruteSceneSetupAttack);
-            consumed = true;
+        } else if(event.event == SubBruteCustomEventTypeError) {
+            notification_message(instance->notifications, &sequence_error);
         } else if(event.event == SubBruteCustomEventTypeUpdateView) {
-            subbrute_attack_view_set_current_step(view, instance->device->key_index);
+            //subbrute_attack_view_set_current_step(view, instance->device->key_index);
         }
+        consumed = true;
     } else if(event.type == SceneManagerEventTypeTick) {
-        if(subbrute_worker_get_continuous_worker(instance->worker)) {
-            if(subbrute_worker_can_transmit(instance->worker)) {
-                // Blink
-                notification_message(instance->notifications, &sequence_blink_yellow_100);
-
-                subbrute_device_create_packet_parsed(
-                    instance->device, instance->device->key_index, true);
-
-                if(subbrute_worker_transmit(instance->worker, instance->device->payload)) {
-                    // Make payload for new iteration or exit
-                    if(instance->device->key_index + 1 > instance->device->max_value) {
-                        // End of list
-                        view_dispatcher_send_custom_event(
-                            instance->view_dispatcher, SubBruteCustomEventTypeTransmitFinished);
-                    } else {
-                        instance->device->key_index++;
-                        view_dispatcher_send_custom_event(
-                            instance->view_dispatcher, SubBruteCustomEventTypeUpdateView);
-                        //subbrute_attack_view_set_current_step(view, instance->device->key_index);
-                    }
-                }
-
-                // Stop
-                notification_message(instance->notifications, &sequence_blink_stop);
-            }
-        } else {
-            if(subbrute_worker_can_manual_transmit(instance->worker, false)) {
-                // Blink
-                notification_message(instance->notifications, &sequence_blink_yellow_100);
-
-                subbrute_device_create_packet_parsed(
-                    instance->device, instance->device->key_index, true);
-
-                if(subbrute_worker_manual_transmit(instance->worker, instance->device->payload)) {
-                    // Make payload for new iteration or exit
-                    if(instance->device->key_index + 1 > instance->device->max_value) {
-                        // End of list
-                        view_dispatcher_send_custom_event(
-                            instance->view_dispatcher, SubBruteCustomEventTypeTransmitFinished);
-                    } else {
-                        instance->device->key_index++;
-                        view_dispatcher_send_custom_event(
-                            instance->view_dispatcher, SubBruteCustomEventTypeUpdateView);
-                        //subbrute_attack_view_set_current_step(view, instance->device->key_index);
-                    }
-                }
-
-                // Stop
-                notification_message(instance->notifications, &sequence_blink_stop);
-            }
-        }
+        subbrute_attack_view_set_current_step(view, subbrute_device_get_step(instance->device));
 
         consumed = true;
     }

+ 11 - 21
applications/plugins/subbrute/scenes/subbrute_scene_save_name.c

@@ -1,35 +1,29 @@
-#include <m-string.h>
-#include <subghz/types.h>
-#include <lib/toolbox/random_name.h>
-#include <gui/modules/validators.h>
-#include <lib/toolbox/path.h>
-
 #include "../subbrute_i.h"
-#include "../subbrute_custom_event.h"
+#include "subbrute_scene.h"
+#include <lib/toolbox/random_name.h>
 
 #define TAG "SubBruteSceneSaveFile"
 
 void subbrute_scene_save_name_on_enter(void* context) {
     SubBruteState* instance = (SubBruteState*)context;
-    SubBruteDevice* device = instance->device;
 
     // Setup view
     TextInput* text_input = instance->text_input;
-    set_random_name(device->text_store, sizeof(device->text_store));
+    set_random_name(instance->text_store, sizeof(instance->text_store));
 
     text_input_set_header_text(text_input, "Name of file");
     text_input_set_result_callback(
         text_input,
         subbrute_text_input_callback,
         instance,
-        device->text_store,
+        instance->text_store,
         SUBBRUTE_MAX_LEN_NAME,
         true);
 
-    string_set_str(device->load_path, SUBBRUTE_PATH);
+    string_set_str(instance->file_path, SUBBRUTE_PATH);
 
     ValidatorIsFile* validator_is_file =
-        validator_is_file_alloc_init(string_get_cstr(device->load_path), SUBBRUTE_FILE_EXT, "");
+        validator_is_file_alloc_init(string_get_cstr(instance->file_path), SUBBRUTE_FILE_EXT, "");
     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
 
     view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewTextInput);
@@ -46,18 +40,14 @@ bool subbrute_scene_save_name_on_event(void* context, SceneManagerEvent event) {
         event.type == SceneManagerEventTypeCustom &&
         event.event == SubBruteCustomEventTypeTextEditDone) {
 #ifdef FURI_DEBUG
-        FURI_LOG_D(TAG, "Saving: %s", instance->device->text_store);
+        FURI_LOG_D(TAG, "Saving: %s", instance->text_store);
 #endif
         bool success = false;
-        if(strcmp(instance->device->text_store, "")) {
+        if(strcmp(instance->text_store, "")) {
             string_cat_printf(
-                instance->device->load_path,
-                "/%s%s",
-                instance->device->text_store,
-                SUBBRUTE_FILE_EXT);
+                instance->file_path, "/%s%s", instance->text_store, SUBBRUTE_FILE_EXT);
 
-            if(subbrute_device_save_file(
-                   instance->device, string_get_cstr(instance->device->load_path))) {
+            if(subbrute_device_save_file(instance->device, string_get_cstr(instance->file_path))) {
                 scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveSuccess);
                 success = true;
                 consumed = true;
@@ -83,5 +73,5 @@ void subbrute_scene_save_name_on_exit(void* context) {
 
     text_input_reset(instance->text_input);
 
-    string_reset(instance->device->load_path);
+    string_reset(instance->file_path);
 }

+ 1 - 1
applications/plugins/subbrute/scenes/subbrute_scene_save_success.c

@@ -1,5 +1,5 @@
 #include "../subbrute_i.h"
-#include "../subbrute_custom_event.h"
+#include "subbrute_scene.h"
 
 void subbrute_scene_save_success_on_enter(void* context) {
     furi_assert(context);

+ 51 - 117
applications/plugins/subbrute/scenes/subbrute_scene_setup_attack.c

@@ -1,6 +1,5 @@
 #include "../subbrute_i.h"
-#include "../subbrute_custom_event.h"
-#include "../views/subbrute_attack_view.h"
+#include "subbrute_scene.h"
 
 #define TAG "SubBruteSceneSetupAttack"
 
@@ -11,28 +10,32 @@ static void subbrute_scene_setup_attack_callback(SubBruteCustomEvent event, void
     view_dispatcher_send_custom_event(instance->view_dispatcher, event);
 }
 
+static void
+    subbrute_scene_setup_attack_device_state_changed(void* context, SubBruteDeviceState state) {
+    furi_assert(context);
+
+    SubBruteState* instance = (SubBruteState*)context;
+
+    if(state == SubBruteDeviceStateIDLE) {
+        // Can't be IDLE on this step!
+        view_dispatcher_send_custom_event(instance->view_dispatcher, SubBruteCustomEventTypeError);
+    }
+}
+
 void subbrute_scene_setup_attack_on_enter(void* context) {
     furi_assert(context);
     SubBruteState* instance = (SubBruteState*)context;
     SubBruteAttackView* view = instance->view_attack;
 
 #ifdef FURI_DEBUG
-    FURI_LOG_D(TAG, "Enter Attack: %d", instance->device->attack);
+    FURI_LOG_D(TAG, "Enter Attack: %d", subbrute_device_get_attack(instance->device));
 #endif
 
-    subbrute_attack_view_init_values(
-        view,
-        instance->device->attack,
-        instance->device->max_value,
-        instance->device->key_index,
-        false);
-
-    if(!subbrute_worker_init_manual_transmit(
-           instance->worker,
-           instance->device->frequency,
-           instance->device->preset,
-           string_get_cstr(instance->device->protocol_name))) {
-        FURI_LOG_W(TAG, "Worker init failed!");
+    subbrute_device_set_callback(
+        instance->device, subbrute_scene_setup_attack_device_state_changed, context);
+
+    if(subbrute_device_is_worker_running(instance->device)) {
+        subbrute_worker_stop(instance->device);
     }
 
     instance->current_view = SubBruteViewAttack;
@@ -46,7 +49,7 @@ void subbrute_scene_setup_attack_on_exit(void* context) {
     FURI_LOG_D(TAG, "subbrute_scene_setup_attack_on_exit");
 #endif
     SubBruteState* instance = (SubBruteState*)context;
-    subbrute_worker_manual_transmit_stop(instance->worker);
+    subbrute_worker_stop(instance->device);
     notification_message(instance->notifications, &sequence_blink_stop);
 }
 
@@ -57,129 +60,60 @@ bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SubBruteCustomEventTypeTransmitStarted) {
-            subbrute_worker_set_continuous_worker(instance->worker, false);
             subbrute_attack_view_set_worker_type(view, false);
             scene_manager_next_scene(instance->scene_manager, SubBruteSceneRunAttack);
-        } else if(event.event == SubBruteCustomEventTypeTransmitContinuousStarted) {
-            // Setting different type of worker
-            subbrute_worker_set_continuous_worker(instance->worker, true);
-            subbrute_attack_view_set_worker_type(view, true);
-            scene_manager_next_scene(instance->scene_manager, SubBruteSceneRunAttack);
         } else if(event.event == SubBruteCustomEventTypeSaveFile) {
-            subbrute_worker_manual_transmit_stop(instance->worker);
-
             subbrute_attack_view_init_values(
                 view,
-                instance->device->attack,
-                instance->device->max_value,
-                instance->device->key_index,
+                subbrute_device_get_attack(instance->device),
+                subbrute_device_get_max_value(instance->device),
+                subbrute_device_get_step(instance->device),
                 false);
             scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveName);
         } else if(event.event == SubBruteCustomEventTypeBackPressed) {
-#ifdef FURI_DEBUG
-            FURI_LOG_D(TAG, "SubBruteCustomEventTypeBackPressed");
-#endif
-            instance->device->key_index = 0x00;
-            //subbrute_attack_view_stop_worker(view);
+            subbrute_device_reset_step(instance->device);
             subbrute_attack_view_init_values(
                 view,
-                instance->device->attack,
-                instance->device->max_value,
-                instance->device->key_index,
+                subbrute_device_get_attack(instance->device),
+                subbrute_device_get_max_value(instance->device),
+                subbrute_device_get_step(instance->device),
                 false);
             scene_manager_next_scene(instance->scene_manager, SubBruteSceneStart);
+        } else if(event.event == SubBruteCustomEventTypeError) {
+            notification_message(instance->notifications, &sequence_error);
+        } else if(event.event == SubBruteCustomEventTypeTransmitCustom) {
+            // We can transmit only in not working states
+            if(subbrute_device_can_manual_transmit(instance->device)) {
+                // MANUAL Transmit!
+                // Blink
+                notification_message(instance->notifications, &sequence_blink_green_100);
+                subbrute_device_transmit_current_key(instance->device);
+                // Stop
+                notification_message(instance->notifications, &sequence_blink_stop);
+            }
         } else if(event.event == SubBruteCustomEventTypeChangeStepUp) {
             // +1
-            if((instance->device->key_index + 1) - instance->device->max_value == 1) {
-                instance->device->key_index = 0x00;
-            } else {
-                uint64_t value = instance->device->key_index + 1;
-                if(value == instance->device->max_value) {
-                    instance->device->key_index = value;
-                } else {
-                    instance->device->key_index = value % instance->device->max_value;
-                }
-            }
-            subbrute_attack_view_set_current_step(view, instance->device->key_index);
+            uint64_t step = subbrute_device_add_step(instance->device, 1);
+            subbrute_attack_view_set_current_step(view, step);
         } else if(event.event == SubBruteCustomEventTypeChangeStepUpMore) {
             // +50
-            uint64_t value = instance->device->key_index + 50;
-            if(value == instance->device->max_value) {
-                instance->device->key_index += value;
-            } else {
-                instance->device->key_index = value % instance->device->max_value;
-            }
-            subbrute_attack_view_set_current_step(view, instance->device->key_index);
+            uint64_t step = subbrute_device_add_step(instance->device, 50);
+            subbrute_attack_view_set_current_step(view, step);
         } else if(event.event == SubBruteCustomEventTypeChangeStepDown) {
             // -1
-            if(instance->device->key_index - 1 == 0) {
-                instance->device->key_index = 0x00;
-            } else if(instance->device->key_index == 0) {
-                instance->device->key_index = instance->device->max_value;
-            } else {
-                uint64_t value = ((instance->device->key_index - 1) + instance->device->max_value);
-                if(value == instance->device->max_value) {
-                    instance->device->key_index = value;
-                } else {
-                    instance->device->key_index = value % instance->device->max_value;
-                }
-            }
-            subbrute_attack_view_set_current_step(view, instance->device->key_index);
+            uint64_t step = subbrute_device_add_step(instance->device, -1);
+            subbrute_attack_view_set_current_step(view, step);
         } else if(event.event == SubBruteCustomEventTypeChangeStepDownMore) {
             // -50
-            uint64_t value = ((instance->device->key_index - 50) + instance->device->max_value);
-            if(value == instance->device->max_value) {
-                instance->device->key_index = value;
-            } else {
-                instance->device->key_index = value % instance->device->max_value;
-            }
-            subbrute_attack_view_set_current_step(view, instance->device->key_index);
-        } else if(event.event == SubBruteCustomEventTypeTransmitCustom) {
-            if(subbrute_worker_can_manual_transmit(instance->worker, true)) {
-                // Blink
-                notification_message(instance->notifications, &sequence_blink_green_100);
-
-                //                if(!subbrute_attack_view_is_worker_running(view)) {
-                //                    subbrute_attack_view_start_worker(
-                //                        view,
-                //                        instance->device->frequency,
-                //                        instance->device->preset,
-                //                        string_get_cstr(instance->device->protocol_name));
-                //                }
-                subbrute_device_create_packet_parsed(
-                    instance->device, instance->device->key_index, false);
-                subbrute_worker_manual_transmit(instance->worker, instance->device->payload);
-
-                // Stop
-                notification_message(instance->notifications, &sequence_blink_stop);
-            }
+            uint64_t step = subbrute_device_add_step(instance->device, -50);
+            subbrute_attack_view_set_current_step(view, step);
         }
 
+        consumed = true;
+    } else if(event.type == SceneManagerEventTypeTick) {
+        subbrute_attack_view_set_current_step(view, subbrute_device_get_step(instance->device));
         consumed = true;
     }
 
-    //    if(event.type == SceneManagerEventTypeCustom) {
-    //        switch(event.event) {
-    //        case SubBruteCustomEventTypeMenuSelected:
-    //            with_view_model(
-    //                view, (SubBruteMainViewModel * model) {
-    //                    instance->menu_index = model->index;
-    //                    return false;
-    //                });
-    //            scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile);
-    //            consumed = true;
-    //            break;
-    //        case SubBruteCustomEventTypeLoadFile:
-    //            with_view_model(
-    //                view, (SubBruteMainViewModel * model) {
-    //                    instance->menu_index = model->index;
-    //                    return false;
-    //                });
-    //            scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack);
-    //            consumed = true;
-    //            break;
-    //        }
-    //    }
-
     return consumed;
-}
+}

+ 2 - 3
applications/plugins/subbrute/scenes/subbrute_scene_start.c

@@ -1,6 +1,5 @@
 #include "../subbrute_i.h"
-#include "../subbrute_custom_event.h"
-#include "../views/subbrute_main_view.h"
+#include "subbrute_scene.h"
 
 #define TAG "SubBruteSceneStart"
 
@@ -24,7 +23,7 @@ void subbrute_scene_start_on_enter(void* context) {
 
     instance->current_view = SubBruteViewMain;
     subbrute_main_view_set_callback(view, subbrute_scene_start_callback, instance);
-    subbrute_main_view_set_index(view, instance->device->attack, false, NULL);
+    subbrute_main_view_set_index(view, subbrute_device_get_attack(instance->device), false, NULL);
 
     view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view);
 }

+ 8 - 69
applications/plugins/subbrute/subbrute.c

@@ -1,55 +1,9 @@
-#include <furi.h>
-#include <furi_hal.h>
-#include <input/input.h>
-#include <m-string.h>
-
-#include <gui/gui.h>
-#include <gui/view_dispatcher.h>
-#include <gui/view_stack.h>
-#include <gui/scene_manager.h>
-#include <gui/modules/text_input.h>
-#include <gui/modules/popup.h>
-#include <gui/modules/widget.h>
-#include <gui/modules/loading.h>
-
-#include <dialogs/dialogs.h>
-
-#include "subbrute.h"
 #include "subbrute_i.h"
 #include "subbrute_custom_event.h"
+#include "scenes/subbrute_scene.h"
 
 #define TAG "SubBruteApp"
 
-static const char* subbrute_menu_names[] = {
-    [SubBruteAttackCAME12bit307] = "CAME 12bit 307MHz",
-    [SubBruteAttackCAME12bit433] = "CAME 12bit 433MHz",
-    [SubBruteAttackCAME12bit868] = "CAME 12bit 868MHz",
-    [SubBruteAttackNICE12bit433] = "NICE 12bit 433MHz",
-    [SubBruteAttackNICE12bit868] = "NICE 12bit 868MHz",
-    [SubBruteAttackChamberlain9bit300] = "Chamberlain 9bit 300MHz",
-    [SubBruteAttackChamberlain9bit315] = "Chamberlain 9bit 315MHz",
-    [SubBruteAttackChamberlain9bit390] = "Chamberlain 9bit 390MHz",
-    [SubBruteAttackLinear10bit300] = "Linear 10bit 300MHz",
-    [SubBruteAttackLinear10bit310] = "Linear 10bit 310MHz",
-    [SubBruteAttackLoadFile] = "BF existing dump",
-    [SubBruteAttackTotalCount] = "Total Count",
-};
-
-static const char* subbrute_menu_names_small[] = {
-    [SubBruteAttackCAME12bit307] = "CAME 307MHz",
-    [SubBruteAttackCAME12bit433] = "CAME 433MHz",
-    [SubBruteAttackCAME12bit868] = "CAME 868MHz",
-    [SubBruteAttackNICE12bit433] = "NICE 433MHz",
-    [SubBruteAttackNICE12bit868] = "NICE 868MHz",
-    [SubBruteAttackChamberlain9bit300] = "Cham 300MHz",
-    [SubBruteAttackChamberlain9bit315] = "Cham 315MHz",
-    [SubBruteAttackChamberlain9bit390] = "Cham 390MHz",
-    [SubBruteAttackLinear10bit300] = "Linear 300MHz",
-    [SubBruteAttackLinear10bit310] = "Linear 310MHz",
-    [SubBruteAttackLoadFile] = "Existing",
-    [SubBruteAttackTotalCount] = "Total Count",
-};
-
 static bool subbrute_custom_event_callback(void* context, uint32_t event) {
     furi_assert(context);
     SubBruteState* instance = context;
@@ -71,6 +25,9 @@ static void subbrute_tick_event_callback(void* context) {
 SubBruteState* subbrute_alloc() {
     SubBruteState* instance = malloc(sizeof(SubBruteState));
 
+    memset(instance->text_store, 0, sizeof(instance->text_store));
+    string_init(instance->file_path);
+
     instance->scene_manager = scene_manager_alloc(&subbrute_scene_handlers, instance);
     instance->view_dispatcher = view_dispatcher_alloc();
 
@@ -94,9 +51,6 @@ SubBruteState* subbrute_alloc() {
     // Devices
     instance->device = subbrute_device_alloc();
 
-    // Worker
-    instance->worker = subbrute_worker_alloc();
-
     // TextInput
     instance->text_input = text_input_alloc();
     view_dispatcher_add_view(
@@ -144,17 +98,11 @@ SubBruteState* subbrute_alloc() {
 void subbrute_free(SubBruteState* instance) {
     furi_assert(instance);
 
-    // SubBruteWorker
-#ifdef FURI_DEBUG
-    FURI_LOG_D(TAG, "free SubBruteDevice");
-#endif
-    subbrute_worker_stop(instance->worker);
-    subbrute_worker_free(instance->worker);
-
     // SubBruteDevice
 #ifdef FURI_DEBUG
     FURI_LOG_D(TAG, "free SubBruteDevice");
 #endif
+    subbrute_worker_stop(instance->device);
     subbrute_device_free(instance->device);
 
     // Notifications
@@ -239,6 +187,9 @@ void subbrute_free(SubBruteState* instance) {
     furi_record_close(RECORD_GUI);
     instance->gui = NULL;
 
+    string_clear(instance->file_path);
+    string_init(instance->file_path);
+
     // The rest
 #ifdef FURI_DEBUG
     FURI_LOG_D(TAG, "free instance");
@@ -277,18 +228,6 @@ void subbrute_popup_closed_callback(void* context) {
         instance->view_dispatcher, SubBruteCustomEventTypePopupClosed);
 }
 
-const char* subbrute_get_menu_name(SubBruteAttacks index) {
-    furi_assert(index < SubBruteAttackTotalCount);
-
-    return subbrute_menu_names[index];
-}
-
-const char* subbrute_get_small_menu_name(SubBruteAttacks index) {
-    furi_assert(index < SubBruteAttackTotalCount);
-
-    return subbrute_menu_names_small[index];
-}
-
 // ENTRYPOINT
 int32_t subbrute_app(void* p) {
     UNUSED(p);

+ 1 - 1
applications/plugins/subbrute/subbrute_custom_event.h

@@ -10,7 +10,7 @@ typedef enum {
     SubBruteCustomEventTypeBackPressed,
     SubBruteCustomEventTypeIndexSelected,
     SubBruteCustomEventTypeTransmitStarted,
-    SubBruteCustomEventTypeTransmitContinuousStarted,
+    SubBruteCustomEventTypeError,
     SubBruteCustomEventTypeTransmitFinished,
     SubBruteCustomEventTypeTransmitNotStarted,
     SubBruteCustomEventTypeTransmitCustom,

+ 379 - 289
applications/plugins/subbrute/subbrute_device.c

@@ -1,64 +1,45 @@
 #include "subbrute_device.h"
-#include "subbrute_i.h"
-
-#include <furi.h>
-#include <furi_hal.h>
-#include <furi_hal_subghz.h>
 
+#include <lib/toolbox/stream/stream.h>
 #include <stdint.h>
-#include <stdbool.h>
-
-#include <lib/subghz/types.h>
-#include <lib/subghz/protocols/base.h>
-
-#include <storage/storage.h>
-#include <dialogs/dialogs.h>
-#include <stream/stream.h>
 #include <stream/buffered_file_stream.h>
-#include <lib/toolbox/path.h>
 #include <lib/flipper_format/flipper_format_i.h>
 
 #define TAG "SubBruteDevice"
 
-/**
- * List of protocols
- */
-static const char* protocol_came = "CAME";
-static const char* protocol_cham_code = "Cham_Code";
-static const char* protocol_linear = "Linear";
-static const char* protocol_nice_flo = "Nice FLO";
-static const char* protocol_princeton = "Princeton";
-static const char* protocol_raw = "RAW";
+#define SUBBRUTE_TX_TIMEOUT 5
+#define SUBBRUTE_MANUAL_TRANSMIT_INTERVAL 400
 
 /**
  * Values to not use less memory for packet parse operations
  */
 static const char* subbrute_key_file_start =
     "Filetype: Flipper SubGhz Key File\nVersion: 1\nFrequency: %u\nPreset: %s\nProtocol: %s\nBit: %d";
-static const char* subbrute_key_file_key = "%s\nKey: %s\n";
-static const char* subbrute_key_file_princeton_end = "%s\nKey: %s\nTE: %d\n";
-static const char* subbrute_key_small_no_tail = "Bit: %d\nKey: %s\n";
-static const char* subbrute_key_small_with_tail = "Bit: %d\nKey: %s\nTE: %d\n";
-
-// Why nobody set in as const in all codebase?
-static const char* preset_ook270_async = "FuriHalSubGhzPresetOok270Async";
-static const char* preset_ook650_async = "FuriHalSubGhzPresetOok650Async";
-static const char* preset_2fsk_dev238_async = "FuriHalSubGhzPreset2FSKDev238Async";
-static const char* preset_2fsk_dev476_async = "FuriHalSubGhzPreset2FSKDev476Async";
-static const char* preset_msk99_97_kb_async = "FuriHalSubGhzPresetMSK99_97KbAsync";
-static const char* preset_gfs99_97_kb_async = "FuriHalSubGhzPresetGFS99_97KbAsync";
+static const char* subbrute_key_file_key = "%s\nKey: %s\nRepeat: %d\n";
+static const char* subbrute_key_file_key_with_tail = "%s\nKey: %s\nTE: %d\nRepeat: %d\n";
+static const char* subbrute_key_small_no_tail = "Bit: %d\nKey: %s\nRepeat: %d\nRepeat: %d\n";
+static const char* subbrute_key_small_with_tail = "Bit: %d\nKey: %s\nTE: %d\nRepeat: %d\n";
 
 SubBruteDevice* subbrute_device_alloc() {
     SubBruteDevice* instance = malloc(sizeof(SubBruteDevice));
 
     instance->state = SubBruteDeviceStateIDLE;
     instance->key_index = 0;
+    instance->worker_running = false;
+    instance->last_time_tx_data = 0;
+
+    instance->thread = furi_thread_alloc();
+    furi_thread_set_name(instance->thread, "SubBruteAttackWorker");
+    furi_thread_set_stack_size(instance->thread, 2048);
+    furi_thread_set_context(instance->thread, instance);
+    furi_thread_set_callback(instance->thread, subbrute_worker_thread);
 
-    string_init(instance->load_path);
-    string_init(instance->preset_name);
-    string_init(instance->protocol_name);
+    instance->context = NULL;
+    instance->callback = NULL;
 
+    instance->protocol_info = NULL;
     instance->decoder_result = NULL;
+    instance->transmitter = NULL;
     instance->receiver = NULL;
     instance->environment = subghz_environment_alloc();
 
@@ -84,6 +65,11 @@ void subbrute_device_free(SubBruteDevice* instance) {
         instance->receiver = NULL;
     }
 
+    if(instance->transmitter != NULL) {
+        subghz_transmitter_free(instance->transmitter);
+        instance->transmitter = NULL;
+    }
+
     subghz_environment_free(instance->environment);
     instance->environment = NULL;
 
@@ -91,109 +77,280 @@ void subbrute_device_free(SubBruteDevice* instance) {
     FURI_LOG_D(TAG, "before free");
 #endif
 
-    string_clear(instance->load_path);
-    string_clear(instance->preset_name);
-    string_clear(instance->protocol_name);
+    furi_thread_free(instance->thread);
+    subbrute_device_free_protocol_info(instance);
 
     free(instance);
 }
 
-bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_name) {
-    furi_assert(instance);
+/**
+ * Entrypoint for worker
+ *
+ * @param context SubBruteWorker*
+ * @return 0 if ok
+ */
+int32_t subbrute_worker_thread(void* context) {
+    furi_assert(context);
+    SubBruteDevice* instance = (SubBruteDevice*)context;
 
+    if(!instance->worker_running) {
+        FURI_LOG_W(TAG, "Worker is not set to running state!");
+        return -1;
+    }
+    if(instance->state != SubBruteDeviceStateReady &&
+       instance->state != SubBruteDeviceStateFinished) {
+        FURI_LOG_W(TAG, "Invalid state for running worker! State: %d", instance->state);
+        return -2;
+    }
 #ifdef FURI_DEBUG
-    FURI_LOG_D(TAG, "subbrute_device_save_file: %s", dev_file_name);
+    FURI_LOG_I(TAG, "Worker start");
 #endif
-    bool result = subbrute_device_create_packet_parsed(instance, instance->key_index, false);
 
-    if(!result) {
-        FURI_LOG_E(TAG, "subbrute_device_create_packet_parsed failed!");
-        //subbrute_device_notification_message(instance, &sequence_error);
+    SubBruteDeviceState local_state = instance->state = SubBruteDeviceStateTx;
+    subbrute_device_send_callback(instance);
+
+    FlipperFormat* flipper_format = flipper_format_string_alloc();
+
+    while(instance->worker_running) {
+        if(!subbrute_device_create_packet_parsed(
+               instance, flipper_format, instance->key_index, true)) {
+            FURI_LOG_W(TAG, "Error creating packet! BREAK");
+            instance->worker_running = false;
+            local_state = SubBruteDeviceStateIDLE;
+            break;
+        }
+        subbrute_device_subghz_transmit(instance, flipper_format);
+
+        if(instance->key_index + 1 > instance->max_value) {
+#ifdef FURI_DEBUG
+            FURI_LOG_I(TAG, "Worker finished to end");
+#endif
+            local_state = SubBruteDeviceStateFinished;
+            break;
+        }
+        instance->key_index++;
+
+        furi_delay_ms(SUBBRUTE_TX_TIMEOUT);
+    }
+
+    flipper_format_free(flipper_format);
+
+    instance->worker_running = false; // Because we have error states
+    instance->state = local_state == SubBruteDeviceStateTx ? SubBruteDeviceStateReady :
+                                                             local_state;
+    subbrute_device_send_callback(instance);
+
+#ifdef FURI_DEBUG
+    FURI_LOG_I(TAG, "Worker stop");
+#endif
+    return 0;
+}
+
+bool subbrute_worker_start(SubBruteDevice* instance) {
+    furi_assert(instance);
+
+    if(instance->worker_running) {
+        FURI_LOG_W(TAG, "Worker is already running!");
+        return false;
+    }
+    if(instance->state != SubBruteDeviceStateReady &&
+       instance->state != SubBruteDeviceStateFinished) {
+        FURI_LOG_W(TAG, "Worker cannot start, invalid device state: %d", instance->state);
+        return false;
+    }
+    if(instance->protocol_info == NULL) {
+        FURI_LOG_W(TAG, "Worker cannot start, protocol_info is NULL!");
+        return false;
+    }
+
+    instance->worker_running = true;
+    furi_thread_start(instance->thread);
+
+    return true;
+}
+
+void subbrute_worker_stop(SubBruteDevice* instance) {
+    furi_assert(instance);
+
+    instance->worker_running = false;
+
+    furi_thread_join(instance->thread);
+
+    furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
+    furi_hal_subghz_sleep();
+}
+
+SubBruteAttacks subbrute_device_get_attack(SubBruteDevice* instance) {
+    return instance->attack;
+}
+bool subbrute_device_is_worker_running(SubBruteDevice* instance) {
+    return instance->worker_running;
+}
+uint64_t subbrute_device_get_step(SubBruteDevice* instance) {
+    return instance->key_index;
+}
+const char* subbrute_device_get_file_key(SubBruteDevice* instance) {
+    return instance->file_key;
+}
+uint64_t subbrute_device_add_step(SubBruteDevice* instance, int8_t step) {
+    if(!subbrute_device_can_manual_transmit(instance)) {
+        return instance->key_index;
+    }
+    if(step > 0) {
+        if((instance->key_index + step) - instance->max_value == 1) {
+            instance->key_index = 0x00;
+        } else {
+            uint64_t value = instance->key_index + step;
+            if(value == instance->max_value) {
+                instance->key_index = value;
+            } else {
+                instance->key_index = value % instance->max_value;
+            }
+        }
+    } else {
+        if(instance->key_index + step == 0) {
+            instance->key_index = 0x00;
+        } else if(instance->key_index == 0) {
+            instance->key_index = instance->max_value;
+        } else {
+            uint64_t value = ((instance->key_index - step) + instance->max_value);
+            if(value == instance->max_value) {
+                instance->key_index = value;
+            } else {
+                instance->key_index = value % instance->max_value;
+            }
+        }
+    }
+
+    return instance->key_index;
+}
+void subbrute_device_set_load_index(SubBruteDevice* instance, uint64_t load_index) {
+    instance->load_index = load_index;
+}
+void subbrute_device_reset_step(SubBruteDevice* instance) {
+    instance->key_index = 0x00;
+}
+void subbrute_device_subghz_transmit(SubBruteDevice* instance, FlipperFormat* flipper_format) {
+    instance->transmitter = subghz_transmitter_alloc_init(
+        instance->environment, subbrute_protocol_name(instance->attack));
+    subghz_transmitter_deserialize(instance->transmitter, flipper_format);
+    furi_hal_subghz_reset();
+    furi_hal_subghz_load_preset(instance->protocol_info->preset);
+    furi_hal_subghz_set_frequency_and_path(instance->protocol_info->preset);
+
+    furi_hal_subghz_start_async_tx(subghz_transmitter_yield, instance->transmitter);
+
+    while(!furi_hal_subghz_is_async_tx_complete()) {
+        furi_delay_ms(SUBBRUTE_TX_TIMEOUT);
+    }
+    furi_hal_subghz_stop_async_tx();
+
+    furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
+    furi_hal_subghz_sleep();
+    subghz_transmitter_free(instance->transmitter);
+    instance->transmitter = NULL;
+}
+
+bool subbrute_device_transmit_current_key(SubBruteDevice* instance) {
+    furi_assert(instance);
+
+    if(instance->worker_running) {
+        FURI_LOG_W(TAG, "Worker in running state!");
+        return false;
+    }
+    if(instance->state != SubBruteDeviceStateReady &&
+       instance->state != SubBruteDeviceStateFinished) {
+        FURI_LOG_W(TAG, "Invalid state for running worker! State: %d", instance->state);
+        return false;
+    }
+
+    uint32_t ticks = furi_get_tick();
+    if((ticks - instance->last_time_tx_data) < SUBBRUTE_MANUAL_TRANSMIT_INTERVAL) {
+#if FURI_DEBUG
+        FURI_LOG_D(TAG, "Need to wait, current: %d", ticks - instance->last_time_tx_data);
+#endif
+        return false;
+    }
+
+    instance->last_time_tx_data = ticks;
+    FlipperFormat* flipper_format = flipper_format_string_alloc();
+
+    if(!subbrute_device_create_packet_parsed(instance, flipper_format, instance->key_index, true)) {
+        FURI_LOG_W(TAG, "Error creating packet! EXIT");
+        return false;
+    }
+    subbrute_device_subghz_transmit(instance, flipper_format);
+
+    flipper_format_free(flipper_format);
+
+    return true;
+}
+
+void subbrute_device_set_callback(
+    SubBruteDevice* instance,
+    SubBruteDeviceWorkerCallback callback,
+    void* context) {
+    furi_assert(instance);
+
+    instance->callback = callback;
+    instance->context = context;
+}
+
+bool subbrute_device_can_manual_transmit(SubBruteDevice* instance) {
+    furi_assert(instance);
+
+    return !instance->worker_running && instance->state != SubBruteDeviceStateIDLE &&
+           instance->state != SubBruteDeviceStateTx &&
+           ((furi_get_tick() - instance->last_time_tx_data) > SUBBRUTE_MANUAL_TRANSMIT_INTERVAL);
+}
+
+bool subbrute_device_save_file(SubBruteDevice* instance, const char* dev_file_name) {
+    furi_assert(instance);
+
+    if(instance->state != SubBruteDeviceStateReady &&
+       instance->state != SubBruteDeviceStateFinished) {
+        FURI_LOG_W(TAG, "Worker is not set to running state!");
         return false;
     }
 
+#ifdef FURI_DEBUG
+    FURI_LOG_D(TAG, "subbrute_device_save_file: %s", dev_file_name);
+#endif
+
     Storage* storage = furi_record_open(RECORD_STORAGE);
-    Stream* stream = buffered_file_stream_alloc(storage);
+    FlipperFormat* file = flipper_format_file_alloc(storage);
 
-    result = false;
+    bool result = false;
     do {
-        if(!buffered_file_stream_open(stream, dev_file_name, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) {
-            buffered_file_stream_close(stream);
+        if(!flipper_format_file_open_always(file, dev_file_name)) {
+            break;
+        }
+
+        if(!subbrute_device_create_packet_parsed(instance, file, instance->key_index, false)) {
+            FURI_LOG_E(TAG, "subbrute_device_create_packet_parsed failed!");
             break;
         }
-        stream_write_cstring(stream, instance->payload);
 
         result = true;
     } while(false);
 
-    buffered_file_stream_close(stream);
-    stream_free(stream);
     if(!result) {
-        FURI_LOG_E(TAG, "stream_write_string failed!");
-        //subbrute_device_notification_message(instance, &sequence_error);
+        FURI_LOG_E(TAG, "flipper_format_file_open_always failed!");
     }
 
+    flipper_format_free(file);
     furi_record_close(RECORD_STORAGE);
 
     return result;
 }
 
-const char* subbrute_device_error_get_desc(SubBruteFileResult error_id) {
-    const char* result;
-    switch(error_id) {
-    case(SubBruteFileResultOk):
-        result = "OK";
-        break;
-    case(SubBruteFileResultErrorOpenFile):
-        result = "invalid name/path";
-        break;
-    case(SubBruteFileResultMissingOrIncorrectHeader):
-        result = "Missing or incorrect header";
-        break;
-    case(SubBruteFileResultFrequencyNotAllowed):
-        result = "Invalid frequency!";
-        break;
-    case(SubBruteFileResultMissingOrIncorrectFrequency):
-        result = "Missing or incorrect Frequency";
-        break;
-    case(SubBruteFileResultPresetInvalid):
-        result = "Preset FAIL";
-        break;
-    case(SubBruteFileResultMissingProtocol):
-        result = "Missing Protocol";
-        break;
-    case(SubBruteFileResultProtocolNotSupported):
-        result = "RAW unsupported";
-        break;
-    case(SubBruteFileResultDynamicProtocolNotValid):
-        result = "Dynamic protocol unsupported";
-        break;
-    case(SubBruteFileResultProtocolNotFound):
-        result = "Protocol not found";
-        break;
-    case(SubBruteFileResultMissingOrIncorrectBit):
-        result = "Missing or incorrect Bit";
-        break;
-    case(SubBruteFileResultMissingOrIncorrectKey):
-        result = "Missing or incorrect Key";
-        break;
-    case(SubBruteFileResultMissingOrIncorrectTe):
-        result = "Missing or incorrect TE";
-        break;
-    case SubBruteFileResultUnknown:
-    default:
-        result = "Unknown error";
-        break;
-    }
-    return result;
-}
-
-bool subbrute_device_create_packet_parsed(SubBruteDevice* instance, uint64_t step, bool small) {
+bool subbrute_device_create_packet_parsed(
+    SubBruteDevice* instance,
+    FlipperFormat* flipper_format,
+    uint64_t step,
+    bool small) {
     furi_assert(instance);
 
-    //char step_payload[32];
-    //memset(step_payload, '0', sizeof(step_payload));
-    memset(instance->payload, 0, sizeof(instance->payload));
     string_t candidate;
     string_init(candidate);
 
@@ -231,42 +388,44 @@ bool subbrute_device_create_packet_parsed(SubBruteDevice* instance, uint64_t ste
     FURI_LOG_D(TAG, "candidate: %s, step: %d", string_get_cstr(candidate), step);
 #endif
 
+    Stream* stream = flipper_format_get_raw_stream(flipper_format);
+    stream_clean(stream);
+
     if(small) {
-        if(instance->has_tail) {
-            snprintf(
-                instance->payload,
-                sizeof(instance->payload),
+        if(instance->protocol_info->te) {
+            stream_write_format(
+                stream,
                 subbrute_key_small_with_tail,
-                instance->bit,
+                instance->protocol_info->bits,
                 string_get_cstr(candidate),
-                instance->te);
+                instance->protocol_info->te,
+                instance->protocol_info->repeat);
         } else {
-            snprintf(
-                instance->payload,
-                sizeof(instance->payload),
+            stream_write_format(
+                stream,
                 subbrute_key_small_no_tail,
-                instance->bit,
-                string_get_cstr(candidate));
+                instance->protocol_info->bits,
+                string_get_cstr(candidate),
+                instance->protocol_info->repeat);
         }
     } else {
-        if(instance->has_tail) {
-            snprintf(
-                instance->payload,
-                sizeof(instance->payload),
-                subbrute_key_file_princeton_end,
+        if(instance->protocol_info->te) {
+            stream_write_format(
+                stream,
+                subbrute_key_file_key_with_tail,
                 instance->file_template,
                 string_get_cstr(candidate),
-                instance->te);
+                instance->protocol_info->te,
+                instance->protocol_info->repeat);
         } else {
-            snprintf(
-                instance->payload,
-                sizeof(instance->payload),
+            stream_write_format(
+                stream,
                 subbrute_key_file_key,
                 instance->file_template,
-                string_get_cstr(candidate));
+                string_get_cstr(candidate),
+                instance->protocol_info->repeat);
         }
     }
-
 #ifdef FURI_DEBUG
     //FURI_LOG_D(TAG, "payload: %s", instance->payload);
 #endif
@@ -282,77 +441,11 @@ SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBrute
     FURI_LOG_D(TAG, "subbrute_device_attack_set: %d", type);
 #endif
     subbrute_device_attack_set_default_values(instance, type);
-    switch(type) {
-    case SubBruteAttackLoadFile:
-        // In this case values must be already set
-        //        file_result =
-        //            subbrute_device_load_from_file(instance, string_get_cstr(instance->load_path));
-        //        if(file_result != SubBruteFileResultOk) {
-        //            // Failed load file so failed to set attack type
-        //            return file_result; // RETURN
-        //        }
-        break;
-    case SubBruteAttackCAME12bit307:
-    case SubBruteAttackCAME12bit433:
-    case SubBruteAttackCAME12bit868:
-        if(type == SubBruteAttackCAME12bit307) {
-            instance->frequency = 307800000;
-        } else if(type == SubBruteAttackCAME12bit433) {
-            instance->frequency = 433920000;
-        } else /* ALWAYS TRUE if(type == SubBruteAttackCAME12bit868) */ {
-            instance->frequency = 868350000;
-        }
-        instance->bit = 12;
-        string_set_str(instance->protocol_name, protocol_came);
-        string_set_str(instance->preset_name, preset_ook650_async);
-        break;
-    case SubBruteAttackChamberlain9bit300:
-    case SubBruteAttackChamberlain9bit315:
-    case SubBruteAttackChamberlain9bit390:
-        if(type == SubBruteAttackChamberlain9bit300) {
-            instance->frequency = 300000000;
-        } else if(type == SubBruteAttackChamberlain9bit315) {
-            instance->frequency = 315000000;
-        } else /* ALWAYS TRUE if(type == SubBruteAttackChamberlain9bit390) */ {
-            instance->frequency = 390000000;
-        }
-        instance->bit = 9;
-        string_set_str(instance->protocol_name, protocol_cham_code);
-        string_set_str(instance->preset_name, preset_ook650_async);
-        break;
-    case SubBruteAttackLinear10bit300:
-        instance->frequency = 300000000;
-        instance->bit = 10;
-        string_set_str(instance->protocol_name, protocol_linear);
-        string_set_str(instance->preset_name, preset_ook650_async);
-        break;
-    case SubBruteAttackLinear10bit310:
-        instance->frequency = 310000000;
-        instance->bit = 10;
-        string_set_str(instance->protocol_name, protocol_linear);
-        string_set_str(instance->preset_name, preset_ook650_async);
-        break;
-    case SubBruteAttackNICE12bit433:
-        instance->frequency = 433920000;
-        instance->bit = 12;
-        string_set_str(instance->protocol_name, protocol_nice_flo);
-        string_set_str(instance->preset_name, preset_ook650_async);
-        break;
-    case SubBruteAttackNICE12bit868:
-        instance->frequency = 868350000;
-        instance->bit = 12;
-        string_set_str(instance->protocol_name, protocol_nice_flo);
-        string_set_str(instance->preset_name, preset_ook650_async);
-        break;
-    default:
-        FURI_LOG_E(TAG, "Unknown attack type: %d", type);
-        return SubBruteFileResultProtocolNotFound; // RETURN
-    }
 
-    /*if(!furi_hal_subghz_is_tx_allowed(instance->frequency)) {
-        FURI_LOG_E(TAG, "Frequency invalid: %d", instance->frequency);
-        return SubBruteFileResultMissingOrIncorrectFrequency; // RETURN
-    }*/
+    if(type != SubBruteAttackLoadFile) {
+        subbrute_device_free_protocol_info(instance);
+        instance->protocol_info = subbrute_protocol(type);
+    }
 
     // For non-file types we didn't set SubGhzProtocolDecoderBase
     instance->receiver = subghz_receiver_alloc_init(instance->environment);
@@ -362,7 +455,7 @@ SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBrute
     uint8_t protocol_check_result = SubBruteFileResultProtocolNotFound;
     if(type != SubBruteAttackLoadFile) {
         instance->decoder_result = subghz_receiver_search_decoder_base_by_name(
-            instance->receiver, string_get_cstr(instance->protocol_name));
+            instance->receiver, subbrute_protocol_file(instance->protocol_info->file));
 
         if(!instance->decoder_result ||
            instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) {
@@ -372,7 +465,6 @@ SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBrute
         }
     } else {
         // And here we need to set preset enum
-        instance->preset = subbrute_device_convert_preset(string_get_cstr(instance->preset_name));
         protocol_check_result = SubBruteFileResultOk;
     }
 
@@ -383,16 +475,13 @@ SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBrute
         return SubBruteFileResultProtocolNotFound;
     }
 
-    instance->has_tail =
-        (strcmp(string_get_cstr(instance->protocol_name), protocol_princeton) == 0);
-
     // Calc max value
     if(instance->attack == SubBruteAttackLoadFile) {
         instance->max_value = 0xFF;
     } else {
         string_t max_value_s;
         string_init(max_value_s);
-        for(uint8_t i = 0; i < instance->bit; i++) {
+        for(uint8_t i = 0; i < instance->protocol_info->bits; i++) {
             string_cat_printf(max_value_s, "1");
         }
         instance->max_value = (uint64_t)strtol(string_get_cstr(max_value_s), NULL, 2);
@@ -405,24 +494,22 @@ SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* instance, SubBrute
         instance->file_template,
         sizeof(instance->file_template),
         subbrute_key_file_start,
-        instance->frequency,
-        string_get_cstr(instance->preset_name),
-        string_get_cstr(instance->protocol_name),
-        instance->bit);
-//    strncat(instance->file_template, "\n", sizeof(instance->file_template));
-//    strncat(instance->file_template, subbrute_key_file_key, sizeof(instance->file_template));
-//    if(instance->has_tail) {
-//        strncat(
-//            instance->file_template,
-//            subbrute_key_file_princeton_end,
-//            sizeof(instance->file_template));
-//    }
+        instance->protocol_info->frequency,
+        subbrute_protocol_preset(instance->protocol_info->preset),
+        subbrute_protocol_file(instance->protocol_info->file),
+        instance->protocol_info->bits);
 #ifdef FURI_DEBUG
-    FURI_LOG_D(TAG, "tail: %d, file_template: %s", instance->has_tail, instance->file_template);
+    FURI_LOG_D(
+        TAG, "tail: %d, file_template: %s", instance->protocol_info->te, instance->file_template);
 #endif
 
     // Init payload
-    subbrute_device_create_packet_parsed(instance, instance->key_index, false);
+    FlipperFormat* flipper_format = flipper_format_string_alloc();
+    if(subbrute_device_create_packet_parsed(instance, flipper_format, instance->key_index, false)) {
+        instance->state = SubBruteDeviceStateReady;
+        subbrute_device_send_callback(instance);
+    }
+    flipper_format_free(flipper_format);
 
     return SubBruteFileResultOk;
 }
@@ -459,8 +546,8 @@ uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, string_t file_p
 
         // Frequency
         if(flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) {
-            instance->frequency = temp_data32;
-            if(!furi_hal_subghz_is_tx_allowed(instance->frequency)) {
+            instance->protocol_info->frequency = temp_data32;
+            if(!furi_hal_subghz_is_tx_allowed(instance->protocol_info->frequency)) {
                 result = SubBruteFileResultFrequencyNotAllowed;
                 break;
             }
@@ -469,31 +556,33 @@ uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, string_t file_p
             result = SubBruteFileResultMissingOrIncorrectFrequency;
             break;
         }
+
         // Preset
         if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
             FURI_LOG_E(TAG, "Preset FAIL");
             result = SubBruteFileResultPresetInvalid;
         } else {
-            string_init_set_str(instance->preset_name, string_get_cstr(temp_str));
+            instance->protocol_info->preset = subbrute_protocol_convert_preset(temp_str);
         }
 
+        const char* protocol_file = NULL;
         // Protocol
         if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
             FURI_LOG_E(TAG, "Missing Protocol");
             result = SubBruteFileResultMissingProtocol;
             break;
         } else {
-            string_init_set_str(instance->protocol_name, string_get_cstr(temp_str));
+            instance->protocol_info->file = subbrute_protocol_file_protocol_name(temp_str);
+            protocol_file = subbrute_protocol_file(instance->protocol_info->file);
 #ifdef FURI_DEBUG
-            FURI_LOG_D(TAG, "Protocol: %s", string_get_cstr(instance->protocol_name));
+            FURI_LOG_D(TAG, "Protocol: %s", protocol_file);
 #endif
         }
 
-        instance->decoder_result = subghz_receiver_search_decoder_base_by_name(
-            instance->receiver, string_get_cstr(instance->protocol_name));
+        instance->decoder_result =
+            subghz_receiver_search_decoder_base_by_name(instance->receiver, protocol_file);
 
-        if(!instance->decoder_result ||
-           strcmp(string_get_cstr(instance->protocol_name), "RAW") == 0) {
+        if(!instance->decoder_result || strcmp(protocol_file, "RAW") == 0) {
             FURI_LOG_E(TAG, "RAW unsupported");
             result = SubBruteFileResultProtocolNotSupported;
             break;
@@ -510,24 +599,15 @@ uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, string_t file_p
         }
 #endif
 
-        //        instance->decoder_result = subghz_receiver_search_decoder_base_by_name(
-        //            instance->receiver, string_get_cstr(instance->protocol_name));
-        //
-        //        if(!instance->decoder_result) {
-        //            FURI_LOG_E(TAG, "Protocol not found");
-        //            result = SubBruteFileResultProtocolNotFound;
-        //            break;
-        //        }
-
         // Bit
         if(!flipper_format_read_uint32(fff_data_file, "Bit", &temp_data32, 1)) {
             FURI_LOG_E(TAG, "Missing or incorrect Bit");
             result = SubBruteFileResultMissingOrIncorrectBit;
             break;
         } else {
-            instance->bit = temp_data32;
+            instance->protocol_info->bits = temp_data32;
 #ifdef FURI_DEBUG
-            FURI_LOG_D(TAG, "Bit: %d", instance->bit);
+            FURI_LOG_D(TAG, "Bit: %d", instance->protocol_info->bits);
 #endif
         }
 
@@ -550,8 +630,7 @@ uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, string_t file_p
             //result = SubBruteFileResultMissingOrIncorrectTe;
             //break;
         } else {
-            instance->te = temp_data32;
-            instance->has_tail = true;
+            instance->protocol_info->te = temp_data32 != 0;
         }
 
         // Repeat
@@ -559,12 +638,12 @@ uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, string_t file_p
 #ifdef FURI_DEBUG
             FURI_LOG_D(TAG, "Repeat: %d", temp_data32);
 #endif
-            instance->repeat = temp_data32;
+            instance->protocol_info->repeat = (uint8_t)temp_data32;
         } else {
 #ifdef FURI_DEBUG
             FURI_LOG_D(TAG, "Repeat: 3 (default)");
 #endif
-            instance->repeat = 3;
+            instance->protocol_info->repeat = 3;
         }
 
         result = SubBruteFileResultOk;
@@ -601,55 +680,66 @@ void subbrute_device_attack_set_default_values(
     instance->load_index = 0x00;
     memset(instance->file_template, 0, sizeof(instance->file_template));
     memset(instance->current_key, 0, sizeof(instance->current_key));
-    memset(instance->text_store, 0, sizeof(instance->text_store));
-    memset(instance->payload, 0, sizeof(instance->payload));
 
     if(default_attack != SubBruteAttackLoadFile) {
         memset(instance->file_key, 0, sizeof(instance->file_key));
 
         instance->max_value = (uint64_t)0x00;
-
-        string_clear(instance->protocol_name);
-        string_clear(instance->preset_name);
-
-        string_clear(instance->load_path);
-        string_init(instance->load_path);
-
-        string_init_set_str(instance->protocol_name, protocol_raw);
-        string_init_set_str(instance->preset_name, preset_ook650_async);
-        instance->preset = FuriHalSubGhzPresetOok650Async;
-
-        instance->repeat = 5;
-        instance->te = 0;
-        instance->has_tail = false;
     }
-#ifdef FURI_DEBUG
-    FURI_LOG_D(
-        TAG, "subbrute_device_attack_set_default_values done. has_tail: %d", instance->has_tail);
-    //furi_delay_ms(250);
-#endif
 }
 
-FuriHalSubGhzPreset subbrute_device_convert_preset(const char* preset_name) {
-    string_t preset;
-    string_init_set_str(preset, preset_name);
-    FuriHalSubGhzPreset preset_value;
-    if(string_cmp_str(preset, preset_ook270_async) == 0) {
-        preset_value = FuriHalSubGhzPresetOok270Async;
-    } else if(string_cmp_str(preset, preset_ook650_async) == 0) {
-        preset_value = FuriHalSubGhzPresetOok650Async;
-    } else if(string_cmp_str(preset, preset_2fsk_dev238_async) == 0) {
-        preset_value = FuriHalSubGhzPreset2FSKDev238Async;
-    } else if(string_cmp_str(preset, preset_2fsk_dev476_async) == 0) {
-        preset_value = FuriHalSubGhzPreset2FSKDev476Async;
-    } else if(string_cmp_str(preset, preset_msk99_97_kb_async) == 0) {
-        preset_value = FuriHalSubGhzPresetMSK99_97KbAsync;
-    } else if(string_cmp_str(preset, preset_gfs99_97_kb_async) == 0) {
-        preset_value = FuriHalSubGhzPresetMSK99_97KbAsync;
-    } else {
-        preset_value = FuriHalSubGhzPresetCustom;
+void subbrute_device_send_callback(SubBruteDevice* instance) {
+    if(instance->callback != NULL) {
+        instance->callback(instance->context, instance->state);
     }
-
-    string_clear(preset);
-    return preset_value;
 }
+
+const char* subbrute_device_error_get_desc(SubBruteFileResult error_id) {
+    const char* result;
+    switch(error_id) {
+    case(SubBruteFileResultOk):
+        result = "OK";
+        break;
+    case(SubBruteFileResultErrorOpenFile):
+        result = "invalid name/path";
+        break;
+    case(SubBruteFileResultMissingOrIncorrectHeader):
+        result = "Missing or incorrect header";
+        break;
+    case(SubBruteFileResultFrequencyNotAllowed):
+        result = "Invalid frequency!";
+        break;
+    case(SubBruteFileResultMissingOrIncorrectFrequency):
+        result = "Missing or incorrect Frequency";
+        break;
+    case(SubBruteFileResultPresetInvalid):
+        result = "Preset FAIL";
+        break;
+    case(SubBruteFileResultMissingProtocol):
+        result = "Missing Protocol";
+        break;
+    case(SubBruteFileResultProtocolNotSupported):
+        result = "RAW unsupported";
+        break;
+    case(SubBruteFileResultDynamicProtocolNotValid):
+        result = "Dynamic protocol unsupported";
+        break;
+    case(SubBruteFileResultProtocolNotFound):
+        result = "Protocol not found";
+        break;
+    case(SubBruteFileResultMissingOrIncorrectBit):
+        result = "Missing or incorrect Bit";
+        break;
+    case(SubBruteFileResultMissingOrIncorrectKey):
+        result = "Missing or incorrect Key";
+        break;
+    case(SubBruteFileResultMissingOrIncorrectTe):
+        result = "Missing or incorrect TE";
+        break;
+    case SubBruteFileResultUnknown:
+    default:
+        result = "Unknown error";
+        break;
+    }
+    return result;
+}

+ 29 - 73
applications/plugins/subbrute/subbrute_device.h

@@ -1,102 +1,58 @@
 #pragma once
 
-#include <lib/toolbox/stream/stream.h>
-#include <gui/gui.h>
-#include <dialogs/dialogs.h>
+#include "subbrute_device_i.h"
 #include <lib/subghz/protocols/base.h>
 #include <lib/subghz/transmitter.h>
 #include <lib/subghz/receiver.h>
 #include <lib/subghz/environment.h>
 
-#define SUBBRUTE_TEXT_STORE_SIZE 256
-
-#define SUBBRUTE_MAX_LEN_NAME 64
-#define SUBBRUTE_PATH EXT_PATH("subghz")
-#define SUBBRUTE_FILE_EXT ".sub"
-
-#define SUBBRUTE_PAYLOAD_SIZE 16
-
-typedef enum {
-    SubBruteAttackCAME12bit307,
-    SubBruteAttackCAME12bit433,
-    SubBruteAttackCAME12bit868,
-    SubBruteAttackNICE12bit433,
-    SubBruteAttackNICE12bit868,
-    SubBruteAttackChamberlain9bit300,
-    SubBruteAttackChamberlain9bit315,
-    SubBruteAttackChamberlain9bit390,
-    SubBruteAttackLinear10bit300,
-    SubBruteAttackLinear10bit310,
-    SubBruteAttackLoadFile,
-    SubBruteAttackTotalCount,
-} SubBruteAttacks;
-
-typedef enum {
-    SubBruteFileResultUnknown,
-    SubBruteFileResultOk,
-    SubBruteFileResultErrorOpenFile,
-    SubBruteFileResultMissingOrIncorrectHeader,
-    SubBruteFileResultFrequencyNotAllowed,
-    SubBruteFileResultMissingOrIncorrectFrequency,
-    SubBruteFileResultPresetInvalid,
-    SubBruteFileResultMissingProtocol,
-    SubBruteFileResultProtocolNotSupported,
-    SubBruteFileResultDynamicProtocolNotValid,
-    SubBruteFileResultProtocolNotFound,
-    SubBruteFileResultMissingOrIncorrectBit,
-    SubBruteFileResultMissingOrIncorrectKey,
-    SubBruteFileResultMissingOrIncorrectTe,
-} SubBruteFileResult;
-
-typedef enum {
-    SubBruteDeviceStateIDLE,
-    SubBruteDeviceStateReady,
-    SubBruteDeviceStateTx,
-    SubBruteDeviceStateFinished,
-} SubBruteDeviceState;
-
-typedef struct {
+struct SubBruteDevice {
     SubBruteDeviceState state;
+    SubBruteProtocol* protocol_info;
+    volatile bool worker_running;
 
     // Current step
     uint64_t key_index;
-    string_t load_path;
     // Index of group to bruteforce in loaded file
     uint8_t load_index;
 
+    // SubGhz
+    FuriThread* thread;
     SubGhzReceiver* receiver;
     SubGhzProtocolDecoderBase* decoder_result;
     SubGhzEnvironment* environment;
+    SubGhzTransmitter* transmitter;
 
     // Attack state
     SubBruteAttacks attack;
     char file_template[SUBBRUTE_TEXT_STORE_SIZE];
-    bool has_tail;
-    char payload[SUBBRUTE_TEXT_STORE_SIZE * 2];
+
     uint64_t max_value;
 
     // Loaded info for attack type
-    FuriHalSubGhzPreset preset;
-    string_t preset_name;
-    string_t protocol_name;
-    uint32_t frequency;
-    uint32_t repeat;
-    uint32_t bit;
     char current_key[SUBBRUTE_PAYLOAD_SIZE];
-    uint32_t te;
-
     char file_key[SUBBRUTE_MAX_LEN_NAME];
-    char text_store[SUBBRUTE_PAYLOAD_SIZE];
-} SubBruteDevice;
 
-SubBruteDevice* subbrute_device_alloc();
-void subbrute_device_free(SubBruteDevice* instance);
-bool subbrute_device_save_file(SubBruteDevice* instance, const char* key_name);
-const char* subbrute_device_error_get_desc(SubBruteFileResult error_id);
-bool subbrute_device_create_packet_parsed(SubBruteDevice* context, uint64_t step, bool small);
-SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* context, SubBruteAttacks type);
-uint8_t subbrute_device_load_from_file(SubBruteDevice* context, string_t file_path);
-FuriHalSubGhzPreset subbrute_device_convert_preset(const char* preset);
+    // Manual transmit
+    uint32_t last_time_tx_data;
+
+    // Callback for changed states
+    SubBruteDeviceWorkerCallback callback;
+    void* context;
+};
+
+/*
+ * PRIVATE METHODS
+ */
+void subbrute_device_free_protocol_info(SubBruteDevice* instance);
+int32_t subbrute_worker_thread(void* context);
 void subbrute_device_attack_set_default_values(
     SubBruteDevice* context,
-    SubBruteAttacks default_attack);
+    SubBruteAttacks default_attack);
+bool subbrute_device_create_packet_parsed(
+    SubBruteDevice* instance,
+    FlipperFormat* flipper_format,
+    uint64_t step,
+    bool small);
+void subbrute_device_send_callback(SubBruteDevice* instance);
+void subbrute_device_subghz_transmit(SubBruteDevice* instance, FlipperFormat* flipper_format);

+ 64 - 0
applications/plugins/subbrute/subbrute_device_i.h

@@ -0,0 +1,64 @@
+#pragma once
+
+#include "subbrute_protocols.h"
+
+#define SUBBRUTE_TEXT_STORE_SIZE 256
+
+#define SUBBRUTE_MAX_LEN_NAME 64
+#define SUBBRUTE_PATH EXT_PATH("subghz")
+#define SUBBRUTE_FILE_EXT ".sub"
+
+#define SUBBRUTE_PAYLOAD_SIZE 16
+
+typedef enum {
+    SubBruteFileResultUnknown,
+    SubBruteFileResultOk,
+    SubBruteFileResultErrorOpenFile,
+    SubBruteFileResultMissingOrIncorrectHeader,
+    SubBruteFileResultFrequencyNotAllowed,
+    SubBruteFileResultMissingOrIncorrectFrequency,
+    SubBruteFileResultPresetInvalid,
+    SubBruteFileResultMissingProtocol,
+    SubBruteFileResultProtocolNotSupported,
+    SubBruteFileResultDynamicProtocolNotValid,
+    SubBruteFileResultProtocolNotFound,
+    SubBruteFileResultMissingOrIncorrectBit,
+    SubBruteFileResultMissingOrIncorrectKey,
+    SubBruteFileResultMissingOrIncorrectTe,
+} SubBruteFileResult;
+
+typedef enum {
+    SubBruteDeviceStateIDLE,
+    SubBruteDeviceStateReady,
+    SubBruteDeviceStateTx,
+    SubBruteDeviceStateFinished
+} SubBruteDeviceState;
+
+typedef void (*SubBruteDeviceWorkerCallback)(void* context, SubBruteDeviceState state);
+typedef struct SubBruteDevice SubBruteDevice;
+
+SubBruteDevice* subbrute_device_alloc();
+void subbrute_device_free(SubBruteDevice* instance);
+bool subbrute_device_save_file(SubBruteDevice* instance, const char* key_name);
+const char* subbrute_device_error_get_desc(SubBruteFileResult error_id);
+SubBruteFileResult subbrute_device_attack_set(SubBruteDevice* context, SubBruteAttacks type);
+uint8_t subbrute_device_load_from_file(SubBruteDevice* context, string_t file_path);
+
+
+bool subbrute_device_is_worker_running(SubBruteDevice* instance);
+SubBruteAttacks subbrute_device_get_attack(SubBruteDevice* instance);
+uint64_t subbrute_device_get_max_value(SubBruteDevice* instance);
+uint64_t subbrute_device_get_step(SubBruteDevice* instance);
+uint64_t subbrute_device_add_step(SubBruteDevice* instance, int8_t step);
+void subbrute_device_set_load_index(SubBruteDevice* instance, uint64_t load_index);
+void subbrute_device_reset_step(SubBruteDevice* instance);
+const char* subbrute_device_get_file_key(SubBruteDevice* instance);
+
+bool subbrute_worker_start(SubBruteDevice* instance);
+void subbrute_worker_stop(SubBruteDevice* instance);
+bool subbrute_device_transmit_current_key(SubBruteDevice* instance);
+bool subbrute_device_can_manual_transmit(SubBruteDevice* instance);
+void subbrute_device_set_callback(
+    SubBruteDevice* instance,
+    SubBruteDeviceWorkerCallback callback,
+    void* context);

+ 7 - 19
applications/plugins/subbrute/subbrute_i.h

@@ -4,14 +4,10 @@
 #include <furi_hal.h>
 #include <input/input.h>
 
-#include "lib/toolbox/path.h"
 #include <notification/notification.h>
 #include <notification/notification_messages.h>
 #include <m-string.h>
 
-#include <lib/toolbox/stream/stream.h>
-#include <stream_buffer.h>
-
 #include <gui/gui.h>
 #include <gui/view_dispatcher.h>
 #include <gui/view_stack.h>
@@ -23,17 +19,11 @@
 
 #include <dialogs/dialogs.h>
 
-#include <lib/subghz/protocols/base.h>
-#include <lib/subghz/transmitter.h>
-#include <lib/subghz/receiver.h>
-#include <lib/subghz/environment.h>
 #include <notification/notification.h>
 #include <notification/notification_messages.h>
 
-#include "subbrute_device.h"
-#include "helpers/subbrute_worker.h"
 #include "subbrute.h"
-#include "scenes/subbrute_scene.h"
+#include "subbrute_device_i.h"
 #include "views/subbrute_attack_view.h"
 #include "views/subbrute_main_view.h"
 
@@ -60,6 +50,10 @@ struct SubBruteState {
     DialogsApp* dialogs;
     Loading* loading;
 
+    // Text store
+    char text_store[SUBBRUTE_MAX_LEN_NAME];
+    string_t file_path;
+
     // Views
     SubBruteMainView* view_main;
     SubBruteAttackView* view_attack;
@@ -68,16 +62,10 @@ struct SubBruteState {
     // Scene
     SceneManager* scene_manager;
 
+    // SubBruteDevice
     SubBruteDevice* device;
-    SubBruteWorker* worker;
-
-    //Menu stuff
-    // TODO: Do we need it?
-    uint8_t menu_index;
 };
 
 void subbrute_show_loading_popup(void* context, bool show);
 void subbrute_text_input_callback(void* context);
-void subbrute_popup_closed_callback(void* context);
-const char* subbrute_get_menu_name(uint8_t index);
-const char* subbrute_get_small_menu_name(uint8_t index);
+void subbrute_popup_closed_callback(void* context);

+ 127 - 0
applications/plugins/subbrute/subbrute_protocols.c

@@ -0,0 +1,127 @@
+#include "subbrute_protocols.h"
+
+static const SubBruteProtocol subbrute_protocols[SubBruteAttackTotalCount] = {
+    [SubBruteAttackCAME12bit307] =
+        {307800000, 12, 0, 3, FuriHalSubGhzPresetOok650Async, CAMEFileProtocol},
+    [SubBruteAttackCAME12bit433] =
+        {433920000, 12, 0, 3, FuriHalSubGhzPresetOok650Async, CAMEFileProtocol},
+    [SubBruteAttackCAME12bit868] =
+        {868350000, 12, 0, 3, FuriHalSubGhzPresetOok650Async, CAMEFileProtocol},
+    [SubBruteAttackNICE12bit433] =
+        {433920000, 12, 0, 3, FuriHalSubGhzPresetOok650Async, NICEFileProtocol},
+    [SubBruteAttackNICE12bit868] =
+        {868350000, 12, 0, 3, FuriHalSubGhzPresetOok650Async, NICEFileProtocol},
+    [SubBruteAttackChamberlain9bit300] =
+        {300000000, 9, 0, 3, FuriHalSubGhzPresetOok650Async, ChamberlainFileProtocol},
+    [SubBruteAttackChamberlain9bit315] =
+        {315000000, 9, 0, 3, FuriHalSubGhzPresetOok650Async, ChamberlainFileProtocol},
+    [SubBruteAttackChamberlain9bit390] =
+        {390000000, 9, 0, 3, FuriHalSubGhzPresetOok650Async, ChamberlainFileProtocol},
+    [SubBruteAttackLinear10bit300] =
+        {300000000, 10, 0, 5, FuriHalSubGhzPresetOok650Async, LinearFileProtocol},
+    [SubBruteAttackLinear10bit310] =
+        {300000000, 10, 0, 5, FuriHalSubGhzPresetOok650Async, LinearFileProtocol},
+    [SubBruteAttackLoadFile] = {0, 0, 0, 3, FuriHalSubGhzPresetOok650Async, RAWFileProtocol},
+};
+//static const uint32_t subbrute_protocols[SubBruteAttackTotalCount][TotalProtocolFields] = {
+//    [SubBruteAttackCAME12bit307] = {307800000, 12, 0, 3, FuriHalSubGhzPresetOok650Async, CAMEFileProtocol},
+//    [SubBruteAttackCAME12bit433] = {433920000, 12, 0, 3, FuriHalSubGhzPresetOok650Async, CAMEFileProtocol},
+//    [SubBruteAttackCAME12bit868] = {868350000, 12, 0, 3, FuriHalSubGhzPresetOok650Async, CAMEFileProtocol},
+//    [SubBruteAttackNICE12bit433] = {433920000, 12, 0, 3, FuriHalSubGhzPresetOok650Async, NICEFileProtocol},
+//    [SubBruteAttackNICE12bit868] = {868350000, 12, 0, 3, FuriHalSubGhzPresetOok650Async, NICEFileProtocol},
+//    [SubBruteAttackChamberlain9bit300] = {300000000, 9, 0, 3, FuriHalSubGhzPresetOok650Async, ChamberlainFileProtocol},
+//    [SubBruteAttackChamberlain9bit315] = {315000000, 9, 0, 3, FuriHalSubGhzPresetOok650Async, ChamberlainFileProtocol},
+//    [SubBruteAttackChamberlain9bit390] = {390000000, 9, 0, 3, FuriHalSubGhzPresetOok650Async, ChamberlainFileProtocol},
+//    [SubBruteAttackLinear10bit300] = {300000000, 10, 0, 5, FuriHalSubGhzPresetOok650Async, LinearFileProtocol},
+//    [SubBruteAttackLinear10bit310] = {300000000, 10, 0, 5, FuriHalSubGhzPresetOok650Async, LinearFileProtocol},
+//    [SubBruteAttackLoadFile] = {0, 0, 0, 3, FuriHalSubGhzPresetOok650Async, RAWFileProtocol},
+//};
+
+static const char* subbrute_protocol_names[] = {
+    [SubBruteAttackCAME12bit307] = "CAME 12bit 307MHz",
+    [SubBruteAttackCAME12bit433] = "CAME 12bit 433MHz",
+    [SubBruteAttackCAME12bit868] = "CAME 12bit 868MHz",
+    [SubBruteAttackNICE12bit433] = "NICE 12bit 433MHz",
+    [SubBruteAttackNICE12bit868] = "NICE 12bit 868MHz",
+    [SubBruteAttackChamberlain9bit300] = "Chamberlain 9bit 300MHz",
+    [SubBruteAttackChamberlain9bit315] = "Chamberlain 9bit 315MHz",
+    [SubBruteAttackChamberlain9bit390] = "Chamberlain 9bit 390MHz",
+    [SubBruteAttackLinear10bit300] = "Linear 10bit 300MHz",
+    [SubBruteAttackLinear10bit310] = "Linear 10bit 310MHz",
+    [SubBruteAttackLoadFile] = "BF existing dump",
+    [SubBruteAttackTotalCount] = "Total Count",
+};
+
+static const char* subbrute_protocol_presets[] = {
+    [FuriHalSubGhzPresetIDLE] = "FuriHalSubGhzPresetIDLE",
+    [FuriHalSubGhzPresetOok270Async] = "FuriHalSubGhzPresetOok270Async",
+    [FuriHalSubGhzPresetOok650Async] = "FuriHalSubGhzPresetOok650Async",
+    [FuriHalSubGhzPreset2FSKDev238Async] = "FuriHalSubGhzPreset2FSKDev238Async",
+    [FuriHalSubGhzPreset2FSKDev476Async] = "FuriHalSubGhzPreset2FSKDev476Async",
+    [FuriHalSubGhzPresetMSK99_97KbAsync] = "FuriHalSubGhzPresetMSK99_97KbAsync",
+    [FuriHalSubGhzPresetGFSK9_99KbAsync] = "FuriHalSubGhzPresetGFSK9_99KbAsync",
+};
+
+static const char* subbrute_protocol_file_types[TotalFileProtocol] = {
+    [CAMEFileProtocol] = "CAME",
+    [NICEFileProtocol] = "Nice FLO",
+    [ChamberlainFileProtocol] = "Cham_Code",
+    [LinearFileProtocol] = "Linear",
+    [PrincetonFileProtocol] = "Princeton",
+    [RAWFileProtocol] = "RAW"};
+
+SubBruteProtocol* subbrute_protocol_alloc(void) {
+    SubBruteProtocol* protocol = malloc(sizeof(SubBruteProtocol));
+    protocol->frequency = subbrute_protocols[SubBruteAttackLoadFile].frequency;
+    protocol->repeat = subbrute_protocols[SubBruteAttackLoadFile].repeat;
+    protocol->preset = subbrute_protocols[SubBruteAttackLoadFile].preset;
+    protocol->file = subbrute_protocols[SubBruteAttackLoadFile].file;
+    protocol->te = subbrute_protocols[SubBruteAttackLoadFile].te;
+    protocol->bits = subbrute_protocols[SubBruteAttackLoadFile].bits;
+
+    return protocol;
+}
+
+const char* subbrute_protocol_name(SubBruteAttacks index) {
+    return subbrute_protocol_names[index];
+}
+
+SubBruteProtocol* subbrute_protocol(SubBruteAttacks index) {
+    SubBruteProtocol* protocol = subbrute_protocol_alloc();
+    protocol->frequency = subbrute_protocols[index].frequency;
+    protocol->repeat = subbrute_protocols[index].repeat;
+    protocol->preset = subbrute_protocols[index].preset;
+    protocol->file = subbrute_protocols[index].file;
+    protocol->te = subbrute_protocols[index].te;
+    protocol->bits = subbrute_protocols[index].bits;
+
+    return protocol;
+}
+
+const char* subbrute_protocol_preset(FuriHalSubGhzPreset preset) {
+    return subbrute_protocol_presets[preset];
+}
+
+const char* subbrute_protocol_file(SubBruteFileProtocol protocol) {
+    return subbrute_protocol_file_types[protocol];
+}
+
+FuriHalSubGhzPreset subbrute_protocol_convert_preset(string_t preset_name) {
+    for(size_t i = FuriHalSubGhzPresetIDLE; i<FuriHalSubGhzPresetCustom;i++) {
+        if(string_cmp_str(preset_name, subbrute_protocol_presets[i]) == 0) {
+            return i;
+        }
+    }
+
+    return FuriHalSubGhzPresetIDLE;
+}
+
+SubBruteFileProtocol subbrute_protocol_file_protocol_name(string_t name) {
+    for(size_t i = CAMEFileProtocol; i<TotalFileProtocol-1;i++) {
+        if(string_cmp_str(name, subbrute_protocol_file_types[i]) == 0) {
+            return i;
+        }
+    }
+
+    return RAWFileProtocol;
+}

+ 42 - 0
applications/plugins/subbrute/subbrute_protocols.h

@@ -0,0 +1,42 @@
+#pragma once
+
+#include "subbrute_protocols_i.h"
+#include <furi.h>
+#include <m-string.h>
+#include <furi_hal_subghz.h>
+
+//typedef enum {
+//    FrequencyProtocolField,
+//    BitsProtocolField,
+//    HasTeProtocolField,
+//    RepeatProtocolField,
+//    PresetProtocolField,
+//    FileProtocolField,
+//    TotalProtocolFields
+//} ProtocolFields;
+
+typedef enum {
+    CAMEFileProtocol,
+    NICEFileProtocol,
+    ChamberlainFileProtocol,
+    LinearFileProtocol,
+    PrincetonFileProtocol,
+    RAWFileProtocol,
+    TotalFileProtocol,
+} SubBruteFileProtocol;
+
+typedef struct {
+    uint32_t frequency;
+    uint8_t bits;
+    uint8_t te;
+    uint8_t repeat;
+    FuriHalSubGhzPreset preset;
+    SubBruteFileProtocol file;
+} SubBruteProtocol;
+
+SubBruteProtocol* subbrute_protocol_alloc(void);
+SubBruteProtocol* subbrute_protocol(SubBruteAttacks index);
+const char* subbrute_protocol_preset(FuriHalSubGhzPreset preset);
+const char* subbrute_protocol_file(SubBruteFileProtocol protocol);
+FuriHalSubGhzPreset subbrute_protocol_convert_preset(string_t preset_name);
+SubBruteFileProtocol subbrute_protocol_file_protocol_name(string_t name);

+ 18 - 0
applications/plugins/subbrute/subbrute_protocols_i.h

@@ -0,0 +1,18 @@
+#pragma once
+
+typedef enum {
+    SubBruteAttackCAME12bit307,
+    SubBruteAttackCAME12bit433,
+    SubBruteAttackCAME12bit868,
+    SubBruteAttackNICE12bit433,
+    SubBruteAttackNICE12bit868,
+    SubBruteAttackChamberlain9bit300,
+    SubBruteAttackChamberlain9bit315,
+    SubBruteAttackChamberlain9bit390,
+    SubBruteAttackLinear10bit300,
+    SubBruteAttackLinear10bit310,
+    SubBruteAttackLoadFile,
+    SubBruteAttackTotalCount,
+} SubBruteAttacks;
+
+const char* subbrute_protocol_name(SubBruteAttacks index);

+ 2 - 1
applications/plugins/subbrute/views/subbrute_attack_view.c

@@ -1,5 +1,6 @@
 #include "subbrute_attack_view.h"
 #include "../subbrute_i.h"
+#include "../subbrute_protocols_i.h"
 
 #include "assets_icons.h"
 #include <input/input.h>
@@ -350,7 +351,7 @@ void subbrute_attack_view_draw(Canvas* canvas, void* context) {
     char buffer[26];
 
     const char* attack_name = NULL;
-    attack_name = subbrute_get_menu_name(model->index);
+    attack_name = subbrute_protocol_name(model->index);
     // Title
     if(model->is_attacking) {
         canvas_set_color(canvas, ColorBlack);

+ 1 - 1
applications/plugins/subbrute/views/subbrute_attack_view.h

@@ -1,12 +1,12 @@
 #pragma once
 
+#include "../subbrute_custom_event.h"
 #include <gui/view.h>
 #include "assets_icons.h"
 #include <input/input.h>
 #include <gui/elements.h>
 #include <gui/icon.h>
 #include <subghz/types.h>
-#include "../subbrute_custom_event.h"
 
 typedef void (*SubBruteAttackViewCallback)(SubBruteCustomEvent event, void* context);
 typedef struct SubBruteAttackView SubBruteAttackView;

+ 3 - 3
applications/plugins/subbrute/views/subbrute_main_view.c

@@ -1,5 +1,6 @@
 #include "subbrute_main_view.h"
 #include "../subbrute_i.h"
+#include "../subbrute_protocols_i.h"
 
 #include <input/input.h>
 #include <gui/elements.h>
@@ -123,7 +124,6 @@ void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) {
             uint8_t item_position = position - model->window_position;
 
             if(item_position < items_on_screen) {
-                const char* str = subbrute_get_menu_name(position);
                 if(m->index == position) {
                     canvas_draw_str_aligned(
                         canvas,
@@ -131,7 +131,7 @@ void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) {
                         9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
                         AlignLeft,
                         AlignCenter,
-                        str);
+                        subbrute_protocol_name(position));
                     elements_frame(
                         canvas, 1, 1 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, 124, 15);
                 } else {
@@ -141,7 +141,7 @@ void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) {
                         9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT,
                         AlignLeft,
                         AlignCenter,
-                        str);
+                        subbrute_protocol_name(position));
                 }
             }
         }