Alexander Kopachov 2 лет назад
Родитель
Сommit
03a7f9389e

+ 7 - 1
application.fam

@@ -10,10 +10,16 @@ App(
         "dialogs",
         "storage",
         "input", 
-        "notification"
+        "notification",
+#ifdef TOTP_BADBT_TYPE_ENABLED
+        "bt"
+#endif
     ],
     stack_size=2 * 1024,
     order=20,
+    fap_author="Alexander Kopachov (@akopachov)",
+    fap_description="Software-based TOTP authenticator for Flipper Zero device",
+    fap_weburl="https://github.com/akopachov/flipper-zero_authenticator",
     fap_category="Misc",
     fap_icon_assets="images",
     fap_icon="totp_10px.png",

+ 3 - 0
cli/cli.c

@@ -12,6 +12,7 @@
 #include "commands/pin/pin.h"
 #include "commands/notification/notification.h"
 #include "commands/reset/reset.h"
+#include "commands/automation/automation.h"
 
 static void totp_cli_print_unknown_command(const FuriString* unknown_command) {
     TOTP_CLI_PRINTF_ERROR(
@@ -57,6 +58,8 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) {
         totp_cli_command_pin_handle(plugin_state, args, cli);
     } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_NOTIFICATION) == 0) {
         totp_cli_command_notification_handle(plugin_state, args, cli);
+    } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_AUTOMATION) == 0) {
+        totp_cli_command_automation_handle(plugin_state, args, cli);
     } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) {
         totp_cli_command_reset_handle(cli, cli_context->event_queue);
     } else {

+ 1 - 1
cli/commands/add/add.c

@@ -62,7 +62,7 @@ void totp_cli_command_add_docopt_usage() {
 }
 
 void totp_cli_command_add_docopt_arguments() {
-    TOTP_CLI_PRINTF("  " TOTP_CLI_COMMAND_ADD_ARG_NAME "        Token name\r\n");
+    TOTP_CLI_PRINTF("  " TOTP_CLI_COMMAND_ADD_ARG_NAME "          Token name\r\n");
 }
 
 void totp_cli_command_add_docopt_options() {

+ 133 - 0
cli/commands/automation/automation.c

@@ -0,0 +1,133 @@
+#include "automation.h"
+#include <lib/toolbox/args.h>
+#include "../../../services/config/config.h"
+#include "../../../ui/scene_director.h"
+#include "../../cli_helpers.h"
+
+#define TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD "automation"
+#define TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE "none"
+#define TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB "usb"
+#ifdef TOTP_BADBT_TYPE_ENABLED
+#define TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT "bt"
+#endif
+
+void totp_cli_command_automation_docopt_commands() {
+    TOTP_CLI_PRINTF("  " TOTP_CLI_COMMAND_AUTOMATION "       Get or set automation method\r\n");
+}
+
+void totp_cli_command_automation_docopt_usage() {
+    TOTP_CLI_PRINTF("  " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_AUTOMATION " " DOCOPT_OPTIONAL(
+        DOCOPT_MULTIPLE(DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD))) "\r\n");
+}
+
+void totp_cli_command_automation_docopt_arguments() {
+    TOTP_CLI_PRINTF(
+        "  " TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD
+        "    Automation method to be set. Must be one of [" TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE
+        ", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB
+#ifdef TOTP_BADBT_TYPE_ENABLED
+        ", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT
+#endif
+        "]\r\n");
+}
+
+static void totp_cli_command_automation_print_method(AutomationMethod method, char* color) {
+#ifdef TOTP_BADBT_TYPE_ENABLED
+    bool has_previous_method = false;
+#endif
+    if(method & AutomationMethodBadUsb) {
+        TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB "\"");
+#ifdef TOTP_BADBT_TYPE_ENABLED
+        has_previous_method = true;
+#endif
+    }
+
+#ifdef TOTP_BADBT_TYPE_ENABLED
+    if(method & AutomationMethodBadBt) {
+        if(has_previous_method) {
+            TOTP_CLI_PRINTF_COLORFUL(color, " and ");
+        }
+
+        TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT "\"");
+    }
+#endif
+
+    if(method == AutomationMethodNone) {
+        TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE "\"");
+    }
+}
+
+void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {
+    if(!totp_cli_ensure_authenticated(plugin_state, cli)) {
+        return;
+    }
+
+    FuriString* temp_str = furi_string_alloc();
+    bool new_method_provided = false;
+    AutomationMethod new_method = AutomationMethodNone;
+    bool args_valid = true;
+    while(args_read_string_and_trim(args, temp_str)) {
+        if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE) == 0) {
+            new_method_provided = true;
+            new_method = AutomationMethodNone;
+        } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB) == 0) {
+            new_method_provided = true;
+            new_method |= AutomationMethodBadUsb;
+        }
+#ifdef TOTP_BADBT_TYPE_ENABLED
+        else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT) == 0) {
+            new_method_provided = true;
+            new_method |= AutomationMethodBadBt;
+        }
+#endif
+        else {
+            args_valid = false;
+            break;
+        }
+    }
+
+    do {
+        if(!args_valid) {
+            TOTP_CLI_PRINT_INVALID_ARGUMENTS();
+            break;
+        }
+
+        if(new_method_provided) {
+            Scene previous_scene = TotpSceneNone;
+            if(plugin_state->current_scene == TotpSceneGenerateToken ||
+               plugin_state->current_scene == TotpSceneAppSettings) {
+                previous_scene = plugin_state->current_scene;
+                totp_scene_director_activate_scene(plugin_state, TotpSceneNone, NULL);
+            }
+
+            plugin_state->automation_method = new_method;
+            if(totp_config_file_update_automation_method(new_method) ==
+               TotpConfigFileUpdateSuccess) {
+                TOTP_CLI_PRINTF_SUCCESS("Automation method is set to ");
+                totp_cli_command_automation_print_method(new_method, TOTP_CLI_COLOR_SUCCESS);
+                cli_nl();
+            } else {
+                TOTP_CLI_PRINT_ERROR_UPDATING_CONFIG_FILE();
+            }
+
+#ifdef TOTP_BADBT_TYPE_ENABLED
+            if(!(new_method & AutomationMethodBadBt) &&
+               plugin_state->bt_type_code_worker_context != NULL) {
+                totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context);
+                plugin_state->bt_type_code_worker_context = NULL;
+            }
+#endif
+
+            if(previous_scene != TotpSceneNone) {
+                totp_scene_director_activate_scene(plugin_state, previous_scene, NULL);
+            }
+        } else {
+            TOTP_CLI_PRINTF_INFO("Current automation method is ");
+            totp_cli_command_automation_print_method(
+                plugin_state->automation_method, TOTP_CLI_COLOR_INFO);
+            cli_nl();
+        }
+    } while(false);
+
+    furi_string_free(temp_str);
+}

+ 11 - 0
cli/commands/automation/automation.h

@@ -0,0 +1,11 @@
+#pragma once
+
+#include <cli/cli.h>
+#include "../../../types/plugin_state.h"
+
+#define TOTP_CLI_COMMAND_AUTOMATION "automation"
+
+void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli);
+void totp_cli_command_automation_docopt_commands();
+void totp_cli_command_automation_docopt_usage();
+void totp_cli_command_automation_docopt_arguments();

+ 1 - 1
cli/commands/delete/delete.c

@@ -24,7 +24,7 @@ void totp_cli_command_delete_docopt_usage() {
 }
 
 void totp_cli_command_delete_docopt_arguments() {
-    TOTP_CLI_PRINTF("  " TOTP_CLI_COMMAND_DELETE_ARG_INDEX "       Token index in the list\r\n");
+    TOTP_CLI_PRINTF("  " TOTP_CLI_COMMAND_DELETE_ARG_INDEX "         Token index in the list\r\n");
 }
 
 void totp_cli_command_delete_docopt_options() {

+ 4 - 0
cli/commands/help/help.c

@@ -8,6 +8,7 @@
 #include "../pin/pin.h"
 #include "../notification/notification.h"
 #include "../reset/reset.h"
+#include "../automation/automation.h"
 
 void totp_cli_command_help_docopt_commands() {
     TOTP_CLI_PRINTF("  " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT
@@ -31,6 +32,7 @@ void totp_cli_command_help_handle() {
     totp_cli_command_pin_docopt_usage();
     totp_cli_command_notification_docopt_usage();
     totp_cli_command_reset_docopt_usage();
+    totp_cli_command_automation_docopt_usage();
     cli_nl();
     TOTP_CLI_PRINTF("Commands:\r\n");
     totp_cli_command_help_docopt_commands();
@@ -42,12 +44,14 @@ void totp_cli_command_help_handle() {
     totp_cli_command_pin_docopt_commands();
     totp_cli_command_notification_docopt_commands();
     totp_cli_command_reset_docopt_commands();
+    totp_cli_command_automation_docopt_commands();
     cli_nl();
     TOTP_CLI_PRINTF("Arguments:\r\n");
     totp_cli_command_add_docopt_arguments();
     totp_cli_command_delete_docopt_arguments();
     totp_cli_command_timezone_docopt_arguments();
     totp_cli_command_notification_docopt_arguments();
+    totp_cli_command_automation_docopt_arguments();
     cli_nl();
     TOTP_CLI_PRINTF("Options:\r\n");
     totp_cli_command_add_docopt_options();

+ 2 - 2
cli/commands/notification/notification.c

@@ -4,7 +4,7 @@
 #include "../../../ui/scene_director.h"
 #include "../../cli_helpers.h"
 
-#define TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD "method"
+#define TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD "notification"
 #define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE "none"
 #define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "sound"
 #define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "vibro"
@@ -23,7 +23,7 @@ void totp_cli_command_notification_docopt_usage() {
 void totp_cli_command_notification_docopt_arguments() {
     TOTP_CLI_PRINTF(
         "  " TOTP_CLI_COMMAND_NOTIFICATION_ARG_METHOD
-        "      Notification method to be set. Must be one of [" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE
+        "  Notification method to be set. Must be one of [" TOTP_CLI_COMMAND_NOTIFICATION_METHOD_NONE
         ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND
         ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "]\r\n");
 }

+ 1 - 1
cli/commands/timezone/timezone.c

@@ -20,7 +20,7 @@ void totp_cli_command_timezone_docopt_usage() {
 
 void totp_cli_command_timezone_docopt_arguments() {
     TOTP_CLI_PRINTF("  " TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE
-                    "    Timezone offset in hours to be set\r\n");
+                    "      Timezone offset in hours to be set\r\n");
 }
 
 void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli) {

+ 2 - 0
features_config.h

@@ -0,0 +1,2 @@
+#define TOTP_BADBT_TYPE_ENABLED
+#define TOTP_BADBT_TYPE_ICON_ENABLED

BIN
images/hid_ble_10x7.png


+ 59 - 0
services/config/config.c

@@ -4,6 +4,7 @@
 #include "../list/list.h"
 #include "../../types/common.h"
 #include "../../types/token_info.h"
+#include "../../features_config.h"
 #include "migrations/config_migration_v1_to_v2.h"
 #include "migrations/config_migration_v2_to_v3.h"
 
@@ -136,6 +137,14 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF
         flipper_format_write_uint32(
             fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1);
 
+        tmp_uint32 = AutomationMethodBadUsb;
+        flipper_format_write_comment_cstr(fff_data_file, " ");
+        flipper_format_write_comment_cstr(
+            fff_data_file,
+            "Automation method (0 - None, 1 - BadUSB, 2 - BadBT, 3 - BadUSB and BadBT)");
+        flipper_format_write_uint32(
+            fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1);
+
         FuriString* temp_str = furi_string_alloc();
 
         flipper_format_write_comment_cstr(fff_data_file, " ");
@@ -329,6 +338,33 @@ TotpConfigFileUpdateResult
     return update_result;
 }
 
+TotpConfigFileUpdateResult
+    totp_config_file_update_automation_method(AutomationMethod new_automation_method) {
+    Storage* cfg_storage = totp_open_storage();
+    FlipperFormat* file;
+    TotpConfigFileUpdateResult update_result;
+
+    if(totp_open_config_file(cfg_storage, &file) == TotpConfigFileOpenSuccess) {
+        do {
+            uint32_t tmp_uint32 = new_automation_method;
+            if(!flipper_format_insert_or_update_uint32(
+                   file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
+                update_result = TotpConfigFileUpdateError;
+                break;
+            }
+
+            update_result = TotpConfigFileUpdateSuccess;
+        } while(false);
+
+        totp_close_config_file(file);
+    } else {
+        update_result = TotpConfigFileUpdateError;
+    }
+
+    totp_close_storage();
+    return update_result;
+}
+
 TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginState* plugin_state) {
     Storage* cfg_storage = totp_open_storage();
     FlipperFormat* file;
@@ -347,6 +383,13 @@ TotpConfigFileUpdateResult totp_config_file_update_user_settings(const PluginSta
                 break;
             }
 
+            tmp_uint32 = plugin_state->automation_method;
+            if(!flipper_format_insert_or_update_uint32(
+                   file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
+                update_result = TotpConfigFileUpdateError;
+                break;
+            }
+
             update_result = TotpConfigFileUpdateSuccess;
         } while(false);
 
@@ -409,6 +452,13 @@ TotpConfigFileUpdateResult totp_full_save_config_file(const PluginState* const p
             break;
         }
 
+        tmp_uint32 = plugin_state->automation_method;
+        if(!flipper_format_write_uint32(
+               fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
+            result = TotpConfigFileUpdateError;
+            break;
+        }
+
         bool tokens_written = true;
         TOTP_LIST_FOREACH(plugin_state->tokens_list, node, {
             const TokenInfo* token_info = node->data;
@@ -594,6 +644,15 @@ TotpConfigFileOpenResult totp_config_file_load_base(PluginState* const plugin_st
         }
 
         plugin_state->notification_method = tmp_uint32;
+
+        flipper_format_rewind(fff_data_file);
+
+        if(!flipper_format_read_uint32(
+               fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) {
+            tmp_uint32 = AutomationMethodBadUsb;
+        }
+
+        plugin_state->automation_method = tmp_uint32;
     } while(false);
 
     furi_string_free(temp_str);

+ 8 - 0
services/config/config.h

@@ -103,6 +103,14 @@ TotpConfigFileUpdateResult totp_config_file_update_timezone_offset(float new_tim
 TotpConfigFileUpdateResult
     totp_config_file_update_notification_method(NotificationMethod new_notification_method);
 
+/**
+ * @brief Updates automation method in an application config file
+ * @param new_automation_method new automation method to be set
+ * @return Config file update result
+ */
+TotpConfigFileUpdateResult
+    totp_config_file_update_automation_method(AutomationMethod new_automation_method);
+
 /**
  * @brief Updates application user settings
  * @param plugin_state application state

+ 1 - 0
services/config/constants.h

@@ -13,6 +13,7 @@
 #define TOTP_CONFIG_KEY_BASE_IV "BaseIV"
 #define TOTP_CONFIG_KEY_PINSET "PinIsSet"
 #define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod"
+#define TOTP_CONFIG_KEY_AUTOMATION_METHOD "AutomationMethod"
 
 #define TOTP_CONFIG_TOKEN_ALGO_SHA1_NAME "sha1"
 #define TOTP_CONFIG_TOKEN_ALGO_SHA256_NAME "sha256"

+ 19 - 3
totp_app.c

@@ -8,6 +8,7 @@
 #include <notification/notification.h>
 #include <notification/notification_messages.h>
 #include <dolphin/dolphin.h>
+#include "features_config.h"
 #include "services/config/config.h"
 #include "types/plugin_state.h"
 #include "types/token_info.h"
@@ -25,7 +26,7 @@
 static void render_callback(Canvas* const canvas, void* ctx) {
     furi_assert(ctx);
     PluginState* plugin_state = ctx;
-    if (furi_mutex_acquire(plugin_state->mutex, 25) == FuriStatusOk) {
+    if(furi_mutex_acquire(plugin_state->mutex, 25) == FuriStatusOk) {
         totp_scene_director_render(canvas, plugin_state);
         furi_mutex_release(plugin_state->mutex);
     }
@@ -103,11 +104,19 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) {
     }
 
     plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
-    if (plugin_state->mutex == NULL) {
+    if(plugin_state->mutex == NULL) {
         FURI_LOG_E(LOGGING_TAG, "Cannot create mutex\r\n");
         return false;
     }
 
+#ifdef TOTP_BADBT_TYPE_ENABLED
+    if(plugin_state->automation_method & AutomationMethodBadBt) {
+        plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init();
+    } else {
+        plugin_state->bt_type_code_worker_context = NULL;
+    }
+#endif
+
     return true;
 }
 
@@ -130,6 +139,13 @@ static void totp_plugin_state_free(PluginState* plugin_state) {
         free(plugin_state->crypto_verify_data);
     }
 
+#ifdef TOTP_BADBT_TYPE_ENABLED
+    if(plugin_state->bt_type_code_worker_context != NULL) {
+        totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context);
+        plugin_state->bt_type_code_worker_context = NULL;
+    }
+#endif
+
     furi_mutex_free(plugin_state->mutex);
     free(plugin_state);
 }
@@ -170,7 +186,7 @@ int32_t totp_app() {
     while(processing) {
         FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
 
-        if (furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) == FuriStatusOk) {
+        if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) == FuriStatusOk) {
             if(event_status == FuriStatusOk) {
                 if(event.type == EventTypeKey) {
                     last_user_interaction_time = furi_get_tick();

+ 13 - 0
types/automation_method.h

@@ -0,0 +1,13 @@
+#pragma once
+
+#include "../features_config.h"
+
+typedef uint8_t AutomationMethod;
+
+enum AutomationMethods {
+    AutomationMethodNone = 0b00,
+    AutomationMethodBadUsb = 0b01,
+#ifdef TOTP_BADBT_TYPE_ENABLED
+    AutomationMethodBadBt = 0b10,
+#endif
+};

+ 17 - 0
types/plugin_state.h

@@ -3,9 +3,14 @@
 #include <notification/notification.h>
 #include <gui/gui.h>
 #include <dialogs/dialogs.h>
+#include "../features_config.h"
 #include "../lib/list/list.h"
 #include "../ui/totp_scenes_enum.h"
 #include "notification_method.h"
+#include "automation_method.h"
+#ifdef TOTP_BADBT_TYPE_ENABLED
+#include "../workers/bt_type_code/bt_type_code.h"
+#endif
 
 #define TOTP_IV_SIZE 16
 
@@ -92,4 +97,16 @@ typedef struct {
      * @brief Main rendering loop mutex
      */
     FuriMutex* mutex;
+
+    /**
+     * @brief Automation method
+     */
+    AutomationMethod automation_method;
+
+#ifdef TOTP_BADBT_TYPE_ENABLED
+    /**
+     * @brief Bad-Bluetooth worker context
+     */
+    TotpBtTypeCodeWorkerContext* bt_type_code_worker_context;
+#endif
 } PluginState;

+ 3 - 1
ui/scene_director.c

@@ -37,7 +37,9 @@ void totp_scene_director_activate_scene(
 }
 
 void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state) {
-    switch(plugin_state->current_scene) {
+    Scene current_scene = plugin_state->current_scene;
+    plugin_state->current_scene = TotpSceneNone;
+    switch(current_scene) {
     case TotpSceneGenerateToken:
         totp_scene_generate_token_deactivate(plugin_state);
         break;

+ 96 - 8
ui/scenes/app_settings/totp_app_settings.c

@@ -10,16 +10,35 @@
 #include "../../../services/convert/convert.h"
 #include "../../../lib/roll_value/roll_value.h"
 #include "../../../types/nullable.h"
+#include "../../../features_config.h"
+#ifdef TOTP_BADBT_TYPE_ENABLED
+#include "../../../workers/bt_type_code/bt_type_code.h"
+#endif
 
 char* YES_NO_LIST[] = {"NO", "YES"};
+char* ON_OFF_LIST[] = {"OFF", "ON"};
 
-typedef enum { HoursInput, MinutesInput, Sound, Vibro, ConfirmButton } Control;
+typedef enum {
+    HoursInput,
+    MinutesInput,
+    Sound,
+    Vibro,
+    BadUsb,
+#ifdef TOTP_BADBT_TYPE_ENABLED
+    BadBt,
+#endif
+    ConfirmButton
+} Control;
 
 typedef struct {
     int8_t tz_offset_hours;
     uint8_t tz_offset_minutes;
     bool notification_sound;
     bool notification_vibro;
+    bool badusb_enabled;
+#ifdef TOTP_BADBT_TYPE_ENABLED
+    bool badbt_enabled;
+#endif
     uint8_t y_offset;
     TotpNullable_uint16_t current_token_index;
     Control selected_control;
@@ -47,6 +66,10 @@ void totp_scene_app_settings_activate(
     scene_state->tz_offset_minutes = 60.0f * off_dec;
     scene_state->notification_sound = plugin_state->notification_method & NotificationMethodSound;
     scene_state->notification_vibro = plugin_state->notification_method & NotificationMethodVibro;
+    scene_state->badusb_enabled = plugin_state->automation_method & AutomationMethodBadUsb;
+#ifdef TOTP_BADBT_TYPE_ENABLED
+    scene_state->badbt_enabled = plugin_state->automation_method & AutomationMethodBadBt;
+#endif
 }
 
 static void two_digit_to_str(int8_t num, char* str) {
@@ -73,7 +96,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
 
     char tmp_str[4];
     two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]);
-    canvas_draw_str_aligned(canvas, 0, 16 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:");
+    canvas_draw_str_aligned(canvas, 0, 17 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:");
     ui_control_select_render(
         canvas,
         36,
@@ -84,7 +107,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
 
     two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]);
     canvas_draw_str_aligned(
-        canvas, 0, 34 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:");
+        canvas, 0, 35 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:");
     ui_control_select_render(
         canvas,
         36,
@@ -104,7 +127,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
         canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications");
     canvas_set_font(canvas, FontSecondary);
 
-    canvas_draw_str_aligned(canvas, 0, 80 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:");
+    canvas_draw_str_aligned(canvas, 0, 81 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:");
     ui_control_select_render(
         canvas,
         36,
@@ -113,7 +136,7 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
         YES_NO_LIST[scene_state->notification_sound],
         scene_state->selected_control == Sound);
 
-    canvas_draw_str_aligned(canvas, 0, 98 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:");
+    canvas_draw_str_aligned(canvas, 0, 99 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:");
     ui_control_select_render(
         canvas,
         36,
@@ -122,10 +145,43 @@ void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plu
         YES_NO_LIST[scene_state->notification_vibro],
         scene_state->selected_control == Vibro);
 
+    canvas_draw_icon(
+        canvas, SCREEN_WIDTH_CENTER - 5, 123 - scene_state->y_offset, &I_totp_arrow_bottom_10x5);
+
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str_aligned(
+        canvas, 0, 128 - scene_state->y_offset, AlignLeft, AlignTop, "Automation");
+    canvas_set_font(canvas, FontSecondary);
+
+    canvas_draw_str_aligned(
+        canvas, 0, 145 - scene_state->y_offset, AlignLeft, AlignTop, "BadUSB:");
+    ui_control_select_render(
+        canvas,
+        36,
+        138 - scene_state->y_offset,
+        SCREEN_WIDTH - 36,
+        ON_OFF_LIST[scene_state->badusb_enabled],
+        scene_state->selected_control == BadUsb);
+
+#ifdef TOTP_BADBT_TYPE_ENABLED
+    canvas_draw_str_aligned(canvas, 0, 163 - scene_state->y_offset, AlignLeft, AlignTop, "BadBT:");
+    ui_control_select_render(
+        canvas,
+        36,
+        156 - scene_state->y_offset,
+        SCREEN_WIDTH - 36,
+        ON_OFF_LIST[scene_state->badbt_enabled],
+        scene_state->selected_control == BadBt);
+#endif
+
     ui_control_button_render(
         canvas,
         SCREEN_WIDTH_CENTER - 24,
-        115 - scene_state->y_offset,
+#ifdef TOTP_BADBT_TYPE_ENABLED
+        178 - scene_state->y_offset,
+#else
+        165 - scene_state->y_offset,
+#endif
         48,
         13,
         "Confirm",
@@ -152,7 +208,9 @@ bool totp_scene_app_settings_handle_event(
             HoursInput,
             ConfirmButton,
             RollOverflowBehaviorStop);
-        if(scene_state->selected_control > MinutesInput) {
+        if(scene_state->selected_control > Vibro) {
+            scene_state->y_offset = 128;
+        } else if(scene_state->selected_control > MinutesInput) {
             scene_state->y_offset = 64;
         } else {
             scene_state->y_offset = 0;
@@ -161,7 +219,9 @@ bool totp_scene_app_settings_handle_event(
     case InputKeyDown:
         totp_roll_value_uint8_t(
             &scene_state->selected_control, 1, HoursInput, ConfirmButton, RollOverflowBehaviorStop);
-        if(scene_state->selected_control > MinutesInput) {
+        if(scene_state->selected_control > Vibro) {
+            scene_state->y_offset = 128;
+        } else if(scene_state->selected_control > MinutesInput) {
             scene_state->y_offset = 64;
         } else {
             scene_state->y_offset = 0;
@@ -178,7 +238,14 @@ bool totp_scene_app_settings_handle_event(
             scene_state->notification_sound = !scene_state->notification_sound;
         } else if(scene_state->selected_control == Vibro) {
             scene_state->notification_vibro = !scene_state->notification_vibro;
+        } else if(scene_state->selected_control == BadUsb) {
+            scene_state->badusb_enabled = !scene_state->badusb_enabled;
         }
+#ifdef TOTP_BADBT_TYPE_ENABLED
+        else if(scene_state->selected_control == BadBt) {
+            scene_state->badbt_enabled = !scene_state->badbt_enabled;
+        }
+#endif
         break;
     case InputKeyLeft:
         if(scene_state->selected_control == HoursInput) {
@@ -191,7 +258,14 @@ bool totp_scene_app_settings_handle_event(
             scene_state->notification_sound = !scene_state->notification_sound;
         } else if(scene_state->selected_control == Vibro) {
             scene_state->notification_vibro = !scene_state->notification_vibro;
+        } else if(scene_state->selected_control == BadUsb) {
+            scene_state->badusb_enabled = !scene_state->badusb_enabled;
+        }
+#ifdef TOTP_BADBT_TYPE_ENABLED
+        else if(scene_state->selected_control == BadBt) {
+            scene_state->badbt_enabled = !scene_state->badbt_enabled;
         }
+#endif
         break;
     case InputKeyOk:
         if(scene_state->selected_control == ConfirmButton) {
@@ -204,12 +278,26 @@ bool totp_scene_app_settings_handle_event(
                 (scene_state->notification_vibro ? NotificationMethodVibro :
                                                    NotificationMethodNone);
 
+            plugin_state->automation_method =
+                scene_state->badusb_enabled ? AutomationMethodBadUsb : AutomationMethodNone;
+#ifdef TOTP_BADBT_TYPE_ENABLED
+            plugin_state->automation_method |= scene_state->badbt_enabled ? AutomationMethodBadBt :
+                                                                            AutomationMethodNone;
+#endif
+
             if(totp_config_file_update_user_settings(plugin_state) !=
                TotpConfigFileUpdateSuccess) {
                 totp_dialogs_config_updating_error(plugin_state);
                 return false;
             }
 
+#ifdef TOTP_BADBT_TYPE_ENABLED
+            if(!scene_state->badbt_enabled && plugin_state->bt_type_code_worker_context != NULL) {
+                totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context);
+                plugin_state->bt_type_code_worker_context = NULL;
+            }
+#endif
+
             if(!scene_state->current_token_index.is_null) {
                 TokenMenuSceneContext generate_scene_context = {
                     .current_token_index = scene_state->current_token_index.value};

+ 73 - 20
ui/scenes/generate_token/totp_scene_generate_token.c

@@ -14,7 +14,11 @@
 #include "../../../lib/roll_value/roll_value.h"
 #include "../../scene_director.h"
 #include "../token_menu/totp_scene_token_menu.h"
-#include "../../../workers/type_code/type_code.h"
+#include "../../../features_config.h"
+#include "../../../workers/usb_type_code/usb_type_code.h"
+#ifdef TOTP_BADBT_TYPE_ENABLED
+#include "../../../workers/bt_type_code/bt_type_code.h"
+#endif
 
 static const uint8_t PROGRESS_BAR_MARGIN = 3;
 static const uint8_t PROGRESS_BAR_HEIGHT = 4;
@@ -25,9 +29,10 @@ typedef struct {
     bool need_token_update;
     TokenInfo* current_token;
     uint32_t last_token_gen_time;
-    TotpTypeCodeWorkerContext* type_code_worker_context;
+    TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context;
     NotificationMessage const** notification_sequence_new_token;
     NotificationMessage const** notification_sequence_badusb;
+    FuriMutex* last_code_update_sync;
 } SceneState;
 
 static const NotificationSequence*
@@ -72,7 +77,7 @@ static const NotificationSequence*
 }
 
 static const NotificationSequence*
-    get_notification_sequence_badusb(const PluginState* plugin_state, SceneState* scene_state) {
+    get_notification_sequence_automation(const PluginState* plugin_state, SceneState* scene_state) {
     if(scene_state->notification_sequence_badusb == NULL) {
         uint8_t i = 0;
         uint8_t length = 3;
@@ -201,9 +206,28 @@ void totp_scene_generate_token_activate(
     plugin_state->current_scene_state = scene_state;
     FURI_LOG_D(LOGGING_TAG, "Timezone set to: %f", (double)plugin_state->timezone_offset);
     update_totp_params(plugin_state);
-    scene_state->type_code_worker_context = totp_type_code_worker_start();
-    scene_state->type_code_worker_context->string = &scene_state->last_code[0];
-    scene_state->type_code_worker_context->string_length = TOTP_TOKEN_DIGITS_MAX_COUNT + 1;
+
+    scene_state->last_code_update_sync = furi_mutex_alloc(FuriMutexTypeNormal);
+    if(plugin_state->automation_method & AutomationMethodBadUsb) {
+        scene_state->usb_type_code_worker_context = totp_usb_type_code_worker_start(
+            &scene_state->last_code[0],
+            TOTP_TOKEN_DIGITS_MAX_COUNT + 1,
+            scene_state->last_code_update_sync);
+    }
+
+#ifdef TOTP_BADBT_TYPE_ENABLED
+
+    if(plugin_state->automation_method & AutomationMethodBadBt) {
+        if(plugin_state->bt_type_code_worker_context == NULL) {
+            plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init();
+        }
+        totp_bt_type_code_worker_start(
+            plugin_state->bt_type_code_worker_context,
+            &scene_state->last_code[0],
+            TOTP_TOKEN_DIGITS_MAX_COUNT + 1,
+            scene_state->last_code_update_sync);
+    }
+#endif
 }
 
 void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_state) {
@@ -242,8 +266,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
         const TokenInfo* tokenInfo = scene_state->current_token;
 
         if(tokenInfo->token != NULL && tokenInfo->token_length > 0) {
-            furi_mutex_acquire(
-                scene_state->type_code_worker_context->string_sync, FuriWaitForever);
+            furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
             size_t key_length;
             uint8_t* key = totp_crypto_decrypt(
                 tokenInfo->token, tokenInfo->token_length, &plugin_state->iv[0], &key_length);
@@ -262,12 +285,11 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
             memset_s(key, key_length, 0, key_length);
             free(key);
         } else {
-            furi_mutex_acquire(
-                scene_state->type_code_worker_context->string_sync, FuriWaitForever);
+            furi_mutex_acquire(scene_state->last_code_update_sync, FuriWaitForever);
             int_token_to_str(0, scene_state->last_code, tokenInfo->digits);
         }
 
-        furi_mutex_release(scene_state->type_code_worker_context->string_sync);
+        furi_mutex_release(scene_state->last_code_update_sync);
 
         if(is_new_token_time) {
             notification_message(
@@ -327,6 +349,15 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_
         canvas_draw_icon(
             canvas, SCREEN_WIDTH - 9, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9);
     }
+
+#if defined(TOTP_BADBT_TYPE_ENABLED) && defined(TOTP_BADBT_TYPE_ICON_ENABLED)
+    if(plugin_state->automation_method & AutomationMethodBadBt &&
+       plugin_state->bt_type_code_worker_context != NULL &&
+       plugin_state->bt_type_code_worker_context->is_advertising) {
+        canvas_draw_icon(
+            canvas, SCREEN_WIDTH_CENTER - 5, SCREEN_HEIGHT_CENTER + 13, &I_hid_ble_10x7);
+    }
+#endif
 }
 
 bool totp_scene_generate_token_handle_event(
@@ -341,14 +372,27 @@ bool totp_scene_generate_token_handle_event(
     }
 
     SceneState* scene_state;
-    if(event->input.type == InputTypeLong && event->input.key == InputKeyDown) {
-        scene_state = (SceneState*)plugin_state->current_scene_state;
-        totp_type_code_worker_notify(
-            scene_state->type_code_worker_context, TotpTypeCodeWorkerEventType);
-        notification_message(
-            plugin_state->notification_app,
-            get_notification_sequence_badusb(plugin_state, scene_state));
-        return true;
+    if(event->input.type == InputTypeLong) {
+        if(event->input.key == InputKeyDown && plugin_state->automation_method & AutomationMethodBadUsb) {
+            scene_state = (SceneState*)plugin_state->current_scene_state;
+            totp_usb_type_code_worker_notify(
+                scene_state->usb_type_code_worker_context, TotpUsbTypeCodeWorkerEventType);
+            notification_message(
+                plugin_state->notification_app,
+                get_notification_sequence_automation(plugin_state, scene_state));
+            return true;
+        }
+#ifdef TOTP_BADBT_TYPE_ENABLED
+        else if(event->input.key == InputKeyUp && plugin_state->automation_method & AutomationMethodBadBt) {
+            scene_state = (SceneState*)plugin_state->current_scene_state;
+            totp_bt_type_code_worker_notify(
+                plugin_state->bt_type_code_worker_context, TotpBtTypeCodeWorkerEventType);
+            notification_message(
+                plugin_state->notification_app,
+                get_notification_sequence_automation(plugin_state, scene_state));
+            return true;
+        }
+#endif
     }
 
     if(event->input.type != InputTypePress && event->input.type != InputTypeRepeat) {
@@ -400,7 +444,14 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) {
     if(plugin_state->current_scene_state == NULL) return;
     SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
 
-    totp_type_code_worker_stop(scene_state->type_code_worker_context);
+    if(plugin_state->automation_method & AutomationMethodBadUsb) {
+        totp_usb_type_code_worker_stop(scene_state->usb_type_code_worker_context);
+    }
+#ifdef TOTP_BADBT_TYPE_ENABLED
+    if(plugin_state->automation_method & AutomationMethodBadBt) {
+        totp_bt_type_code_worker_stop(plugin_state->bt_type_code_worker_context);
+    }
+#endif
 
     if(scene_state->notification_sequence_new_token != NULL) {
         free(scene_state->notification_sequence_new_token);
@@ -410,6 +461,8 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) {
         free(scene_state->notification_sequence_badusb);
     }
 
+    furi_mutex_free(scene_state->last_code_update_sync);
+
     free(scene_state);
     plugin_state->current_scene_state = NULL;
 }

+ 141 - 0
workers/bt_type_code/bt_type_code.c

@@ -0,0 +1,141 @@
+#include "bt_type_code.h"
+#include <furi_hal_bt_hid.h>
+#include <storage/storage.h>
+#include "../../types/common.h"
+#include "../../services/convert/convert.h"
+#include "../constants.h"
+
+#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("authenticator/.bt_hid.keys")
+
+static inline bool totp_type_code_worker_stop_requested() {
+    return furi_thread_flags_get() & TotpBtTypeCodeWorkerEventStop;
+}
+
+static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context) {
+    uint8_t i = 0;
+    do {
+        furi_delay_ms(500);
+        i++;
+    } while(!furi_hal_bt_is_active() && i < 100 && !totp_type_code_worker_stop_requested());
+
+    if(furi_hal_bt_is_active() && furi_mutex_acquire(context->string_sync, 500) == FuriStatusOk) {
+        furi_delay_ms(500);
+        i = 0;
+        while(i < context->string_length && context->string[i] != 0) {
+            uint8_t digit = CONVERT_CHAR_TO_DIGIT(context->string[i]);
+            if(digit > 9) break;
+            uint8_t hid_kb_key = hid_number_keys[digit];
+            furi_hal_bt_hid_kb_press(hid_kb_key);
+            furi_delay_ms(30);
+            furi_hal_bt_hid_kb_release(hid_kb_key);
+            i++;
+        }
+
+        furi_mutex_release(context->string_sync);
+    }
+}
+
+static int32_t totp_type_code_worker_callback(void* context) {
+    furi_assert(context);
+    FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+    if(context_mutex == NULL) {
+        return 251;
+    }
+
+    TotpBtTypeCodeWorkerContext* bt_context = context;
+
+    furi_hal_bt_start_advertising();
+    bt_context->is_advertising = true;
+
+    while(true) {
+        uint32_t flags = furi_thread_flags_wait(
+            TotpBtTypeCodeWorkerEventStop | TotpBtTypeCodeWorkerEventType,
+            FuriFlagWaitAny,
+            FuriWaitForever);
+        furi_check((flags & FuriFlagError) == 0); //-V562
+        if(flags & TotpBtTypeCodeWorkerEventStop) break;
+
+        if(furi_mutex_acquire(context_mutex, FuriWaitForever) == FuriStatusOk) {
+            if(flags & TotpBtTypeCodeWorkerEventType) {
+                totp_type_code_worker_type_code(bt_context);
+            }
+
+            furi_mutex_release(context_mutex);
+        }
+    }
+
+    furi_hal_bt_stop_advertising();
+
+    bt_context->is_advertising = false;
+
+    furi_mutex_free(context_mutex);
+
+    return 0;
+}
+
+void totp_bt_type_code_worker_start(
+    TotpBtTypeCodeWorkerContext* context,
+    char* code_buf,
+    uint8_t code_buf_length,
+    FuriMutex* code_buf_update_sync) {
+    furi_assert(context != NULL);
+    context->string = code_buf;
+    context->string_length = code_buf_length;
+    context->string_sync = code_buf_update_sync;
+    context->thread = furi_thread_alloc();
+    furi_thread_set_name(context->thread, "TOTPBtHidWorker");
+    furi_thread_set_stack_size(context->thread, 1024);
+    furi_thread_set_context(context->thread, context);
+    furi_thread_set_callback(context->thread, totp_type_code_worker_callback);
+    furi_thread_start(context->thread);
+}
+
+void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context) {
+    furi_assert(context != NULL);
+    furi_thread_flags_set(furi_thread_get_id(context->thread), TotpBtTypeCodeWorkerEventStop);
+    furi_thread_join(context->thread);
+    furi_thread_free(context->thread);
+    context->thread = NULL;
+}
+
+void totp_bt_type_code_worker_notify(
+    TotpBtTypeCodeWorkerContext* context,
+    TotpBtTypeCodeWorkerEvent event) {
+    furi_assert(context != NULL);
+    furi_thread_flags_set(furi_thread_get_id(context->thread), event);
+}
+
+TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init() {
+    TotpBtTypeCodeWorkerContext* context = malloc(sizeof(TotpBtTypeCodeWorkerContext));
+    furi_check(context != NULL);
+
+    context->bt = furi_record_open(RECORD_BT);
+    context->is_advertising = false;
+    bt_disconnect(context->bt);
+    furi_delay_ms(200);
+    bt_keys_storage_set_storage_path(context->bt, HID_BT_KEYS_STORAGE_PATH);
+    if(!bt_set_profile(context->bt, BtProfileHidKeyboard)) {
+        FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to keyboard HID profile");
+    }
+
+    return context;
+}
+
+void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) {
+    furi_assert(context != NULL);
+
+    if(context->thread != NULL) {
+        totp_bt_type_code_worker_stop(context);
+    }
+
+    bt_disconnect(context->bt);
+    bt_keys_storage_set_default_path(context->bt);
+
+    if(!bt_set_profile(context->bt, BtProfileSerial)) {
+        FURI_LOG_E(LOGGING_TAG, "Failed to switch BT to Serial profile");
+    }
+    furi_record_close(RECORD_BT);
+    context->bt = NULL;
+
+    free(context);
+}

+ 35 - 0
workers/bt_type_code/bt_type_code.h

@@ -0,0 +1,35 @@
+#pragma once
+
+#include <stdlib.h>
+#include <furi/furi.h>
+#include <furi_hal.h>
+#include <bt/bt_service/bt.h>
+
+typedef uint8_t TotpBtTypeCodeWorkerEvent;
+
+typedef struct {
+    char* string;
+    uint8_t string_length;
+    FuriThread* thread;
+    FuriMutex* string_sync;
+    Bt* bt;
+    bool is_advertising;
+} TotpBtTypeCodeWorkerContext;
+
+enum TotpBtTypeCodeWorkerEvents {
+    TotpBtTypeCodeWorkerEventReserved = (1 << 0),
+    TotpBtTypeCodeWorkerEventStop = (1 << 1),
+    TotpBtTypeCodeWorkerEventType = (1 << 2)
+};
+
+TotpBtTypeCodeWorkerContext* totp_bt_type_code_worker_init();
+void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context);
+void totp_bt_type_code_worker_start(
+    TotpBtTypeCodeWorkerContext* context,
+    char* code_buf,
+    uint8_t code_buf_length,
+    FuriMutex* code_buf_update_sync);
+void totp_bt_type_code_worker_stop(TotpBtTypeCodeWorkerContext* context);
+void totp_bt_type_code_worker_notify(
+    TotpBtTypeCodeWorkerContext* context,
+    TotpBtTypeCodeWorkerEvent event);

+ 14 - 0
workers/constants.c

@@ -0,0 +1,14 @@
+#include "constants.h"
+#include <furi_hal.h>
+
+const uint8_t hid_number_keys[10] = {
+    HID_KEYBOARD_0,
+    HID_KEYBOARD_1,
+    HID_KEYBOARD_2,
+    HID_KEYBOARD_3,
+    HID_KEYBOARD_4,
+    HID_KEYBOARD_5,
+    HID_KEYBOARD_6,
+    HID_KEYBOARD_7,
+    HID_KEYBOARD_8,
+    HID_KEYBOARD_9};

+ 4 - 0
workers/constants.h

@@ -0,0 +1,4 @@
+#pragma once
+#include <stdint.h>
+
+extern const uint8_t hid_number_keys[10];

+ 0 - 27
workers/type_code/type_code.h

@@ -1,27 +0,0 @@
-#pragma once
-
-#include <stdlib.h>
-#include <furi/furi.h>
-#include <furi_hal.h>
-
-typedef uint8_t TotpTypeCodeWorkerEvent;
-
-typedef struct {
-    char* string;
-    uint8_t string_length;
-    FuriThread* thread;
-    FuriMutex* string_sync;
-    FuriHalUsbInterface* usb_mode_prev;
-} TotpTypeCodeWorkerContext;
-
-enum TotpTypeCodeWorkerEvents {
-    TotpTypeCodeWorkerEventReserved = (1 << 0),
-    TotpTypeCodeWorkerEventStop = (1 << 1),
-    TotpTypeCodeWorkerEventType = (1 << 2)
-};
-
-TotpTypeCodeWorkerContext* totp_type_code_worker_start();
-void totp_type_code_worker_stop(TotpTypeCodeWorkerContext* context);
-void totp_type_code_worker_notify(
-    TotpTypeCodeWorkerContext* context,
-    TotpTypeCodeWorkerEvent event);

+ 24 - 30
workers/type_code/type_code.c → workers/usb_type_code/usb_type_code.c

@@ -1,19 +1,8 @@
-#include "type_code.h"
+#include "usb_type_code.h"
 #include "../../services/convert/convert.h"
+#include "../constants.h"
 
-static const uint8_t hid_number_keys[10] = {
-    HID_KEYBOARD_0,
-    HID_KEYBOARD_1,
-    HID_KEYBOARD_2,
-    HID_KEYBOARD_3,
-    HID_KEYBOARD_4,
-    HID_KEYBOARD_5,
-    HID_KEYBOARD_6,
-    HID_KEYBOARD_7,
-    HID_KEYBOARD_8,
-    HID_KEYBOARD_9};
-
-static void totp_type_code_worker_restore_usb_mode(TotpTypeCodeWorkerContext* context) {
+static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) {
     if(context->usb_mode_prev != NULL) {
         furi_hal_usb_set_config(context->usb_mode_prev, NULL);
         context->usb_mode_prev = NULL;
@@ -21,10 +10,10 @@ static void totp_type_code_worker_restore_usb_mode(TotpTypeCodeWorkerContext* co
 }
 
 static inline bool totp_type_code_worker_stop_requested() {
-    return furi_thread_flags_get() & TotpTypeCodeWorkerEventStop;
+    return furi_thread_flags_get() & TotpUsbTypeCodeWorkerEventStop;
 }
 
-static void totp_type_code_worker_type_code(TotpTypeCodeWorkerContext* context) {
+static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* context) {
     context->usb_mode_prev = furi_hal_usb_get_config();
     furi_hal_usb_unlock();
     furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true);
@@ -57,6 +46,7 @@ static void totp_type_code_worker_type_code(TotpTypeCodeWorkerContext* context)
 }
 
 static int32_t totp_type_code_worker_callback(void* context) {
+    furi_assert(context);
     FuriMutex* context_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
     if(context_mutex == NULL) {
         return 251;
@@ -64,14 +54,14 @@ static int32_t totp_type_code_worker_callback(void* context) {
 
     while(true) {
         uint32_t flags = furi_thread_flags_wait(
-            TotpTypeCodeWorkerEventStop | TotpTypeCodeWorkerEventType,
+            TotpUsbTypeCodeWorkerEventStop | TotpUsbTypeCodeWorkerEventType,
             FuriFlagWaitAny,
             FuriWaitForever);
         furi_check((flags & FuriFlagError) == 0); //-V562
-        if(flags & TotpTypeCodeWorkerEventStop) break;
+        if(flags & TotpUsbTypeCodeWorkerEventStop) break;
 
-        if (furi_mutex_acquire(context_mutex, FuriWaitForever) == FuriStatusOk) {
-            if(flags & TotpTypeCodeWorkerEventType) {
+        if(furi_mutex_acquire(context_mutex, FuriWaitForever) == FuriStatusOk) {
+            if(flags & TotpUsbTypeCodeWorkerEventType) {
                 totp_type_code_worker_type_code(context);
             }
 
@@ -84,13 +74,18 @@ static int32_t totp_type_code_worker_callback(void* context) {
     return 0;
 }
 
-TotpTypeCodeWorkerContext* totp_type_code_worker_start() {
-    TotpTypeCodeWorkerContext* context = malloc(sizeof(TotpTypeCodeWorkerContext));
+TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start(
+    char* code_buf,
+    uint8_t code_buf_length,
+    FuriMutex* code_buf_update_sync) {
+    TotpUsbTypeCodeWorkerContext* context = malloc(sizeof(TotpUsbTypeCodeWorkerContext));
     furi_check(context != NULL);
-    context->string_sync = furi_mutex_alloc(FuriMutexTypeNormal);
+    context->string = code_buf;
+    context->string_length = code_buf_length;
+    context->string_sync = code_buf_update_sync;
     context->thread = furi_thread_alloc();
     context->usb_mode_prev = NULL;
-    furi_thread_set_name(context->thread, "TOTPHidWorker");
+    furi_thread_set_name(context->thread, "TOTPUsbHidWorker");
     furi_thread_set_stack_size(context->thread, 1024);
     furi_thread_set_context(context->thread, context);
     furi_thread_set_callback(context->thread, totp_type_code_worker_callback);
@@ -98,19 +93,18 @@ TotpTypeCodeWorkerContext* totp_type_code_worker_start() {
     return context;
 }
 
-void totp_type_code_worker_stop(TotpTypeCodeWorkerContext* context) {
+void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context) {
     furi_assert(context != NULL);
-    furi_thread_flags_set(furi_thread_get_id(context->thread), TotpTypeCodeWorkerEventStop);
+    furi_thread_flags_set(furi_thread_get_id(context->thread), TotpUsbTypeCodeWorkerEventStop);
     furi_thread_join(context->thread);
     furi_thread_free(context->thread);
-    furi_mutex_free(context->string_sync);
     totp_type_code_worker_restore_usb_mode(context);
     free(context);
 }
 
-void totp_type_code_worker_notify(
-    TotpTypeCodeWorkerContext* context,
-    TotpTypeCodeWorkerEvent event) {
+void totp_usb_type_code_worker_notify(
+    TotpUsbTypeCodeWorkerContext* context,
+    TotpUsbTypeCodeWorkerEvent event) {
     furi_assert(context != NULL);
     furi_thread_flags_set(furi_thread_get_id(context->thread), event);
 }

+ 30 - 0
workers/usb_type_code/usb_type_code.h

@@ -0,0 +1,30 @@
+#pragma once
+
+#include <stdlib.h>
+#include <furi/furi.h>
+#include <furi_hal.h>
+
+typedef uint8_t TotpUsbTypeCodeWorkerEvent;
+
+typedef struct {
+    char* string;
+    uint8_t string_length;
+    FuriThread* thread;
+    FuriMutex* string_sync;
+    FuriHalUsbInterface* usb_mode_prev;
+} TotpUsbTypeCodeWorkerContext;
+
+enum TotpUsbTypeCodeWorkerEvents {
+    TotpUsbTypeCodeWorkerEventReserved = (1 << 0),
+    TotpUsbTypeCodeWorkerEventStop = (1 << 1),
+    TotpUsbTypeCodeWorkerEventType = (1 << 2)
+};
+
+TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start(
+    char* code_buf,
+    uint8_t code_buf_length,
+    FuriMutex* code_buf_update_sync);
+void totp_usb_type_code_worker_stop(TotpUsbTypeCodeWorkerContext* context);
+void totp_usb_type_code_worker_notify(
+    TotpUsbTypeCodeWorkerContext* context,
+    TotpUsbTypeCodeWorkerEvent event);